|
|
1.1 root 1: /*
2: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
3: * Copyright (c) 1988, 1989 by Adam de Boor
4: * Copyright (c) 1989 by Berkeley Softworks
5: * All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Adam de Boor.
9: *
10: * Redistribution and use in source and binary forms are permitted provided
11: * that: (1) source distributions retain this entire copyright notice and
12: * comment, and (2) distributions including binaries display the following
13: * acknowledgement: ``This product includes software developed by the
14: * University of California, Berkeley and its contributors'' in the
15: * documentation or other materials provided with the distribution and in
16: * all advertising materials mentioning features or use of this software.
17: * Neither the name of the University nor the names of its contributors may
18: * be used to endorse or promote products derived from this software without
19: * specific prior written permission.
20: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
21: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
22: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23: */
24:
25: #ifndef lint
26: char copyright[] =
27: "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
28: All rights reserved.\n";
29: #endif /* not lint */
30:
31: #ifndef lint
32: static char sccsid[] = "@(#)main.c 5.22 (Berkeley) 6/28/90";
33: #endif /* not lint */
34:
35: /*-
36: * main.c --
37: * The main file for this entire program. Exit routines etc
38: * reside here.
39: *
40: * Utility functions defined in this file:
41: * Main_ParseArgLine Takes a line of arguments, breaks them and
42: * treats them as if they were given when first
43: * invoked. Used by the parse module to implement
44: * the .MFLAGS target.
45: *
46: * Error Print a tagged error message. The global
47: * MAKE variable must have been defined. This
48: * takes a format string and two optional
49: * arguments for it.
50: *
51: * Fatal Print an error message and exit. Also takes
52: * a format string and two arguments.
53: *
54: * Punt Aborts all jobs and exits with a message. Also
55: * takes a format string and two arguments.
56: *
57: * Finish Finish things up by printing the number of
58: * errors which occured, as passed to it, and
59: * exiting.
60: */
61:
62: #include <sys/param.h>
63: #include <sys/signal.h>
64: #include <sys/stat.h>
65: #include <fcntl.h>
66: #include <stdio.h>
67: #include <varargs.h>
68: #include "make.h"
69: #include "pathnames.h"
70:
71: #ifndef DEFMAXLOCAL
72: #define DEFMAXLOCAL DEFMAXJOBS
73: #endif DEFMAXLOCAL
74:
75: #define MAKEFLAGS ".MAKEFLAGS"
76:
77: Lst create; /* Targets to be made */
78: time_t now; /* Time at start of make */
79: GNode *DEFAULT; /* .DEFAULT node */
80: Boolean allPrecious; /* .PRECIOUS given on line by itself */
81:
82: static Boolean noBuiltins; /* -r flag */
83: static Lst makefiles; /* ordered list of makefiles to read */
84: int maxJobs; /* -J argument */
85: static int maxLocal; /* -L argument */
86: Boolean debug; /* -d flag */
87: Boolean noExecute; /* -n flag */
88: Boolean keepgoing; /* -k flag */
89: Boolean queryFlag; /* -q flag */
90: Boolean touchFlag; /* -t flag */
91: Boolean usePipes; /* !-P flag */
92: Boolean ignoreErrors; /* -i flag */
93: Boolean beSilent; /* -s flag */
94: Boolean oldVars; /* variable substitution style */
95: Boolean checkEnvFirst; /* -e flag */
96: static Boolean jobsRunning; /* TRUE if the jobs might be running */
97:
98: static Boolean ReadMakefile();
99:
100: static char *curdir; /* if chdir'd for an architecture */
101:
102: /*-
103: * MainParseArgs --
104: * Parse a given argument vector. Called from main() and from
105: * Main_ParseArgLine() when the .MAKEFLAGS target is used.
106: *
107: * XXX: Deal with command line overriding .MAKEFLAGS in makefile
108: *
109: * Results:
110: * None
111: *
112: * Side Effects:
113: * Various global and local flags will be set depending on the flags
114: * given
115: */
116: static void
117: MainParseArgs(argc, argv)
118: int argc;
119: char **argv;
120: {
121: extern int optind;
122: extern char *optarg;
123: register int i;
124: register char *cp;
125: char c;
126:
127: optind = 1; /* since we're called more than once */
128: rearg: while((c = getopt(argc, argv, "D:I:d:ef:ij:knqrst")) != -1) {
129: switch(c) {
130: case 'D':
131: Var_Set(optarg, "1", VAR_GLOBAL);
132: Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL);
133: Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
134: break;
135: case 'I':
136: Parse_AddIncludeDir(optarg);
137: Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL);
138: Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
139: break;
140: #ifdef notdef
141: case 'L':
142: maxLocal = atoi(optarg);
143: Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL);
144: Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
145: break;
146: case 'P':
147: usePipes = FALSE;
148: Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL);
149: break;
150: case 'S':
151: keepgoing = FALSE;
152: Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL);
153: break;
154: #endif
155: case 'd': {
156: char *modules = optarg;
157:
158: for (; *modules; ++modules)
159: switch (*modules) {
160: case 'A':
161: debug = ~0;
162: break;
163: case 'a':
164: debug |= DEBUG_ARCH;
165: break;
166: case 'c':
167: debug |= DEBUG_COND;
168: break;
169: case 'd':
170: debug |= DEBUG_DIR;
171: break;
172: case 'g':
173: if (modules[1] == '1') {
174: debug |= DEBUG_GRAPH1;
175: ++modules;
176: }
177: else if (modules[1] == '2') {
178: debug |= DEBUG_GRAPH2;
179: ++modules;
180: }
181: break;
182: case 'j':
183: debug |= DEBUG_JOB;
184: break;
185: case 'm':
186: debug |= DEBUG_MAKE;
187: break;
188: case 's':
189: debug |= DEBUG_SUFF;
190: break;
191: case 't':
192: debug |= DEBUG_TARG;
193: break;
194: case 'v':
195: debug |= DEBUG_VAR;
196: break;
197: }
198: Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL);
199: Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
200: break;
201: }
202: case 'e':
203: checkEnvFirst = TRUE;
204: Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL);
205: break;
206: case 'f':
207: (void)Lst_AtEnd(makefiles, (ClientData)optarg);
208: break;
209: case 'i':
210: ignoreErrors = TRUE;
211: Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL);
212: break;
213: case 'j':
214: maxJobs = atoi(optarg);
215: Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
216: Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
217: break;
218: case 'k':
219: keepgoing = TRUE;
220: Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL);
221: break;
222: case 'n':
223: noExecute = TRUE;
224: Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL);
225: break;
226: case 'q':
227: queryFlag = TRUE;
228: /* Kind of nonsensical, wot? */
229: Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL);
230: break;
231: case 'r':
232: noBuiltins = TRUE;
233: Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL);
234: break;
235: case 's':
236: beSilent = TRUE;
237: Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL);
238: break;
239: case 't':
240: touchFlag = TRUE;
241: Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL);
242: break;
243: default:
244: case '?':
245: usage();
246: }
247: }
248:
249: oldVars = TRUE;
250:
251: /*
252: * See if the rest of the arguments are variable assignments and
253: * perform them if so. Else take them to be targets and stuff them
254: * on the end of the "create" list.
255: */
256: for (argv += optind, argc -= optind; *argv; ++argv, --argc)
257: if (Parse_IsVar(*argv))
258: Parse_DoVar(*argv, VAR_CMD);
259: else {
260: if (!**argv)
261: Punt("illegal (null) argument.");
262: if (**argv == '-') {
263: optind = 0;
264: goto rearg;
265: }
266: (void)Lst_AtEnd(create, (ClientData)*argv);
267: }
268: }
269:
270: /*-
271: * Main_ParseArgLine --
272: * Used by the parse module when a .MFLAGS or .MAKEFLAGS target
273: * is encountered and by main() when reading the .MAKEFLAGS envariable.
274: * Takes a line of arguments and breaks it into its
275: * component words and passes those words and the number of them to the
276: * MainParseArgs function.
277: * The line should have all its leading whitespace removed.
278: *
279: * Results:
280: * None
281: *
282: * Side Effects:
283: * Only those that come from the various arguments.
284: */
285: void
286: Main_ParseArgLine(line)
287: char *line; /* Line to fracture */
288: {
289: char **argv; /* Manufactured argument vector */
290: int argc; /* Number of arguments in argv */
291:
292: if (line == NULL)
293: return;
294: for (; *line == ' '; ++line);
295: if (!*line)
296: return;
297:
298: argv = brk_string(line, &argc);
299: MainParseArgs(argc, argv);
300: }
301:
302: /*-
303: * main --
304: * The main function, for obvious reasons. Initializes variables
305: * and a few modules, then parses the arguments give it in the
306: * environment and on the command line. Reads the system makefile
307: * followed by either Makefile, makefile or the file given by the
308: * -f argument. Sets the .MAKEFLAGS PMake variable based on all the
309: * flags it has received by then uses either the Make or the Compat
310: * module to create the initial list of targets.
311: *
312: * Results:
313: * If -q was given, exits -1 if anything was out-of-date. Else it exits
314: * 0.
315: *
316: * Side Effects:
317: * The program exits when done. Targets are created. etc. etc. etc.
318: */
319: main(argc, argv)
320: int argc;
321: char **argv;
322: {
323: Lst targs; /* target nodes to create -- passed to Make_Init */
324: Boolean outOfDate; /* FALSE if all targets up to date */
325: struct stat sb;
326: char *p, *path, *getenv();
327:
328: /*
329: * if the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory
330: * exists, change into it and build there. Once things are
331: * initted, have to add the original directory to the search path,
332: * and modify the paths for the Makefiles apropriately. The
333: * current directory is also placed as a variable for make scripts.
334: */
335: if (!(path = getenv("MAKEOBJDIR")))
336: path = _PATH_OBJDIR;
337: if (!lstat(path, &sb)) {
338: if (S_ISDIR(sb.st_mode))
339: curdir = "..";
340: else {
341: curdir = emalloc((u_int)MAXPATHLEN + 1);
342: if (!getwd(curdir)) {
343: (void)fprintf(stderr, "make: %s.\n", curdir);
344: exit(2);
345: }
346: }
347: if (chdir(path)) {
348: extern int errno;
349:
350: (void)fprintf(stderr, "make: %s: %s.\n",
351: path, strerror(errno));
352: exit(2);
353: }
354: }
355:
356: create = Lst_Init(FALSE);
357: makefiles = Lst_Init(FALSE);
358: beSilent = FALSE; /* Print commands as executed */
359: ignoreErrors = FALSE; /* Pay attention to non-zero returns */
360: noExecute = FALSE; /* Execute all commands */
361: keepgoing = FALSE; /* Stop on error */
362: allPrecious = FALSE; /* Remove targets when interrupted */
363: queryFlag = FALSE; /* This is not just a check-run */
364: noBuiltins = FALSE; /* Read the built-in rules */
365: touchFlag = FALSE; /* Actually update targets */
366: usePipes = TRUE; /* Catch child output in pipes */
367: debug = 0; /* No debug verbosity, please. */
368: jobsRunning = FALSE;
369:
370: maxJobs = DEFMAXJOBS; /* Set default max concurrency */
371: maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */
372:
373: /*
374: * Initialize the parsing, directory and variable modules to prepare
375: * for the reading of inclusion paths and variable settings on the
376: * command line
377: */
378: Dir_Init(); /* Initialize directory structures so -I flags
379: * can be processed correctly */
380: Parse_Init(); /* Need to initialize the paths of #include
381: * directories */
382: Var_Init(); /* As well as the lists of variables for
383: * parsing arguments */
384:
385: if (curdir) {
386: Dir_AddDir(dirSearchPath, curdir);
387: Var_Set(".CURDIR", curdir, VAR_GLOBAL);
388: } else
389: Var_Set(".CURDIR", ".", VAR_GLOBAL);
390:
391: /*
392: * Initialize various variables.
393: * MAKE also gets this name, for compatibility
394: * .MAKEFLAGS gets set to the empty string just in case.
395: * MFLAGS also gets initialized empty, for compatibility.
396: */
397: Var_Set("MAKE", argv[0], VAR_GLOBAL);
398: Var_Set(MAKEFLAGS, "", VAR_GLOBAL);
399: Var_Set("MFLAGS", "", VAR_GLOBAL);
400: Var_Set("MACHINE", MACHINE, VAR_GLOBAL);
401:
402: /*
403: * First snag any flags out of the MAKE environment variable.
404: * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's
405: * in a different format).
406: */
407: #ifdef POSIX
408: Main_ParseArgLine(getenv("MAKEFLAGS"));
409: #else
410: Main_ParseArgLine(getenv("MAKE"));
411: #endif
412:
413: MainParseArgs(argc, argv);
414:
415: /*
416: * Initialize archive, target and suffix modules in preparation for
417: * parsing the makefile(s)
418: */
419: Arch_Init();
420: Targ_Init();
421: Suff_Init();
422:
423: DEFAULT = NILGNODE;
424: (void)time(&now);
425:
426: /*
427: * Set up the .TARGETS variable to contain the list of targets to be
428: * created. If none specified, make the variable empty -- the parser
429: * will fill the thing in with the default or .MAIN target.
430: */
431: if (!Lst_IsEmpty(create)) {
432: LstNode ln;
433:
434: for (ln = Lst_First(create); ln != NILLNODE;
435: ln = Lst_Succ(ln)) {
436: char *name = (char *)Lst_Datum(ln);
437:
438: Var_Append(".TARGETS", name, VAR_GLOBAL);
439: }
440: } else
441: Var_Set(".TARGETS", "", VAR_GLOBAL);
442:
443: /*
444: * Read in the built-in rules first, followed by the specified makefile,
445: * if it was (makefile != (char *) NULL), or the default Makefile and
446: * makefile, in that order, if it wasn't.
447: */
448: if (!noBuiltins && !ReadMakefile(_PATH_DEFSYSMK))
449: Fatal("make: no system rules (%s).", _PATH_DEFSYSMK);
450:
451: if (!Lst_IsEmpty(makefiles)) {
452: LstNode ln;
453:
454: ln = Lst_Find(makefiles, (ClientData)NULL, ReadMakefile);
455: if (ln != NILLNODE)
456: Fatal("make: cannot open %s.", (char *)Lst_Datum(ln));
457: } else if (!ReadMakefile("makefile"))
458: (void)ReadMakefile("Makefile");
459:
460: (void)ReadMakefile(".depend");
461:
462: Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL), VAR_GLOBAL);
463:
464: /* Install all the flags into the MAKE envariable. */
465: if ((p = Var_Value(MAKEFLAGS, VAR_GLOBAL)) && *p)
466: #ifdef POSIX
467: setenv("MAKEFLAGS", p, 1);
468: #else
469: setenv("MAKE", p, 1);
470: #endif
471:
472: /*
473: * For compatibility, look at the directories in the VPATH variable
474: * and add them to the search path, if the variable is defined. The
475: * variable's value is in the same format as the PATH envariable, i.e.
476: * <directory>:<directory>:<directory>...
477: */
478: if (Var_Exists("VPATH", VAR_CMD)) {
479: char *vpath, *path, *cp, savec;
480: /*
481: * GCC stores string constants in read-only memory, but
482: * Var_Subst will want to write this thing, so store it
483: * in an array
484: */
485: static char VPATH[] = "${VPATH}";
486:
487: vpath = Var_Subst(VPATH, VAR_CMD, FALSE);
488: path = vpath;
489: do {
490: /* skip to end of directory */
491: for (cp = path; *cp != ':' && *cp != '\0'; cp++);
492: /* Save terminator character so know when to stop */
493: savec = *cp;
494: *cp = '\0';
495: /* Add directory to search path */
496: Dir_AddDir(dirSearchPath, path);
497: *cp = savec;
498: path = cp + 1;
499: } while (savec == ':');
500: (void)free((Address)vpath);
501: }
502:
503: /*
504: * Now that all search paths have been read for suffixes et al, it's
505: * time to add the default search path to their lists...
506: */
507: Suff_DoPaths();
508:
509: /* print the initial graph, if the user requested it */
510: if (DEBUG(GRAPH1))
511: Targ_PrintGraph(1);
512:
513: /*
514: * Have now read the entire graph and need to make a list of targets
515: * to create. If none was given on the command line, we consult the
516: * parsing module to find the main target(s) to create.
517: */
518: if (Lst_IsEmpty(create))
519: targs = Parse_MainName();
520: else
521: targs = Targ_FindList(create, TARG_CREATE);
522:
523: /*
524: * this was original amMake -- want to allow parallelism, so put this
525: * back in, eventually.
526: */
527: if (0) {
528: /*
529: * Initialize job module before traversing the graph, now that
530: * any .BEGIN and .END targets have been read. This is done
531: * only if the -q flag wasn't given (to prevent the .BEGIN from
532: * being executed should it exist).
533: */
534: if (!queryFlag) {
535: if (maxLocal == -1)
536: maxLocal = maxJobs;
537: Job_Init(maxJobs, maxLocal);
538: jobsRunning = TRUE;
539: }
540:
541: /* Traverse the graph, checking on all the targets */
542: outOfDate = Make_Run(targs);
543: } else
544: /*
545: * Compat_Init will take care of creating all the targets as
546: * well as initializing the module.
547: */
548: Compat_Run(targs);
549:
550: /* print the graph now it's been processed if the user requested it */
551: if (DEBUG(GRAPH2))
552: Targ_PrintGraph(2);
553:
554: if (queryFlag && outOfDate)
555: exit(1);
556: else
557: exit(0);
558: }
559:
560: /*-
561: * ReadMakefile --
562: * Open and parse the given makefile.
563: *
564: * Results:
565: * TRUE if ok. FALSE if couldn't open file.
566: *
567: * Side Effects:
568: * lots
569: */
570: static Boolean
571: ReadMakefile(fname)
572: char *fname; /* makefile to read */
573: {
574: extern Lst parseIncPath, sysIncPath;
575: FILE *stream;
576: char *name, path[MAXPATHLEN + 1];
577:
578: if (!strcmp(fname, "-")) {
579: Parse_File("(stdin)", stdin);
580: Var_Set("MAKEFILE", "", VAR_GLOBAL);
581: } else {
582: if (stream = fopen(fname, "r"))
583: goto found;
584: /* if we've chdir'd, rebuild the path name */
585: if (curdir && *fname != '/') {
586: (void)sprintf(path, "%s/%s", curdir, fname);
587: if (stream = fopen(path, "r")) {
588: fname = path;
589: goto found;
590: }
591: }
592: /* look in -I and system include directories. */
593: name = Dir_FindFile(fname, parseIncPath);
594: if (!name)
595: name = Dir_FindFile(fname, sysIncPath);
596: if (!name || !(stream = fopen(name, "r")))
597: return(FALSE);
598: fname = name;
599: /*
600: * set the MAKEFILE variable desired by System V fans -- the
601: * placement of the setting here means it gets set to the last
602: * makefile specified, as it is set by SysV make.
603: */
604: found: Var_Set("MAKEFILE", fname, VAR_GLOBAL);
605: Parse_File(fname, stream);
606: (void)fclose(stream);
607: }
608: return(TRUE);
609: }
610:
611: /*-
612: * Error --
613: * Print an error message given its format.
614: *
615: * Results:
616: * None.
617: *
618: * Side Effects:
619: * The message is printed.
620: */
621: /* VARARGS */
622: void
623: Error(va_alist)
624: va_dcl
625: {
626: va_list ap;
627: char *fmt;
628:
629: va_start(ap);
630: fmt = va_arg(ap, char *);
631: (void)vfprintf(stderr, fmt, ap);
632: va_end(ap);
633: (void)fprintf(stderr, "\n");
634: (void)fflush(stderr);
635: }
636:
637: /*-
638: * Fatal --
639: * Produce a Fatal error message. If jobs are running, waits for them
640: * to finish.
641: *
642: * Results:
643: * None
644: *
645: * Side Effects:
646: * The program exits
647: */
648: /* VARARGS */
649: void
650: Fatal(va_alist)
651: va_dcl
652: {
653: va_list ap;
654: char *fmt;
655:
656: if (jobsRunning)
657: Job_Wait();
658:
659: va_start(ap);
660: fmt = va_arg(ap, char *);
661: (void)vfprintf(stderr, fmt, ap);
662: va_end(ap);
663: (void)fprintf(stderr, "\n");
664: (void)fflush(stderr);
665:
666: if (DEBUG(GRAPH2))
667: Targ_PrintGraph(2);
668: exit(2); /* Not 1 so -q can distinguish error */
669: }
670:
671: /*
672: * Punt --
673: * Major exception once jobs are being created. Kills all jobs, prints
674: * a message and exits.
675: *
676: * Results:
677: * None
678: *
679: * Side Effects:
680: * All children are killed indiscriminately and the program Lib_Exits
681: */
682: /* VARARGS */
683: void
684: Punt(va_alist)
685: va_dcl
686: {
687: va_list ap;
688: char *fmt;
689:
690: (void)fprintf(stderr, "make: ");
691: va_start(ap);
692: fmt = va_arg(ap, char *);
693: (void)vfprintf(stderr, fmt, ap);
694: va_end(ap);
695: (void)fprintf(stderr, "\n");
696: (void)fflush(stderr);
697:
698: DieHorribly();
699: }
700:
701: /*-
702: * DieHorribly --
703: * Exit without giving a message.
704: *
705: * Results:
706: * None
707: *
708: * Side Effects:
709: * A big one...
710: */
711: void
712: DieHorribly()
713: {
714: if (jobsRunning)
715: Job_AbortAll();
716: if (DEBUG(GRAPH2))
717: Targ_PrintGraph(2);
718: exit(2); /* Not 1, so -q can distinguish error */
719: }
720:
721: /*
722: * Finish --
723: * Called when aborting due to errors in child shell to signal
724: * abnormal exit.
725: *
726: * Results:
727: * None
728: *
729: * Side Effects:
730: * The program exits
731: */
732: void
733: Finish(errors)
734: int errors; /* number of errors encountered in Make_Make */
735: {
736: Fatal("%d error%s", errors, errors == 1 ? "" : "s");
737: }
738:
739: /*
740: * emalloc --
741: * malloc, but die on error.
742: */
743: char *
744: emalloc(len)
745: u_int len;
746: {
747: extern int errno;
748: char *p, *malloc();
749:
750: if (!(p = malloc(len)))
751: enomem();
752: return(p);
753: }
754:
755: /*
756: * enomem --
757: * die when out of memory.
758: */
759: enomem()
760: {
761: (void)fprintf(stderr, "make: %s.\n", strerror(errno));
762: exit(2);
763: }
764:
765: /*
766: * usage --
767: * exit with usage message
768: */
769: usage()
770: {
771: (void)fprintf(stderr,
772: "usage: make [-eiknqrst] [-D variable] [-d flags] [-f makefile ]\n\t\
773: [-I directory] [-j max_jobs] [variable=value]\n");
774: exit(2);
775: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.