copyright 2001, D. Glenn Arthur Jr.
[What's new at this site] Last updated 2005-11-23:.

[ About Donations ]
(Back to source code index)

Extending abcpp

A great thing about open source software is that when it almost does what you want it to, sometimes it really is easy to just tweak the source code, recompile, and have the program you really wanted. That is, once in a while you actually want to modify one of the few programs that's adequately commented, maintainably organized, and/or small enough to grok at one in the morning without already having been familiar with it.

Guido Gonzato wrote a handy little tool called 'abcpp', which adds some of the capabilities of the C preprocessor to ABC music files. If you want the official version (which includes documentation, examples, and a makefile), you can find that on the ABC Plus page at SourceForge (along with convenient descriptions, links to, and mirrors of other useful ABC software).

With abcpp 1.3.2, in addition to several other useful features (all described in the README that comes with the official distribution), you can define a token on the command line to be tested by an #ifdef, but not define a substitution macro on the command line. So I've hacked it, in a sleepy, one-in-the-morning fashion. So far I have added "-<macro>=<value>" syntax and a flag, "-o", which indicates that macros defined on the command line should override macros in #define statements in the input, instead of 'tother way 'round. A plan for sometime when I'm more awake is to add a way to designate the override behaviour on a per-macro basis instead of for the whole command line at once. In case anyone else finds this feature useful, I'm sharing it here.

I've left most of the original comments intact; what changes I've made to comments outside my "#ifndef OMIT_DGA_CODE" blocks should be either obvious or trivial. I've tested this under Linux but not Windows. I have no idea as of this writing, whether Mr. Gonzato will want to incorporate this into the official version of abcpp or not. And now I can change the MIDI instrument assignments for a whole directory of ABC tunes with a single "make" command. :-)

That's probably more than enough preamble. Here's the code ...

abcpp-132dga2.c

(download just the code without the rest of this page)

/*  **************************************************************************
 * 
 * ====================================================================== 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@panix.com
 * 
 * ====================================================================== 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 --- */

(download just the code without the rest of this page)

(Back to source code index)
(* email D. Glenn Arthur Jr. * Map Of My Web Pages * my main page * about me * musings and observations * me the musician * writings * events * humour *)
[ About Donations ]