forgotten commit. disabled until egl is adapted.
[AROS-Contrib.git] / regina / stack.c
blobd7a502293462de4d2471d4f89972e901e88a2b56
1 /*
2 * The Regina Rexx Interpreter
3 * Copyright (C) 1992-1994 Anders Christensen <anders@pvv.unit.no>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Concept of implementation:
22 * In this description, the stack is described as growing upwards
24 * The stack has two pointers to the ends of a double-linked list,
25 * which contains entries consisting of just a pointer to the text (in
26 * a struct streng) in addition to pointers to next and previous
27 * entry. Last box' next, and first box' prev is both NIL. The text
28 * pointer points to the text of that line in the stack.
30 * If the text pointer is NIL, then that box in the stack-structure is
31 * just symbolizing a stack buffer mark. The integer variable
32 * 'buffers' contains the current number of buffermarks in the stack.
33 * A buffer is the lines between two buffermarks. The first buffer mark
34 * (for buffer 0) is not marked, but is implicit the bottom of the
35 * stack. The last buffer is the lines between the uppermost
36 * buffermark and the top of the stack.
38 * Initialy all lines are put in buffer 0. When a new buffer is
39 * created, lines pushed lifo is put on top of that buffer, and lines
40 * queued fifo are put in the bottom of that buffer (i.e. not in the
41 * bottom of buffer 0.) Lines pushed on the stack are 'eaten' and
42 * there must not be any referances to them in other parts of the
43 * program after they have been pushed.
45 * When reading lines from the stack, and all lines in the current
46 * buffer has been read, the buffer-mark will be removed, and lines
47 * are read from the buffer underneath. Lines read from the stack must
48 * be free'ed by the routine that popped them from the stack.
50 * When the whole stack is empty, lines are read from standard input.
52 * Buffer 0 contains the lines between the bottom of the stack, and
53 * the first buffermark on the stack, the first buffer, is the lines
54 * between buffermark 1 and buffer mark 2.
56 * When creating a buffer, the value returned will be the number of
57 * buffermarks in the stack after the new buffermark is created.
58 * When destroying buffers, the parameter given will equal the number
59 * of the lowest buffermark to be destroyed (i.e dropbuf 4) will leave
60 * 3 buffermarks on the stack).
62 * A special variable buf is now part of a stack element. It points to
63 * the last buffer' begin or is NULL for buffer 0.
64 * In short:
66 * Overview of internal or temporary queues. Even temporary queues may
67 * contain buffer marks if the content was moved from an internal queue to
68 * a temporary queue.
69 * The firstbox is equivalent to firstline and lastbox to lastline.
70 * The ends of the double linked list are NULL values. p is short for prev,
71 * n for next. b is short for buf. buf is either NULL or points to the element
72 * which is the next buffer mark.
73 * A buffer mark is a StackLine (SL) with a contents-field of NULL.
75 * -->---------->---------->---------->---------->-+
76 * ^ ^ ^ ^ ^ | NULL NULL
77 * b b b b b / b b
78 * | | | | | / | |
79 * p-SL-n <-> p-SL-n <-> p-SL-n <-> p-SL-n <-> p-SL-n <-> p-SL-n <-> p-SL-n
80 * || || ||
81 * firstbox Buffer lastbox
82 * Mark
84 * READER'S END FIFO'S END
85 * LIFO'S END
88 * Possible extentions:
89 * o A new 'hard-buffer', that is not removed when a line is popped
90 * from it while it is empty
93 #include "rexx.h"
94 #include <stdio.h>
95 #include <string.h>
96 #include <assert.h>
98 #ifdef HAVE_UNISTD_H
99 #include <unistd.h>
100 #endif
102 #ifdef HAVE_PROCESS_H
103 # include <process.h>
104 #endif
106 #include "extstack.h"
108 #define NUMBER_QUEUES 100
110 * Queue 0 is SESSION and cannot be added/deleted
113 #define ENSURE_BUFFER(iq) { \
114 /* Ensures that at least one buffer exists for an internal queue. \
115 */ \
116 if ( (iq)->u.i.top == NULL ) \
118 (iq)->u.i.top = (iq)->u.i.bottom = (Buffer *)MallocTSD( sizeof( Buffer ) ) ; \
119 memset( (iq)->u.i.top, 0, sizeof( Buffer ) ) ; \
120 assert( (iq)->u.i.elements == 0 ) ; \
121 assert( (iq)->u.i.buffers == 0 ) ; \
122 (iq)->u.i.elements = 0 ; \
123 (iq)->u.i.buffers = 1 ; \
127 typedef struct { /* stk_tsd: static variables of this module (thread-safe) */
128 #if !defined(NO_EXTERNAL_QUEUES)
129 int initialized ;
130 #endif
132 * The pointer of the current queue points to one of the elements of queue.
134 Queue *current_queue;
135 Queue queue[ NUMBER_QUEUES ] ;
137 * The current queue's name is buffered for a faster access.
139 streng *current_queue_name;
141 * runner is needed to create fresh ids.
143 int runner ;
144 } stk_tsd_t; /* thread-specific but only needed by this module. see
145 * init_stacks
148 #if !defined(NO_EXTERNAL_QUEUES)
149 static int get_socket_details_and_connect( tsd_t *TSD, Queue *target );
150 static int save_parse_queue( tsd_t *TSD, streng *queue, Queue *q,
151 int ignore_name);
152 static Queue *open_external( tsd_t *TSD, const streng *queue, Queue *q,
153 int *rc, int ignore_name, streng **basename );
154 #endif
156 /* init_stacks initializes the module.
157 * Currently, we set up the thread specific data.
158 * The function returns 1 on success, 0 if memory is short.
160 int init_stacks( tsd_t *TSD )
162 stk_tsd_t *st;
164 if (TSD->stk_tsd != NULL)
165 return(1);
167 if ( ( TSD->stk_tsd = MallocTSD( sizeof(stk_tsd_t) ) ) == NULL )
168 return(0);
169 st = (stk_tsd_t *)TSD->stk_tsd;
170 memset( st, 0, sizeof(stk_tsd_t) ); /* correct for all values */
172 * Create the default internal SESSION queue on demand. purge_stacks()
173 * deletes the queuename very often.
175 st->queue[0].type = QisSESSION ;
176 st->queue[0].u.i.isReal = 1 ;
177 st->current_queue = &st->queue[0] ;
178 return(1);
181 /* delete_buffer_content deletes the content of a buffer and resets the
182 * buffer to an empty buffer.
184 static void delete_buffer_content( const tsd_t *TSD, stk_tsd_t *st, Buffer *b )
186 StackLine *p, *h ;
188 /* prepare the deletion and become reentrant in case of exceptions */
189 p = b->top ;
190 b->top = b->bottom = NULL ;
191 b->elements = 0 ;
193 while ( p != NULL ) {
194 h = p ;
195 p = p->lower ;
196 if ( h->contents != NULL )
197 Free_stringTSD( h->contents ) ;
198 FreeTSD( h ) ;
202 /* delete_an_internal_queue deletes the content of an internal queue (including
203 * SESSION) resets the queue to "not used" or "empty" in case of "SESSION".
205 static void delete_an_internal_queue( const tsd_t *TSD, stk_tsd_t *st, Queue *q )
207 Buffer *p, *h ;
208 streng *n ;
209 int type = q->type ;
211 if ( type == QisUnused )
212 return ;
213 assert( ( type == QisInternal ) || ( type == QisSESSION ) ) ;
215 /* prepare the deletion and become reentrant in case of exceptions */
216 p = q->u.i.top ;
217 n = q->u.i.name ;
218 memset( q, 0, sizeof( Queue ) ) ;
219 q->type = ( type == QisSESSION ) ? QisSESSION : QisUnused ;
221 /* delete the name and all content buffers */
222 if ( n != NULL )
223 Free_stringTSD( n ) ;
224 while ( p != NULL ) {
225 h = p ;
226 p = p->lower ;
227 delete_buffer_content( TSD, st, h ) ;
228 FreeTSD( h ) ;
232 /* delete_a_temp_queue cleans up one of the internal stacks by freeing its
233 * content. The queue is reset to "not used".
235 static void delete_a_temp_queue( const tsd_t *TSD, stk_tsd_t *st, Queue *q )
237 Buffer b ;
239 if ( q->type == QisUnused ) /* happens very often */
240 return ;
241 assert( q->type == QisTemp ) ;
243 /* prepare the deletion and become reentrant in case of exceptions */
244 b = q->u.t ;
245 memset( q, 0, sizeof( Queue ) ) ;
246 q->type = QisUnused ;
248 delete_buffer_content( TSD, st, &b ) ;
251 #if !defined(NO_EXTERNAL_QUEUES)
252 /* delete_an_external_queue cleans up one of the external stacks by
253 * disconnecting from the server.
255 static void delete_an_external_queue( const tsd_t *TSD, stk_tsd_t *st, Queue *q )
257 Queue b ;
259 if ( q->type == QisUnused )
260 return ;
261 assert( q->type == QisExternal ) ;
263 /* prepare the deletion and become reentrant in case of exceptions */
264 b = *q ;
265 memset( q, 0, sizeof( Queue ) ) ;
266 q->type = QisUnused ;
268 disconnect_from_rxstack( TSD, &b ) ;
270 #endif
272 /* delete_a_queue cleans up any queue by purging or disconnecting. */
273 static void delete_a_queue( const tsd_t *TSD, stk_tsd_t *st, Queue *q )
275 switch ( q->type )
277 case QisTemp:
278 delete_a_temp_queue( TSD, st, q ) ;
279 break;
281 case QisInternal:
282 case QisSESSION:
283 delete_an_internal_queue( TSD, st, q ) ;
284 break;
286 #if !defined(NO_EXTERNAL_QUEUES)
287 case QisExternal:
288 delete_an_external_queue( TSD, st, q ) ;
289 break;
290 #endif
292 default:
293 assert( q->type == QisUnused ) ;
298 * This routine clears all the internal queues we have made
299 * and if we have a connection to rxstack, disconnects from it
301 void purge_stacks( const tsd_t *TSD )
303 stk_tsd_t *st;
304 int i;
306 st = (stk_tsd_t *)TSD->stk_tsd;
307 for ( i = 0; i < NUMBER_QUEUES; i++ )
308 delete_a_queue( TSD, st, &st->queue[i] );
309 st->current_queue = &st->queue[0];
310 if ( st->current_queue_name != NULL )
311 Free_stringTSD( st->current_queue_name );
312 st->current_queue_name = NULL;
315 static void SetSessionName( const tsd_t *TSD, stk_tsd_t *st )
317 Queue *q = &st->queue[0];
319 assert( q->type == QisSESSION ) ;
320 if ( q->u.i.name == NULL )
322 q->u.i.name = Str_creTSD( "SESSION" ) ;
323 q->u.i.isReal = 1 ;
324 st->current_queue_name = Str_dupTSD( q->u.i.name );
329 * Find the named internal queue - case insensitive
331 static Queue *find_queue( const tsd_t *TSD, stk_tsd_t *st, const streng *queue_name )
333 int i;
335 if ( st->queue[0].u.i.name == NULL )
336 SetSessionName( TSD, st ) ;
337 for ( i = 0; i < NUMBER_QUEUES; i++ ) /* inefficient !! */
339 if ( ( st->queue[i].type != QisInternal )
340 && ( st->queue[i].type != QisSESSION ) )
341 continue ;
343 if ( Str_ccmp( st->queue[i].u.i.name, queue_name ) == 0 )
344 return &st->queue[i] ;
346 return NULL ;
349 /* This function creates a new queue.
350 * The returned queue's type must be overwritten.
352 Queue *find_free_slot( const tsd_t *TSD )
354 int i;
355 stk_tsd_t *st = (stk_tsd_t *)TSD->stk_tsd ;
357 for ( i = 1; i < NUMBER_QUEUES; i++ ) /* never ever select SESSION */
359 if ( st->queue[i].type == QisUnused )
360 return &st->queue[i] ;
362 if ( !TSD->called_from_saa )
363 exiterror( ERR_STORAGE_EXHAUSTED, 0 ); /* message not very helpful */
364 return NULL ;
368 * Glues the content of the temporary stack src to the top buffer of
369 * dst which must not be temporary. is_fifo controls the position of
370 * the glue. is_fifo should reflect the type of insertion in the
371 * temporary buffer.
372 * src is destroyed at the end of the operation.
374 void flush_stack( const tsd_t *TSD, Queue *src, Queue *dst, int is_fifo )
376 #if !defined( NO_EXTERNAL_QUEUES )
377 StackLine *ptr, *h ;
378 #endif
379 stk_tsd_t *st;
381 if ( src == NULL ) /* no temporary stack? may happen */
382 return ;
383 st = (stk_tsd_t *)TSD->stk_tsd;
385 * I think this is never used. Let's verify it. Florian, 06.01.2005
387 fprintf( stderr, "Regina internal error detected in %s, line %u.\n"
388 "Please, send an email to M.Hessling@qut.edu.au.\n",
389 __FILE__, __LINE__ );
391 assert( src->type == QisTemp ) ;
392 assert( dst->type == QisSESSION
393 || dst->type == QisInternal
394 || dst->type == QisExternal ) ;
395 if ( src->u.t.top == NULL )
397 /* nothing in temporary stack to flush */
398 assert( src->u.t.bottom == NULL ) ;
399 return ;
402 #if !defined(NO_EXTERNAL_QUEUES)
403 if ( dst->type == QisExternal )
405 for ( ptr = src->u.t.top; ptr != NULL; )
407 if (is_fifo)
409 queue_line_fifo_to_rxstack( TSD, dst->u.e.socket, ptr->contents ) ;
411 else
413 queue_line_lifo_to_rxstack( TSD, dst->u.e.socket, ptr->contents ) ;
416 h = ptr ;
417 ptr = ptr->lower ;
418 Free_stringTSD( h->contents ) ;
419 FreeTSD( h ) ;
421 src->u.t.top = src->u.t.bottom = NULL ; /* allow safe cleanup */
422 delete_a_temp_queue( TSD, st, src ) ;
423 return ;
425 #endif
427 /* dst->type is QisSESSION or QisInternal, stack it either fifo or lifo */
428 ENSURE_BUFFER( dst ) ;
430 dst->u.i.elements += src->u.t.elements ;
431 if ( is_fifo )
433 GLUE_BUFFER2( &src->u.t, dst->u.i.top ); /* begin of newest buffer */
435 else
437 GLUE_BUFFER1( dst->u.i.top, &src->u.t );
439 delete_a_temp_queue( TSD, st, src ) ;
443 * stack_to_line converts the content of the temporary stack to one blank
444 * delimited line.
445 * An empty string is returned if nothing is in the queue.
447 streng *stack_to_line( const tsd_t *TSD, Queue *q )
449 StackLine *ptr, *h ;
450 unsigned size = 0;
451 char *dest;
452 stk_tsd_t *st;
453 streng *retval;
455 st = (stk_tsd_t *)TSD->stk_tsd;
457 if (q->type == QisUnused )
458 return(nullstringptr());
460 assert( q->type == QisTemp ) ;
462 if ( q->u.t.top == NULL )
464 /* nothing in temporary stack to flush */
465 assert( q->u.t.bottom == NULL ) ;
466 delete_a_temp_queue( TSD, st, q ) ;
467 return(nullstringptr());
470 /* first, count the needed string length. */
471 for ( ptr = q->u.t.top; ptr != NULL; ptr=ptr->lower )
473 size += Str_len(ptr->contents) + 1 ; /* blank is delimiter */
476 retval = Str_makeTSD( size ) ;
477 dest = retval->value ;
479 for ( ptr = q->u.t.top; ptr != NULL; )
481 memcpy( dest, ptr->contents->value, Str_len( ptr->contents ) ) ;
482 dest += Str_len( ptr->contents ) ;
483 *dest++ = ' ';
485 h = ptr ;
486 ptr = ptr->lower ;
487 Free_stringTSD( h->contents ) ;
488 FreeTSD( h ) ;
491 /* strip any blanks at the end of the string. */
492 while ((dest != retval->value) && (dest[-1] == ' '))
493 dest--;
495 /* finally, terminate the string */
496 *dest = '\0'; /* DON'T increment dest */
497 Str_len( retval ) = (int)( dest - retval->value ) ;
499 /* reset the pointers, to signal that temporary stack is empty */
500 q->u.t.top = q->u.t.bottom = NULL ;
501 delete_a_temp_queue( TSD, st, q ) ;
502 return(retval);
506 * The input for a command may be overwritten if both input and output or
507 * input and error are STEM values. A temporary queue is created in this
508 * case to prevent the destruction of an unread value.
509 * fill_input_queue_stem fills up a new queue with a copy of a stems
510 * content. stemname must end with a period and stem0 is the count of lines
511 * in stem.
512 * OPTIMIZING HINT: The function can be rewritten to access just only the
513 * stem leaves.
515 Queue *fill_input_queue_stem(tsd_t *TSD, streng *stemname, int stem0)
517 int i,stemlen = Str_len( stemname ) ;
518 streng *stem, *newstr ;
519 StackLine *line ;
520 Queue *q = find_free_slot( TSD ) ;
522 q->type = QisTemp ;
524 stem = Str_makeTSD(stemlen + 1 + 3*sizeof(int));
525 memcpy(stem->value, stemname->value, stemlen);
527 for (i = 1; i <= stem0; i++)
528 { /* Fetch the value: */
529 stem->len = stemlen + sprintf(stem->value + stemlen, "%d", i);
530 newstr = Str_dupTSD(get_it_anyway_compound(TSD, stem));
532 line = (StackLine *) MallocTSD( sizeof( StackLine ) ) ;
533 line->contents = newstr ;
534 FIFO_LINE( &q->u.t, line ) ;
537 Free_stringTSD(stem);
538 return q ;
542 * The input for a command may be overwritten if both input and output or
543 * input and error are STREAM values. A temporary queue is created in this
544 * case to prevent the destruction of an unread value.
545 * fill_input_queue_stream fills up a new queue with a copy of a streams
546 * content. fileptr must be an opened file stream pointer gotten by
547 * addr_reopen_file() for an input stream.
549 Queue *fill_input_queue_stream( tsd_t *TSD, void *fileptr )
551 StackLine *line;
552 streng *c;
553 Queue *q = find_free_slot( TSD );
555 q->type = QisTemp;
557 for ( ; ; )
558 { /* Fetch the value: */
559 c = addr_io_file( TSD, fileptr, NULL );
560 if ( c == NULL )
561 break;
562 if ( c->len == 0 )
564 Free_stringTSD( c );
565 break;
568 line = (StackLine *) MallocTSD( sizeof( StackLine ) );
569 line->contents = c;
570 FIFO_LINE( &q->u.t, line );
573 return q;
577 * use_external checks whether or not an external queue must be processed.
578 * The different reasons why not to do it are checked.
579 * use_external returns either 0, if queue_name is a local queue name or
580 * the current queue is a local queue, or it returns 1 for an external queue
581 * name or if the current queue is external.
583 static int use_external( const tsd_t *TSD, const streng *queue_name )
585 stk_tsd_t *st = (stk_tsd_t *)TSD->stk_tsd;
587 /* A little bit off topic, but purge_stacks() may kill "SESSION" at the
588 * end of RexxStart() and we need a working "SESSION" always. Not deleting
589 * it will produce "errors" with the tracemem feature.
590 * This function is called at top of each relevant function.
592 if ( st->queue[0].u.i.name == NULL )
593 SetSessionName( TSD, st );
595 #if defined(NO_EXTERNAL_QUEUES)
596 (void)(queue_name);
597 return 0; /* trivial */
598 #else
600 if ( !st->initialized )
603 * Attention, set the current queue type to default. This may be
604 * either internal or external.
606 st->initialized = 1;
607 init_external_queue( TSD );
610 if ( get_options_flag( TSD->currlevel, EXT_INTERNAL_QUEUES ) )
611 return 0; /* user forces a local queue in every case */
612 if ( ( queue_name == NULL ) || ( PSTRENGLEN( queue_name ) == 0 ) )
613 return st->current_queue->type == QisExternal;
615 if ( get_options_flag( TSD->currlevel, EXT_QUEUES_301 ) == 0 )
618 * A name exists, check it.
620 if ( memchr( queue_name->value, '@', Str_len( queue_name ) ) == NULL )
621 return 0;
624 return 1;
625 #endif
629 * Returns 1 if we have started using external queues, 0 if we haven't
630 * started using external queues
632 int external_queues_used( const tsd_t *TSD )
634 return use_external( TSD, NULL ) ;
638 * Pushes 'line' onto the REXX stack, lifo, and sets 'lastline' to
639 * point to the new line. The line is put on top of the current
640 * buffer.
642 * A queue_name is supplied by external reference, only. See IfcAddQueue.
643 * Reuses the current connection if possible.
645 int stack_lifo( tsd_t *TSD, streng *line, const streng *queue_name )
647 StackLine *newbox ;
648 Buffer *b ;
649 stk_tsd_t *st ;
650 Queue *q ;
651 int rc=0;
653 st = (stk_tsd_t *)TSD->stk_tsd;
655 assert( line != NULL ) ;
657 if ( !use_external( TSD, queue_name ) )
659 if ( queue_name )
661 if ( ( q = find_queue( TSD, st, queue_name ) ) == NULL )
663 return 9;
666 else
667 q = st->current_queue;
668 assert( ( q->type == QisSESSION ) || ( q->type == QisInternal ) ) ;
670 newbox = (StackLine *) MallocTSD( sizeof( StackLine ) ) ;
671 newbox->contents = line ;
672 ENSURE_BUFFER( q ) ;
673 b = q->u.i.top ;
674 LIFO_LINE( b, newbox ) ;
675 q->u.i.elements++;
677 #if !defined(NO_EXTERNAL_QUEUES)
678 else
680 Queue q, *work;
682 if ( ( work = open_external( TSD, queue_name, &q, &rc, 0, NULL ) ) == NULL )
683 return rc;
685 if ( ( rc = queue_line_lifo_to_rxstack( TSD, work->u.e.socket, line ) ) == -1 )
686 rc = 100;
687 disconnect_from_rxstack( TSD, &q ) ;
689 #endif
690 return rc;
695 * Puts 'line' on the REXX stack, fifo. This routine is similar to
696 * stack_lifo but the differences are big enough to have a separate
697 * routine. The line is put in the bottom of the current buffer,
698 * that is just above the uppermost buffer mark, or at the bottom
699 * of the stack, if there are no buffer marks.
701 * A queue_name is supplied by external reference, only. See IfcAddQueue.
702 * Reuses the current connection if possible.
704 int stack_fifo( tsd_t *TSD, streng *line, const streng *queue_name )
706 StackLine *newbox ;
707 stk_tsd_t *st;
708 Queue *q;
709 Buffer *b ;
710 int rc=0;
712 st = (stk_tsd_t *)TSD->stk_tsd;
714 assert( line != NULL ) ;
716 if ( !use_external( TSD, queue_name ) )
718 if ( queue_name )
720 if ( ( q = find_queue( TSD, st, queue_name ) ) == NULL )
722 return 9;
725 else
726 q = st->current_queue;
727 assert( ( q->type == QisSESSION ) || ( q->type == QisInternal ) ) ;
729 newbox = (StackLine *) MallocTSD( sizeof( StackLine ) ) ;
730 newbox->contents = line ;
731 ENSURE_BUFFER( q ) ;
732 b = q->u.i.top ;
733 FIFO_LINE( b, newbox ) ;
734 q->u.i.elements++;
736 #if !defined(NO_EXTERNAL_QUEUES)
737 else
739 Queue q, *work;
741 if ( ( work = open_external( TSD, queue_name, &q, &rc, 0, NULL ) ) == NULL )
742 return rc;
744 if ( ( rc = queue_line_fifo_to_rxstack( TSD, work->u.e.socket, line ) ) == -1 )
745 rc = 100;
746 disconnect_from_rxstack( TSD, &q ) ;
748 #endif
749 return rc;
754 * Removes one (or several) buffers from the stack. Number must be a
755 * non-negative number giving the number of first buffer from the bottom
756 * which has to be removed. You can pass a number of buffers to destroy
757 * from top of the stack if you supply the negative number of buffers.
759 * Example: Queue has buffers 0, 1, 2, 3, 4 and the number is 3. Then
760 * the buffers 3 and 4 are removed and 2 becomes the current buffer.
761 * If the number is -2 then the top two buffers are destroyed and
762 * 2 becomes the current buffer.
764 * Return values: -2: specified buffer doesn't exist
765 * -1: external queues don't support buffers
766 * others: remaining buffers after success
769 int drop_buffer( const tsd_t *TSD, int number )
771 Buffer *b, *h ;
772 stk_tsd_t *st;
773 Queue *curr ;
775 st = (stk_tsd_t *)TSD->stk_tsd;
776 curr = st->current_queue ;
777 if ( curr->type == QisExternal )
778 exiterror( ERR_EXTERNAL_QUEUE, 110, "DROPBUF" ) ;
779 assert( ( curr->type == QisInternal ) || ( curr->type == QisSESSION ) ) ;
781 ENSURE_BUFFER( curr ) ; /* makes things easier */
782 if ( number < 0 )
783 number = curr->u.i.buffers + number ;
785 if ( number >= (int) curr->u.i.buffers )
786 return -2 ;
788 for ( b = curr->u.i.bottom; ( number > 0 ) && ( b != NULL ); number-- )
789 b = b->higher ;
791 assert( b ) ; /* must exist */
792 /* disconnect the buffer sequence marked for deletion */
793 if ( b == curr->u.i.bottom )
795 /* preserve en empty buffer */
796 curr->u.i.elements -= b->elements ;
797 delete_buffer_content( TSD, st, b ) ;
798 b = b->higher ;
799 curr->u.i.top = curr->u.i.bottom ;
800 curr->u.i.top->higher = NULL ;
802 else
804 curr->u.i.top = b->lower ;
805 curr->u.i.top->higher = NULL ;
808 /* delete each buffer and its content */
809 while ( b != NULL ) {
810 curr->u.i.elements -= b->elements ;
811 delete_buffer_content( TSD, st, b ) ;
813 h = b ;
814 b = b->higher ;
815 FreeTSD( h ) ;
816 curr->u.i.buffers-- ;
819 return curr->u.i.buffers - 1 ;
824 * Fetches a line from the top of the stack. If the current buffer
825 * does not contain any lines, it is removed and the second current
826 * buffer is search for a line etc. If there isn't any lines in the
827 * stack, a line is read from the standard input.
828 * Reuses the current connection if possible.
830 streng *popline( tsd_t *TSD, const streng *queue_name, int *result, unsigned long waitflag )
832 streng *contents=NULL ;
833 StackLine *line ;
834 stk_tsd_t *st ;
835 int need_line_from_stdin=0;
836 int rc=0;
837 Queue *q ;
838 Buffer *b ;
840 st = (stk_tsd_t *)TSD->stk_tsd;
842 if ( !use_external( TSD, queue_name ) )
844 if ( queue_name )
846 if ( ( q = find_queue( TSD, st, queue_name ) ) == NULL )
848 if ( result )
849 *result = 9;
850 if ( !TSD->called_from_saa )
851 contents = nullstringptr();
852 return contents;
855 else
856 q = st->current_queue;
857 assert( ( q->type == QisSESSION ) || ( q->type == QisInternal ) ) ;
859 line = NULL ;
860 while ( ( b = q->u.i.top ) != NULL )
862 POP_LINE( b, line ) ;
863 if ( line != NULL )
865 q->u.i.elements-- ;
866 break ;
869 /* buffer is empty, fetch the next one and drop the empty one
871 q->u.i.top = b->lower ;
872 if ( q->u.i.top )
873 q->u.i.top->higher = NULL ; /* fixes bug 1068204 */
874 q->u.i.buffers-- ;
875 FreeTSD( b ) ;
877 if ( line != NULL )
879 contents = line->contents ;
880 FreeTSD( line ) ;
882 else
884 /* q->u.i.top == NULL, too; but bottom's not set */
885 q->u.i.bottom = NULL ;
886 assert( q->u.i.elements == 0 ) ;
887 assert( q->u.i.buffers == 0 ) ;
888 need_line_from_stdin = 1;
891 #if !defined(NO_EXTERNAL_QUEUES)
892 else
894 Queue q, *work;
896 if ( ( work = open_external( TSD, queue_name, &q, &rc, 0, NULL ) ) == NULL )
898 if ( result )
899 *result = rc;
900 return NULL;
903 * waitflag is 0 if called from a Rexx program as [PARSE] PULL, but can be
904 * 1 when called from the Rexx API. We override the waitflag when the current
905 * queue is external and a timeout has been set for the queue
907 if ( work->u.e.timeoutSet )
908 waitflag = 1;
909 rc = get_line_from_rxstack( TSD, work->u.e.socket, &contents, ( waitflag == 0 ) );
910 switch ( rc )
912 case -1: rc = 100; break;
913 case RXSTACK_ERROR: rc = 9; break; /* map generic error to RXQUEUE_NOTREG */
914 default: ;
916 disconnect_from_rxstack( TSD, &q ) ;
917 if ( rc == RXSTACK_TIMEOUT )
919 condition_hook( TSD, SIGNAL_NOTREADY, 94, 1, -1, NULL, Str_cre_TSD( TSD, "Timeout on external queue" ) );
921 if ( ( rc == RXSTACK_EMPTY ) || ( rc == RXSTACK_TIMEOUT ) ) /* empty or timeout */
922 need_line_from_stdin = 1;
924 #endif
926 if ( need_line_from_stdin )
928 if ( TSD->called_from_saa )
930 rc = 8; /* RXQUEUE_EMPTY */
932 else if ( rc == RXSTACK_TIMEOUT )
934 rc = 8; /* RXQUEUE_EMPTY */
936 else
938 int rc2 = HOOK_GO_ON ;
939 if ( TSD->systeminfo->hooks & HOOK_MASK( HOOK_PULL ) )
940 rc2 = hookup_input( TSD, HOOK_PULL, &contents );
942 if ( rc2 == HOOK_GO_ON )
943 contents = readkbdline( TSD );
945 assert( contents );
947 rc = 0;
949 if ( result )
950 *result = rc;
951 else if ( ( contents == NULL ) && !TSD->called_from_saa )
952 contents = nullstringptr();
954 return contents;
959 * Counts the lines in the stack.
960 * Reuses the current connection if possible.
962 int lines_in_stack( tsd_t *TSD, const streng *queue_name )
964 stk_tsd_t *st ;
965 int lines = 0 ;
966 Queue *q ;
968 st = (stk_tsd_t *)TSD->stk_tsd;
969 if ( !use_external( TSD, queue_name ) )
971 if ( queue_name )
973 if ( ( q = find_queue( TSD, st, queue_name ) ) == NULL )
975 return -9;
978 else
979 q = st->current_queue;
980 assert( ( q->type == QisSESSION ) || ( q->type == QisInternal ) ) ;
982 lines = q->u.i.elements ;
984 #if !defined(NO_EXTERNAL_QUEUES)
985 else
987 Queue q, *work;
988 int rc;
990 if ( ( work = open_external( TSD, queue_name, &q, &rc, 0, NULL ) ) == NULL )
991 return -rc;
993 lines = get_number_in_queue_from_rxstack( TSD, work->u.e.socket, &rc );
994 disconnect_from_rxstack( TSD, &q ) ;
995 if ( rc != 0 )
996 lines = -rc;
998 #endif
999 return lines ;
1002 #ifdef TRACEMEM
1004 * Marks all chunks of dynamic allocated memory that are allocated
1005 * to the stack subsystem.
1007 static void mark_buffer( const Buffer *b )
1009 StackLine *ptr ;
1011 for ( ptr = b->top; ptr != NULL; ptr = ptr->lower )
1013 markmemory( ptr, TRC_STACKBOX ) ;
1014 markmemory( ptr->contents, TRC_STACKLINE ) ;
1018 void mark_stack( const tsd_t *TSD )
1020 Buffer *b ;
1021 Queue *q ;
1022 stk_tsd_t *st;
1023 int i;
1025 st = (stk_tsd_t *)TSD->stk_tsd;
1026 q = st->current_queue ;
1027 for ( i = 0; i < NUMBER_QUEUES; i++ )
1029 switch ( st->queue[i].type )
1031 default:
1032 assert( st->queue[i].type ) ;
1033 case QisUnused:
1034 break;
1036 case QisSESSION:
1037 case QisInternal:
1038 if ( st->queue[i].u.i.name )
1040 markmemory( st->queue[i].u.i.name, TRC_STACKBOX ) ;
1043 for ( b = st->queue[i].u.i.top; b != NULL; b = b->lower )
1045 markmemory( b, TRC_STACKBOX ) ;
1046 mark_buffer( b ) ;
1048 break ;
1050 case QisTemp:
1051 mark_buffer( &st->queue[i].u.t ) ;
1052 break ;
1054 case QisExternal:
1055 markmemory( st->queue[i].u.e.name, TRC_STACKBOX ) ;
1056 break ;
1060 #endif
1064 * Creates a new buffer and returns the number of buffers in the stack
1065 * excluding the zeroth buffer.
1067 int make_buffer( tsd_t *TSD )
1069 stk_tsd_t *st;
1070 Buffer *b ;
1071 Queue *q ;
1073 st = (stk_tsd_t *)TSD->stk_tsd;
1074 q = st->current_queue ;
1075 if ( q->type == QisExternal )
1076 exiterror( ERR_EXTERNAL_QUEUE, 110, "MAKEBUF" ) ;
1077 assert( ( q->type == QisSESSION )
1078 || ( q->type == QisInternal ) ) ;
1080 ENSURE_BUFFER( q ) ; /* Possibly create the zeroth buffer */
1082 /* Make a *new* buffer */
1083 b = (Buffer *)MallocTSD( sizeof( Buffer ) ) ;
1084 memset( b, 0, sizeof( Buffer ) ) ;
1086 b->lower = q->u.i.top ; /* The zeroth buffer exists */
1087 b->lower->higher = b ;
1088 q->u.i.top = b ;
1089 q->u.i.buffers++ ;
1091 return q->u.i.buffers - 1 ;
1096 * Dumps the contents of the stack to standard error. Buffer marks are
1097 * indicated in the printout.
1099 void type_buffer( tsd_t *TSD )
1101 Buffer *b ;
1102 StackLine *ptr ;
1103 char *cptr, *stop ;
1104 int counter ;
1105 streng *name ;
1106 stk_tsd_t *st;
1107 Queue *q ;
1109 if (TSD->stddump == NULL)
1110 return;
1111 st = (stk_tsd_t *)TSD->stk_tsd;
1112 q = st->current_queue ;
1113 name = get_queue( TSD ) ;
1114 fprintf(TSD->stddump,"==> Name: %.*s\n", Str_len(name), name->value ) ;
1115 fprintf(TSD->stddump,"==> Lines: %d\n", lines_in_stack( TSD, NULL )) ;
1116 if ( q->type == QisExternal )
1117 return ;
1119 assert( ( q->type == QisSESSION ) || ( q->type == QisInternal ) ) ;
1120 ENSURE_BUFFER( q ) ; /* Possibly create the zeroth buffer */
1121 for ( counter = q->u.i.buffers, b = q->u.i.top; b != NULL; b = b->lower )
1123 fprintf(TSD->stddump,"==> Buffer: %d\n", --counter ) ;
1124 for ( ptr = b->top; ptr != NULL; ptr = ptr->lower )
1126 putc( '"', TSD->stddump ) ;
1127 stop = Str_end( ptr->contents ) ;
1128 for ( cptr = ptr->contents->value ; cptr < stop; cptr++ )
1129 putc( ( rx_isprint( *cptr ) ? ( *cptr ) : '?' ), TSD->stddump ) ;
1131 putc( '"', TSD->stddump ) ;
1132 #if defined(DOS) || defined(OS2) || defined(WIN32)
1133 putc( REGINA_CR, TSD->stddump ) ;
1134 #endif
1135 putc( REGINA_EOL, TSD->stddump ) ;
1139 fprintf(TSD->stddump,"==> End of Stack\n") ;
1140 fflush(TSD->stddump) ;
1143 #if !defined(NO_EXTERNAL_QUEUES)
1145 * The following functions allow interfacing to the external
1146 * queue process; rxstack
1147 * The connection to eq is opened if and only if it isn't opened yet.
1149 static int get_socket_details_and_connect( tsd_t *TSD, Queue *q )
1151 stk_tsd_t *st;
1153 st = (stk_tsd_t *)TSD->stk_tsd;
1154 assert( q->type == QisExternal ) ;
1156 /* Fill in default values. Missing values are rarely used but it may
1157 * happen.
1159 if ( q->u.e.name == NULL )
1160 q->u.e.name = default_external_name( TSD ) ;
1161 if ( q->u.e.portno == 0 )
1162 q->u.e.portno = default_port_number() ;
1163 if ( q->u.e.address == 0 )
1164 q->u.e.address = default_external_address() ;
1166 if ( q->u.e.socket == -1 )
1168 if ( connect_to_rxstack( TSD, q ) == -1 )
1169 return 100; /* RXQUEUE_NETERROR */
1171 q->u.e.timeoutSet = 0;
1172 return 0;
1176 * save_parse_queue wraps parse_queue and exiterrors in case of an error.
1177 * Returns 1 if queue is (probably) different from the current queue, 0 else.
1178 * *q is filled even with a return value of 0.
1179 * A negative return code indicates a RXQUEUE_??? error code which must be
1180 * passed back to the SAA interface immediately.
1181 * Queues are treated as equal if ignore_name is set but host address and port
1182 * numbers are the same.
1184 static int save_parse_queue( tsd_t *TSD, streng *queue, Queue *q,
1185 int ignore_name)
1187 stk_tsd_t *st = (stk_tsd_t *)TSD->stk_tsd;
1188 Queue *curr ;
1189 int rc;
1191 if ( ( rc = parse_queue( TSD, queue, q ) ) <= 0 )
1192 return rc;
1194 if ( !ignore_name && ( PSTRENGLEN( queue ) != 0 ) )
1195 return 1 ;
1196 curr = st->current_queue ;
1197 if ( curr->type != QisExternal )
1198 return 1 ;
1199 if ( q->u.e.address != curr->u.e.address )
1200 return 1 ;
1201 if ( q->u.e.portno != curr->u.e.portno )
1202 return 1 ;
1203 return 0 ;
1207 * open_external opens the connection to the external queue will a full
1208 * error checking. NULL is returned on error and *rc describes the
1209 * RXQUEUE_??? error.
1210 * On success the opened queue is returned and *q is set to the old or
1211 * temporary queue. queue is either NULL or describes the new temporary
1212 * queue's name.
1213 * Queues are treated as equal if ignore_name is set but host address and port
1214 * numbers are the same. No set_queue_in_rxstack() is called if ignore_name
1215 * is set.
1216 * *basename is set to the queue's name without the network extension if
1217 * basename != NULL. It must be freed later. *basename may become NULL.
1219 static Queue *open_external( tsd_t *TSD, const streng *queue, Queue *q,
1220 int *rc, int ignore_name, streng **basename )
1222 stk_tsd_t *st = (stk_tsd_t *)TSD->stk_tsd;
1223 streng *qn;
1224 int h;
1225 Queue *retval;
1227 qn = ( queue == NULL ) ? NULL : Str_dupTSD( queue );
1229 if ( ( h = save_parse_queue( TSD, qn, q, ignore_name ) ) < 0 )
1231 if ( qn != NULL )
1232 Free_stringTSD( qn );
1233 *rc = -h;
1234 return NULL;
1236 if ( h == 1 )
1238 if ( ( h = get_socket_details_and_connect( TSD, q ) ) != 0 )
1240 if ( qn != NULL )
1241 Free_stringTSD( qn );
1242 disconnect_from_rxstack( TSD, q );
1243 *rc = h;
1244 return NULL;
1246 if ( !ignore_name )
1248 if ( ( h = set_queue_in_rxstack( TSD, q->u.e.socket, qn ) ) != 0 )
1250 if ( qn != NULL )
1251 Free_stringTSD( qn );
1252 disconnect_from_rxstack( TSD, q );
1253 *rc = h;
1254 return NULL;
1257 retval = q ;
1259 else
1261 retval = st->current_queue ;
1264 if ( basename != NULL )
1265 *basename = qn;
1266 else if ( qn != NULL )
1267 Free_stringTSD( qn );
1269 *rc = 0;
1270 return retval;
1272 #endif
1274 /* SetCurrentQueue set the current queue and makes all the necessary cleanup
1275 * of the "old" current queue.
1276 * new_name is assigned to the current queue's name, the former value is
1277 * returned.
1279 static streng *SetCurrentQueue( const tsd_t *TSD, stk_tsd_t *st, Queue *q,
1280 streng *new_name )
1282 streng *retval;
1284 #if !defined( NO_EXTERNAL_QUEUES )
1285 if ( ( st->current_queue->type == QisExternal )
1286 && ( st->current_queue != q ) )
1287 delete_an_external_queue( TSD, st, st->current_queue );
1288 #else
1289 (void)(TSD);
1290 #endif
1291 st->current_queue = q;
1293 retval = st->current_queue_name;
1294 st->current_queue_name = new_name;
1295 return retval;
1299 * Create a new queue
1300 * RXQUEUE( 'Create' )
1301 * Reuses the current connection if possible. The newly created current
1302 * queue becomes the current queue.
1304 int create_queue( tsd_t *TSD, const streng *queue_name, streng **result )
1306 streng *new_queue = NULL ;
1307 stk_tsd_t *st;
1308 char buf[50];
1309 Queue *q = NULL ;
1310 int rc = 0 ;
1312 st = (stk_tsd_t *)TSD->stk_tsd;
1313 if ( !use_external( TSD, queue_name ) )
1315 if ( queue_name == NULL )
1318 * Create a unique queue name
1320 sprintf(buf,"S%d-%ld-%d", (int)getpid(), clock(), st->runner++ );
1321 new_queue = Str_cre_TSD( TSD, buf ) ;
1323 else
1325 if ( ( q = find_queue( TSD, st, queue_name ) ) == NULL )
1328 * No queue of that name, so use it.
1330 new_queue = Str_dupTSD( queue_name ) ;
1332 else
1334 if ( q->type == QisSESSION )
1336 if ( !TSD->called_from_saa )
1337 exiterror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, rc, "Getting queue from stack" );
1339 return 5 ;
1342 * If the queue we found is a false queue, we can still
1343 * use the supplied name and the slot
1345 if ( q->u.i.isReal )
1348 * Create a unique queue name
1350 sprintf(buf,"S%d-%ld-%d", (int)getpid(), clock(), st->runner++ );
1351 new_queue = Str_cre_TSD( TSD, buf ) ;
1352 rc = 1;
1357 if ( new_queue != NULL ) /* new name for a new slot */
1359 q = find_free_slot( TSD );
1360 if ( q == NULL )
1362 if ( new_queue != NULL )
1363 Free_stringTSD( new_queue );
1364 return 12; /* RXQUEUE_MEMFAIL */
1366 q->type = QisInternal ;
1367 if ( new_queue == queue_name ) /* need a fresh one */
1368 new_queue = Str_dupTSD( new_queue ) ;
1369 q->u.i.name = Str_upper( new_queue ) ;
1371 assert( q->type == QisInternal ) ;
1372 q->u.i.isReal = 1;
1374 * result created here; up to caller to free it
1376 *result = Str_dupTSD( q->u.i.name );
1378 #if !defined(NO_EXTERNAL_QUEUES)
1379 else
1381 Queue q, *work;
1382 streng *base;
1384 if ( ( work = open_external( TSD, queue_name, &q, &rc, 1, &base ) ) == NULL )
1385 return rc;
1387 if ( ( rc = create_queue_on_rxstack( TSD, work, base, result ) ) == -1 )
1388 rc = 100;
1390 if ( base != NULL )
1391 Free_stringTSD( base );
1392 disconnect_from_rxstack( TSD, &q ) ;
1394 #endif
1395 return rc;
1399 * Delete a queue
1400 * RXQUEUE( 'Delete' )
1401 * Reuses the current connection if possible. SESSION becomes the current
1402 * queue always.
1404 int delete_queue( tsd_t *TSD, const streng *queue_name )
1406 int rc = 0;
1407 stk_tsd_t *st;
1408 Queue *q;
1409 streng *h;
1411 st = (stk_tsd_t *)TSD->stk_tsd;
1412 if ( !use_external( TSD, queue_name ) )
1414 if ( ( queue_name == NULL ) || ( PSTRENGLEN( queue_name ) == 0 ) )
1415 return 9 ;
1416 if ( ( q = find_queue( TSD, st, queue_name ) ) == NULL )
1417 return 9 ;
1418 if ( q->type == QisSESSION )
1419 return 5 ; /* SESSION can't be deleted */
1420 assert( q->type == QisInternal ) ;
1422 * If we found a false queue, return 9
1424 if ( !q->u.i.isReal )
1425 rc = 9;
1427 * Delete the contents of the queue
1428 * and mark it as gone.
1430 delete_an_internal_queue( TSD, st, q );
1431 h = SetCurrentQueue( TSD, st, &st->queue[0], Str_creTSD( "SESSION" ) ) ;
1432 Free_stringTSD( h );
1434 #if !defined(NO_EXTERNAL_QUEUES)
1435 else
1437 Queue q, *work;
1438 streng *base;
1440 if ( ( work = open_external( TSD, queue_name, &q, &rc, 1, &base ) ) == NULL )
1441 return rc;
1443 if ( ( base == NULL ) || ( PSTRENGLEN( base ) == 0 ) )
1445 if ( base != NULL )
1446 Free_stringTSD( base );
1447 if ( TSD->called_from_saa )
1448 return 9;
1449 exiterror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INVALID_QUEUE, tmpstr_of(TSD, queue_name ) ) ;
1452 if ( ( rc = delete_queue_from_rxstack( TSD, work->u.e.socket, base ) ) == 0 )
1454 /* This will disconnect the current external queue if needed */
1455 h = SetCurrentQueue( TSD, st, &st->queue[0], Str_creTSD( "SESSION" ) );
1456 Free_stringTSD( h );
1459 if ( base != NULL )
1460 Free_stringTSD( base );
1461 disconnect_from_rxstack( TSD, &q ) ;
1463 if ( rc == -1 )
1464 rc = 100;
1466 #endif
1467 return rc;
1471 * Set timeout for queue
1472 * RXQUEUE( 'Timeout' )
1473 * Do not reuse the current connection. A different external queue name
1474 * leads to an expensive NOP.
1475 * Timeout only valid for external queues.
1477 int timeout_queue( tsd_t *TSD, const streng *timeout, const streng *queue_name )
1479 int rc = 0 ;
1481 if ( !use_external( TSD, queue_name ) )
1483 exiterror( ERR_EXTERNAL_QUEUE, 111, "TIMEOUT" ) ;
1485 #if !defined(NO_EXTERNAL_QUEUES)
1486 else
1488 Queue q, *work;
1489 int val, err;
1491 if ( ( work = open_external( TSD, queue_name, &q, &rc, 1, NULL ) ) == NULL )
1492 return rc;
1494 * Convert incoming streng to positive (or zero) integer
1496 val = streng_to_int( TSD, timeout, &err ) ;
1497 if ( ( val < 0 ) || err )
1499 disconnect_from_rxstack( TSD, &q ) ;
1500 exiterror( ERR_INCORRECT_CALL, 930, 999999999, tmpstr_of( TSD, timeout ) );
1503 * Indicate that this queue has a timeout
1505 work->u.e.timeoutSet = 1;
1506 rc = timeout_queue_on_rxstack( TSD, work->u.e.socket, val );
1507 disconnect_from_rxstack( TSD, &q ) ;
1509 #endif
1510 return rc ;
1514 * Return the name of the current queue. The value is silently nul terminated.
1515 * RXQUEUE( 'Get' )
1516 * Returns queue name even if it is a false queue
1518 streng *get_queue( tsd_t *TSD )
1520 streng *result;
1521 stk_tsd_t *st;
1522 int l;
1523 char *p;
1525 st = (stk_tsd_t *)TSD->stk_tsd;
1526 if ( st->queue[0].u.i.name == NULL )
1527 SetSessionName( TSD, st ) ;
1529 assert( st->current_queue_name != NULL );
1531 l = Str_len( st->current_queue_name );
1532 result = Str_makeTSD( l + 1 );
1533 p = Str_val( result );
1534 memcpy( p, Str_val( st->current_queue_name ), l );
1535 p[l] = '\0';
1536 Str_len( result ) = l;
1538 return result;
1542 * Return the name and length of the current queue in argument pointers. No
1543 * copy is done.
1545 void fill_queue_name( const tsd_t *TSD, int *len, char **name )
1547 stk_tsd_t *st;
1549 st = (stk_tsd_t *)TSD->stk_tsd;
1550 if ( st->queue[0].u.i.name == NULL )
1551 SetSessionName( TSD, st ) ;
1553 assert( st->current_queue_name != NULL );
1555 *len = Str_len( st->current_queue_name );
1556 *name = Str_val( st->current_queue_name );
1560 * Set the current queue
1561 * RXQUEUE( 'Set' )
1562 * Object Rexx allows the queue to be set to an unknown queue
1563 * This is stupid; the user should be told its not valid, and
1564 * return an empty string.
1565 * But we validly set the false queue :-(
1566 * Reuses the current connection if possible.
1568 streng *set_queue( tsd_t *TSD, const streng *queue_name )
1570 stk_tsd_t *st ;
1571 Queue *q ;
1573 st = (stk_tsd_t *)TSD->stk_tsd;
1574 if ( !use_external( TSD, queue_name ) )
1576 if ( ( q = find_queue( TSD, st, queue_name ) ) == NULL )
1579 * We didn't find a real or a false queue, so create
1580 * a false queue
1582 q = find_free_slot( TSD );
1583 q->type = QisInternal ;
1584 q->u.i.name = Str_upper( Str_dupTSD( queue_name ) ) ;
1585 q->u.i.isReal = 0 ; /* false queue */
1587 assert( ( q->type == QisSESSION ) || ( q->type == QisInternal ) ) ;
1588 return SetCurrentQueue( TSD, st, q, Str_dupTSD( q->u.i.name ) );
1590 #if !defined(NO_EXTERNAL_QUEUES)
1591 else
1593 Queue q, *work;
1594 streng *base, *result;
1595 int rc;
1597 if ( ( work = open_external( TSD, queue_name, &q, &rc, 1, &base ) ) == NULL )
1598 exiterror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, rc, "Setting queue from stack" );
1600 if ( ( base == NULL ) || ( PSTRENGLEN( base ) == 0 ) )
1602 if ( base != NULL )
1603 Free_stringTSD( base );
1604 disconnect_from_rxstack( TSD, &q ) ;
1605 assert( !TSD->called_from_saa );
1606 exiterror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INVALID_QUEUE, tmpstr_of(TSD, queue_name ) ) ;
1608 if ( ( rc = set_queue_in_rxstack( TSD, work->u.e.socket, base ) ) == 0 )
1610 Free_stringTSD( base );
1611 if ( ( rc = get_queue_from_rxstack( TSD, work, &result ) ) != 0 )
1613 disconnect_from_rxstack( TSD, &q );
1614 assert( !TSD->called_from_saa );
1615 exiterror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, rc, "Getting queue back from stack" );
1617 if ( work == &q )
1619 work = find_free_slot( TSD );
1620 *work = q;
1622 return SetCurrentQueue( TSD, st, work, result );
1624 Free_stringTSD( base );
1625 disconnect_from_rxstack( TSD, &q ) ;
1626 assert( !TSD->called_from_saa );
1627 exiterror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, rc, "Setting queue from stack" );
1629 #endif
1630 return NULL;
1633 Queue *addr_reopen_queue( tsd_t *TSD, const streng *queuename, char code )
1634 /* This is the open routine for the ADDRESS WITH-redirection. queuename is
1635 * the name of the queue. code is either 'r' for "READ",
1636 * 'A' for "WRITE APPEND", 'R' for "WRITE REPLACE".
1637 * The queue named queuename will be (re)opened in all cases.
1638 * In case of REPLACE the queue's content must be purged later by
1639 * addr_purge_queue.
1640 * A structure for queues is returned and should be used for calls
1641 * to addr_io_queue.
1642 * The return value *may* be NULL in case of an error. A NOTREADY condition
1643 * may have been raised in this case.
1646 stk_tsd_t *st ;
1647 Queue *q ;
1648 streng *name ;
1650 st = (stk_tsd_t *)TSD->stk_tsd ;
1652 if ( ( queuename == NULL ) || ( PSTRENGLEN( queuename ) == 0 ) )
1653 return st->current_queue ;
1655 if ( !use_external( TSD, queuename ) )
1657 q = find_queue( TSD, st, queuename ) ;
1658 if ( ( code == 'r' ) || ( q != NULL ) )
1660 if ( q != NULL )
1661 q->u.i.isReal = 1 ; /* not sure if this's right */
1662 return q ;
1664 /* create a new queue */
1665 q = find_free_slot( TSD ) ;
1666 q->type = QisInternal ;
1667 name = Str_dupTSD( queuename ) ;
1668 q->u.i.name = Str_upper( name ) ;
1669 q->u.i.isReal = 1 ;
1670 return q ;
1672 #if !defined( NO_EXTERNAL_QUEUES )
1673 /* always use a fresh connection for external queues. There are too many
1674 * circumstances to keep in mind to check cleanly, whether or not to use
1675 * the current connection or if it exists already. The caller will check
1676 * for same names later.
1678 q = find_free_slot( TSD ) ;
1680 name = Str_dupTSD( queuename ) ;
1681 if ( save_parse_queue( TSD, name, q, 1 ) < 0 )
1683 Free_stringTSD( name );
1684 return NULL;
1686 if ( get_socket_details_and_connect( TSD, q ) != 0 )
1688 disconnect_from_rxstack( TSD, q ) ;
1689 return NULL;
1691 set_queue_in_rxstack( TSD, q->u.e.socket, name );
1693 return q ;
1694 #else
1695 return NULL ;
1696 #endif
1699 /* returns 1 if the two queue pointers point to the same physical queue,
1700 * 0 else.
1702 int addr_same_queue( const tsd_t *TSD, const Queue *q1, const Queue *q2 )
1704 int t1, t2, retval = 0 ;
1705 #if !defined( NO_EXTERNAL_QUEUES )
1706 streng *n1, *n2 ;
1707 #endif
1709 if ( q1 == q2 )
1710 return 1 ;
1711 if ( ( t1 = q1->type ) == QisSESSION )
1712 t1 = QisInternal ;
1713 if ( ( t2 = q2->type ) == QisSESSION )
1714 t2 = QisInternal ;
1715 if ( t1 != t2 )
1716 return 0 ;
1717 if (t1 == QisInternal)
1718 return !Str_cmp( q1->u.i.name, q2->u.i.name ) ;
1719 assert( q1->type == QisExternal ) ;
1720 if ( q1->u.e.address != q2->u.e.address )
1721 return 0 ;
1722 if ( q1->u.e.portno != q2->u.e.portno )
1723 return 0 ;
1724 /* we have to use the slow name comparison */
1725 #if !defined( NO_EXTERNAL_QUEUES )
1726 if ( get_queue_from_rxstack( TSD, q1, &n1 ) != 0 )
1727 return 0;
1728 if ( get_queue_from_rxstack( TSD, q2, &n2 ) != 0 )
1730 Free_stringTSD( n1 ) ;
1731 return 0;
1733 retval = !Str_cmp( n1, n2 ) ;
1734 Free_stringTSD( n1 ) ;
1735 Free_stringTSD( n2 ) ;
1736 #endif
1737 return retval ;
1740 /* addr_redir_queue moves the content of the top buffer of an existing queue
1741 * to a new temporary queue.
1743 Queue *addr_redir_queue( const tsd_t *TSD, Queue *q )
1745 Queue *retval ;
1746 Buffer *b ;
1747 #if !defined( NO_EXTERNAL_QUEUES )
1748 streng *contents ;
1749 StackLine *ptr ;
1750 #endif
1752 assert( ( q->type == QisSESSION )
1753 || ( q->type == QisInternal )
1754 || ( q->type == QisExternal ) ) ;
1755 retval = find_free_slot( TSD ) ;
1756 retval->type = QisTemp ;
1758 if ( ( q->type == QisSESSION ) || ( q->type == QisInternal ) )
1760 /* trivial */
1761 if ( ( b = q->u.i.top ) != NULL )
1764 * Fixes bug 777645
1766 q->u.i.elements -= b->elements ;
1767 retval->u.t = *b ;
1768 retval->u.t.higher = retval->u.t.lower = NULL ;
1769 b->top = b->bottom = NULL ;
1770 b->elements = 0 ;
1773 #if !defined( NO_EXTERNAL_QUEUES )
1774 else
1776 /* loop until EOF. We can't use lines_in_stack, since the number may
1777 * change because of multiple accesses by different clients.
1779 while ( get_line_from_rxstack( TSD, q->u.e.socket, &contents, 1 ) == 0 )
1781 ptr = (StackLine *) MallocTSD( sizeof( StackLine ) ) ;
1782 ptr->contents = contents ;
1783 FIFO_LINE( &retval->u.t, ptr ) ;
1786 #endif
1787 return retval ;
1790 /* addr_purge_queue discards the contents of a queue. It is caused by a
1791 * ADDRESS WITH ??? REPLACE ??? or at the end of the use of an input
1792 * queue.
1794 void addr_purge_queue( const tsd_t *TSD, Queue *q )
1796 stk_tsd_t *st ;
1797 Buffer *b ;
1799 st = (stk_tsd_t *)TSD->stk_tsd ;
1800 assert( ( q->type == QisSESSION )
1801 || ( q->type == QisInternal )
1802 || ( q->type == QisExternal ) ) ;
1804 if ( ( q->type == QisSESSION ) || ( q->type == QisInternal ) )
1806 ENSURE_BUFFER( q ) ;
1807 b = q->u.i.top ;
1808 q->u.i.elements -= b->elements ;
1809 delete_buffer_content( TSD, st, b ) ;
1811 #if !defined( NO_EXTERNAL_QUEUES )
1812 else
1814 clear_queue_on_rxstack( TSD, q->u.e.socket ) ;
1816 #endif
1819 streng *addr_io_queue( tsd_t *TSD, Queue *q, streng *buffer, int isFIFO )
1820 /* This is the working routine for the ADDRESS WITH-redirection. q is
1821 * the return value of addr_reopen_queue. buffer must be NULL for a read
1822 * operation or a filled buffer.
1823 * The return value is NULL in case of a write operation or in case of EOF
1824 * while reading.
1825 * buffer is consumed if given and indicates a push operation instead of a
1826 * pull operation. isFIFO indicates the type of the push operation.
1829 StackLine *ptr, *newbox ;
1830 Buffer *b ;
1831 streng *retval ;
1833 if ( buffer == NULL )
1835 /* pull operation */
1836 switch ( q->type )
1838 case QisSESSION:
1839 case QisInternal:
1840 ENSURE_BUFFER( q ) ;
1841 b = q->u.i.top ; /* Work on the top buffer only */
1842 POP_LINE( b, ptr ) ;
1843 if ( ptr == NULL )
1844 return NULL ;
1845 q->u.i.elements-- ;
1846 retval = ptr->contents ;
1847 FreeTSD( ptr ) ;
1848 return retval ;
1850 case QisTemp:
1851 b = &q->u.t ;
1852 POP_LINE( b, ptr ) ;
1853 if ( ptr == NULL )
1854 return NULL ;
1855 retval = ptr->contents ;
1856 FreeTSD( ptr ) ;
1857 return retval ;
1859 #if !defined( NO_EXTERNAL_QUEUES )
1860 case QisExternal:
1861 if ( get_line_from_rxstack( TSD, q->u.e.socket, &retval, 1 ) == 0 )
1862 return retval ;
1863 return NULL ;
1864 #endif
1866 default:
1867 break ;
1869 /* default: (probably an unused queue) */
1870 return NULL ;
1873 /* buffer's not NULL */
1874 assert( ( Str_max( buffer ) >= Str_len( buffer ) ) ) ;
1876 #if !defined(NO_EXTERNAL_QUEUES)
1877 if ( q->type == QisExternal )
1879 if ( isFIFO )
1880 queue_line_fifo_to_rxstack( TSD, q->u.e.socket, buffer ) ;
1881 else
1882 queue_line_lifo_to_rxstack( TSD, q->u.e.socket, buffer ) ;
1883 return NULL ;
1885 #endif
1887 newbox = (StackLine *) MallocTSD( sizeof( StackLine ) ) ;
1888 newbox->contents = buffer ;
1889 if ( isFIFO )
1891 /* push FIFO operation */
1892 switch ( q->type )
1894 case QisSESSION:
1895 case QisInternal:
1896 ENSURE_BUFFER( q ) ;
1897 b = q->u.i.top ;
1898 q->u.i.elements++ ;
1899 FIFO_LINE( b, newbox ) ;
1900 break ;
1902 case QisTemp:
1903 b = &q->u.t ;
1904 q->u.i.elements++ ;
1905 FIFO_LINE( b, newbox ) ;
1906 break ;
1908 default:
1909 break ;
1911 return NULL ;
1914 /* finally, we process push LIFO */
1915 switch ( q->type )
1917 case QisSESSION:
1918 case QisInternal:
1919 ENSURE_BUFFER( q ) ;
1920 b = q->u.i.top ;
1921 q->u.i.elements++ ;
1922 LIFO_LINE( b, newbox ) ;
1923 break ;
1925 case QisTemp:
1926 b = &q->u.t ;
1927 q->u.i.elements++ ;
1928 LIFO_LINE( b, newbox ) ;
1929 break ;
1931 default:
1932 break ;
1934 return NULL ;
1937 /* addr_close_queue closes a queue opened by addr_reopen_queue.
1938 * Never use another routine to do this.
1939 * It is also capable to purge and close temporary stacks.
1941 void addr_close_queue( const tsd_t *TSD, Queue *q )
1943 stk_tsd_t *st ;
1945 st = (stk_tsd_t *)TSD->stk_tsd;
1946 if ( q->type == QisTemp )
1948 delete_a_temp_queue( TSD, st, q ) ;
1949 return ;
1951 #if !defined(NO_EXTERNAL_QUEUES)
1952 if ( ( q != st->current_queue ) && ( q->type == QisExternal ) )
1953 delete_an_external_queue( TSD, st, q ) ;
1954 #endif