/*  **************************************************************************
 * 
 * --- File: abcpp.c
 * 
 * --- Purpose: simple preprocessor for abc music files.
 * 
 * --- Copyright (C) Guido Gonzato, guido dot gonzato at poste dot it
 *     Modifications by John Fattaruso, johnf@ti.com, 
 *     Ewan A. Macpherson emacpher@umich.edu, and
 *     D. Glenn Arthur Jr. dglenn@radix.net
 * 
 * --- Last updated: 22 November 2005 (unofficial hack, branching from the
 *                   source code dated 14 April 2005 -- search for instances
 *                   of the line "#ifndef OMIT_DGA_MODS" to find my changes)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * ************************************************************************ */


/* this is a no-brainer program. No efficient memory allocation, no lists,
 * no trees and some such. Only fixed-length arrays for now!
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define PROGNAME       "abcpp"

#ifndef OMIT_DGA_MODS

#define DATE           "23 November 2005"
#define VERSION        "1.3.2dga2"

#else

#define DATE           "14 April 2005"
#define VERSION        "1.3.2"

#endif

#define MAX_TOKENS     20       /* # of tokens following #ifdef etc. */
#define MAX_MACROS     100      /* # of #defined macros */
#define TOKEN_LENGHT   1024     /* max. token length */
#define LINE_LENGTH    1024     /* max. length of input line */
#define TRUE           1
#define FALSE          0
#ifdef WIN32
#define LIB_DIR    "C:\\ABCPP\\"
#else
#define LIB_DIR    "/usr/share/abcpp/"
#endif

/* function prototypes */

void error (int, char *);
void handle_directive (char *, FILE *);
void include_file (char *, FILE *);
void output_line (FILE *, char *);
void preprocess_line (FILE *, FILE *, char *);
void remove_bang (char [], short int);
void remove_deco (char [], char);
void replace (char [], char *, char *);
void replace_plus (char []);
void strdel (char [], int, int);
void strins (char [], char *, int);
void define_macro (char [], char []);
void undefine_macro (char []);
void usage (void);
void warning (int, char *);

/* global variables */

int ndefines = 0;               /* # of command line defines    */
int condition = TRUE;           /* condition after #ifdef       */
int cond_else = FALSE;          /* condition for #else          */
int ifdef = FALSE;              /* #ifdef was read              */
int nmacros = 0;                /* # of defined macros          */
int line_num = 0;               /* # of line being processed    */

/* program options */

short int strip = FALSE;        /* strip option                 */
short int strip_chords = FALSE; /* strip chords option          */
short int strip_plus = FALSE;   /* change +abc+ to [abc]        */
short int strip_bang = FALSE;   /* remove single '!'            */
short int change_bang = FALSE;  /* change single '!' to !break! */
short int undefine = FALSE;     /* #undefine was read           */
short int doremi = FALSE;       /* #define 'do', 're', 'mi'...  */
#ifndef OMIT_DGA_MODS
short int DGAoverride_defines = FALSE;  /* cmd line defs override #define */
#endif

/* this array contains command-line simple defines (existence but no value) */

char defines[MAX_TOKENS][TOKEN_LENGHT];

struct macro {
  char macroname [TOKEN_LENGHT];
  char replacement [TOKEN_LENGHT];
#ifndef OMIT_DGA_MODS
  int  cmdline;  /* Indicates whether macro was in file or on command line. */
#endif
} macros [MAX_MACROS];

#define NUM_NOTES 14

/* this one will contain Latin notes */

char notes[NUM_NOTES][2][4] = {
  {"DO", "C"}, {"RE", "D"}, {"MI", "E"}, {"FA", "F"},
  {"SOL", "G"}, {"LA", "A"}, {"SI", "B"},
  {"do", "c"}, {"re", "d"}, {"mi", "e"}, {"fa", "f"},
  {"sol", "g"}, {"la", "a"}, {"si", "b"}
};

/* ----- */

