190 lines
4.2 KiB
C
190 lines
4.2 KiB
C
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
#include "util.h"
|
||
|
#include "ReadLine.h"
|
||
|
|
||
|
/* Pass in a pointer to a token list. Is freed and then set to null */
|
||
|
void FreeTokens(INOUTP char ***TokensPtr) {
|
||
|
assert(*TokensPtr);
|
||
|
assert(**TokensPtr);
|
||
|
|
||
|
free(**TokensPtr); /* Free the string data */
|
||
|
free(*TokensPtr); /* Free token list */
|
||
|
*TokensPtr = NULL; /* Invalidate pointer since mem is gone */
|
||
|
}
|
||
|
|
||
|
/* Returns number of tokens in list. Zero if null list */
|
||
|
int CountTokens(INP char **Tokens) {
|
||
|
int count = 0;
|
||
|
|
||
|
if (NULL == Tokens) {
|
||
|
return 0;
|
||
|
};
|
||
|
while (Tokens[count]) {
|
||
|
++count;
|
||
|
};
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
/* Reads in a single line from file, splits into tokens and allocates
|
||
|
* a list of tokens. Returns the an array of character arrays with the
|
||
|
* final item being marked by an empty string.
|
||
|
* Returns NULL on EOF
|
||
|
* NB: Token list is does as two allocations, one for pointer list
|
||
|
* and one for character array. Free what pointer points to and then
|
||
|
* free the pointer itself */
|
||
|
char **
|
||
|
ReadLineTokens(INOUTP FILE * InFile, INOUTP int *LineNum) {
|
||
|
|
||
|
enum {
|
||
|
BUFFSIZE = 65536
|
||
|
};
|
||
|
/* This is much more than enough */
|
||
|
char Buffer[BUFFSIZE]; /* Must match BUFFSIZE */
|
||
|
char *Res;
|
||
|
char *Last;
|
||
|
char *Cur;
|
||
|
char *Dst;
|
||
|
char **Tokens;
|
||
|
int TokenCount;
|
||
|
int Len;
|
||
|
int CurToken;
|
||
|
boolean InToken;
|
||
|
|
||
|
do {
|
||
|
/* Read the string */
|
||
|
Res = fgets(Buffer, BUFFSIZE, InFile);
|
||
|
if (NULL == Res) {
|
||
|
if (feof(InFile)) {
|
||
|
return NULL; /* Return NULL on EOF */
|
||
|
} else {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "Unexpected error reading file\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
++(*LineNum);
|
||
|
|
||
|
/* Strip newline if any */
|
||
|
Last = Buffer + strlen(Buffer);
|
||
|
if ((Last > Buffer) && ('\n' == Last[-1])) {
|
||
|
--Last;
|
||
|
}
|
||
|
if ((Last > Buffer) && ('\r' == Last[-1])) {
|
||
|
--Last;
|
||
|
}
|
||
|
|
||
|
/* Handle continued lines */
|
||
|
while ((Last > Buffer) && ('\\' == Last[-1])) {
|
||
|
/* Strip off the backslash */
|
||
|
--Last;
|
||
|
|
||
|
/* Read next line by giving pointer to null-char as start for next */
|
||
|
Res = fgets(Last, (BUFFSIZE - (Last - Buffer)), InFile);
|
||
|
if (NULL == Res) {
|
||
|
if (feof(InFile)) {
|
||
|
return NULL; /* Return NULL on EOF */
|
||
|
} else {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR,
|
||
|
"Unexpected error reading file\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
++(*LineNum);
|
||
|
|
||
|
/* Strip newline */
|
||
|
Last = Buffer + strlen(Buffer);
|
||
|
if ((Last > Buffer) && ('\n' == Last[-1])) {
|
||
|
--Last;
|
||
|
}
|
||
|
if ((Last > Buffer) && ('\r' == Last[-1])) {
|
||
|
--Last;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Strip comment if any */
|
||
|
Cur = Buffer;
|
||
|
while (Cur < Last) {
|
||
|
if ('#' == *Cur) {
|
||
|
Last = Cur;
|
||
|
break;
|
||
|
}
|
||
|
++Cur;
|
||
|
}
|
||
|
|
||
|
/* Count tokens and find size */
|
||
|
assert(Last < (Buffer + BUFFSIZE));
|
||
|
Len = 0;
|
||
|
TokenCount = 0;
|
||
|
Cur = Buffer;
|
||
|
InToken = FALSE;
|
||
|
while (Cur < Last) {
|
||
|
if (InToken) {
|
||
|
if ((' ' == *Cur) || ('\t' == *Cur)) {
|
||
|
InToken = FALSE;
|
||
|
} else {
|
||
|
++Len;
|
||
|
}
|
||
|
} else {
|
||
|
if ((' ' != *Cur) && ('\t' != *Cur)) {
|
||
|
++TokenCount;
|
||
|
++Len;
|
||
|
InToken = TRUE;
|
||
|
}
|
||
|
}
|
||
|
++Cur; /* Advance pointer */
|
||
|
}
|
||
|
} while (0 == TokenCount);
|
||
|
|
||
|
/* Find the size of mem to alloc. Use a contiguous block so is
|
||
|
* easy to deallocate */
|
||
|
Len = (sizeof(char) * Len) + /* Length of actual data */
|
||
|
(sizeof(char) * TokenCount); /* Null terminators */
|
||
|
|
||
|
/* Alloc the pointer list and data list. Count the final
|
||
|
* empty string we will use as list terminator */
|
||
|
Tokens = (char **) my_malloc(sizeof(char *) * (TokenCount + 1));
|
||
|
*Tokens = (char *) my_malloc(sizeof(char) * Len);
|
||
|
|
||
|
/* Copy tokens to result */
|
||
|
Cur = Buffer;
|
||
|
Dst = *Tokens;
|
||
|
InToken = FALSE;
|
||
|
CurToken = 0;
|
||
|
while (Cur < Last) {
|
||
|
if (InToken) {
|
||
|
if ((' ' == *Cur) || ('\t' == *Cur)) {
|
||
|
InToken = FALSE;
|
||
|
*Dst = '\0'; /* Null term token */
|
||
|
++Dst;
|
||
|
++CurToken;
|
||
|
} else {
|
||
|
*Dst = *Cur; /* Copy char */
|
||
|
++Dst;
|
||
|
}
|
||
|
} else {
|
||
|
if ((' ' != *Cur) && ('\t' != *Cur)) {
|
||
|
Tokens[CurToken] = Dst; /* Set token start pointer */
|
||
|
*Dst = *Cur; /* Copy char */
|
||
|
++Dst;
|
||
|
InToken = TRUE;
|
||
|
}
|
||
|
}
|
||
|
++Cur; /* Advance pointer */
|
||
|
}
|
||
|
if (InToken) {
|
||
|
*Dst = '\0'; /* Null term final token */
|
||
|
++Dst;
|
||
|
++CurToken;
|
||
|
}
|
||
|
assert(CurToken == TokenCount);
|
||
|
|
||
|
/* Set the final empty string entry */
|
||
|
Tokens[CurToken] = NULL;
|
||
|
|
||
|
/* Return the string list */
|
||
|
return Tokens;
|
||
|
}
|
||
|
|