/*	
	docfmt.c - Take the output from extract program and format it.

Copyright (c) 1989, Microsoft.	All Rights Reserved.

...
10-06-89 Matt Saettler
...
10-15-89 Matt Saettler - Autodoc'ed added functions

3-26-91  Ported for use with Win32 (NigelT)

*/



#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <assert.h>
#include <search.h>
#include <io.h>
#include <direct.h>
#include <ctype.h>
#ifdef MMWIN
#include <mmsysver.h>
#endif
#include "version.h"
#include "types.h"
#include "text.h"
#include "docfmt.h"
#include "readext.h"
#include "ventura.h"
#include "process.h"
#include "misc.h"
#include "rtf.h"
#include "errstr.h"

void addexternal(char *pch);
int levelOK(aBlock *pBlock);
void parseargfile(char *filename);

/* globals */
char	*progName = "docfmt";	// name of our program.

char	*dirPath = NULL;	// path for creation of temporary files
char	*outputFile = NULL;	// the final output file name

int	noOutput = OFF; 	// if ON then no output is generated.
int	outputType;		// type of output to be generated
int	fprocessFiles;		// flag: should files be processed?
int	fCleanFiles;		// flag: should temporary files be cleaned up?

int	fMMUserEd=FALSE;	// flag: generating for User Ed?

logentry *head_log=NULL;	// head of list of log files
aLine *headExternal=NULL;	// head of list of external names to process

int fprocessOverride=FALSE;	// has fprocessFiles been set by user?
int fCleanOverride=FALSE;	// has fCleanFiles been set by user?


char achtmpbuf[128];		// a global temporary buffer

int verbose=FALSE;

// Definitions for Error Strings
//
int warninglevel=2;		// warning level
int prlineno=TRUE;		// print line numbers
int prinfo=TRUE;		// print INFO messages ?
long haderror=0;		// count of number of errors
int therewaserror=FALSE;	// flag: TRUE if there was an error
int dumplevels=FALSE;		// flag: TRUE if want to dump doc levels
int fSortMessage=TRUE;		// flag: TRUE if want to sort messages
				//        seperately from APIs

/*
 *	@doc INTERNAL
 *
 *	@func int | docfmt | Formats the input from the given file and
 *	prints it to 'stdout'.  If the given filename is NULL, input is 
 *	taken from 'stdin'.
 *
 *	@parm char * | fileName | Specifies the name of the 
 *	file to be processed.
 *
 *	@rdesc A return value of zero indicates success; a non-zero return value
 *	indicates an error.
 */
int docfmt(fileName, pcur_log)
char *fileName;
logentry *pcur_log;
{
    int     nStatus;
    int     wtag;
    aBlock  *pcurBlock;
    EXTFile *pExt;
    char    ach[120];


    pExt=(EXTFile *)clear_alloc(sizeof (struct _EXTFile));
    if(!pExt)
    {
	error(ERROR3);
	exit(7);
    }
    /* file from standard input? */
    if( fileName == NULL )
    {
	    pExt->EXTFilename=cp_alloc("<stdin>");
	    pExt->fp = stdin;
    }
    else
    {
	pExt->EXTFilename=cp_alloc(fileName);
	pExt->fp = fopen( pExt->EXTFilename, "r");
	if(pExt->fp == NULL )
	{
	    /* or from user-supplied filename? */
	    strcpy( achtmpbuf, dirPath );
	    strcat( achtmpbuf, fileName );
	    my_free(pExt->EXTFilename);
	    pExt->EXTFilename=cp_alloc(achtmpbuf);

	    pExt->fp = fopen( achtmpbuf, "r");


	    /* can file be opened? */
	    if(pExt->fp == NULL)
	    {
		    error( ERROR1, achtmpbuf);
		    exit(1);
	    }
	}
    }

#if 0
// get header information
    getLine(pExt);
    wtag=HIWORD(getTag(pExt));

    while(wtag == T2_HEADER)
    {
	wtag=(int)getTag(pExt);
	switch( wtag)
	{
	    case TG_BEGINHEAD:
		getLine(pExt);
		break;

	    case TG_EXTRACTID:
		nStatus = processText( pExt, &((pcur_log->pExt)->extractid) );
		if( nStatus)
		    return( nStatus );
		getLine(pExt);
		break;

	    case TG_EXTRACTVER:
		nStatus = processText( pExt, &((pcur_log->pExt)->extractver) );
		if( nStatus)
		    return( nStatus );
		getLine(pExt);
		break;
	    case TG_EXTRACTDATE:
		nStatus = processText( pExt, &((pcur_log->pExt)->extractdate) );
		if( nStatus)
		    return( nStatus );
		getLine(pExt);
		break;
	    case TG_ENDHEAD:
		break;

	}

	wtag=HIWORD(getTag(pExt));
    }

#endif

    if( getLine(pExt) )
    {
	pcurBlock=(aBlock *)1;			// a do.. while loop
	while( pcurBlock )
	{
	    if(verbose>1)
		fprintf(errfp,"  Reading: ");
	    pcurBlock = getBlock(pExt);
	    if(pcurBlock && levelOK(pcurBlock) )
	    {
		if(verbose>1)
		    fprintf(errfp," Writing.\n");
		putblock(pcurBlock, pcur_log);
	    }
	    else if(verbose>1)
		if(pcurBlock)
		    fprintf(errfp," Skipping\n");
		else
		    fprintf(errfp," End of File.\n");
		    
	    if(pcurBlock)
		destroyblock(pcurBlock);
	}
    }
    else
    {
	nStatus = ST_EOF;
    }

    /* close the file */
    fclose(pExt->fp);
    pExt->fp=NULL;

    if( nStatus == ST_EOF )
	return( 0 );
    else
	return( nStatus );
}

