CCF I/O Functions

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