/* Tango/Weevil - A WEB Tangler and Weaver Copyright (C) 1995 Corey Minyard 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. Corey Minyard - minyard@metronet.com */ #include #include #include #include #define SKIP_DOCUMENTATION 1 #define OUTPUT_CODE 2 #define GOT_LINE 1 #define END_OF_FILE 0 #define MAX_FILENAME_LENGTH 200 #include "tango.h" char * r_strtok(char *data, char *sstr, char **next_data) { char *retval; while ( (*data != '\0') && (strchr(sstr, *data) != NULL)) { data++; } retval = data; while ( (*data != '\0') && (strchr(sstr, *data) == NULL)) { data++; } if (retval == data) { retval = NULL; } else { if (*data != '\0') { *data = '\0'; data++; } if (next_data != NULL) { *next_data = data; } } return(retval); } char * stralloc(char *str, int length) { char *retval; if (length == 0) { length = strlen(str); } retval = malloc(length+1); if (retval == NULL) { fprintf(stderr, "Fatal: Unable to allocate memory\n"); exit(1); } strncpy(retval, str, length); retval[length] = '\0'; return(retval); } void free_namelist_item(t_lptangodat *lptd, t_namelist *item) { if (item->name != NULL) { free(item->name); } if (item != NULL) { free(item); } } t_namelist * create_namelist_item(t_lptangodat *lptd, char *str, int length) { t_namelist *retval; retval = malloc(sizeof(*retval)); if (retval == NULL) { fprintf(stderr, "Fatal: Unable to allocate memory\n"); exit(1); } retval->name = stralloc(str, length); return(retval); } t_namelist * find_name_in_list(t_lptangodat *lptd, t_namelist *list, t_namelist *item) { while (list != NULL) { if (strcmp(list->name, item->name) == 0) { break; } list = list->next; } return(list); } void list_insert_unique(t_lptangodat *lptd, t_namelist **list, t_namelist *item) { if (find_name_in_list(lptd, *list, item) == NULL) { item->next = *list; *list = item; } else { free_namelist_item(lptd, item); } } /****************************************************************************/ static int get_input_line(t_lptangodat *lptd) { #define FLUSHSIZE 20 int retval; int length; char flushbuf[FLUSHSIZE]; int flushlength; (lptd->curr_lineno)++; if (fgets(lptd->line, lptd->maxlinesize, lptd->infile) == NULL) { retval = END_OF_FILE; /* Did not get a line, end of file. */ } else { retval = GOT_LINE; length = strlen(lptd->line); /* If the line was too long (it was the max size and the last character was not a newline), print a log and flush to end of line. */ if ( (length == (lptd->maxlinesize - 1)) && (lptd->line[length-1] != '\n')) { lptd->line[length-1] = '\n'; fprintf(stderr, "Warning, line %d too long, flushing to end of line\n", lptd->curr_lineno); if (fgets(flushbuf, FLUSHSIZE, lptd->infile) == NULL) { flushlength = 0; } else { flushlength = strlen(flushbuf); } while ( (flushlength == (FLUSHSIZE - 1)) && (lptd->line[flushlength-1] != '\n')) { if (fgets(flushbuf, FLUSHSIZE, lptd->infile) == NULL) { flushlength = 0; } else { flushlength = strlen(flushbuf); } } } } return(retval); } static void process_cmd(t_lptangodat *lptd) { char *strhold; char *cmd; char *name; int namelength; cmd = r_strtok(&(lptd->line[1]), " \t\n", &strhold); if (cmd == NULL) { fprintf(stderr, "Error on line %d: Missing command\n", lptd->curr_lineno); lptd->retcode = 2; } else if (strcmp(cmd, "code") == 0) { if (lptd->outstate == OUTPUT_CODE) { fprintf(stderr, "Error on line %d: Macro begin when already processing a macro\n", lptd->curr_lineno); lptd->retcode = 2; } else { lptd->curr_macro = malloc(sizeof(*(lptd->curr_macro))); if (lptd->curr_macro == NULL) { fprintf(stderr, "Fatal: Unable to allocate memory\n"); exit(1); } while ((*strhold == ' ') || (*strhold == '\t') || (*strhold == '\n')) { strhold++; } if (*strhold != '<') { fprintf(stderr, "Error on line %d: Macro name must be surrounded by <>\n", lptd->curr_lineno); lptd->retcode = 2; } else { strhold++; } name = r_strtok(strhold, ">", &strhold); lptd->curr_macro->name = stralloc(name, 0); lptd->curr_macro->filename = lptd->curr_filename; lptd->curr_macro->startline = lptd->curr_lineno + 1; lptd->curr_macro->firstline = NULL; lptd->curr_macro->lastline = NULL; lptd->curr_macro->staticdefs = NULL; lptd->curr_macro->globaldefs = NULL; lptd->curr_macro->pounddefs = NULL; lptd->curr_macro->uses = NULL; lptd->outstate = OUTPUT_CODE; } } else if (strcmp(cmd, "file") == 0) { if (lptd->outstate == OUTPUT_CODE) { fprintf(stderr, "Error on line %d: File name change not allowed in code\n", lptd->curr_lineno); lptd->retcode = 2; } else { name = r_strtok(strhold, " \t\n", &strhold); if (name == NULL) { fprintf(stderr, "Error on line %d: File name not given with file command\n", lptd->curr_lineno); lptd->retcode = 2; } lptd->curr_filename = stralloc(name, 0); namelength = strlen(name); name = r_strtok(strhold, " \t\n", &strhold); if (name == NULL) { lptd->curr_lineno = 0; } else { lptd->curr_lineno = atoi(name) - 1; } } } else if (strcmp(cmd, "line") == 0) { if (lptd->outstate == OUTPUT_CODE) { fprintf(stderr, "Error on line %d: Line number change not allowed in code\n", lptd->curr_lineno); lptd->retcode = 2; } else { name = r_strtok(strhold, " \t\n", &strhold); if (name == NULL) { fprintf(stderr, "Error on line %d: Line number not specified in line command\n", lptd->curr_lineno); lptd->retcode = 2; } else { lptd->curr_lineno = atoi(name) - 1; } } } else if ( (strcmp(cmd, "includefile") == 0) || (strcmp(cmd, "reffile") == 0)) { if (lptd->outstate == OUTPUT_CODE) { fprintf(stderr, "Error on line %d: %s not allowed in code\n", lptd->curr_lineno, cmd); lptd->retcode = 2; } /* This is ignored in tangling. */ } else { fprintf(stderr, "Error on line %d: Invalid command\n", lptd->curr_lineno); lptd->retcode = 2; } } static void process_codeline(t_lptangodat *lptd) { t_linelist *thisline; thisline = malloc(sizeof(*thisline)); if (thisline == NULL) { fprintf(stderr, "Fatal: Unable to allocate memory\n"); exit(1); } thisline->line = stralloc(lptd->line, 0); thisline->next = NULL; if (lptd->curr_macro->lastline == NULL) { lptd->curr_macro->lastline = thisline; lptd->curr_macro->firstline = thisline; } else { lptd->curr_macro->lastline->next = thisline; lptd->curr_macro->lastline = thisline; } } static void output_macro(t_lptangodat *lptd, t_lpmac *outmac); static void output_line(t_lptangodat *lptd, t_linelist *outline, char *filename, int lineno) { t_lpmac *cm; t_lpmac *prev_macro; char *cp; int currchar; char *name; currchar = 0; cp = outline->line; while (cp[currchar] != '\0') { if (cp[currchar] == '@') { if (currchar > 0) { fwrite(cp, 1, currchar, lptd->outfile); lptd->scan_input(lptd, cp, currchar, lineno); } if ( (lptd->instring) || (lptd->in_comment)) { fputc('@', lptd->outfile); lptd->scan_input(lptd, "@", 1, lineno); currchar++; cp = &(cp[currchar]); currchar = 0; } else { currchar++; if (cp[currchar] == '@') { fputc('@', lptd->outfile); lptd->scan_input(lptd, "@", 1, lineno); currchar++; cp = &(cp[currchar]); currchar = 0; } else if (cp[currchar] == '<') { fputc('\n', lptd->outfile); currchar++; name = r_strtok(&(cp[currchar]), ">", &cp); currchar = 0; cm = lptd->macros; while ( (cm != NULL) && (strcmp(cm->name, name) != 0)) { cm = cm->next; } if (cm == NULL) { fprintf(stderr, "Error: macro %s used on line %d but not found\n", name, lineno); lptd->retcode = 2; } else { prev_macro = lptd->curr_macro; lptd->curr_macro = cm; output_macro(lptd, cm); lptd->curr_macro = prev_macro; if (lptd->do_linenums) { lptd->output_linenum(lptd, lineno, filename); } } } else { fprintf(stderr, "Error on line %d: Invalid operation \"@%c\"\n", lineno, cp[currchar]); lptd->retcode = 2; } } } else { currchar++; } } if (currchar > 0) { fwrite(cp, 1, currchar, lptd->outfile); lptd->scan_input(lptd, cp, currchar, lineno); } } static void output_macro(t_lptangodat *lptd, t_lpmac *outmac) { t_linelist *cl; int lineno; lineno = outmac->startline; cl = outmac->firstline; if (lptd->do_linenums) { lptd->output_linenum(lptd, lineno, outmac->filename); } while (cl != NULL) { lptd->curr_line = cl; output_line(lptd, cl, outmac->filename, lineno); lineno++; cl = cl->next; } } static void output_macros(t_lptangodat *lptd) { t_lpmac *cm; cm = lptd->macros; while ( (cm != NULL) && (strcmp(cm->name, lptd->start_macro) != 0)) { cm = cm->next; } if (cm == NULL) { fprintf(stderr, "Fatal: Start macro %s not found\n", lptd->start_macro); exit(1); } lptd->curr_macro = cm; output_macro(lptd, cm); } static char * get_arg_str(int *argidx, int argc, char *argv[], int start_offset) { char *retval; if (argv[*argidx][start_offset] == '\0') { (*argidx)++; if ((*argidx) == argc) { retval = NULL; } else { retval = argv[*argidx]; } } else { retval = &(argv[*argidx][start_offset]); } return(retval); } void c_scan_input(t_lptangodat *lptd, char *line, int length, int lineno); void c_output_linenum(t_lptangodat *lptd, int lineno, char *filename); void init_c_lang(t_lptangodat *lptd); static struct s_langs { char *lang_name; input_scanner scanner; linenum_output output_linenum; scanner_init init; } langs[] = { { "c", &c_scan_input, &c_output_linenum, &init_c_lang } }; static const num_langs = sizeof(langs) / sizeof(struct s_langs); static void process_args(t_lptangodat *lptd, int argc, char *argv[], int *argidx) { char *str; int i; *argidx = 1; while (argv[*argidx][0] == '-') { if (strcmp(argv[*argidx], "--") == 0) { (*argidx)++; break; } else if (strncmp(argv[*argidx], "-autoxref", 5) == 0) { lptd->do_xref = TRUE; lptd->auto_xref = TRUE; } else if (strncmp(argv[*argidx], "-xref", 5) == 0) { lptd->do_xref = TRUE; } else if (strncmp(argv[*argidx], "-lang", 5) == 0) { if (lptd->scan_input != NULL) { fprintf(stderr, "%s: Error: only 1 -lang is allowed\n", argv[0]); exit(1); } str = get_arg_str(argidx, argc, argv, 5); if (str == NULL) { fprintf(stderr, "%s: Error: -lang specified but no lang given\n", argv[0]); exit(1); } for (i=0; iscan_input = langs[i].scanner; lptd->output_linenum = langs[i].output_linenum; langs[i].init(lptd); break; } } if (lptd->scan_input == NULL) { fprintf(stderr, "%s: Error: Invalid lang given: %s\n", argv[0], str); exit(1); } } else if (strncmp(argv[*argidx], "-nolinenum", 10) == 0) { lptd->do_linenums = FALSE; } else if (strncmp(argv[*argidx], "-xreffile", 9) == 0) { lptd->do_xref = TRUE; str = get_arg_str(argidx, argc, argv, 9); if (str == NULL) { fprintf(stderr, "%s: Error: -xreffile specified but no file given\n", argv[0]); exit(1); } lptd->xreffile = fopen(str, "w"); if (lptd->xreffile == NULL) { fprintf(stderr, "%s: Error: Could not open cross ref file: %s\n", argv[0], str); exit(1); } } else if (strncmp(argv[*argidx], "-start_macro", 11) == 0) { lptd->start_macro = get_arg_str(argidx, argc, argv, 11); if (lptd->start_macro == NULL) { fprintf(stderr, "Error: -s specified but no start macro given\n"); exit(1); } } else { fprintf(stderr, "Error: Invalid option specified: %s\n", argv[*argidx]); exit(1); } (*argidx)++; } } static void print_list(t_lptangodat *lptd, char *prefix, t_namelist *names) { while (names != NULL) { fprintf(lptd->xreffile, "%s %s\n", prefix, names->name); names = names->next; } } static void output_xref(t_lptangodat *lptd) { t_lpmac *macros; fprintf(lptd->xreffile, "f %s\n", lptd->curr_filename); macros = lptd->macros; while (macros != NULL) { fprintf(lptd->xreffile, "m %s\n", macros->name); print_list(lptd, "d", macros->pounddefs); print_list(lptd, "e", macros->globaldefs); print_list(lptd, "s", macros->staticdefs); print_list(lptd, "u", macros->uses); macros = macros->next; } } int main(int argc, char *argv[]) { t_lptangodat *lptd; int argidx; char *str; char fname[MAX_FILENAME_LENGTH+1]; int len; char *name; t_namelist *item; lptd = malloc(sizeof(*lptd)); if (lptd == NULL) { fprintf(stderr, "Unable to allocate enough memory\n"); exit(1); } lptd->scan_input = NULL; lptd->maxlinesize = MAXLINESIZE; lptd->curr_lineno = 0; lptd->macros = NULL; lptd->start_macro = "*"; lptd->outfile = stdout; lptd->xreffile = NULL; lptd->do_xref = FALSE; lptd->auto_xref = FALSE; lptd->in_comment = FALSE; lptd->do_linenums = TRUE; lptd->instring = FALSE; lptd->retcode = 0; process_args(lptd, argc, argv, &argidx); if (lptd->scan_input == NULL) { fprintf(stderr, "%s: No lang specified\n", argv[0]); exit(1); } if (argc != (argidx + 1)) { fprintf(stderr, "%s: No input file specified\n", argv[0]); exit(1); } else { lptd->infile = fopen(argv[argidx], "r"); if (lptd->infile == NULL) { fprintf(stderr, "%s: Unable to open file %s\n", argv[0], argv[argidx]); exit(1); } lptd->curr_filename = argv[argidx]; } if ((lptd->do_xref) && (lptd->xreffile == NULL)) { str = strrchr(argv[argidx], '.'); if (str != NULL) { len = str - argv[argidx]; } else { len = strlen(argv[argidx]); } if ((len+4) >= MAX_FILENAME_LENGTH) /* Add 4 for the .xfr */ { fprintf(stderr, "filename to long, no xref file generated\n"); } else { memcpy(fname, argv[argidx], len); strcpy(&(fname[len]), ".xfr"); lptd->xreffile = fopen(fname, "w"); if (lptd->xreffile == NULL) { fprintf(stderr, "%s: Error: Could not open cross ref file: %s\n", argv[0], str); exit(1); } } } lptd->outstate = SKIP_DOCUMENTATION; while (get_input_line(lptd) == GOT_LINE) { if (lptd->outstate == OUTPUT_CODE) { if (strncmp(lptd->line, "@endcode", 8) == 0) { lptd->curr_macro->next = lptd->macros; lptd->macros = lptd->curr_macro; lptd->outstate = SKIP_DOCUMENTATION; } else if (strncmp(lptd->line, "@uses", 5) == 0) { if (lptd->outstate != OUTPUT_CODE) { fprintf(stderr, "Error on line %d: %s not allowed outside of code\n", lptd->curr_lineno, lptd->line); lptd->retcode = 2; } else { name = r_strtok(&(lptd->line[5]), " \t\n", NULL); if (name == NULL) { fprintf(stderr, "Error on line %d: name not given with uses command\n", lptd->curr_lineno); lptd->retcode = 2; } else { item = create_namelist_item(lptd, name, 0); list_insert_unique(lptd, &(lptd->curr_macro->uses), item); } } strcpy(lptd->line, "\n"); process_codeline(lptd); } else if (strncmp(lptd->line, "@defines", 8) == 0) { if (lptd->outstate != OUTPUT_CODE) { fprintf(stderr, "Error on line %d: %s not allowed outside of code\n", lptd->curr_lineno, lptd->line); lptd->retcode = 2; } else { name = r_strtok(&(lptd->line[8]), " \t\n", NULL); if (name == NULL) { fprintf(stderr, "Error on line %d: name not given with defines command\n", lptd->curr_lineno); lptd->retcode = 2; } else { item = create_namelist_item(lptd, name, 0); list_insert_unique(lptd, &(lptd->curr_macro->pounddefs), item); } } strcpy(lptd->line, "\n"); process_codeline(lptd); } else if (strncmp(lptd->line, "@externdecls", 12) == 0) { if (lptd->outstate != OUTPUT_CODE) { fprintf(stderr, "Error on line %d: %s not allowed outside of code\n", lptd->curr_lineno, lptd->line); lptd->retcode = 2; } else { name = r_strtok(&(lptd->line[12]), " \t\n", NULL); if (name == NULL) { fprintf(stderr, "Error on line %d: name not given with externdecls command\n", lptd->curr_lineno); lptd->retcode = 2; } else { item = create_namelist_item(lptd, name, 0); list_insert_unique(lptd, &(lptd->curr_macro->globaldefs), item); } } strcpy(lptd->line, "\n"); process_codeline(lptd); } else if (strncmp(lptd->line, "@staticdecls", 12) == 0) { if (lptd->outstate != OUTPUT_CODE) { fprintf(stderr, "Error on line %d: %s not allowed outside of code\n", lptd->curr_lineno, lptd->line); lptd->retcode = 2; } else { name = r_strtok(&(lptd->line[12]), " \t\n", NULL); if (name == NULL) { fprintf(stderr, "Error on line %d: name not given with staticdecls command\n", lptd->curr_lineno); lptd->retcode = 2; } else { item = create_namelist_item(lptd, name, 0); list_insert_unique(lptd, &(lptd->curr_macro->staticdefs), item); } } strcpy(lptd->line, "\n"); process_codeline(lptd); } else { process_codeline(lptd); } } else if (lptd->line[0] == '@') { process_cmd(lptd); } /* Ignore documentation. */ } if (lptd->outstate == OUTPUT_CODE) { fprintf(stderr, "Warning: File %s ended while outputting code", lptd->curr_filename); lptd->retcode = 2; lptd->outstate = SKIP_DOCUMENTATION; } output_macros(lptd); if (lptd->xreffile != NULL) { output_xref(lptd); } return(lptd->retcode); }