/*
 *	@doc INTERNAL
 *
 *	@func int | levelOK | Returns TRUE if the specified Block is
 *	OK to output.
 *
 *	@parm aBlock * | pBlock | Specifies the Block to check.
 *
 *	@rdesc A return value of TRUE indicates the specified Block is
 *	valid to output.  If not valid to print, the return value is FALSE.
 */
int
levelOK(aBlock *pBlock)
{
    aLine *pextern;
    aLine *pLevel;

    pextern=headExternal;
    while(pextern)
    {
	if(!strcmp(pextern->text,"*"))		// '*' means everything
	    return TRUE;
	    
	pLevel=pBlock->doclevel;
	while(pLevel)
	{
	    if(!stricmp(pLevel->text,pextern->text))
		return(TRUE);	/* we found a match */
	    pLevel=pLevel->next;
	}
	pextern=pextern->next;

    }

    /* no matches. */
    return FALSE;
}

/*
 *	@doc INTERNAL
 *
 *	@func void | Usage | Prints usage information to 'stderr'.
 *
 *	@comm This function does not exit.
 */
void Usage()
{
	fprintf(stdout, "usage: \n%s [-x[ ]name] [-r[dh]] [-c[01] [-p[01] \n", progName);
	fprintf(stdout, "\t[-o[ ]filename] [-d[ ]dirpath] [-l[ ]logfile] [-Zs] [-V[level]] [files]\n");
	fprintf(stdout, "\t[@argfile]\n\n");
	fprintf(stdout, "[-x[ ]name\tDefines <name> to be generated from the extracted file.\n");
	fprintf(stdout, "\t\t\t\t(Default is external.)\n");
	fprintf(stdout, "[-X\t\tProcesses all names from extract file.\n");
	fprintf(stdout, "[-Zs]\t\t\tNo output, error check only.\n");
	fprintf(stdout, "[-Ze]\t\t\tEnables extensions: dump doc level in API.\n");
	fprintf(stdout, "[-rd]\t\t\tGenerate RTF output for printing.\n");
	fprintf(stdout, "[-rh]\t\t\tGenerate RTF output for WinHelp.\n");
	fprintf(stdout, "\t\t\t\t(Default is generate for Ventura.)\n");
	fprintf(stdout, "\t\t\t\t(Default RTF is for printing.)\n");
	fprintf(stdout, "[-p[01]] \t\tProcess output files.\n");
	fprintf(stdout, "\t\t\t\t(0 means don't process)\n");
	fprintf(stdout, "[-c[01]] \t\tClean up intermediate files.\n");
	fprintf(stdout, "\t\t\t\t(0 means don't clean)\n");
	fprintf(stdout, "[-m[01]] \t\tSort Messages seperately from APIs.\n");
	fprintf(stdout, "\t\t\t\t(0 means don't sort seperate)\n");
	fprintf(stdout, "[-d dirpath] \t\tSpecify the directory for temporary files.\n");
	fprintf(stdout, "\t\t\t\t(only one tmp path should be specified)\n");
	fprintf(stdout, "[-l logfile] \t\tSpecify the logfile.\n");
	fprintf(stdout, "\t\t\t\t(many logfiles may be specified)\n");
	fprintf(stdout, "\t\t\t\t(the logfile maps functions to files)\n"); 
	fprintf(stdout, "[-o outputfile] \tSpecify the output file.\n");
	fprintf(stdout, "[-v[level]] \t\tSets the Verbosity level.\n");
	fprintf(stdout, "[-M]] \t\t\tSets processing for MM User Ed.\n");
	fprintf(stdout, "[files] \t\tList of files to be processed.\n");
	fprintf(stdout, "[@argfile]\t\tName of file to get list of arguments from.\n");
	fprintf(stdout, "\nOptions may be placed in any order.\n");
	fprintf(stdout, "example: %s /xinternal /rd dspmgr.ext\n",progName);
	fprintf(stdout, "example: %s dspmgr.ext\n",progName);
	fprintf(stdout, "example: %s /d \\tmp /rh dspmgr.ext\n",progName);
	fprintf(stdout, "example: %s -x internal -x dspmgr -d\\tmp /rh dspmgr.ext /c0 /p0\n",progName);
	fprintf(stdout, "example: %s /rd @args /c0\n",progName);
}

