
/*---------------------------------------------------------------------*\
|									|
| CPP -- a stand-alone C preprocessor					|
| Copyright (c) 1993 Hacker Ltd.		Author: Scott Bigham	|
|									|
| Permission is granted to anyone to use this software for any purpose	|
| on any computer system, and to redistribute it freely, with the	|
| following restrictions:						|
| - No charge may be made other than reasonable charges for repro-	|
|     duction.								|
| - Modified versions must be clearly marked as such.			|
| - The author is not responsible for any harmful consequences of	|
|     using this software, even if they result from defects therein.	|
|									|
| process.c -- general input processing and output			|
\*---------------------------------------------------------------------*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "global.h"

struct file_state {
  char *old_curfile;
  char *old_curdir;
  FILE *old_inf;
  unsigned long old_lastline, old_thisline, old_nextline;
};

#define MAX_BLANK_LINES 5

/*
   sync_line() -- generate a synchronization line of the form "# line-num
   filename", where line-num is determined by |this_line|. Sets |last_line|
   to |this_line|.  |n| is an additional parameter to print on the line, 1
   when you enter a file and 2 when you leave it; normally it is 0 and not
   printed
*/
void sync_line(n)
  int n;
{
  switch (sl_style) {
  case SL_NONE:
    break;
  case SL_LINE:
    fprintf(outf, "#line %lu \"%s\"\n", this_line, cur_file);
    break;
  case SL_NORMAL:
    switch (n) {
    case 0:
      fprintf(outf, "# %lu \"%s\"\n", this_line, cur_file);
      break;
    case 1:
    case 2:
      fprintf(outf, "# %lu \"%s\" %d\n", this_line, cur_file, n);
      break;
    default:
      bugchk("can't sync_line(%lu,%d)\n", this_line, n);
      exit(1);
    }
    break;
  }
  last_line = this_line;
}

/*
   synchronize() -- synchronize the line number in the output file with the
   input file, either by spitting line feeds or generating a synch line. This
   should not be used if the filename has changed; use sync_line() for that.
*/
void synchronize()
{
  if (last_line > this_line || this_line - last_line > MAX_BLANK_LINES)
    sync_line(0);
  else
    for (; last_line < this_line; last_line++)
      fputc('\n', outf);
}

/*
   process_line() -- read a line from the input file token by token, doing
   the appropriate thing
*/
static int process_line()
{
  register TokenP T;
  register Macro *M;
  register int start_of_line = 1;

  for (;;) {

    /*
       We must be careful here -- the '#' for a directive cannot come from a
       macro expansion.
    */
    T = (start_of_line ? token(): exp_token());
    if (T->type == EOF_) {
      free_token(T);
      return 0;
    }
    if (T->type == EOL) {
      if (keep_comments) {
	print_token(T);
	last_line++;
      } else if (!start_of_line) {
	fputc('\n', outf);
	last_line++;
      }
      free_token(T);
      if (start_of_line)
	continue;
      else
	break;
    }
    if (start_of_line) {
      /* check for preprocessor directive */
      if (T->type == POUND) {
	free_token(T);
	directive();
	continue;
      }

      /*
         If we're in the false branch of an #ifdef and we're not a
         preprocessor directive, just pitch the line and get the next one.
      */
      if (!cond_true()) {
	free_token(T);
	flush_line();
	continue;
      }

      /*
         If we get here, we're dealing with a normal line.  We push back the
         token we just read and re-read it with exp_token() to make sure it
         gets expanded.
      */
      if (start_of_line && !in_config_file)
	synchronize();
      start_of_line = 0;
      push_tlist(T);
      T = exp_token();
    }
    print_token(T);
    free_token(T);
  }
  return 1;
}

/* path_of_file() -- extract the pathlist from filename |fnam| */
static char *path_of_file(fnam)
  char *fnam;
{
  char *s, *path;

  if (!(s = strrchr(fnam, PATH_SEP))) {
    path = mallok(2);
    path[0] = '.';
    path[1] = '\0';
  } else
    path = copy_filename(fnam, (int)(s - fnam));
  return path;
}

/*
   save_state() -- save various values particular to the current input file,
   just before opening a new file to process
*/
static void save_state(FS)
  register struct file_state *FS;
{
  FS->old_curfile = cur_file;
  FS->old_curdir = I_list[0];
  FS->old_lastline = last_line;
  FS->old_thisline = this_line;
  FS->old_nextline = next_line;
  FS->old_inf = inf;
}

/* restore_state() -- restore values saved with save_state() */
static void restore_state(FS)
  register struct file_state *FS;
{
  cur_file = FS->old_curfile;
  I_list[0] = FS->old_curdir;
  last_line = FS->old_lastline;
  this_line = FS->old_thisline;
  next_line = FS->old_nextline;
  inf = FS->old_inf;
}

/* process_file() -- open and process file |fnam| */
void process_file(fnam)
  char *fnam;
{
  struct file_state FS;

  /* save state values from the previous file */
  save_state(&FS);
  if (streq(fnam, STDIN_NAME))
    inf = stdin;
  else if (!(inf = xfopen(fnam, "r")))
    fatal("cannot open input file %s", fnam);
  cur_file = strdup(fnam);
  I_list[0] = path_of_file(fnam);
  this_line = next_line = 1;
  if (!in_config_file)
    sync_line(1);
  while (process_line()) ;
  if (include_level == 0)
    endif_check();
  if (inf != stdin)
    fclose(inf);
  free(cur_file);
  free(I_list[0]);
  restore_state(&FS);
}
