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); }