/*ScianTextFiles.c
  Eric Pepke
  24 February 1993

  Stuff for reading text files from SciAn
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianIDs.h"
#include "ScianTextFiles.h"

TextFilePtr allTextFiles = 0;

void InitTextFiles()
/*Initialize the text file system*/
{
    allTextFiles = 0;
}

#ifdef PROTO
TextFilePtr OpenTextFile(char *name, TextFlagsTyp flags)
#else
TextFilePtr OpenTextFile(name, flags)
char *name;
TextFlagsTyp flags;
#endif
/*Opens a text file with name and flags.  Returns it, or returns null.*/
{
    FILE *filePtr;
    TextFilePtr retVal;

    if (flags & TF_READ)
    {
	filePtr = fopen(name, "r");
    }
    else
    {
	return NULL;
    }
    if (filePtr)
    {
	/*Create a text file structure*/
	retVal = newp(TextFile);
	if (!retVal)
	{
	    OMErr();
	    return NULL;
	}

	/*Fill in stuff*/
	retVal -> flags = flags;
	retVal -> eof = 0;
	retVal -> lineNum = 0;
	retVal -> nAlloc = TEXTLINEALLOCCHUNK;
	retVal -> curLine = Alloc(TEXTLINEALLOCCHUNK);
	if (!retVal -> curLine)
	{
	    Free(retVal);
	    return NULL;
	}
	retVal -> name = Alloc(strlen(name) + 1);
	if (!retVal -> name)
	{
	    Free(retVal -> curLine);
	    OMErr();
	    return NULL;
	}
	strcpy(retVal -> name, name);
	retVal -> filePtr = filePtr;
	retVal -> lineNum = 0;

	/*Link to all and return*/
	retVal -> next = allTextFiles;
	allTextFiles = retVal;
	return retVal;
    }
    else
    {
	return NULL;
    }
}

#ifdef PROTO
void CloseTextFile(TextFilePtr textFile)
#else
void CloseTextFile(textFile)
TextFilePtr textFile;
#endif
/*Closes a text file*/
{
    TextFilePtr *runner;

    fclose(textFile -> filePtr);
    Free(textFile -> name);
    Free(textFile -> curLine);
    runner = &allTextFiles;
    while (*runner)
    {
	if (*runner == textFile)
	{
	    (*runner) = (*runner) -> next;
	}
	else
	{
	    runner = &((*runner) -> next);
	}
    }
    Free(textFile);
}

#define MAXERROREND	75
#define ERRORPREVIOUS	30

#ifdef PROTO
void CurLineError(TextFilePtr textFile, char *message, char *beginning, char *end)
#else
void CurLineError(textFile, message, beginning, end)
TextFilePtr textFile;
char *message;
char *beginning;
char *end;
#endif
/*Prints to standard error an error message about reading file textFile.
  message is a message to print or NULL.  message should not contain 
    newline at the end.
  beginning and end may point to the begin and end+1 of the portion of the
  string where the error is, in which case the line or a portion of the
  line will be printed, or they may be NULL*/
{
    fprintf(stderr, "Error in file '%s' line %d%c\n",
	textFile -> name, textFile -> lineNum,
	(message || (beginning && end)) ? ':' : '.');
    if (beginning && end)
    {
	/*Determine a good place to begin and end*/
	char *s;
	int column;
	column = 0;

	for (s = textFile -> curLine;
	    (*s) && (s < end) && (column < MAXERROREND);
	    ++s)
	{
	    ++column;
	    if (*s == '\t') column = column - column % 8 + 8;
	}
	if (column < MAXERROREND)
	{
	    /*Just print normally*/

	    /*Print the line first*/
	    column = 0;
	    for (s = textFile -> curLine;
		(*s) && (*s != '\n') && column < MAXERROREND;
		++s)
	    {
		fputc(*s, stderr);
		++column;
		if (*s == '\t') column = column - column % 8 + 8;
	    }
	    if (*s && (*s != '\n'))
	    {
		/*Not really the end, continuation*/
		fprintf(stderr, "...\n");
	    }
	    else
	    {
		fprintf(stderr, "\n");
	    }

	    /*Now print the stuff underneath*/

	    column = 0;
	    for (s = textFile -> curLine;
		(*s) && (s < beginning);
		++s)
	    {
		fputc(*s == '\t' ? '\t' : ' ', stderr);
	    }

	    for (;
		 (*s) && (s < end);
		 ++s)
	    {
		fputc(*s == '\t' ? '\t' : '^', stderr);
	    }
	    fprintf(stderr, "\n");
	}
	else
	{
	    /*Back off a little*/
	}
    }
    if (message)
    {
	fprintf(stderr, "%s\n", message);
    }
}

