/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* Copyright (c) Philip Hazel, 1991 - 2020 */ /* Written by Philip Hazel, starting November 1991 */ /* This file last modified: December 2020 */ /* 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) { next_word(); sigchNL(); DEBUG(("pre_process(%s) entered\n", read_word)); /* Deal with "if" */ if (Ustrcmp(read_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; read_word[0] = 0; if (read_ch != '\n') next_word(); if (Ustrcmp(read_word, "not") == 0) { OK = !OK; sigchNL(); read_word[0] = 0; if (read_ch != '\n') next_word(); } if (read_word[0] == 0) { error_moan(ERR10, "Word"); break; } else { int i; if (Ustrcmp(read_word, "score") == 0) { for (i = 0; i < STAVE_BITVEC_SIZE; i++) if (curmovt->staves[i] != ~0u) { OK = !OK; break; } } else if (Ustrcmp(read_word, "part") == 0) { for (i = 0; i < STAVE_BITVEC_SIZE; i++) if (curmovt->staves[i] != ~0u) 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(read_word, "staff") == 0 || Ustrcmp(read_word, "stave") == 0 || Ustrcmp(read_word, "staves") == 0) { usint 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(ERR10, "Number"); t = s; } else t = read_integer(FALSE); } if (t < s) error_moan(ERR21); else if (t > MAX_STAVE) error_moan(ERR22, MAX_STAVE+1); else { 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(read_word, "undef") == 0) { sigchNL(); read_word[0] = 0; if (read_ch != '\n') next_word(); if (read_word[0] == 0) error_moan(ERR10, "Macro name"); else { if (Tree_Search(define_tree, read_word) != NULL) OK = !OK; } } /* Test if any format is set */ else if (Ustrcmp(read_word, "format") == 0) { if (main_format[0] == 0) OK = !OK; } /* Not recognized; take as format word */ else { if (Ustrcmp(read_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; read_word[0] = 0; next_word(); if (Ustrcmp(read_word, "or") != 0) { error_moan(ERR10, "\"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(read_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(ERR17, "\"*else\""); } DEBUG(("pre_process(else) ending\n")); return; } /* Deal with "fi" */ if (Ustrcmp(read_word, "fi") == 0) { if (read_skipdepth > 0) read_skipdepth--; else if (read_okdepth > 0) read_okdepth--; else error_moan(ERR17, "*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(read_word, "define") == 0) { if (read_ch != '\n') /* Don't use next_word(), because it */ { /* converts to lower case */ int i = 0; sigch(); if (isalnum(read_ch)) { do { if (i >= WORD_BUFFERSIZE - 1) error_moan(ERR136, "Macro name", WORD_BUFFERSIZE - 1); /* Hard */ read_word[i++] = read_ch; next_ch(); } while (isalnum(read_ch)); } read_word[i] = 0; } if (read_word[0] == 0) error_moan(ERR10, "Macro name"); else { int len; int argcount = 0; uschar *args[MAX_MACROARGS]; uschar arg[MAX_MACRODEFAULT + 1]; tree_node *p = store_Xget(sizeof(tree_node)); p->name = store_Xget(Ustrlen(read_word) + 1); Ustrcpy(p->name, read_word); if (*(--read_chptr) == '(') { while (read_chptr < read_endptr && *read_chptr != ')') { int bracount = 0; BOOL inquotes = FALSE; uschar *s = arg; if (argcount >= MAX_MACROARGS) error_moan(ERR137, MAX_MACROARGS); /* Hard */ while (++read_chptr < read_endptr && ((*read_chptr != ',' && *read_chptr != ')') || bracount > 0 || inquotes)) { int ch = *read_chptr; if (s - arg >= MAX_MACRODEFAULT) error_moan(ERR136, "Macro default argument", MAX_MACRODEFAULT); /* Hard */ 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(ERR99); 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((int)sizeof(macrostr) + (argcount-1)*((int)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(ERR14, read_word); read_chptr = read_endptr; read_ch = '\n'; } } /* Deal with included files */ else if (Ustrcmp(read_word, "include") == 0) { FILE *f; if (read_filestackptr >= MAX_INCLUDE) { error_moan(ERR15, MAX_INCLUDE); return; } if (read_ch == '\n' || !read_plainstring()) { error_moan(ERR10, "File name in quotes"); return; } sys_relativize(read_word, WORD_BUFFERSIZE); f = Ufopen(read_word, "r"); if (f == NULL) error_moan(ERR4, read_word, strerror(errno)); /* Hard error */ /* 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(read_word); DEBUG(("including file %s\n", main_filename)); } /* Deal with comment */ else if (Ustrcmp(read_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(ERR13, read_word); read_chptr = read_endptr; read_ch = '\n'; } /* Test for extraneous characters */ sigchNL(); if (read_ch != '\n') error_moan(ERR16, "Extraneous characters ignored"); DEBUG(("pre_process() ending\n")); } /* End of preprocess.c */