/* Macro.c * * Purpose: Let the user create and execute macros. */ #include "Sys.h" #include #include "Util.h" #include "Macro.h" #include "Cmds.h" #include "Cmdline.h" #include "MakeArgv.h" /* First and last nodes in the macro list. */ MacroNodePtr gFirstMacro = NULL; MacroNodePtr gLastMacro = NULL; /* Number of macros in the macro list. */ int gNumGlobalMacros = 0; /* We need this routine in case the user wants to redefine an existing * macro. */ void DisposeMacro(MacroNodePtr macro) { MacroNodePtr prevMac, nextMac; if (macro == NULL) return; /* Dispose of the linked-list of data lines first. */ DisposeLineListContents(¯o->macroData); /* Repair the macro list. */ prevMac = macro->prev; nextMac = macro->next; if (prevMac == NULL) gFirstMacro = nextMac; if (nextMac != NULL) nextMac->prev = prevMac; if (nextMac == NULL) gLastMacro = prevMac; if (prevMac != NULL) prevMac->next = nextMac; free(macro->name); free(macro); --gNumGlobalMacros; } /* DisposeMacro */ char *MacroGetLine(char *macline, FILE *fp) { char *cp; string str; *macline = '\0'; while ((fgets(str, ((int) sizeof(str)) - 1, fp)) != NULL) { cp = strrchr(str, '#'); if (cp != NULL) *cp = '\0'; cp = str + strlen(str) - 1; /* Strip trailing space. */ while (cp >= str && isspace(*cp)) *cp-- = '\0'; /* Strip leading space. */ for (cp = str; isspace(*cp); ) ++cp; if (*cp != '\0') { Strncpy(macline, cp, sizeof(string)); return (macline); } } return (NULL); } /* MacroGetLine */ /* Reads in the macro line by line, until we reach a special keyword * that designates the end. */ MacroNodePtr CollectMacro(char *macroName, char *endKeyword, FILE *fp) { string macline; MacroNodePtr macro; char *name; /* If we already have a macro by that name, we'll dispose of this * macro and replace it. */ DisposeMacro(FindMacro(macroName)); if ((name = StrDup(macroName)) == NULL) return NULL; macro = (MacroNodePtr) calloc((size_t)1, sizeof(MacroNode)); if (macro == NULL) return NULL; macro->name = name; InitLineList(¯o->macroData); while (1) { if ((MacroGetLine(macline, fp)) == NULL) return NULL; /* incomplete macro. */ /* See if we have found the terminating keyword. */ if (STREQ(endKeyword, macline)) break; AddLine(¯o->macroData, macline); } return (macro); } /* CollectMacro */ /* Adds a macro to the macro list. */ void AttachMacro(MacroNodePtr macro) { if (macro != NULL) { macro->next = NULL; if (gFirstMacro == NULL) { macro->prev = NULL; gFirstMacro = gLastMacro = macro; } else { gLastMacro->next = macro; macro->prev = gLastMacro; gLastMacro = macro; } ++gNumGlobalMacros; } } /* AttachMacro */ void ReadMacroFile(void) { FILE *fp; string str; longstring fName; char *cp; MacroNodePtr macp; size_t len; (void) OurDirectoryPath(fName, sizeof(fName), kMacroFileName); fp = fopen(fName, "r"); if (fp == NULL) /* It's okay not to have one. Most folks won't. */ return; len = strlen(kMacroStartToken); while (1) { if ((MacroGetLine(str, fp)) == NULL) break; /* See if we have found a starting keyword. */ if (STRNEQ(kMacroStartToken, str, len)) { cp = str + 6; if (*cp != '\0') { macp = CollectMacro(cp, kMacroEndToken, fp); AttachMacro(macp); } } } DebugMsg("Read %d macros from %s.\n", gNumGlobalMacros, fName); (void) fclose(fp); } /* ReadMacroFile */ /* Runs each line in a macro. */ int ExecuteMacro(MacroNodePtr macro, int argc, char **argv) { LinePtr mlp; int err, i; char *line2; if (macro == NULL) return -1; line2 = (char *) malloc((size_t) 1024); if (line2 == NULL) { Error(kDontPerror, "Not enough memory to run macro \"%s.\"\n", macro->name); return -1; } DebugMsg("Macro: %s\n", macro->name); for (i=1; imacroData.first; mlp != NULL; mlp = mlp->next) { /* Insert arguments passed to the macro. */ (void) Strncpy(line2, mlp->line, (size_t)1024); ExpandDollarVariables(line2, (size_t)1024, argc, argv); DebugMsg("%s: %s\n", macro->name, line2); if ((err = ExecCommandLine(line2)) != 0) break; } free(line2); return err; } /* ExecuteMacro */ int ShowMacro(MacroNodePtr macro) { LinePtr mlp; if (macro == NULL) return -1; PrintF("macro %s\n", macro->name); for (mlp = macro->macroData.first; mlp != NULL; mlp = mlp->next) { PrintF(" %s\n", mlp->line); } PrintF("end\n\n"); return 0; } /* ShowMacro */ void DumpMacro(char *macName) { MacroNodePtr mnp; if (macName == NULL) { /* Dump 'em all. */ for (mnp = gFirstMacro; mnp != NULL; mnp = mnp->next) (void) ShowMacro(mnp); } else { mnp = FindMacro(macName); (void) ShowMacro(mnp); } } /* DumpMacro */ /* This routine can be used before the CAName list has been constructed * (see cmdtab.c). We use this to make sure we don't have any duplicate * macro names when we try to add a new macro. You can also use this * if you only want to look for a macro, but don't want to bother with * the possibility of GetCommandOrMacro() returning a command, or an * ambiguous name error. */ MacroNodePtr FindMacro(char *macroName) { MacroNodePtr mnp; for (mnp = gFirstMacro; mnp != NULL; mnp = mnp->next) { if (STREQ(mnp->name, macroName)) break; } return mnp; } /* FindMacro */ int RunPrefixedMacro(char *pfx, char *sfx) { string macName; MacroNodePtr mnp; STRNCPY(macName, "."); STRNCAT(macName, pfx); STRNCAT(macName, sfx); mnp = FindMacro(macName); return ExecuteMacro(mnp, 0, NULL); } /* RunPrefixedMacro */ /* eof Macro.c */