/*****************************************************************************
*   Documentation processor of IRIT- a 3d solid modeller.		     *
******************************************************************************
* Usage:								     *
*   irithlp [-t] [-l] [-h] [-o OutFileName] [-z] [InFileName]		     *
*									     *
* Written by:  Gershon Elber				Ver 1.0, Nov. 1991   *
*****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "irit_sm.h"

#define MAX_ARRAY_SIZE	1000

typedef enum {
    UNDEFINED_DOC_PROCESS = 0,

    PLAIN_TEXT_DOCS,
    LATEX_DOCS,
    IRIT_HELP_DOCS
} DocProcessType;

struct {
    char *FindString;
    char *ReplaceString;
} GlblStrSubstStruct[] = {
    { "_IRIT_VERSION_", VERSION },
    { NULL, NULL }
};

void ProcessFiles(FILE *Fin, FILE *Fout, DocProcessType OutputDocType);
void FilterLatexMods(char *Line1, char *Line2);
void MacroSubstitute(char *Line);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module - process command line options.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv : command line options.					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   main                                                                     M
*****************************************************************************/
void main(int argc, char **argv)
{
    FILE
	*Fin = stdin,
	*Fout = stdout;
    DocProcessType
	OutputDocType = UNDEFINED_DOC_PROCESS;

    while (argc > 1) {
	if (strcmp(argv[1], "-t") == 0) {
	    OutputDocType = PLAIN_TEXT_DOCS;
	    argc--;
	    argv++;
	}
	else if (strcmp(argv[1], "-l") == 0) {
	    OutputDocType = LATEX_DOCS;
	    argc--;
	    argv++;
	}
	else if (strcmp(argv[1], "-h") == 0) {
	    OutputDocType = IRIT_HELP_DOCS;
	    argc--;
	    argv++;
	}
	else if (strcmp(argv[1], "-o") == 0) {
	    if ((Fout = fopen(argv[2], "w")) == NULL) {
		fprintf(stderr, "Failed to open \"%s\".\n", argv[2]);
		exit(1);
	    }
	    argc -= 2;
	    argv += 2;
	}
	else if (strcmp(argv[1], "-z") == 0) {
	    fprintf(stderr, "Usage: IritHlp [-t] [-l] [-h] [-o OutFileName] [-z] [InFileName].\n");
	    exit(0);
	}
	else
	    break;
    }

    if (argc > 1) {
	if ((Fin = fopen(argv[1], "r")) == NULL) {
	    fprintf(stderr, "Failed to open \"%s\".\n", argv[2]);
	    exit(1);
	}
    }

    if (OutputDocType == UNDEFINED_DOC_PROCESS) {
	fprintf(stderr, "One of [-t] [-l] [-h] must be specified.\n");
	exit(1);
    }

    ProcessFiles(Fin, Fout, OutputDocType);

    fclose(Fin);
    fclose(Fout);

    exit(0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Reads lines from Fin and dumps them to Fout after the following	     *
* processing take place:						     *
* Allways:						       		     *
*    a. Lines start with ';' are ignored ans skipped (comments).	     *
*    b. Lines start with '#' followed by four ingeters (indentation,         *
*       # elements per line, a Boolean to specify vertical seperator, and    *
*       a Boolean flag on the row (TRUE) or col (FALSE) entries order)	     *
*       signifies an array. All entries, one per line are part of the array  *
*       until another '#' occurs. Each of the method below should do its     *
*       best to create such an array.					     *
*    c. Lines start with '%' are copied verbatim to all output types.	     *
* 1. OutputDocType = PLAIN_TEXT_DOCS					     *
*    + Lines start with '*' are echoed as is with no tex related processing. *
*    + Lines start with '@' are ignored.				     *
*    + Lines start with '!' are echoed as is with underline.		     *
*    + Lines start with '$' are ignored.				     *
*    + Lines start with '^' are echoed as is.				     *
*    + Lines start with '&' are echoed as is with underline.		     *
*    + Other lines are dumped out as is after filtering out of '{\cmd * }'   *
*      latex modifiers.							     *
*    + all lines are dumped out without their first character.		     *
* 2. OutputDocType = LATEX_DOCS						     *
*    + Lines start with '*' are echoed as is with no tex related processing. *
*    + Lines start with '@' are echoed as is.				     *
*    + Lines start with '!' are ignored.				     *
*    + Lines start with '$' are ignored.				     *
*    + Lines start with '^' are ignored.				     *
*    + Lines start with '&' are ignored.				     *
*    + Other lines are dumped out as is.				     *
*    + All lines are dumped out without their first character.		     *
* 3. OutputDocType = IRIT_HELP_DOCS					     *
*    + Lines start with '*' are echoed as is with no tex related processing. *
*    + Lines start with '@' are ignored.				     *
*    + Lines start with '!' are dumped out as is.			     *
*    + Lines start with '$' causes echoing of the '$' in a single line.      *
*    + Lines start with '^' are echoed as is.				     *
*    + Lines start with '&' are ignored.				     *
*    + Other lines are dumped out as is after filtering out of '{\cmd * }'   *
*      latex modifiers.							     *
*    + All lines are dumped out with their first character as in input.      *
*                                                                            *
*                                                                            *
*                                                                            *
* PARAMETERS:                                                                *
*   Fin:           File to read from, probably irit.src                      *
*   Fout:          Output file.                                              *
*   OutputDocType: Type of output - tex, hlp, doc, etc.                      *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
void ProcessFiles(FILE *Fin, FILE *Fout, DocProcessType OutputDocType)
{
    int i, j, k,
	LineNum = 0;
    char Line1[LINE_LEN_LONG], Line2[LINE_LEN_LONG], *p;

    while (fgets(Line1, LINE_LEN_LONG - 1, Fin) != NULL) {
	LineNum++;
	MacroSubstitute(Line1);
	if (Line1[0] == ';')
	    continue;

	if (Line1[0] == '%') {
	    if (OutputDocType != LATEX_DOCS)
		fprintf(Fout, " ");
	    fprintf(Fout, &Line1[1]);
	    continue;
	}
	    
	if (Line1[0] == '#') {             /* Beginning of array definition. */
#if defined(AMIGA) && defined(__SASC)
	    static char __far ArrayEntries[MAX_ARRAY_SIZE][LINE_LEN_LONG];
#else
  	    static char ArrayEntries[MAX_ARRAY_SIZE][LINE_LEN_LONG];
#endif
	    int Indentation, NumPerLine, VertSep, NumLines, EntriesRowBased,
		StartLineNum = LineNum,
		NumArrayEntries = 0;

	    if (sscanf(&Line1[1], "%d %d %d %d",
		       &Indentation, &NumPerLine,
		       &VertSep, &EntriesRowBased) != 4) {
		fprintf(stderr, "Four integers expected in line %d\n", LineNum);
		exit(1);
	    }

	    /* Gets all the entries of the array: */
	    while ((p =	fgets(ArrayEntries[NumArrayEntries++],
					    LINE_LEN_LONG - 1, Fin)) != NULL &&
		   p[0] != '#' &&
		   NumArrayEntries < MAX_ARRAY_SIZE) {
		p += strlen(p);		      /* Remove the EOL from string. */
		while (*--p < ' ');
		p[1] = 0;

		LineNum++;
            }
	    if (p != NULL)
		LineNum++;
	    NumArrayEntries--;

	    if (p[0] != '#') {
		fprintf(stderr,
			"Array at line %d too large (max %d) or missing '#'.\n",
			StartLineNum, MAX_ARRAY_SIZE);
		exit(1);
	    }

	    if (NumPerLine == 0) {
		fprintf(stderr,
			"Number of items per line is zero at line %d.\n",
			StartLineNum);
		exit(1);
	    }

	    /* Now we have all the array elements. Computes how many lines   */
	    /* need and print the lines in the format this documentation is  */
	    /* suppose to be in.					     */
	    NumLines = (NumArrayEntries + NumPerLine - 1) / NumPerLine;
	    switch (OutputDocType) {
		case PLAIN_TEXT_DOCS:
		    sprintf(Line2, "%%-%ds", Indentation);
		    fprintf(Fout, "\n");
		    for (i = 0; i < NumLines; i++) {
			k = EntriesRowBased ? i * NumPerLine : i;
			fprintf(Fout, "   ");
			for (j = 0; j < NumPerLine; j++) {
			    if (k >= NumArrayEntries)
				fprintf(Fout, Line2, "");
			    else {
				FilterLatexMods(ArrayEntries[k],
						ArrayEntries[k]);
				fprintf(Fout, Line2, &ArrayEntries[k][1]);
			    }
			    k += EntriesRowBased ? 1 : NumLines;
			}
			fprintf(Fout, "\n");
		    }
		    fprintf(Fout, "\n");
		    break;
		case LATEX_DOCS:
		    fprintf(Fout,
			    "\n\n\\smallskip\n\n\\begin{center}\n    \\begin{tabular}{|");
		    if (VertSep)
			for (i = 0; i < NumPerLine; i++)
			    fprintf(Fout, "|l");
		    else {
			fprintf(Fout, "|");
			for (i = 0; i < NumPerLine; i++)
			    fprintf(Fout, "l");
		    }
		    fprintf(Fout,"||} \\hline \\hline\n");

		    for (i = 0; i < NumLines; i++) {
			k = EntriesRowBased ? i * NumPerLine : i;
			fprintf(Fout, "\t");
			for (j = 0; j < NumPerLine; j++) {
			    fprintf(Fout, "%s %s ",
				    k < NumArrayEntries ? &ArrayEntries[k][1] : "",
				    j == NumPerLine - 1 ? "\\\\\n" : "&");
			    k += EntriesRowBased ? 1 : NumLines;
			}
		    }
		    fprintf(Fout,
			    "\t\\hline\n    \\end{tabular}\n\\end{center}\n\n\\smallskip\n\n");
		    break;
		case IRIT_HELP_DOCS:
		    sprintf(Line2, "%%-%ds", Indentation);
		    fprintf(Fout, "\n");
		    for (i = 0; i < NumLines; i++) {
			k = EntriesRowBased ? i * NumPerLine : i;
			fprintf(Fout, "   ");
			for (j = 0; j < NumPerLine; j++) {
			    if (k >= NumArrayEntries)
				fprintf(Fout, Line2, "");
			    else {
				FilterLatexMods(ArrayEntries[k],
						ArrayEntries[k]);
				fprintf(Fout, Line2, &ArrayEntries[k][1]);
			    }
			    k += EntriesRowBased ? 1 : NumLines;
			}
			fprintf(Fout, "\n");
		    }
		    fprintf(Fout, "\n");
		    break;
		case UNDEFINED_DOC_PROCESS:
		    break;
	    }

	    continue;
	}

	switch (OutputDocType) {
	    case PLAIN_TEXT_DOCS:
		switch (Line1[0]) {
		    case '@':
		    case '$':
			break;
		    case '^':
		    case '*':
			fprintf(Fout, "%s", &Line1[1]);
			break;
		    case '&':
		    case '!':
		    	j = (78 - (int) strlen(&Line1[1])) / 2;
		    	for (i = 0; i < j; i++)
			    fprintf(Fout, " ");
			fprintf(Fout, "%s", &Line1[1]);
		    	for (i = 0; i < j; i++)
			    fprintf(Fout, " ");
			for (i = 1; i < (int) strlen(&Line1[1]); i++)
			    fprintf(Fout, "-");
			fprintf(Fout, "\n");
			break;
		    default:
			fprintf(stderr,
			    "Undefined first char command at line %d.\n",
			    LineNum);
			/* Let it print without the first char... */
		    case 0:
		    case 10:
		    case 13:
		    case ' ':
			FilterLatexMods(Line1, Line2);
			fprintf(Fout, "%s", &Line2[1]);
			break;
		}
		break;
	    case LATEX_DOCS:
		switch (Line1[0]) {
		    case '!':
		    case '$':
		    case '&':
		    case '^':
			break;
		    case '@':
		    case '*':
			fprintf(Fout, "%s", &Line1[1]);
			break;
		    default:
			fprintf(stderr,
			    "Undefined first char command at line %d.\n",
			    LineNum);
			/* Let it print without the first char... */
		    case ' ':
		    case 0:
    		    case 10:
		    case 13:
			fprintf(Fout, "%s", Line1[1] == 0 ? "\n" : &Line1[1]);
			break;
		}
		break;
	    case IRIT_HELP_DOCS:
		switch (Line1[0]) {
		    case '@':
		    case '&':
			break;
		    case '*':
		    case '^':
		    case '!':
			fprintf(Fout, "%s", &Line1[1]);
			break;
		    case '$':
			fprintf(Fout, "$\n");
			break;
		    default:
			fprintf(stderr,
			    "Undefined first char command at line %d.\n",
			    LineNum);
			/* Let it print without the first char... */
		    case ' ':
		    case 0:
		    case 10:
		    case 13:
			FilterLatexMods(Line1, Line2);
			fprintf(Fout, "%s", &Line2[1]);
			break;
		}
		break;
	    case UNDEFINED_DOC_PROCESS:
		break;
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Filters out expression of the form '{\cmd * }' in Line1 into '*' in line2. *
* These are latex modifiers not wanted in plain text output.		     *
*   Also are filtered out any \? to ?, any $*$ to * and \verb+^+ to ^.       *
*   A space is forced as first char in line.				     *
*                                                                            *
*                                                                            *
* PARAMETERS:                                                                *
*   Line1:     Input line to filter out.                                      *
*   Line2:     Filtered version of Line1.                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
void FilterLatexMods(char *Line1, char *Line2)
{
    int i = 0,
	j = 0,
	InModifier = FALSE;
    char Line3[LINE_LEN_LONG];

    Line3[j++] = ' ';

    while (Line1[i]) {
	if (Line1[i] == '{' && Line1[i + 1] == '\\' && !isspace(Line1[i + 2])) {
	    /* Here is one - filter it out. */
	    while (!isspace(Line1[i++]));
	    while (isspace(Line1[i++]));
	    i--;
	    InModifier = TRUE;
	}
	else if (Line1[i] == '}' && InModifier) {
	    i++;
	    InModifier = FALSE;
	}
	else if (strncmp(&Line1[i], "\\verb+^+", 8) == 0) {
	    i += 8;
	    Line3[j++] = '^';
	}
	else if (Line1[i] == '\\') {
	    i++;
	    Line3[j++] = Line1[i++];
	}
	else if (Line1[i] == '$' && i > 0) {
	    i++;
	}
	else
	    Line3[j++] = Line1[i++];
    }
    Line3[j] = 0;
    strcpy(Line2, Line3);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Find and replace all occurances of the string in GlblStrSubstStruct.       *
*                                                                            *
* PARAMETERS:                                                                *
*   Line:     Line to global find and replace on.                            *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
void MacroSubstitute(char *Line)
{
    int i;
    char *p, TmpLine[LINE_LEN_LONG];

    for (i = 0; GlblStrSubstStruct[i].FindString != NULL; i++) {
	while ((p = strstr(Line, GlblStrSubstStruct[i].FindString)) != NULL) {
	    strcpy(TmpLine, p + strlen(GlblStrSubstStruct[i].FindString));
	    strcpy(p, GlblStrSubstStruct[i].ReplaceString);
	    strcat(p, TmpLine);
	}
    }
}