int
  main (int argc, char *argv[])
{

  FILE *in, *out;
  char line[LINE_LENGTH];
  char tmp[256], tmp1[256], tmp2[256];
  int i, nfilespec = 0;

  /* parse command line */
  /* stdin is default unless in-file specified on command line */
  in = stdin;
  /* stdout is default unless in-file and out-file specified 
   * on command line */
  out = stdout;
  for (i = 1; i < argc; ++i) {
    if ('-' == argv[i][0]) { /* it's a command-line define */
      /* check if it's a built-in define */
      if (!strcmp ("-s", argv [i])) /* strip */
	strip = TRUE;
      if (!strcmp ("-c", argv [i])) /* strip_chords */
	strip_chords = TRUE;
      if (!strcmp ("-p", argv [i])) /* strip_plus */
	strip_plus = TRUE;
      if (!strcmp ("-b", argv [i])) /* strip_bang */
	strip_bang = TRUE;
      if (!strcmp ("-k", argv [i])) /* change_bang */
	change_bang = TRUE;
#ifndef OMIT_DGA_MODS
      if (!strcmp ("-o", argv [i])) /* override defines */
	DGAoverride_defines = TRUE;
#endif
      if (!strcmp ("-h", argv [i])) {
	usage ();
	exit (0);
      }
      
      /* in any case, copy it to defines */
      if (MAX_TOKENS == ndefines) {
	sprintf (tmp, "Too many command line defines (max. %d).",
		 MAX_TOKENS);
	error (1, tmp);
	exit (1);
      }

      else {
#ifndef OMIT_DGA_MODS
	/* Look for an '=' within the token.  If present, copy the entire 
	 * token to a buffer where we can munge it into a fake "#define" 
	 * input line, and feed that to handle_directive() -- both because 
	 * I'm lazy and because I want to minimize the number of footprints 
	 * I leave on the code without knowing whether this mod will be 
	 * rolled into the official code or not.  And also because this way
	 * if the logic in handle_directives() changes later, the change
	 * does not need to be propogated to here by hand.  Trading a bit
	 * of efficiency for maintainability.  If the original author wants
	 * to redo this, my feelings will not be hurt.  (At the very least,
	 * my variables should be renamed.)  -- D. Glenn Arthur Jr.
	 */

	if ( NULL != strchr(argv[i], '=') ) {
	  char DGAtmpbuff[LINE_LENGTH]; /* Declared here to keep my mods in */
	  int  DGAtmpidx;		/* one place.  Should really be at  */
					/* top of function or re-use some   */
					/* other convenient buffer.         */
	  
	  /* Build a string that looks just like a #define in an input
	   * file:  append the command-line token to the string "#define ",
	   * then change the '=' to a space.  If the macro or the definition
	   * need to contain spaces and quotes, figuring out how to get them
	   * in here is the user's problem...
	   */

	  strcpy ( DGAtmpbuff, "#define " ); 
	  strcat ( DGAtmpbuff, &argv[i][1] );
	  DGAtmpidx = strcspn(DGAtmpbuff,"=");
	  DGAtmpbuff[DGAtmpidx] = ' ';
	  
	  /* Make the call.  It'll be as though command-line macro 
	   * definitions had been prepended to the input file; if
	   * they're redefined within the input file, those definitions
	   * will override command-line definitions, unless the '-o'
	   * option was used.
	   */

	  handle_directive ( DGAtmpbuff , out );
	}
	
	/* No '=' in the arg, so just add the token to the defines[] list
	 * instead of the macros[] list.
	 */

	else {
	  strcpy ((char *) defines[ndefines], &argv[i][1]);
	  ndefines++;
	}
#else
	strcpy ((char *) defines[ndefines], &argv[i][1]);
	ndefines++;
#endif
      }
    }
    
    else { /* it's a filespec */
      switch (nfilespec) {
	/* no files specified yet, so this is the infile spec */
      case 0:
	strcpy (tmp1, argv[i]);
	if (NULL == (in = fopen (tmp1, "r"))) {
	  sprintf (tmp, "%s: can't open %s\n", PROGNAME, tmp1);
	  error (1, tmp);
	  exit (1);
	}
	nfilespec++;
	break;
	/* 1 file specified already, so this is the outfile spec */
      case 1:
	strcpy (tmp2, argv[i]);
	
	/* check if input and output are the same file */
	if (!strcmp (tmp1, tmp2)) {
	  sprintf (tmp, "%s and %s cannot be the same file.", tmp1,
		   tmp2);
	  error (1, tmp);
	  fclose (in);
	  exit (1);
	}
	if (NULL == (out = fopen (tmp2, "w"))) {
	  sprintf (tmp, "%s: can't open %s\n", PROGNAME, tmp2);
	  error (1, tmp);
	  fclose (in);
	  exit (1);
	}
	nfilespec++;
	break;
	/* 2 files specified already - error! */
      default:
	sprintf (tmp, "Too many files specified!\n");
	error (1, tmp);
	fclose (in);
	fclose (out);
	usage ();
	exit (1);
      } /* switch */
    } /* else */
  } /* for */
  
#ifndef OMIT_DGA_MODS
  /* Now that we've finished parsing the command line and dealing with it,
   * flag any macros defined in the macros[] table so that we can avoid
   * overwriting them if the "command-line macros take precedence" option
   * was set.
   */

  {
    int DGActr; /* Declared here while code is provisional; will be moved  */
                /* to top of function if code is accepted into main distro */
    for ( DGActr=0 ; DGActr < nmacros ; DGActr++ ) {
      macros[DGActr].cmdline = TRUE;
    }
  }

  /* (Possible future mod here:  allow specification of precedence on a
   * per macro basis, maybe "-dFOO=zoink" for one that can be overriden by
   * a "#define" in the input and "-DBAR=whee" for one that overrides the
   * "#define"s?)
   */
#endif

  while (fgets (line, LINE_LENGTH, in) != NULL)
    preprocess_line (in, out, line);
  fclose (in);
  fclose (out);
  exit (0);

}