/*
 *	@doc INTERNAL
 *
 *	@func int | main | This function formats documentation information 
 *	from the given input file and sends it to the standard output.  
 *	Information is not sorted or formatted.
 *
 *	@parm int | argc | Specified the number of arguments.
 *
 *	@parm char * | argv[] | Specifies an arrayof points to the arguments.
 *
 *	@rdesc The return value is zero if there are no errors, otherwise the
 *	return value is a non-zero error code.
 */
int main(argc, argv)
int argc;	/* Specifies the number of arguments. */
char *argv[];	/* Specifies an array of pointers to the arguments */
{
    int nFiles = 0;	    /* # of files processed */
    int nStatus;
    int done;
    int temp;

    if(initerror())
    {
	fprintf(stderr, "%s: Internal Error 01\n",progName);
	exit(765);
    }

    errfp=stderr;	// set error logging file to stderr

    /* announce our existance */




    initreadext();

    noOutput = FALSE;
    done = FALSE;

    outputType=VENTURA;
    fprocessFiles=TRUE;

    parsearg(argc,argv, FALSE);

#if MMWIN
    sprintf(achtmpbuf," Version %d.%02d.%02d  %s %s   - %s  %s\n",
	   rmj, rmm, rup, __DATE__, __TIME__, szVerUser, MMSYSVERSIONSTR);

    error(LOGON,achtmpbuf);
#endif
    
    if(!head_log)
    {
	Usage();
	exit(1);
    }

    if(!outputFile)
    {
	if(fprocessFiles)
	{
	    if(head_log->inheadFile)
	    {
		temp=findlshortname(head_log->inheadFile->filename);
		strncpy(achtmpbuf,head_log->inheadFile->filename,temp);
	    }
	    else
	    {
		temp=findlshortname(head_log->pchlogname);
		strncpy(achtmpbuf,head_log->pchlogname,temp);
	    }
	    achtmpbuf[temp]='\0';
	    switch(outputType)
	    {
		case VENTURA:
		    strcat(achtmpbuf,".txt");
		    break;
		case RTFDOC:
		case RTFHELP:
		    strcat(achtmpbuf,".rtf");
		    break;
	    }
	    outputFile=cp_alloc(achtmpbuf);
	    error(INFO_OUTFILEDEFAULT, achtmpbuf);
	    if(!fCleanOverride)
		fCleanFiles=TRUE;
	}

    }
    if(!dirPath)
    {
	getcwd(achtmpbuf,128);
	if (achtmpbuf[strlen(achtmpbuf) - 1] != '\\')
		strcat(achtmpbuf,"\\");
	dirPath=cp_alloc(achtmpbuf);

    }
    if(!headExternal)
    {
if(verbose>1) 
 fprintf(errfp,"Document Level list DEFAULTS to %s\n","external");
	addexternal("external");
    }

if(verbose) fprintf(errfp,"Reading input files...\n");
    formatFiles();


    if(fprocessFiles)
    {
if(verbose) fprintf(errfp,"Creating %s...\n",outputFile);
       processLog();

    }

    return(FALSE);	/* we exit ok */
}


