% -*- mode: Noweb; noweb-code-mode: c-mode -*- % Copyright 1991 by Norman Ramsey. All rights reserved. % See file COPYRIGHT for more information. \subsection{Expanding multiple files from a single source} This main program is used to make the monolithic {\tt noweb} script efficient. Tips of the hat to Ross Williams and Preston Briggs. <<*>>= static char rcsid[] = "$Id: mnt.nw,v 1.19 2006/06/12 21:03:53 nr Exp nr $"; static char rcsname[] = "$Name: v2_11b $"; #include #include #include #include #include #include "modules.h" #include "modtrees.h" #include "notangle.h" #include "errors.h" #include "columns.h" #include "strsave.h" <> #define Clocformat "#line %L \"%F\"%N" static char *locformat = Clocformat; main(int argc, char **argv) { int i; tabsize = 0; /* default for nt is not to use tabs */ progname = argv[0]; finalstage = 1; <> for (i=1; i> break; default: emitfile(argv[i]); break; } nowebexit(NULL); return errorlevel; /* slay warning */ } @ <>= read_defs(stdin); apply_each_module(remove_final_newline); <>= apply_each_module(add_uses_to_usecounts); apply_each_module(emit_if_unused_and_conforming); <>= void add_uses_to_usecounts(Module mp); void emit_if_unused_and_conforming(Module mp); <<*>>= void add_uses_to_usecounts(Module mp) { Module used; struct modpart *p; for (p=mp->head; p!=NULL; p=p->next) if (p->ptype == MODULE) { used = lookup(p->contents); if (used != NULL) used->usecount++; } } @ A conforming output chunk name has no spaces and no metacharacters. Names with spaces are silently ignored, but names that are otherwise conforming but that contain metacharacters cause complaints. <<*>>= void emit_if_unused_and_conforming(Module mp) { char *index; if (mp->usecount == 0 && strpbrk(mp->name, " \n\t\v\r\f") == NULL) if (index = strpbrk(mp->name, "[](){}!$&<>*?;|^`'\\\""), index == NULL || index[0] == '*' && index[1] == 0) if (index == mp->name) errormsg(Error, "@<<*@>> is not a good chunk name for noweb; " "use notangle instead"); else emitfile(mp->name); else errormsg(Error, "@<<%s@>> cannot be an output chunk; " "it contains a metacharacter", mp->name); } <>= static void emitfile(char *modname); <<*>>= static void emitfile(char *modname) { Module root = lookup(modname); char *tempname = tempnam(".", 0); FILE *fp; char *lfmt, *filename; <> <> fp = fopen(tempname, "w"); if (fp == NULL) errormsg(Fatal, "Can't open temporary file %s", tempname); <> <> remove(filename); if (rename(tempname, filename) != 0) { /* different file systems? (may have to copy) */ FILE *fp = fopen(filename, "w"); if (fp == NULL) {remove(tempname); <>} <> remove(tempname); } } <>= { int n = strlen(modname) - 1; if (n >= 0 && modname[n] == '*') { lfmt = locformat; filename = strsave(modname); filename[n] = 0; } else { lfmt = ""; filename = modname; } } <>= resetloc(); (void) expand(root, 0, 0, 0, lfmt, fp); putc('\n', fp); fclose(fp); @ <>= { FILE *dest, *tmp; dest = fopen(filename, "r"); if (dest != NULL) { int x, y; tmp = fopen(tempname, "r"); assert(tmp); do { x = getc(tmp); y = getc(dest); } while (x == y && x != EOF); fclose(tmp); fclose(dest); if (x == y) { remove(tempname); return; } } } <>= errormsg(Error, "Can't open output file %s", filename); return; <>= if (root == NULL) { errormsg(Error, "Chunk @<<%s@>> is undefined", filename); return; } <>= switch (*++argv[i]) { case 'a': if (strcmp(argv[i], "all")) errormsg(Warning, "Ignoring unknown option -%s", argv[i]); else {<>} break; case 't': /* set tab size or turn off */ if (isdigit(argv[i][1])) tabsize = atoi(argv[i]+1); else if (argv[i][1]==0) tabsize = 0; /* no tabs */ else errormsg(Error, "%s: ill-formed option %s\n", argv[0], argv[i]); break; case 'L': /* have a #line number format */ locformat = argv[i] + 1; if (!*locformat) locformat = Clocformat; break; default: errormsg(Warning, "Ignoring unknown option -%s", argv[i]); } @ \subsubsection{Temporary files} Some operating systems provide a [[tempnam]] call that makes it possible to create a temporary file in the current directory. This makes [[mnt]] more efficient by making [[rename]] efficient. If your operating system provides [[tempnam]], define [[TEMPNAM]] for compilation. <>= #ifdef TEMPNAM extern char *tempnam (const char *dir, const char *pfx); /* temp file in dir */ #else #define tempnam(DIR,PFX) (strsave(tmpnam(NULL))) #endif