/* ----- */

void
  usage ()
{
  fprintf (stderr, "%s %s, %s\n", PROGNAME, VERSION, DATE);
  fprintf (stderr,
           "Copyright 2001-2005 Guido Gonzato <guido.gonzato at poste.it>\n");
  fprintf (stderr, "This is free software with ABSOLUTELY NO WARRANTY.\n\n");
  fprintf (stderr, "Usage: %s [-s] [-c] [-p] [-h] [-def1 -def2 ...]"
           " [inputfile] [outputfile]\n\n", PROGNAME);
  fprintf (stderr, "-s:\tstrip input of w: fields and decorations\n");
  fprintf (stderr, "-c:\tstrip input of accompaniment chords\n");
  fprintf (stderr, "-p:\tchange '+'-delimited chords to '[]'-delimited\n");
  fprintf (stderr, "-b:\tremove single '!'\n");
  fprintf (stderr, "-k:\tchange single '!' to !break!\n");
#ifndef OMIT_DGA_MODS
  fprintf (stderr, "-o:\tcommand line macros override defines in input\n");
#endif
  fprintf (stderr, "-h:\tshow usage\n");
  exit (0);
} /* void usage () */

/* ----- */

/* delete num characters from string s, starting from pos. */
void
  strdel (char s[], int pos, int num)
{
  int i, len = strlen (s);

  /* move characters to the left */
  for (i = pos + num; i < len; i++)
    s [i - num] = s [i];
  s [len - num] = '\0';
} /* strdel () */


/* ----- */

/* insert string 'ins' in string 's', starting from 'pos' */
void
  strins (char s[], char *ins, int pos)
{
  int i, inslen = strlen (ins), slen = strlen (s);

  /* move chars to the right */
  for (i = slen - 1; i >= pos; i--)
    s [i + inslen] = s [i];
  s [slen + inslen] = '\0';

  /* insert 'ins' */
  for (i = 0; i < inslen; i++)
    s [i + pos] = ins [i];
} /* strins () */

/* ----- */

void
  replace (char line [], char *orig, char *repl)
{
  int ind, len = strlen (orig), offset = 0;
  char *tmp;

  /* replace all occurrances of 'orig' in 'line' with 'repl'. The 
   * character '\' can be used to prevent 'orig' from being replaced,
   * if need be. For example, if 'do' is #defined as 'c', 
   * Guido -> Guic, Gui\do -> Guido.
   */
  while (NULL != (tmp = strstr (line + offset, orig))) {
    ind = tmp - line;         /* position of text to replace */

    /* if the text to replace isn't preceded by '\', go ahead */
    if ('\\' != line [ind - 1]) {
      strdel (line, ind, len);
      strins (line, repl, ind);
    }

    else { /* remove the '\' */
      strdel (line, ind - 1, 1);
      offset = ind + 1;     /* start searching from this new position */
    }
  }
} /* replace () */