/*
 *	@doc INTERNAL
 *
 *	@func void | formatFiles | This function formats the entire list
 *	   files and sends them to the specified output type.
 *
 *  @comm The log files are filled in on exit.
 */
void
formatFiles()
{
    fileentry *cur_file;
    FILE * fp;
    logentry *cur_log=head_log;

    while(cur_log)
    {
	fp=fopen(cur_log->pchlogname,"w");
	if(!fp)
	{
fprintf(errfp,"Can't open %s\n",cur_log->pchlogname);
	    error(ERROR3);
	    exit(1);
	}

	/* output per-log header data */
	cur_log->outputType=outputType;
	fprintf(fp,"%d\n",cur_log->outputType);

	/* close log header */
	fclose(fp);
if(verbose>1) fprintf(errfp,"Processing log file %s\n",cur_log->pchlogname);
	cur_file=cur_log->inheadFile;
	while(cur_file)
	{
	    cur_file->logfile=cur_log;

if(verbose>1) fprintf(errfp," Reading file %s\n",cur_file->filename);
	    docfmt(cur_file->filename, cur_log);
	    cur_file=cur_file->next;
	}
	cur_log=cur_log->next;
    }
}


/*
 *	@doc INTERNAL
 *
 *	@func void | parsearg | This function parses and process the
 *	  arguments that it is passed.
 *
 *	@parm int | argc | Specified the number of arguments.
 *
 *	@parm char * | argv[] | Specifies an arrayof points to the arguments.
 *
 *	@parm int | flag | Specifies where the arguments came from.
 *
 *	@flag	TRUE  | Arguments came from a file.
 *
 *	@flag	FALSE | Arguments came from the command line.
 *
 *	@rdesc The return value is zero if there are no errors, otherwise the
 *	return value is a non-zero error code.
 */