/*States for reading in text file*/
#define TFS_BOL		1	/*Beginning of line*/
#define TFS_NORMAL	2	/*Normal*/
#define TFS_MAYBECONT	3	/*May be a continuation*/
#define TFS_ANOTHERLINE	4	/*Must read another line*/

#ifdef PROTO
char *GetNextLine(TextFilePtr textFile)
#else
char *GetNextLine(textFile)
TextFilePtr textFile;
#endif
/*Advances to the next line and gets it, or NULL if file closed or EOF or
  error*/
{
    long nChars;
    char *success;
    int k;
    int state;

    if (!(textFile -> flags | TF_READ)) return NULL;

    if (textFile -> eof) return NULL;

    for (;;)
    {
	state = TFS_BOL;

	nChars = 0;
	do
	{
	    /*Get a single line*/
	    success = fgets(tempStr, TEMPSTRSIZE, textFile -> filePtr);
	    if (!success && (state = TFS_BOL))
	    {
		/*Must be an end of file*/
		textFile -> eof = true;
		break;
	    }
	    ++textFile -> lineNum;

	    if (!success)
	    {
		/*There must be nothing more in the line*/
		break;
	    }

	    /*It's OK, add it to what's there*/
	    for (k = 0; tempStr[k]; ++k)
	    {
		if (nChars > (textFile -> nAlloc - 2))
		{
		    /*Expand buffer*/
		    textFile -> nAlloc += TEXTLINEALLOCCHUNK;
		    textFile -> curLine = Realloc(textFile -> curLine, textFile -> nAlloc);
		    if (!(textFile -> curLine))
		    {
			OMErr();
			return NULL;
		    }
		}
		if (tempStr[k] == '\n')
		{
		    /*End of line*/
		    if (state == TFS_MAYBECONT)
		    {
			/*It's a continuation line.*/
			--nChars;
			state = TFS_ANOTHERLINE;
			break;
		    }
		    else if (textFile -> flags & TF_INCLUDENL)
		    {
			textFile -> curLine[nChars++] = '\n';
			state = TFS_NORMAL;
		    }
		}
		else if ((textFile -> flags & TF_CONTINUATION) &&
		    (tempStr[k] == '\\'))
		{
		    /*It might be a continuation line*/
		    textFile -> curLine[nChars++] = '\\';
		    state = TFS_MAYBECONT;
		}
		else
		{
		    textFile -> curLine[nChars++] = tempStr[k];
		    state = TFS_NORMAL;
		}
	    }
	} while (state == TFS_ANOTHERLINE);

	/*Now the buffer has been filled.  Figure out what to do with it.*/
	if (textFile -> eof)
	{
	    /*End of file was hit.*/
	    return NULL;
	}
	textFile -> curLine[nChars] = 0;
	if ((textFile -> flags & TF_HASHCOMMENTS) &&
	    (textFile -> curLine[0] == '#'))
	{
	    /*This is a comment.  Don't do anything, just read another line*/
	}
	else
	{
	    return textFile -> curLine;
	}
    }
}

#ifdef PROTO
char *GetCurLine(TextFilePtr textFile)
#else
char *GetCurLine(textFile)
TextFilePtr textFile;
#endif
/*Gets the current line.  Returns NULL if file is closed or at EOF or error*/
{
    if (!(textFile -> flags | TF_READ)) return NULL;
    if (textFile -> eof) return NULL;
    if (textFile -> lineNum == 0)
    {
	return GetNextLine(textFile);
    }
    return (textFile -> curLine);
}

void KillTextFiles()
/*Kill the text file system*/
{
    TextFilePtr fileToClose;

    while (allTextFiles)
    {
	fileToClose = allTextFiles;
	allTextFiles = allTextFiles -> next;
	CloseTextFile(fileToClose);
    }
}