/* ----- */

void
  define_macro (char newmacroname [], char newdefinition [])
{
   
  struct macro *itemptr;
  char tmp [TOKEN_LENGHT];

  /* replace '~' with ' ' in macro definitions */
  
  replace (newmacroname, "~", " ");
  replace (newdefinition, "~", " ");
  
  /* sort macros, find newmacroname [] to see if it's already there */
  
  qsort (macros, nmacros, sizeof (struct macro),
         (int (*)(const void *, const void *)) strcmp);
  itemptr = bsearch (newmacroname, macros, nmacros, sizeof (struct macro),
                     (int (*)(const void *, const void *)) strcmp);
   
  /* found - already defined */
  
  if (NULL != itemptr) {
#ifndef OMIT_DGA_MODS
    if ( itemptr->cmdline && DGAoverride_defines ) {
      sprintf (tmp, "Macro %s is superseded by command-line definition.",
		      newmacroname);
    warning (1, tmp);
    }
    else {
      strcpy (itemptr->macroname, newmacroname);
      strcpy (itemptr->replacement, newdefinition);
      sprintf (tmp, "Macro %s already defined.", newmacroname);
      warning (1, tmp);
    }
#else
    strcpy (itemptr->macroname, newmacroname);
    strcpy (itemptr->replacement, newdefinition);
    sprintf (tmp, "Macro %s already defined.", newmacroname);
    warning (1, tmp);
#endif
  }
  else {
    strcpy (macros [nmacros].macroname, newmacroname);
    strcpy (macros [nmacros].replacement, newdefinition);
#ifndef OMIT_DGA_MODS
    macros[nmacros].cmdline = FALSE;
#endif
    nmacros++;
  }
  
}
  
/* ----- */

void
  undefine_macro (char macro [])
{
   
  char *itemptr;
  char tmp [TOKEN_LENGHT];

  if (0 == nmacros) {
    warning (1, "No macros #defined yet - ignored.");
    return;
  }

  /* sort macros, find macro [] and remove it */
  
  qsort (macros, nmacros, sizeof (struct macro),
         (int (*)(const void *, const void *)) strcmp);
  itemptr = bsearch (macro, macros, nmacros, sizeof (struct macro),
                     (int (*)(const void *, const void *)) strcmp);
   
  /* not found */
  
  if (NULL == itemptr) {
    sprintf (tmp, "Macro %s non found in definition table - ignored.",
	     macro);
    warning (1, tmp);
    return;
  }
  else {
    strcpy (itemptr, macros [nmacros - 1].macroname);
    strcpy (itemptr + TOKEN_LENGHT, macros [nmacros - 1].replacement);
    nmacros--;
  }
} /* undefine_macro () */

/* ----- */

void
  remove_deco (char line [], char what)
{
  char *beg_deco, *end_deco;
  char drop_me [] = " ";
  int i, j;
  drop_me [0] = what;
  while (NULL != (beg_deco = strstr (line, drop_me))) {

    /* first '!' found */
    i = beg_deco - line;
    if (NULL != (end_deco = strstr (beg_deco + 1, drop_me))) {
      /* second '!' found */
      j = end_deco - beg_deco + 1;
      strdel (line, i, j);
    }

    else {
      warning (i + 1,
	       "Unbalanced char found - the line is probably wrong.");
      return;
    }
  }
} /* remove_deco () */

/* ----- */

void
  replace_plus (char line [])
{
  char *beg_chord, *end_chord;
  int i, j;
  while (NULL != (beg_chord = strstr (line, "+"))) {

    /* first '+' found */
    i = beg_chord - line;
    if (NULL != (end_chord = strstr (beg_chord + 1, "+"))) {

      /* second '+' found */
      j = end_chord - line;
      /* replace the first '+' with '[', 
       then the second '+' with ']' */
      strdel (line, i, 1);
      strins (line, "[", i); 
      strdel (line, j, 1);
      strins (line, "]", j); 
    }

    else {
      warning (i, "Unbalanced char found - the line is probably wrong.");
      return;
    }
  }
} /* replace_plus () */

/* ----- */