void
parsearg(argc,argv, flag)
unsigned argc;
char **argv;
int flag;
{
    unsigned  i,j;
    char copt;
    int fsamearg;

    i = 1;
    while( i < argc )
    {
	switch (*argv[i])
	{
	    case '@':
		++argv[i];
		if(*argv[i]==':')
		    ++argv[i];
		if(strlen(argv[i])==0) /* we have @<space><file> */
		    i++;

		if(*argv[i])
		    parseargfile(argv[i]);
		else
		{
		    error(ERR_FILE_OPTION, '@');
		    Usage();
		    exit(1);
		}

		break;
#ifdef MSDOS
	    case '/' :
#endif
	    case '-' :
		++argv[i];
		fsamearg=TRUE;
		while(*argv[i] && fsamearg)
		{
		    copt=*argv[i];
		    switch( *argv[i] )
		    {

			case 'V':
			case 'v':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i])
				verbose=atoi( argv[i]);
			    else
				verbose=1;
			    if(verbose>0)
				prinfo=TRUE;
			    else
				prinfo=FALSE;
			    break;

			case 'o':
			case 'O':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i]==':')
				++argv[i];
			    if(strlen(argv[i])==0) /* we have /o<space><file> */
				i++;

			    if(*argv[i])
				setoutputfile(argv[i]);
			    else
			    {
				if(flag)
				{
				    fprintf(errfp,"%s. Line %d\n",argv[0],i);
				}
				error(ERR_FILE_OPTION, copt);
				Usage();
				exit(1);
			    }

			    break;
			case 'd':
			case 'D':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i]==':')
				++argv[i];
			    if(strlen(argv[i])==0) /* we have /d<space><file> */
				i++;

			    if(*argv[i])
				settmppath(argv[i]);
			    else
			    {
				if(flag)
				{
				    fprintf(errfp,"%s. Line %d\n",argv[0],i);
				}
				error(ERR_FILE_OPTION, copt);
				Usage();
				exit(1);
			    }

			    break;

			case 'X':
			    ++argv[i];
			    addexternal("*");
			    break;
			    
			case 'x':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i]==':')
				++argv[i];

			    if(strlen(argv[i])==0) /* we have /l<space><name> */
				i++;

			    if(*argv[i])
				addexternal(argv[i]);
			    else
			    {
				if(flag)
				{
				    fprintf(errfp,"%s. Line %d\n",argv[0],i);
				}
				error(ERR_NAME_OPTION, copt);
				Usage();
				exit(1);
			    }

			    break;
			case 'l':
			case 'L':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i]==':')
				++argv[i];

			    if(strlen(argv[i])==0) /* we have /l<space><file> */
				i++;

			    fprocessFiles=TRUE;

			    if(*argv[i])
				add_logtoprocess(argv[i]);
			    else
			    {
				if(flag)
				{
				    fprintf(errfp,"%s. Line %d\n",argv[0],i);
				}
				error(ERR_FILE_OPTION, copt);
				Usage();
				exit(1);
			    }

			    break;

			case 'c':
			case 'C':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i]==':')
				++argv[i];
			    fCleanFiles=atoi( argv[i]);
			    fCleanOverride=TRUE;
			    break;
			    
			case 'm':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i]==':')
				++argv[i];
			    fSortMessage=atoi( argv[i]);
			    break;

			case 'p':
			case 'P':
			    ++argv[i];
			    fsamearg=FALSE;
			    if(*argv[i]==':')
				++argv[i];
			    fprocessFiles=atoi( argv[i]);
			    fprocessOverride=TRUE;
			    break;


			case 'r':
			case 'R':
			    ++argv[i];

			    if(!argv[i][0]) /* if no parms, default to RTFDOC */
			    {
				outputType=RTFDOC;
				break;
			    }

			    switch(argv[i][0])
			    {
				case 'h':
				case 'H':
				    outputType=RTFHELP;
				    break;
				case 'd':
				case 'D':
				    outputType=RTFDOC;
				    break;

				default:
				    if(flag)
				    {
					fprintf(errfp,"%s. Line %d\n",argv[0],i);
				    }
				    error(ERR_XOPTION, copt, argv[i][0]);
				    break;
			    }
			    ++argv[i];
			    break;
			    
			case 'M':
			    ++argv[i];
			    fsamearg=TRUE;
			    fMMUserEd=1;
			    break;

			case 'Z':
			    ++argv[i];
			    while(argv[i][0])
			    {
				switch(argv[i][0])
				{
				    case 'e':	// enable extensions
					dumplevels= TRUE;
					break;
				    case 's':	// syntax check only
					noOutput = YES;
					break;
				    default:
					if(flag)
					{
					    fprintf(errfp,"%s. Line %d\n",argv[0],i);
					}
					error(ERR_XOPTION,copt, argv[i][0]);
					break;
				}
				++argv[i];
			    }
			    break;

			default:
			    error(ERR_OPTION, copt);
			    Usage();
			    exit(1);
		    }
		}
		break;
	    default:
		/* let's look to see what kind of file it is */
		j=findlshortname(argv[i]);
		if(j==strlen(argv[i]))
		{
		    /* it has no extension */
		    if(flag)
		    {
			fprintf(errfp,"%s. Line %d\n",argv[0],i);
		    }
		    error(ERR_UNKNOWN_FILE, argv[i]);
		    Usage();
		    exit(1);

		}
		else
		{
#if 0
		    if(!stricmp(argv[i]+j,".sav"))
		    {
                        add_infile(argv[i]);
		    }
		    else if(!stricmp(argv[i]+j,".ind"))
		    { /* it's an input file */
                        add_infile(argv[i]);
		    }
		    else if(!stricmp(argv[i]+j,".db"))
		    { /* it's an input file */
                        add_infile(argv[i]);
		    }
		    else if(!stricmp(argv[i]+j,".xrf"))
                    {
                        add_xrffile(argv[i]);
		    }
		    else
#endif
		    {
			/* default  file type */
			/* add it */

			if(!head_log)
			{
			    j=findlshortname(argv[i]);
			    strncpy(achtmpbuf,argv[i],j);
			    achtmpbuf[j]='\0';
			    strcat(achtmpbuf,".log");
			    add_logtoprocess(achtmpbuf);
			    error(INFO_LOGFILEDEFAULT, achtmpbuf);
			    if(!fprocessOverride)
				fprocessFiles=TRUE;
			}

			add_filetoprocess(argv[i], head_log);
#if 0
			if(!baseFile)
			{
			    argv[i][j]='\0';
			    while(j>=0 && (argv[i][j]!='\\' || argv[i][j]!='/') )
				j--;
			    j++;

			    baseFile=cp_alloc(argv[i]+j);
			}
#endif
		    }


		}
		break;
	} /* switch */
	i++;
    } /*while */

}

