|
|
1.1 root 1: /*
2: * Create /usr/lib/uucp/L.sys file
3: */
4: #include <curses.h>
5: #include <ctype.h>
6: #include <string.h>
7: #include <assert.h>
8:
9: #include "choices.h" /* include files from various screens */
10: #include "choices1.h"
11: #include "choices2.h"
12: #include "escapes.h"
13: #include "lsys1.h"
14: #include "uuin.h"
15: #include "uunam.h"
16: #include "devices.h"
17: #include "permis.h"
18: #include "helpscn.h"
19: #include "comment.h"
20:
21: #define LIMIT 78 /* Characters per output line */
22:
23: #define UUNAME fileList[0]
24: #define DOMAIN fileList[1]
25: #define SYSFILE fileList[2]
26: #define DEVFILE fileList[3]
27: #define PERMISS fileList[4]
28: #define PASSWD fileList[5]
29:
30: static char **fileList;
31: static *testFiles[] = { /* use these files if uuinstall -d */
32: "../uucpname",
33: "../domain",
34: "../L.sys",
35: "../L-devices",
36: "../Permissions",
37: "../passwd"
38: };
39:
40: static char *realFiles[] = { /* the real files */
41: "/etc/uucpname",
42: "/etc/domain",
43: "/usr/lib/uucp/L.sys",
44: "/usr/lib/uucp/L-devices",
45: "/usr/lib/uucp/Permissions",
46: "/etc/passwd"
47: };
48:
49: static int nameMod; /* 1 if name modified */
50:
51: static char buf[1024];
52: static char work[200];
53: static char schedule[200];
54:
55: static int pos, lineNo; /* current screen position */
56: static char seps[] = " \t\n"; /*strtok seperators */
57: static char oldSys[21];
58:
59: typedef struct line line;
60: struct line {
61: line *next;
62: char str[1]; /* data in line */
63: };
64:
65: #define Trace showError
66: #undef strlcpy
67: #define strlcpy(to, from) cplim((to), (from), sizeof(to))
68:
69: /*
70: * Structure that describes multi line files.
71: */
72: typedef struct fillin fillin;
73: struct fillin {
74: line *root; /* beginning of line list (must be first) */
75: char fileno; /* index into filelist */
76: char mod; /* 1 if mod has been made */
77: void (*zero)(); /* clear fields */
78: void (*distrib)(); /* get fields from line */
79: void (*clump)(); /* put fields back in line */
80: backGrnd *ch_data; /* back ground table for choices */
81: loc *ch_loc; /* loc table for choices */
82: backGrnd *s_data; /* back ground table for data */
83: loc *s_loc; /* loc table for data */
84: };
85:
86: void zeroLsys(), showLsys(), clumpLsys();
87: fillin lsys = {
88: NULL, 2, 0,
89: zeroLsys, showLsys, clumpLsys,
90: choices2_data, choices2_locs,
91: lsys1_data, lsys1_locs
92: };
93:
94: void zeroDev(), showDev(), clumpDev();
95: fillin dev = {
96: NULL, 3, 0,
97: zeroDev, showDev, clumpDev,
98: choices_data, choices_locs,
99: devices_data, devices_locs
100: };
101:
102: void zeroPerm(), showPerm(), clumpPerm();
103: fillin permis = {
104: NULL, 4, 0,
105: zeroPerm, showPerm, clumpPerm,
106: choices1_data, choices1_locs,
107: permis_data, permis_locs
108: };
109:
110: /*
111: * Copy for limit if source not null.
112: */
113: static char *
114: cplim(to, from, len)
115: char *to, *from;
116: {
117: if (NULL == from)
118: return (memchr(to, '\0', len));
119:
120: return (memcpy(to, from, len));
121: }
122:
123: /*
124: * do "" for null.
125: */
126: static char *
127: lump(x)
128: char *x;
129: {
130: return(*x ? x : "\"\"");
131: }
132:
133: /*
134: * Read a 1 line file into a string.
135: */
136: void
137: shortFile(fn, s)
138: char *fn, *s;
139: {
140: FILE *ifp;
141:
142: if (NULL == (ifp = fopen(fn, "r"))) {
143: if (yn("Cannot open %s continue", fn))
144: s[0] = '\0';
145: exit(1);
146: }
147: else {
148: fgets(s, 64, ifp);
149: if (NULL != (s = strchr(s, '\n')))
150: *s = '\0';
151: }
152: fclose(ifp);
153: }
154:
155: /*
156: * Inhale a file to a list.
157: */
158: static void
159: inhale(f)
160: fillin *f;
161: {
162: line *this, *last;
163: FILE *ifp;
164: char *s;
165: int lineNo;
166:
167: if (NULL == (ifp = fopen((s = fileList[f->fileno]), "r"))) {
168: if (yn("Cannot open %s continue", s))
169: return;
170: exit(1);
171: }
172:
173: for (lineNo = 1; NULL != (s = getline(ifp, &lineNo)); ) {
174: this = (line *)alloc(strlen(s) + sizeof(line));
175: if (NULL == f->root)
176: last = f->root = this;
177: else
178: last = last->next = this;
179: strcpy(this->str, s);
180: }
181: fclose(ifp);
182: }
183:
184: /*
185: * Clean out strings.
186: */
187: static void
188: zeroLsys()
189: {
190: register int i;
191:
192: code[0] = sys[0] = Line[0] = baudRate[0] = phoneNo[0] = 0;
193: for (i = 0; i < TIMEPAIRS; i++)
194: day[i][0] = timeFrom[i][0] = timeTo[i][0] = 0;
195: for (i = 0; i < SENDPAIRS; i++) {
196: free(expect[i]);
197: free(send[i]);
198: expect[i] = send[i] = NULL;
199: }
200: }
201: static void
202: zeroDev()
203: {
204: code[0] = devType[0] = devLine[0] = devRemote[0] = devBaud[0] =
205: devBrand[0] = 0;
206: }
207:
208: static void
209: zeroPerm()
210: {
211: perNoWrite = perComm = perRead =
212: perNoRead = perWrite = NULL;
213:
214: perSite[0] = perMyName[0] = perLogn[0] = perEtc[0] =
215: code[0] = perCallIn[1] = perSendFiles[1] = perRequest[1] = 0;
216: }
217:
218: /*
219: * Show off a permissions line.
220: */
221: static void
222: showPerm()
223: {
224: grabKey("MACHINE=", perSite, sizeof(perSite));
225:
226: strcpy(perCallIn,
227: (grabKey("LOGNAME=", perLogn, sizeof(perLogn)) ? "y" : "n"));
228: if (!grabKey("MYNAME=", perMyName, sizeof(perMyName)))
229: strcpy(perMyName, perSite);
230: lgrabKey("COMMANDS=", &perComm);
231: lgrabKey("READ=", &perRead);
232: lgrabKey("NOREAD=", &perNoRead);
233: lgrabKey("WRITE=", &perWrite);
234: lgrabKey("NOWRITE=", &perNoWrite);
235: grabYn("SENDFILES=", perSendFiles, 'y');
236: grabYn("REQUEST=", perRequest, 'y');
237: }
238:
239: /*
240: * Display a single system line.
241: */
242: static void
243: showLsys()
244: {
245: register char *w;
246: int j, k;
247: static char timeErr[] = "Invalid time field in file";
248:
249: strlcpy(sys, strtok(buf, seps));
250: strcpy(oldSys, sys);
251: strlcpy(work, strtok(NULL, seps));
252: for ((w = work), (k = 0); *w && (k < TIMEPAIRS); k++) {
253: if (!isalpha(*w)) {
254: showError(timeErr);
255: break;
256: }
257: for (j = 0; isalpha(*w) && (j < (sizeof(day[0]) - 1)); j++, w++)
258: day[k][j] = *w;
259: day[k][j] = '\0';
260: j = 4;
261: while (isdigit(*w)) { /* time field exists */
262: for (j = 0;
263: isdigit(*w) && (j < (sizeof(timeFrom[0]) - 1));
264: j++, w++)
265: timeFrom[k][j] = *w;
266: timeFrom[k][j] = '\0';
267: if ((j != 4) || (*w++ != '-')) {
268: showError(timeErr);
269: k = TIMEPAIRS;
270: break;
271: }
272: for (j = 0;
273: isdigit(*w) && (j < (sizeof(timeTo[0]) - 1));
274: j++, w++)
275: timeTo[k][j] = *w;
276: timeTo[k][j] = '\0';
277: }
278: if ((j != 4) || (*w && (*w++ != ','))) {
279: showError(timeErr);
280: break;
281: }
282: }
283:
284: strlcpy(Line, strtok(NULL, seps));
285: strlcpy(baudRate, strtok(NULL, seps));
286: strlcpy(phoneNo, strtok(NULL, seps));
287:
288: for (j = 0; j < SENDPAIRS; j++) {
289: free(expect[j]);
290: free(send[j]);
291: send[j] = NULL;
292: if (NULL == (expect[j] = newcpy(strtok(NULL, seps))))
293: break;
294: if (NULL == (send[j] = newcpy(strtok(NULL, seps))))
295: break;
296: }
297: }
298:
299: /*
300: * show an device line.
301: */
302: static void
303: showDev()
304: {
305: char *p;
306: loc *f;
307:
308: p = buf;
309: for (f = devices_locs; NULL != f->field; f++) {
310: cplim(f->field, strtok(p, seps), f->len);
311: p = NULL;
312: }
313: }
314:
315: /*
316: * Find a key on a buffer.
317: */
318: grabKey(key, to, len)
319: char *key, *to;
320: {
321: register char *p, c;
322:
323: if (NULL == (p = strstr(buf, key)))
324: return (to[0] = 0);
325: for (p += strlen(key); --len && (c = *p++) && !isspace(c); )
326: *to++ = c;
327: *to = '\0';
328: return (1);
329: }
330:
331: /*
332: * Find a key on a buffer. For long field.
333: */
334: lgrabKey(key, to)
335: char *key, **to;
336: {
337: register char *q, *p, c;
338: int len;
339:
340: free(*to);
341: *to = NULL;
342: if (NULL == (p = strstr(buf, key)))
343: return;
344: for (len = 0, q = p += strlen(key); (c = *p++) && !isspace(c); )
345: len++;
346: *to = alloc(len + 1);
347: memcpy(*to, q, len);
348: return;
349: }
350:
351: /*
352: * Find a key on a buffer with yes or no.
353: */
354: grabYn(key, to, def)
355: char *key, *to;
356: {
357: register char *p;
358:
359: to[1] = '\0';
360: if (NULL == (p = strstr(buf, key))) {
361: to[0] = def;
362: return (0);
363: }
364: to[0] = p[strlen(key)];
365: return (1);
366: }
367:
368: /*
369: * Check a field against a list.
370: */
371: checkList(field, list)
372: register char *field;
373: register char **list;
374: {
375: for (; NULL != *list; list++)
376: if (!strcmp(field, *list))
377: return (1);
378: return (0);
379: }
380:
381: /*
382: * Check for Yes No Call
383: */
384: yesNoCall(s)
385: register char *s;
386: {
387: *s = tolower(*s);
388: switch (*s) {
389: case 'y':
390: case 'n':
391: case 'c':
392: return (1);
393: }
394: return (0);
395: }
396:
397: /*
398: * Verify site name.
399: */
400: vSite(s)
401: register char *s;
402: {
403: if (!s[0]) {
404: showError("There must be a site name");
405: return (0);
406: }
407: sprintf(perLogn, "u%s", s);
408: putField(permis_locs, perLogn);
409: return (1);
410: }
411:
412: /*
413: * Check day part of scheduele field.
414: */
415: checkDay(s)
416: char *s;
417: {
418: register char *p, c, *w;
419: char work[10];
420: static char *dayList[] = {
421: "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa",
422: "Wk", "Any", "Never", NULL
423: };
424:
425: if (!s[0]) {
426: if (s == day)
427: return (0); /* must have first day field */
428: else
429: return (-1); /* skip remainder */
430: }
431:
432: for (p = s; c = *p; ) {
433: if (!isupper(c)) {
434: showError("All day field Names start upper case");
435: return (0);
436: }
437: work[0] = c;
438: for ((w = work + 1), p++; islower(c = *p) ; p++)
439: *w++ = c;
440: *w = '\0';
441: if (!checkList(work, dayList)) {
442: showError("Invalid day field %s", w);
443: return (0);
444: }
445: }
446: return (1);
447: }
448:
449: /*
450: * Check Line field.
451: */
452: checkLine(s)
453: char *s;
454: {
455: register line *l;
456:
457: if (!strcmp(s, "ACU") || !strcmp(s, "None"))
458: return (1);
459: for (l = dev.root; NULL != l; l = l->next) {
460: if ('#' == l->str[0])
461: continue;
462: strcpy(buf, l->str);
463: if (strcmp("DIR", strtok(buf, seps)))
464: continue;
465: if (!strcmp(s, strtok(NULL, seps)))
466: return (1);
467: }
468: showError("Line must be ACU, None, or DIR line in %s", DEVFILE);
469: return (0);
470: }
471:
472: /*
473: * Check time field.
474: */
475: checkTime(s)
476: register char *s;
477: {
478: register t;
479:
480: if (!s[0])
481: return (-1); /* skip fields */
482: return (numeric(s) && ((t = atoi(s)) >= 0) && (t <= 2400));
483: }
484:
485: /*
486: * Save a list of lines.
487: */
488: static void
489: saveAll(f)
490: register fillin *f;
491: {
492: FILE *ofp;
493: register line *l;
494:
495: if (!f->mod)
496: return;
497: ofp = xopen(fileList[f->fileno], "w");
498: for (l = f->root; NULL != l; l = l->next)
499: if ('#' == l->str[0])
500: fputs(l->str, ofp);
501: else
502: splitter(ofp, l->str, LIMIT);
503: fclose(ofp);
504: }
505:
506: /*
507: * Save a one line file.
508: */
509: saveLine(fn, s)
510: char *fn, *s;
511: {
512: FILE *ofp;
513:
514: ofp = xopen(fn, "w");
515: fprintf(ofp, "%s\n", s);
516: fclose(ofp);
517: }
518:
519: /*
520: * Delete a line entry.
521: */
522: line *
523: deleteEntry(root, l)
524: line **root, *l;
525: {
526: for (; *root != l; root = (line **)root->next)
527: ;
528: *root = l->next;
529: free(l);
530: return(*root);
531: }
532:
533: /*
534: * Add a line to a queue after place.
535: */
536: static line *
537: addLine(f, place, replace)
538: register fillin *f;
539: register line *place;
540: int replace;
541: {
542: register line *this, *l;
543:
544: this = alloc(strlen(buf) + sizeof(line));
545: strcpy(this->str, buf);
546:
547: if (NULL != place)
548: this->next = place->next;
549:
550: if (!replace) /* add entry */
551: return (place->next = this);
552:
553: free(place);
554:
555: if (f->root == place) /* do base of chain */
556: return (f->root = this);
557:
558: /* find pointer to place */
559: for (l = f->root; NULL != l; l = l->next)
560: if (l->next == place)
561: return (l->next = this);
562:
563: fatal("Logic error in addLine");
564: }
565:
566: /*
567: * Put Lsys together into new line.
568: */
569: static void
570: clumpLsys()
571: {
572: int i;
573: register char *s;
574:
575: for (schedule[0] = i = 0; (i < TIMEPAIRS) && day[i][0]; i++) {
576: if (i)
577: strcat(schedule, ",");
578: strcat(schedule, day[i]);
579: if (timeFrom[i][0]) {
580: strcat(schedule, timeFrom[i]);
581: strcat(schedule, "-");
582: strcat(schedule, timeTo[i]);
583: }
584: }
585:
586: sprintf(buf, "%s %s %s %s %s",
587: sys, schedule, Line, lump(baudRate), lump(phoneNo));
588:
589: for (s = buf, i = 0;
590: (i < SENDPAIRS) && (expect[i][0] || send[i][0]);
591: i++) {
592: s = strchr(s, '\0');
593: sprintf(s, " %s %s", lump(expect[i]), lump(send[i]));
594: }
595:
596: /* create /usr/spool/uucp/sys directory */
597: if (strcmp(sys, oldSys)) {
598: /* If we truncate directorys %s becomes %.7s */
599: sprintf(work,
600: "/usr/lib/uucp/uumkdir /usr/spool/uucp/%s > /dev/null", sys);
601: system(work);
602: }
603: }
604:
605: /*
606: * Put device stuff together into new line.
607: */
608: static void
609: clumpDev()
610: {
611: sprintf(buf, "%s\t%s\t%s\t%s\t%s",
612: devType, devLine, devRemote, devBaud, devBrand);
613: }
614:
615: /*
616: * Add passwd.
617: */
618: void
619: addPasswd()
620: {
621: FILE *fp;
622: char *p;
623:
624: if (!perLogn[0]) {
625: showMsg("Must have LOGNAME to add to /etc/passwd");
626: return;
627: }
628:
629: if (NULL == (fp = fopen(PASSWD, "rw"))) {
630: showMsg("Cannot open %s", PASSWD);
631: return;
632: }
633:
634: while (NULL != fgets(work, sizeof(work), fp)) {
635: if (NULL != (p = strchr(work, ':'))) {
636: *p = '\0';
637: if (!strcmp(work, perLogn)) {
638: showMsg("%s already in %s", perLogn, PASSWD);
639: return;
640: }
641: }
642: }
643: fprintf(fp, "%s::6:6::/usr/spool/uucp:/usr/lib/uucp/uucico\n", perLogn);
644: fclose(fp);
645: showMsg("%s added to %s successfully", perLogn, PASSWD);
646: }
647:
648: /*
649: * Put Permissions together into new line.
650: */
651: static void
652: clumpPerm()
653: {
654: if ('y' == perCallIn[0])
655: sprintf(buf, "MACHINE=%s LOGNAME=%s ",
656: perSite, perLogn);
657: else
658: sprintf(buf, "MACHINE=%s ", perSite);
659:
660: if ('y' == perEtc[0])
661: addPasswd();
662:
663: if (perMyName[0] && strcmp(perSite, perMyName)) {
664: sprintf(work, "MYNAME=%s ", perMyName);
665: strcat(buf, work);
666: }
667: if (perComm[0]) {
668: sprintf(work, "COMMANDS=%s ", perComm);
669: strcat(buf, work);
670: }
671: if (perRead[0]) {
672: sprintf(work, "READ=%s ", perRead);
673: strcat(buf, work);
674: }
675: if (perNoRead[0]) {
676: sprintf(work, "NOREAD=%s ", perNoRead);
677: strcat(buf, work);
678: }
679: if (perWrite[0]) {
680: sprintf(work, "WRITE=%s ", perWrite);
681: strcat(buf, work);
682: }
683: if (perNoWrite[0]) {
684: sprintf(work, "NOWRITE=%s ", perNoWrite);
685: strcat(buf, work);
686: }
687: sprintf(work, "SENDFILES=%s REQUEST=%s",
688: ((perSendFiles[0] == 'y') ? "yes" : "no"),
689: ((perRequest[0] == 'y') ? "yes" : "no"));
690: strcat(buf, work);
691: }
692:
693: /*
694: * Fix command char.
695: */
696: static int
697: fixCode(c, l)
698: line *l;
699: {
700: c = tolower(c);
701: if (NULL == l) { /* avoid modify etc with no data on file */
702: switch (c) {
703: case 'm':
704: case 'n':
705: case 'p':
706: case 'd':
707: showError("No data yet on file");
708: return (0);
709: }
710: }
711: return (c);
712: }
713:
714: /*
715: * Fill in a multi line file
716: */
717: getMulti(f)
718: register fillin *f;
719: {
720: register line *l;
721: line *onDisp = NULL;
722:
723: l = f->root;
724: clear();
725: for (;;) {
726: if ((NULL != l) && (onDisp != l)) {
727: onDisp = l;
728: clear();
729: if ('#' == l->str[0]) { /* show a comment */
730: comment = l->str + 1;
731: showDefs(comment_data, comment_locs);
732: }
733: else { /* show a data page */
734: (*(f->zero))();
735: strcpy(buf, l->str);
736: (*(f->distrib))();
737: showDefs(f->s_data, f->s_loc);
738: }
739: }
740: showBak(f->ch_data);
741: getField(f->ch_loc, code);
742: switch (fixCode(code[0], l)) {
743: case 'm': /* modify */
744: f->mod = 1;
745: if ('#' == l->str[0]) {
746: getAll(comment_locs);
747: sprintf(buf, "#%s\n", comment);
748: }
749: else {
750: clearBak(f->ch_data, f->ch_loc);
751: getAll(f->s_loc);
752: (*(f->clump))();
753: }
754: l = addLine(f, l, 1);
755: break;
756: case 'n': /* next line */
757: if (NULL == l->next)
758: showError("No more entrys in file");
759: else
760: l = l->next;
761: break;
762: case 'p': { /* previous line */
763: line *p;
764:
765: if (l == f->root) {
766: showError("No previous entrys on file");
767: break;
768: }
769: for (p = f->root; p != NULL; p = p->next)
770: if (p->next == l) {
771: l = p;
772: break;
773: }
774: break;
775: }
776: case 'a': /* add line */
777: f->mod = 1;
778: (*(f->zero))();
779: clear();
780: showBak(f->s_data);
781: getAll(f->s_loc);
782: (*(f->clump))();
783: l = addLine(f, l, 0);
784: break;
785: case 'c': /* add comment line */
786: f->mod = 1;
787: comment = NULL;
788: clear();
789: showBak(comment_data);
790: getAll(comment_locs);
791: sprintf(buf, "#%s\n", comment);
792: l = addLine(f, l, 0);
793: break;
794: case 'h': /* help message */
795: clear();
796: showBak(helpscn_data);
797: Query("Any key to continue");
798: onDisp = NULL;
799: break;
800: case 'e': /* extra help message for lsys */
801: if (&lsys == f) {
802: showBak(escapes_data);
803: Query("Any key to continue");
804: onDisp = NULL;
805: }
806: break;
807: case 'd': /* delete line */
808: f->mod = 1;
809: l = deleteEntry(f, l);
810: break;
811: case 'x':
812: return;
813: }
814: }
815: }
816:
817: /*
818: * check for clean Ascii.
819: */
820: cleanAlnum(s)
821: register char *s;
822: {
823: register c;
824:
825: if (!*s) {
826: showError("Field must have some data");
827: return (0);
828: }
829: while (c = *s++)
830: if (!isalnum(c)) {
831: showError("Field must be alphanumeric");
832: return (0);
833: }
834: return (1);
835: }
836:
837: /*
838: * check for clean Ascii.
839: */
840: cleanAscii(s)
841: register char *s;
842: {
843: register c;
844:
845: if (!*s) {
846: showError("Field must have some data");
847: return (0);
848: }
849: while (c = *s++)
850: if ((c < ' ') || (c > '~')) {
851: showError("Field must be all printable");
852: return (0);
853: }
854: return (1);
855: }
856:
857: /*
858: * Fill in /etc/uucpname.
859: */
860: void
861: uuName()
862: {
863: clear();
864: nameMod = 1;
865: showDefs(uunam_data, uunam_locs);
866: showBak(helpscn_data);
867: getAll(uunam_locs);
868: }
869:
870: /*
871: * Base Lsys screen.
872: */
873: uuinstall()
874: {
875: clear();
876: for (;;) {
877: clear();
878: showBak(uuin_data);
879: getField(uuin_locs, code);
880: switch (tolower(code[0])) {
881: case 'h':
882: clear();
883: showBak(helpscn_data);
884: Query("Any key to continue");
885: break;
886: case 's':
887: uuName();
888: break;
889: case 'l':
890: getMulti(&lsys);
891: break;
892: case 'd':
893: getMulti(&dev);
894: break;
895: case 'p':
896: getMulti(&permis);
897: break;
898: case 'x':
899: if (!(nameMod | lsys.mod | dev.mod | permis.mod))
900: return(1);
901: for (;;) {
902: switch (Query("Save changes <y/n> ")) {
903: case 'Y':
904: case 'y':
905: saveAll(&dev);
906: saveAll(&lsys);
907: saveAll(&permis);
908: if (nameMod) {
909: saveLine(UUNAME, uuname);
910: saveLine(DOMAIN, uudomain);
911: }
912: return(0);
913: case 'N':
914: case 'n':
915: return(1);
916: }
917: }
918: }
919: }
920: }
921:
922: /*
923: * Create uucp files main line.
924: */
925: main(argc, argv)
926: char **argv[];
927: {
928: int rc;
929:
930: fileList = ((1 < argc) && !strcmp("-d", argv[1])) ?
931: testFiles : realFiles;
932:
933: inhale(&lsys); /* load files */
934: inhale(&dev);
935: inhale(&permis);
936: shortFile(UUNAME, uuname);
937: shortFile(DOMAIN, uudomain);
938:
939: setUpScreen(2, 22); /* set up screen 2 lines for error at line 22 */
940:
941: rc = uuinstall();
942:
943: closeUp();
944:
945: exit(rc);
946: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.