void
  remove_bang (char line[], short int write_break)
{
  char *bang;
  int i, skip = 0;
  short int was_deco = FALSE;
  while (NULL != (bang = strstr (line + skip, "!"))) {

    /* '!' found */
    i = bang - line;
    if ( ('\n' == line [i + 1]) || (' ' == line [i + 1]) ) {
      was_deco = FALSE;
      /* remove it, it cannot be a !decoration! */
      strdel (line, i, 1);
      if (write_break) {
	strins (line, "!break!", i);
	/* skip 7 characters */
	skip = skip + i + 7;
      }
    }
    else
      was_deco = TRUE; /* it's a !decoration! */
  }
} /* remove_bang () */

/* ----- */

void
  output_line (FILE * out, char *line)
{
  int i;
  if (condition) {
    
    /* !!! ADD OPTIONS HERE !!! */
    
    if (TRUE == strip)
      remove_deco (line, '!');
    if (TRUE == strip_chords)
      remove_deco (line, '"');
    if (TRUE == strip_plus)
      replace_plus (line);
    if (TRUE == strip_bang)
      remove_bang (line, FALSE);
    if (TRUE == change_bang)
      remove_bang (line, TRUE);
    
    else { /* replace macros and notes */
      if (!undefine)
	for (i = 0; i < nmacros; i++)
	  replace (line, macros[i].macroname, macros[i].replacement);
      if (doremi)
	for (i = 0; i < NUM_NOTES; i++)
	  replace (line, notes[i][0], notes[i][1]);
    }

    /* replace (line, "\\", ""); */
    if ((FALSE == strip) || (NULL == strstr (line, "w:")))
      fprintf (out, "%s", line);
  }
} /* void output_line () */


/* ----- */

void
  preprocess_line (FILE * in, FILE * out, char *line)
{
  line_num++;
  if (line [0] != '#')
    output_line (out, line);

  else
    handle_directive (line, out);
} /* void preprocess_line () */


/* ----- */

#define FILENAME_LENGTH 256

void
  include_file (char *file, FILE * out)
{
  FILE *in;
  char line [LINE_LENGTH],
    tmp [FILENAME_LENGTH], filename [FILENAME_LENGTH];

  /* if the file name starts with '<', then search for it in LIB_DIR */
  if ('<' == file[0]) {
    strdel (file, 0, 1);
    strdel (file, strlen (file) - 1, 1);
    strcpy (filename, LIB_DIR);
    strcat (filename, file);
  }

  else
    strcpy (filename, file);
  if (NULL == (in = fopen (filename, "r"))) {
    sprintf (tmp, "%s: can't open %s\n", PROGNAME, filename);
    error (1, tmp);
  }
  while (fgets (line, LINE_LENGTH, in) != NULL)
    preprocess_line (in, out, line);
  fclose (in);
} /* include_file () */


/* ----- */

void
  warning (int col, char *line)
{
  fprintf (stderr, "\a%s: *** warning in line %d:%d\n",
           PROGNAME, line_num, col);
  fprintf (stderr, "%s\n", line);
} /* warning () */


/* ----- */

void
  error (int col, char *line)
{
  fprintf (stderr, "\a%s: *** error in line %d:%d\n",
           PROGNAME, line_num, col);
  fprintf (stderr, "%s\n", line);
  exit (1);
} /* error () */


/* ----- */

#define N_DIRECTIVES   15       /* supported directives */

