/*	indirect

Usage:

    #include	<std.h>
    #include	<qioret.h>	For RSX I/O error codes

    int
    indirect (filename, function)

    STRING	filename;	Name of the idirect file
    INTFUNC	function;	Function to process the arguments


Description:

The specified filename is opened in read mode.  If the open fails,
the function returns with the error code; the normal return value is 0.
If an indirect file is already open, the current file is queued onto a
FILE_QUEUE (see qfile.c) and processing continues on the new file.
The only limit to the number of indirect files that may be queued in this
way (the nesting level) is the amount of dynamic memory available to the task.

>>> CAUTION <<<  A single file pointer and queue is used for all files
accessed by this routine.  Thus it cannot be used independently in parallel
for multiple indirect file sets.  It can be used sequentially (indirection
has completed for all nesting levels) for different indirect file sets.

Each text line (up to MAX_LINE_SIZE characters) from the current file
is parsed into an argument vector (see arg_parse) containing arg_count
argument strings.  A call is then made to the function that will process the
vector:

    (*function) (arg_count, argument);

This form is identical to that used by the main function, which, of course,
may be the argument processing function if desired (though care should then
be taken to use the return fucntion from main rather than the exit function).
Note that no consideration is given to the value returned from the argument
processing function.

The text line string and the argument vector are located in dynamically
allocated memory which makes this function reentrant.  One text line buffer 
is allocated for each open indirect file.  Thus when an argument vector
contains an argument that invokes another indirect file, the current argument
vector remains pending until all other nested levels of processing are
completed, so that the processing of the pending argument vector can continue
normally upon the appropriate return from the call to the indirect function
(this, of course, is all invisible to the user).  If the needed dynamic
memory space isn't available, an IE_NBF (no buffer space) error code is
returned. 

>>> NOTE <<< Because the text string from the file is read into dynamically
allocated memory which is reused for each line read from the file; and
because the argument vectors, which are also located in reused dynamic
memory, point to locations in the parsed text string: when the argument
processing function is called it must COPY any argument strings that must
be retained after they have been initially processed.

When the end-of-file for the current indirect file is encountered, the
previously queued indirect file, if any, is dequeued and processing continues
in that file at the next text line following the one where the interruption
occured.

******************************************************************************/

#include	<std.h>
#include	<stdio.h>
#include	<qioret.h>
#include	<filequeue.h>

#define	MAX_LINE_SIZE	132
#define	MAX_ARGUMENTS	20
#define	ARGV_STORAGE	(sizeof (char *) * MAX_ARGUMENTS)

static	FILE		*file = 0;	/* File pointer */
static	FILE_QUEUE	*queue = 0;	/* File queue */


INTEGER
indirect (filename, function)

STRING	filename;
INTFUNC	function;
{
INTEGER	status;
STRING	string;		/* Input string from indirect file */
STRING	argument;	/* Argument vector */

FILE_QUEUE	*enQ_file(), *deQ_file();

IO_ERROR = 0;

if (file)
    {
    /* An indirect file is open:
    	Queue up the current file and start on the next one
    */
    queue = enQ_file (queue, file, filename);
    if (IO_ERROR)
	return (IO_ERROR);
    }
else if (!(file = fopen (filename, "r")))	/* Open the indirect file */
    return (IO_ERROR);

/* Allocate line storage memory */
if (! (argument = malloc (MAX_LINE_SIZE + ARGV_STORAGE)))
    return (IO_ERROR = IE_NBF);	/* Couldn't allocate dynamic storage */

string = argument + ARGV_STORAGE;
/*
Get text lines and process them:
*/
#ifdef DEBUG
printf ("indirect: getting text lines from file\n");
#endif
while (fgetss (string, MAX_LINE_SIZE, file))
    /* Parse the string, and call the argument processing function */
    (*function)
	(arg_parse (string, argument, MAX_ARGUMENTS), argument);
/*
Current indirect file processing ended.
*/
status = IO_ERROR;
mfree (argument);	/* Release the storage area */
if (queue)
    {
    /* Open the previously queued file */
#ifdef DEBUG
printf ("indirect: Previous indirect file being dequeued.\n");
#endif
    queue = deQ_file (queue, file);
    if (IO_ERROR)
	return (IO_ERROR);
    }
else
    {
    /* End of indirect file processing (all levels) */
    fclose (file);
    file = NULL;
    }

return ((status == IE_EOF) ? 0 : status);
}
