The top half of this file implements things like say, warning, and error, as well as letting the program output its own complaints.
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "iofns.h"
#include "memfns.h"
char *current_line;
static size_t line_len;
char *lc_line;
char *token;
char *scan_from;
FILE *src_file;
char *filename;
long line_number;
int had_warning;
static void say_things (char *first, char *msg)
{
if (first)
{
fputs (first, stderr);
if (filename)
{
char *fmt = line_number ? "%s(%ld): " : "%s: ";
fprintf (stderr, fmt, filename, line_number);
}
}
fputs (msg, stderr);
fputc ('\n', stderr);
}
void FatalError (char *msg)
{
say_things ("CCF Error: ", msg);
exit (1);
}
void Warning (char *msg)
{
say_things ("CCF Warning: ", msg);
had_warning = 1;
}
void JustSay (char *msg)
{
say_things (NULL, msg);
}
void FatalPrintf (int severity, char *fmt, ...)
{
char buf [200];
va_list va_ptr;
va_start (va_ptr, fmt);
vsprintf (buf, fmt, va_ptr);
va_end (va_ptr);
(severity ? FatalError : Warning) (buf);
}
We then move on to some slightly more interesting things. GetLine() repeatedly calls fgets() until a whole line is collected, possibly calling my_realloc() (from memfns.c) a few times. Once it's got a whole line it copies and down-cases it into lc_line so the rest of the world can be case-independent.
char *GetLine (void)
{
char *eol;
char *p, *q;
size_t chunk_size;
if (lc_line)
{
my_free (lc_line);
lc_line = NULL;
}
if (line_len == 0)
current_line = my_malloc (line_len = 100);
eol = current_line;
chunk_size = line_len;
while (fgets (eol, chunk_size, src_file))
{
eol += strlen (eol);
if (eol [-1] == '\n')
{
eol [-1] = '\0';
if (eol > current_line + 1 && eol [-2] == '\r')
eol [-2] = '\0';
break;
}
else
{
size_t was_at = eol - current_line;
current_line = my_realloc (current_line, line_len += 100);
chunk_size = 100;
eol = current_line + was_at;
}
}
if (eol == current_line)
return (NULL);
scan_from = lc_line = my_malloc (1 + strlen (current_line));
for (p = lc_line, q = current_line; *q; p += 1, q += 1)
*p = tolower (*q);
*p = '\0';
line_number += 1;
return (current_line);
}
GetToken() is a cheap-and-cheerful lexical analyzer. Its idea of a token is:
Like GetLine() it allocates memory as needed, and takes responsibility for freeing the token when no longer needed.
char *GetToken (void)
{
if (token)
{
my_free (token);
token = NULL;
}
while (*scan_from && isspace (*scan_from))
scan_from += 1;
if (*scan_from)
{
char *start = scan_from;
char ch1 = *scan_from++;
size_t len;
if (ch1 != '(' && ch1 != ')' && ch1 != '-')
while ((ch1 = *scan_from) != '\0' && ! isspace (ch1) &&
ch1 != '(' && ch1 != ')')
scan_from += 1;
token = my_malloc (1 + (len = (scan_from - start)));
memcpy (token, start, len);
token [len] = '\0';
}
return (token);
}