/*
 * @doc INTERNAL
 *
 * @func void | parseargfile | This function processes a file as arguments.
 *
 * @parm char * | filename | specifies the filename to process.
 *
 */
void
parseargfile(char *filename)
{
static reenter=0;

    FILE *fp;
    int lineno,i;
    char * * ppch;
    char *pch;
    aLine *pLine=NULL;
    aLine *pHLine=NULL;

    assert(!reenter);
    reenter++;
    fp=fopen(filename,"r");
    if(!fp)
    {
 fprintf(errfp,"Cannot open argument file %s\n",filename);
	exit(1);
    }

    lineno=0;
    while(fgets(achtmpbuf,128,fp))
    {
	    // comment is # or ; on first significant character

	pch=achtmpbuf;
	while(*pch && isspace(*pch))
	    pch++;

	if(*pch=='#' || *pch==';')
	    continue;

	pLine=lineMake(pch);
	if(pHLine)
	    pLine->next=pHLine;
	pHLine=pLine;
	lineno++;
    }
    fclose(fp);

    if(lineno>0 && pHLine )
    {
	lineno++; // leave room for arg 0
	ppch=(char * *)clear_alloc( (sizeof (char *)) * (lineno+1));
	assert(ppch);

	pLine=pHLine;
	sprintf(achtmpbuf,"Arg file: %s",filename);
	ppch[0]=cp_alloc(achtmpbuf);
	for(i=1;i<lineno;i++)
	{
	    assert(pLine);
	    ppch[i]=pLine->text;
	    pLine=pLine->next;
	}
	parsearg(lineno,ppch, TRUE);  /* do the dirty work */

	lineDestroy(pHLine);
	my_free(*ppch);
	my_free(ppch);
    }

    reenter--;

}

/*
 * @doc INTERNAL
 *
 * @func void | addexternal | This function adds the specified string
 *  to the list of defined externals to extract.
 *
 * @parm char * | pch | Specifies the string to add.
 *
 */
void
addexternal(char *pch)
{
    aLine   *pLine;

    pLine=lineMake(pch);
    assert(pLine);
    assert(pch);

    if(headExternal)	    /* insert at head */
	pLine->next=headExternal;

    headExternal=pLine;
}

/*
 * @doc INTERNAL
 *
 * @func void | setoutputfile | This sets the output file name.
 *
 * @parm char * | pch | Specifies the filename.
 *
 */
void
setoutputfile(char * pch)
{
    assert(pch);
    assert(!outputFile);

    outputFile=cp_alloc(pch);
    return;
}

/*
 * @doc INTERNAL
 *
 * @func void | settmppath | This sets the path for temporary files.
 *
 * @parm char * | pch | Specifies the path name.
 *
 */
void
settmppath(char *pch)
{
    int temp;
    char ach[80];

    assert(pch);
    assert(!dirPath);

    strcpy( ach, pch );
    temp = strlen( ach );
    if( (temp != 0) && (ach[temp-1] != '\\') )
    {
	ach[temp++] = '\\';
	ach[temp] = '\0';
    }
    dirPath=cp_alloc(ach);
    return;
}

/*
 * @doc INTERNAL
 *
 * @func files | add_outfile | This function adds the specified file to
 *  the list of output files for the specified log file.
 *
 * @parm char * | pchfile | Specifies the filename.
 *
 * @parm logentry * | pcur_log | Specifies the log file.
 *
 * @rdesc The return value is the outfile of the specified file.
 *
 */
files
add_outfile(char *pchfile, logentry *pcur_log)
{
    files   pfile;
    files   pcurfile;

    assert(pcur_log);

    pfile=(files)clear_alloc(sizeof (struct strfile));
    if(!pfile)
	return(NULL);

    pfile->filename=cp_alloc(pchfile);
    pfile->logfile=pcur_log;
    if(pcur_log->outheadFile)
    {
	pcurfile=pcur_log->outheadFile;
	while(pcurfile->next)
	{
	    pcurfile=pcurfile->next;
	}
	pcurfile->next=pfile;

    }
    else
	pcur_log->outheadFile=pfile;

    return(pfile);
}


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