void
  handle_directive (char *line, FILE * out)
{
  int esc, ch, i, j, ntoken;
  char tokens [MAX_TOKENS][TOKEN_LENGHT], token [TOKEN_LENGHT],
    tmp [LINE_LENGTH];
  
  enum {
    COMMENT, ABC, DEFINE, DOREMI, ELIFDEF, ELIFNDEF, ELSE, ENDIF, IFDEF,
    IFNDEF, INCLUDE, REDEFINE, RESUME, SUSPEND, UNDEFINE
  } directive;
  
  char *directives [N_DIRECTIVES] = {
    "#", "#abc", "#define", "#doremi", "#elifdef", "#elifndef", "#else",
    "#endif", "#ifdef", "#ifndef", "#include", "#redefine", "#resume",
    "#suspend", "#undefine", };
  
  ch = line[0];
  i = j = ntoken = 0;
  esc = FALSE;
  while ('\0' != line[i]) {
    if ('\\' == line[i]) {
      esc = TRUE;
      i++;
    }

    /* the token starts and ends with " not preceded by \ */
    if (('"' == line[i]) && (FALSE == esc)) {
      i++;
      while (('"' != line[i]) && ('\0' != line[i])) {
	token[j++] = line[i++];
      }
      i++;
    }

    else
      while ((!isspace (line[i])) && ('\0' != line[i])) {
	if ('\\' == line[i])
	  i++;
	token[j++] = line[i++];
      }
    token [j] = '\0';
    esc = FALSE;
    strcpy (tokens [ntoken], token);
    while (isspace (line [i]))
      i++;
    j = 0;
    if (MAX_TOKENS == ++ntoken)
      warning (1, "Too many tokens on directive line - line truncated.");
  } /* while */

  /* ok, now find out the directive and decide what to do */
  /* no binary search for so few directives... */
  directive = -1;
  for (i = 0; i < N_DIRECTIVES; i++)
    if (!strcmp (tokens [0], directives [i])) {
      directive = i;
      break;
    }
  
  switch (directive) {
    
  case IFDEF:
  case IFNDEF:

    if (1 == ntoken)
      error (1, "#if(n)def must be followed by at least 1 symbol.");
    if (TRUE == ifdef)
      error (1, "Cannot nest #if(n)def.");
    ifdef = TRUE;
    condition = FALSE;
    cond_else = TRUE;
    
    /* if any of the tokens are defined, then TRUE */
    for (i = 1; i < ntoken; i++)
      for (j = 0; j < ndefines; j++)
	if (!strcmp (tokens[i], defines[j])) {
	  condition = TRUE;
	  cond_else = FALSE;
	}
    if (IFNDEF == directive) {
      condition = !condition;
      cond_else = !cond_else;
    }
    break;
    
  case ELIFDEF:
  case ELIFNDEF:

    if (1 == ntoken)
      error (1, "#elif(n)def must be followed by at least 1 symbol.");
    if (FALSE == ifdef)
      warning (1, "#elif(n)def without #ifdef - unpredictable behaviour.");
    if (FALSE == condition) {
      /* there was an #ifdef or an #elifdef */
      for (i = 1; i < ntoken; i++)
	for (j = 0; j < ndefines; j++)
	  if (!strcmp (tokens[i], defines[j])) {
	    condition = TRUE;
	    cond_else = FALSE;
	  }
    }

    else
      condition = FALSE;

    /* leave cond_else alone */
    if (ELIFNDEF == directive)
      condition = !condition;
    break;

  case ELSE:
    
    if (ntoken != 1)
      warning (1, "#else should not be followed by symbols - extra ignored.");
    if (FALSE == ifdef)
      error (1, "#else without #ifdef.");
    if (TRUE == cond_else)
      condition = TRUE;
    else
      condition = FALSE;
    break;

  case ENDIF:
    
    if (ntoken != 1)
      warning (1, "#endif should not be followed by symbols - extra ignored.");
    if (FALSE == ifdef)
      warning (1, "#endif without #ifdef - ignored.");
    condition = TRUE;
    ifdef = cond_else = FALSE;
    break;

  case DEFINE:
       
    if (condition) {
      if (ntoken > 3)
	error (1, "#define must be followed by exactly 1 string.");
      if (MAX_MACROS == nmacros) {
	warning (1,
		 "Max. number of macros reached - new definition ignored.");
	break;
      }
      if (2 == ntoken) {
	undefine_macro (tokens [1]);
	break;
      }
      define_macro (tokens [1], tokens [2]);
    }
    break;

  case INCLUDE:
    
    if (condition)
      include_file (tokens[1], out);
    break;

  case UNDEFINE:
  case SUSPEND:
    
    if (condition)
      undefine = TRUE;
    break;

  case REDEFINE:
  case RESUME:
    
    if (condition)
      undefine = FALSE;
    break;

  case ABC:
    
    if (condition)
      doremi = FALSE;
    break;

  case DOREMI:
    
    if (condition)
      doremi = TRUE;
    break;
  
  case COMMENT:
    ;
    break;
    
  default:
    
    sprintf (tmp, "%s: Unknown preprocessor directive", tokens [0]);
    warning (1, tmp);
    ;

  } /* switch (directive) */
} /* void handle_directive () */


/* ----- */

/* --- End of file abcpp.c --- */
