/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2008 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: September 2008 */


/* This file contains code for handling "preprocessing" functions. */


#include "pmwhdr.h"
#include "readhdr.h"




/*************************************************
*        Deal with pre-processing directive      *
*************************************************/

/* We enter with read_ptr pointing to the first letter of the directive's name,
so we are guaranteed to read a word. We can use normal item reading routines,
but must take care not to do so at the end of a line.

Arguments: none
Returns:   nothing
*/

void 
pre_process(void)
{
uschar word[256];
read_word(word);
sigchNL();

DEBUG(("pre_process(%s) entered\n", word));

/* Deal with "if" */

if (Ustrcmp(word, "if") == 0)
  {
  int OK; 
  if (read_skipdepth > 0)
    {
    read_skipdepth++;
    read_chptr = read_endptr;
    read_ch = '\n';
    DEBUG(("pre_process(if) ending\n")); 
    return; 
    }
     
  for (;;)
    { 
    OK = TRUE;
    word[0] = 0;
    if (read_ch != '\n') read_word(word);
    
    if (Ustrcmp(word, "not") == 0)
      {
      OK = !OK;
      sigchNL();
      word[0] = 0;
      if (read_ch != '\n') read_word(word);
      }
    
    if (word[0] == 0) 
      { error_moan(10, "Word"); break; } 
    else
      {
      int i;
       
      if (Ustrcmp(word, "score") == 0)
        {
        for (i = 0; i < stave_bitvec_size; i++) 
          if (curmovt->staves[i] != -1) { OK = !OK; break; }
        }
      else if (Ustrcmp(word, "part") == 0)
        {
        for (i = 0; i < stave_bitvec_size; i++) 
          if (curmovt->staves[i] != -1) break;
        if (i >= stave_bitvec_size) OK = !OK;
        }
    
      /* For a stave list, we can't use a common routine with other directives
      because we mustn't stray over the end of the line! */
    
      else if (Ustrcmp(word, "staff") == 0 || Ustrcmp(word, "stave") == 0 ||
              Ustrcmp(word, "staves") == 0)
        {
        int map[stave_bitvec_size];
        mac_initstave(map, 0); 
        mac_setstave(map, 0);       /* Staff zero is always selected */
        sigchNL();
    
        while (isdigit(read_ch))
          {
          int s = read_integer(FALSE);
          int t = s;
          sigchNL();
          if (read_ch == '-')
            {
            next_ch();
            sigchNL();
            if (!isdigit(read_ch))
              {
              error_moan(10, "Number");
              t = s;
              }
            else t = read_integer(FALSE);
            }
    
          if (t < s) error_moan(21);
            else if (t > max_stave) error_moan(22, max_stave+1);
          else
            {
            int i;
            for (i = s; i <= t; i++) mac_setstave(map, i);
            }
    
          sigchNL();
          if (read_ch == ',')
            {
            next_ch();
            sigchNL();
            }
          }
    
        for (i = 0; i < stave_bitvec_size; i++)
          if (map[i] != curmovt->staves[i]) { OK = !OK; break; }
        }
    
      /* Deal with definition test */
    
      else if (Ustrcmp(word, "undef") == 0)
        {
        sigchNL();
        word[0] = 0;
        if (read_ch != '\n') read_word(word);
        if (word[0] == 0) error_moan(10, "Macro name"); else
          { 
          if (Tree_Search(define_tree, word) != NULL) OK = !OK;
          }
        }
        
      /* Test if any format is set */
      
      else if (Ustrcmp(word, "format") == 0)
        {
        if (main_format[0] == 0) OK = !OK; 
        }   
    
      /* Not recognized; take as format word */
    
      else
        {
        if (Ustrcmp(word, main_format) != 0) OK = !OK;
          else main_format_tested = TRUE; 
        }
      }
       
    /* See if the next thing is "or"; if not and if not newline, error.
    Otherwise, if it's "or" and OK == FALSE, let the loop continue. */
    
    sigchNL();
    if (read_ch == '\n') break;  
    word[0] = 0;
    read_word(word);
    if (Ustrcmp(word, "or") != 0) { error_moan(10, "\"or\""); break; }
    if (OK) break;
    sigchNL(); 
    }   

  /* Decision taken; act appropriately */

  if (OK) read_okdepth++; else read_skipdepth++;
  DEBUG(("pre_process(if) ending\n")); 
  return; 
  }


/* Deal with "else" */

if (Ustrcmp(word, "else") == 0)
  {
  if (read_skipdepth <= 1)
    {
    if (read_skipdepth == 1)
      {
      read_skipdepth--;
      read_okdepth++;
      }
    else if (read_okdepth > 0)
      {
      read_skipdepth++;
      read_okdepth--;
      }
    else error_moan(17, "\"*else\"");
    }
  DEBUG(("pre_process(else) ending\n")); 
  return;   
  }


/* Deal with "fi" */

if (Ustrcmp(word, "fi") == 0)
  {
  if (read_skipdepth > 0) read_skipdepth--; else
    if (read_okdepth > 0) read_okdepth--;
      else error_moan(17, "*fi");
  DEBUG(("pre_process(fi) ending\n")); 
  return;     
  }


/* Others are only looked at when not skipping */

if (read_skipdepth >  0) 
  {
  DEBUG(("pre_process() ending -- skipping\n")); 
  return;
  } 

/* Deal with defining names */

if (Ustrcmp(word, "define") == 0)
  {
  if (read_ch != '\n')     /* Don't use read_word, because it */
    {                      /*   converts to lower case */
    int i = 0;
    sigch();
    if (isalnum(read_ch))
      {
      do
        {
        word[i++] = read_ch;
        next_ch();
        }
      while (isalnum(read_ch));
      }
    word[i] = 0;
    }

  if (word[0] == 0) error_moan(10, "Macro name"); else
    {
    int len;
    int argcount = 0; 
    uschar *args[20]; 
    uschar arg[256]; 
    tree_node *p = store_Xget(sizeof(tree_node));

    p->name = store_Xget(Ustrlen(word) + 1);
    Ustrcpy(p->name, word);
    
    if (*(--read_chptr) == '(')
      {
      while (read_chptr < read_endptr && *read_chptr != ')')
        {
        int bracount = 0; 
        BOOL inquotes = FALSE; 
        uschar *s = arg; 
        while (++read_chptr < read_endptr && 
              ((*read_chptr != ',' && *read_chptr != ')') ||
                bracount > 0 || inquotes))  
          {
          int ch = *read_chptr; 
          if (ch == '&') *s++ = *(++read_chptr); else
            { 
            if (ch == '\"') inquotes = !inquotes; 
            if (!inquotes)
              { 
              if (ch == '(') bracount++;
                else if (ch == ')') bracount--; 
              }   
            *s++ = ch;
            } 
          }
          
        if (read_chptr >= read_endptr) error_moan(99);
        if (s - arg > 0) 
          { 
          uschar *ss = store_Xget(s - arg + 1); 
          *s = 0; 
          Ustrcpy(ss, arg); 
          args[argcount++] = ss; 
          }  
        else args[argcount++] = NULL;   
        } 

      if (*read_chptr == ')') read_chptr++;
      }  

    while (*read_chptr == ' ' || *read_chptr == '\t') read_chptr++;
    while (read_endptr[-1] == ' ' || read_endptr[-1] == '\t') read_endptr--;

    len = read_endptr - read_chptr;
    if (len <= 0 && argcount == 0) p->data = NULL; else
      {
      int i; 
      uschar *pp = store_Xget(len + 1);
      
      macrostr *mm = 
        store_Xget(sizeof(macrostr) + (argcount-1)*(sizeof(uschar *)));

      mm->argcount = argcount;
      p->data = (uschar *)mm;
      mm->text = pp; 
      while (len-- > 0) *pp++ = *read_chptr++;
      *pp = 0;
      for (i = 0; i < argcount; i++) mm->args[i] = args[i]; 
      }

    if (!Tree_InsertNode(&define_tree, p)) error_moan(14, word);
    read_chptr = read_endptr;
    read_ch = '\n';
    }
  }


/* Deal with included files */

else if (Ustrcmp(word, "include") == 0)
  {
  FILE *f;
  
  if (read_filestackptr >= max_include)
    { error_moan(15, max_include); return; }

  if (read_ch == '\n' || !read_plainstring(word))
    { error_moan(10, "File name in quotes"); return; }

  sys_relativize(word);
 
  f = Ufopen(word, "r");
  if (f == NULL) { error_moan(4, word, strerror(errno)); return; }

  /* Stack the current variables and replace with new ones. The
  size is added to the total size expected. */

  read_filestack[read_filestackptr].file = input_file;
  read_filestack[read_filestackptr].filename = main_filename;
  read_filestack[read_filestackptr].linenumber = read_linenumber;
  read_filestack[read_filestackptr++].okdepth = read_okdepth;

  input_file = f;
  read_okdepth = 0;
  read_linenumber = 0;
  main_filename = store_copystring(word);
  
  DEBUG(("including file %s\n", main_filename));
  }


/* Deal with comment */

else if (Ustrcmp(word, "comment") == 0)
  {
  uschar *s = read_chptr - 1;
  fprintf(stderr, "%s\n", s);
  read_chptr = read_endptr;
  read_ch = '\n';
  }


/* Else unknown preprocessing directive */

else
  {
  error_moan(13, word);
  read_chptr = read_endptr;
  read_ch = '\n';
  }

/* Test for extraneous characters */

sigchNL();
if (read_ch != '\n') error_moan(16, "Extraneous characters ignored");

DEBUG(("pre_process() ending\n")); 
}


/* End of preprocess.c */