/*
 * @doc INTERNAL
 *
 * @func int | putblock | This function outputs a specifed block.
 *
 * @parm aBlock * |pBlock | Specifies the block to output.
 *
 * @parm logentry * | pcur_log | Specifies the log to add the output file.
 *
 * @rdesc The return value is TRUE if no error has occured.
 *
 */
int
putblock( aBlock *pBlock, logentry *pcur_log)
{
    char    achFile[128];
    FILE    *tFile;
    files   pfile;

    assert(pBlock);
    assert(pcur_log);

    /* output function documentation */
    if( !noOutput )
    {

        mymktemp(dirPath,achFile);

	pfile=add_outfile(achFile, pcur_log);

	assert(pfile);

	/* open the file */
	tFile = fopen( pfile->filename, "w" );
	if( !tFile )
	{
	    error(ERROR10, pfile->filename);
	    return(FALSE);
	}

        /* output information */
        switch(outputType)
        {
            case VENTURA:
		VenturaBlockOut(pBlock, tFile);
		break;

            case RTFDOC:
            case RTFHELP:

		RTFBlockOut( pBlock, tFile );
                break;

            default:
  fprintf(errfp, "\nInternal Error: Unknown Output Type. Block name:%s\n",pBlock->name->text);
                break;
	}

	fclose(tFile);


        /* add to the logfile */
	tFile = fopen(pcur_log->pchlogname, "a");
	if( !tFile )
        {
	    error(ERROR9, pcur_log->pchlogname );
        }
        else
	{
	    fprintf(tFile, "%d ", pBlock->blockType);	// output type
	    VentextOut( tFile, pBlock->name, FALSE );		// block name
	    fprintf( tFile, "  \t%s\n", pfile->filename );	// filename
	    fclose( tFile );
        }

    }
    return(TRUE);

}

/*
 * @doc INTERNAL
 *
 * @func void |destroyblock | This functions deletes all the data structures
 *  associated withe a block.
 *
 * @parm aBlock *| pcurblock | Specifies the Block to be destroyed.
 *
 */
void
destroyblock(aBlock *pcurblock)
{
    aBlock *pcbBlock;

    if(!pcurblock)
	return;

    destroyBlockchain(pcurblock->cb);
    pcurblock->cb=NULL;

    /* free all elements of the aBlock structure */
	my_free( pcurblock->srcfile );
	pcurblock->srcfile=NULL;
	
    lineDestroy( pcurblock->doclevel );
    pcurblock->doclevel=NULL;

    lineDestroy( pcurblock->name );
    pcurblock->name=NULL;

    lineDestroy( pcurblock->type );
    pcurblock->type=NULL;

    lineDestroy( pcurblock->desc );
    pcurblock->desc=NULL;

    parmDestroy( pcurblock->parm );
    pcurblock->parm=NULL;

    regDestroy( pcurblock->reg );
    pcurblock->reg=NULL;

    fieldDestroy( pcurblock->field);
    pcurblock->field=NULL;
    
    otherDestroy( pcurblock->other);
    pcurblock->other=NULL;
    
    lineDestroy( pcurblock->tagname);
    pcurblock->tagname=NULL;
    
    lineDestroy( pcurblock->rtndesc );
    pcurblock->rtndesc=NULL;

    flagDestroy( pcurblock->rtnflag );
    pcurblock->rtnflag=NULL;

    lineDestroy( pcurblock->comment );
    pcurblock->comment=NULL;

    lineDestroy( pcurblock->xref );
    pcurblock->xref=NULL;
	
	/* now, free the aBlock structure itself */
	my_free( pcurblock );
}

/*
 * @doc INTERNAL
 *
 * @func void |destroyBlockchain | This function deletes a list of Blocks.
 *
 * @parm aBlock * |pcurblock | Specifies the head of the list of Blocks.
 *
 * @comm This function is recursive.
 */
void
destroyBlockchain(aBlock *pcurblock)
{
    if(!pcurblock)
	return;

    if(pcurblock->next)
	destroyBlockchain(pcurblock->next);

    destroyblock(pcurblock);
}