Annotation of src/usr.bin/mail/lex.c, Revision 1.27
1.27 ! millert 1: /* $OpenBSD: lex.c,v 1.26 2001/11/21 15:26:39 millert Exp $ */
1.6 millert 2: /* $NetBSD: lex.c,v 1.10 1997/05/17 19:55:13 pk Exp $ */
1.2 niklas 3:
1.1 deraadt 4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
1.3 deraadt 38: #if 0
1.26 millert 39: static const char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/20/95";
1.3 deraadt 40: #else
1.27 ! millert 41: static const char rcsid[] = "$OpenBSD: lex.c,v 1.26 2001/11/21 15:26:39 millert Exp $";
1.3 deraadt 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: #include "rcv.h"
46: #include <errno.h>
47: #include <fcntl.h>
48: #include "extern.h"
49:
50: /*
51: * Mail -- a mail program
52: *
53: * Lexical processing of commands.
54: */
55:
56: char *prompt = "& ";
57:
1.22 millert 58: const struct cmd *com; /* command we are running */
59:
1.1 deraadt 60: /*
61: * Set up editing on the given file name.
62: * If the first character of name is %, we are considered to be
63: * editing the file, otherwise we are reading our mail which has
64: * signficance for mbox and so forth.
65: */
66: int
1.26 millert 67: setfile(char *name)
1.1 deraadt 68: {
69: FILE *ibuf;
1.11 millert 70: int i, fd;
1.1 deraadt 71: struct stat stb;
72: char isedit = *name != '%';
73: char *who = name[1] ? name + 1 : myname;
1.12 millert 74: char tempname[PATHSIZE];
1.1 deraadt 75: static int shudclob;
76:
1.8 millert 77: if ((name = expand(name)) == NULL)
1.6 millert 78: return(-1);
1.1 deraadt 79:
80: if ((ibuf = Fopen(name, "r")) == NULL) {
81: if (!isedit && errno == ENOENT)
82: goto nomail;
1.20 millert 83: warn("%s", name);
1.1 deraadt 84: return(-1);
85: }
86:
87: if (fstat(fileno(ibuf), &stb) < 0) {
1.6 millert 88: warn("fstat");
89: (void)Fclose(ibuf);
90: return(-1);
1.1 deraadt 91: }
92:
93: switch (stb.st_mode & S_IFMT) {
94: case S_IFDIR:
1.6 millert 95: (void)Fclose(ibuf);
1.1 deraadt 96: errno = EISDIR;
1.20 millert 97: warn("%s", name);
1.6 millert 98: return(-1);
1.1 deraadt 99:
100: case S_IFREG:
101: break;
102:
103: default:
1.6 millert 104: (void)Fclose(ibuf);
1.1 deraadt 105: errno = EINVAL;
1.20 millert 106: warn("%s", name);
1.6 millert 107: return(-1);
1.1 deraadt 108: }
109:
110: /*
111: * Looks like all will be well. We must now relinquish our
112: * hold on the current set of stuff. Must hold signals
113: * while we are reading the new file, else we will ruin
114: * the message[] data structure.
115: */
116: holdsigs();
117: if (shudclob)
118: quit();
119:
120: /*
121: * Copy the messages into /tmp
122: * and set pointers.
123: */
124: readonly = 0;
1.26 millert 125: if ((i = open(name, O_WRONLY, 0)) < 0)
1.1 deraadt 126: readonly++;
127: else
1.6 millert 128: (void)close(i);
1.1 deraadt 129: if (shudclob) {
1.6 millert 130: (void)fclose(itf);
131: (void)fclose(otf);
1.1 deraadt 132: }
133: shudclob = 1;
134: edit = isedit;
1.27 ! millert 135: strlcpy(prevfile, mailname, PATHSIZE);
1.26 millert 136: if (name != mailname)
137: strlcpy(mailname, name, sizeof(mailname));
1.1 deraadt 138: mailsize = fsize(ibuf);
1.11 millert 139: (void)snprintf(tempname, sizeof(tempname),
140: "%s/mail.RxXXXXXXXXXX", tmpdir);
141: if ((fd = mkstemp(tempname)) == -1 ||
142: (otf = fdopen(fd, "w")) == NULL)
1.20 millert 143: err(1, "%s", tempname);
1.7 millert 144: (void)fcntl(fileno(otf), F_SETFD, 1);
1.11 millert 145: if ((itf = fopen(tempname, "r")) == NULL)
1.20 millert 146: err(1, "%s", tempname);
1.7 millert 147: (void)fcntl(fileno(itf), F_SETFD, 1);
1.12 millert 148: (void)rm(tempname);
1.6 millert 149: setptr(ibuf, 0);
1.1 deraadt 150: setmsize(msgCount);
1.6 millert 151: /*
152: * New mail may have arrived while we were reading
153: * the mail file, so reset mailsize to be where
154: * we really are in the file...
155: */
156: mailsize = ftell(ibuf);
157: (void)Fclose(ibuf);
1.1 deraadt 158: relsesigs();
159: sawcom = 0;
160: if (!edit && msgCount == 0) {
161: nomail:
162: fprintf(stderr, "No mail for %s\n", who);
1.6 millert 163: return(-1);
1.1 deraadt 164: }
165: return(0);
166: }
167:
1.6 millert 168: /*
169: * Incorporate any new mail that has arrived since we first
170: * started reading mail.
171: */
172: int
1.26 millert 173: incfile(void)
1.6 millert 174: {
175: int newsize;
176: int omsgCount = msgCount;
177: FILE *ibuf;
178:
179: ibuf = Fopen(mailname, "r");
180: if (ibuf == NULL)
181: return(-1);
182: holdsigs();
1.24 deraadt 183: if (!spool_lock()) {
184: (void)Fclose(ibuf);
185: relsesigs();
1.10 millert 186: return(-1);
1.24 deraadt 187: }
1.6 millert 188: newsize = fsize(ibuf);
1.9 millert 189: /* make sure mail box has grown and is non-empty */
190: if (newsize == 0 || newsize <= mailsize) {
1.24 deraadt 191: (void)Fclose(ibuf);
1.10 millert 192: spool_unlock();
1.9 millert 193: relsesigs();
194: return(newsize == mailsize ? 0 : -1);
195: }
1.6 millert 196: setptr(ibuf, mailsize);
197: setmsize(msgCount);
198: mailsize = ftell(ibuf);
199: (void)Fclose(ibuf);
1.10 millert 200: spool_unlock();
1.6 millert 201: relsesigs();
202: return(msgCount - omsgCount);
203: }
204:
205:
1.1 deraadt 206: int *msgvec;
1.25 millert 207: int reset_on_stop; /* reset prompt if stopped */
1.1 deraadt 208:
209: /*
210: * Interpret user commands one by one. If standard input is not a tty,
211: * print no prompt.
212: */
213: void
1.26 millert 214: commands(void)
1.1 deraadt 215: {
1.25 millert 216: int n, sig, *sigp;
217: int eofloop = 0;
1.1 deraadt 218: char linebuf[LINESIZE];
219:
1.25 millert 220: prompt:
1.1 deraadt 221: for (;;) {
222: /*
223: * Print the prompt, if needed. Clear out
224: * string space, and flush the output.
225: */
1.8 millert 226: if (!sourcing && value("interactive") != NULL) {
227: if ((value("autoinc") != NULL) && (incfile() > 0))
1.6 millert 228: puts("New mail has arrived.");
1.1 deraadt 229: reset_on_stop = 1;
1.21 deraadt 230: printf("%s", prompt);
1.1 deraadt 231: }
232: fflush(stdout);
233: sreset();
234: /*
235: * Read a line of commands from the current input
236: * and handle end of file specially.
237: */
238: n = 0;
1.25 millert 239: sig = 0;
240: sigp = sourcing ? NULL : &sig;
1.1 deraadt 241: for (;;) {
1.25 millert 242: if (readline(input, &linebuf[n], LINESIZE - n, sigp) < 0) {
243: if (sig) {
244: if (sig == SIGINT)
245: dointr();
246: else if (sig == SIGHUP)
247: /* nothing to do? */
248: exit(1);
249: else {
250: /* Stopped by job control */
251: (void)kill(0, sig);
252: if (reset_on_stop)
253: reset_on_stop = 0;
254: }
255: goto prompt;
256: }
1.1 deraadt 257: if (n == 0)
258: n = -1;
259: break;
260: }
261: if ((n = strlen(linebuf)) == 0)
262: break;
263: n--;
264: if (linebuf[n] != '\\')
265: break;
266: linebuf[n++] = ' ';
267: }
268: reset_on_stop = 0;
269: if (n < 0) {
270: /* eof */
271: if (loading)
272: break;
273: if (sourcing) {
274: unstack();
275: continue;
276: }
1.8 millert 277: if (value("interactive") != NULL &&
278: value("ignoreeof") != NULL &&
1.1 deraadt 279: ++eofloop < 25) {
1.6 millert 280: puts("Use \"quit\" to quit.");
1.1 deraadt 281: continue;
282: }
283: break;
284: }
285: eofloop = 0;
286: if (execute(linebuf, 0))
287: break;
288: }
289: }
290:
291: /*
292: * Execute a single command.
293: * Command functions return 0 for success, 1 for error, and -1
294: * for abort. A 1 or -1 aborts a load or source. A -1 aborts
295: * the interactive command loop.
296: * Contxt is non-zero if called while composing mail.
297: */
298: int
1.26 millert 299: execute(char *linebuf, int contxt)
1.1 deraadt 300: {
301: char word[LINESIZE];
302: char *arglist[MAXARGC];
1.15 millert 303: char *cp, *cp2;
304: int c, muvec[2];
1.1 deraadt 305: int e = 1;
306:
1.22 millert 307: com = NULL;
308:
1.1 deraadt 309: /*
310: * Strip the white space away from the beginning
311: * of the command, then scan out a word, which
312: * consists of anything except digits and white space.
313: *
314: * Handle ! escapes differently to get the correct
315: * lexical conventions.
316: */
317: for (cp = linebuf; isspace(*cp); cp++)
318: ;
319: if (*cp == '!') {
320: if (sourcing) {
1.6 millert 321: puts("Can't \"!\" while sourcing");
1.1 deraadt 322: goto out;
323: }
324: shell(cp+1);
325: return(0);
326: }
327: cp2 = word;
1.8 millert 328: while (*cp && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL)
1.1 deraadt 329: *cp2++ = *cp++;
330: *cp2 = '\0';
331:
332: /*
333: * Look up the command; if not found, bitch.
334: * Normally, a blank command would map to the
335: * first command in the table; while sourcing,
336: * however, we ignore blank lines to eliminate
337: * confusion.
338: */
339: if (sourcing && *word == '\0')
340: return(0);
341: com = lex(word);
1.26 millert 342: if (com == NULL) {
1.1 deraadt 343: printf("Unknown command: \"%s\"\n", word);
344: goto out;
345: }
346:
347: /*
348: * See if we should execute the command -- if a conditional
349: * we always execute it, otherwise, check the state of cond.
350: */
351: if ((com->c_argtype & F) == 0)
1.3 deraadt 352: if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
1.1 deraadt 353: return(0);
354:
355: /*
356: * Process the arguments to the command, depending
357: * on the type he expects. Default to an error.
358: * If we are sourcing an interactive command, it's
359: * an error.
360: */
361: if (!rcvmode && (com->c_argtype & M) == 0) {
362: printf("May not execute \"%s\" while sending\n",
363: com->c_name);
364: goto out;
365: }
366: if (sourcing && com->c_argtype & I) {
367: printf("May not execute \"%s\" while sourcing\n",
368: com->c_name);
369: goto out;
370: }
371: if (readonly && com->c_argtype & W) {
372: printf("May not execute \"%s\" -- message file is read only\n",
373: com->c_name);
374: goto out;
375: }
376: if (contxt && com->c_argtype & R) {
377: printf("Cannot recursively invoke \"%s\"\n", com->c_name);
378: goto out;
379: }
380: switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
1.22 millert 381: case MSGLIST|STRLIST:
382: /*
383: * A message list defaulting to nearest forward
384: * legal message.
385: */
386: if (msgvec == 0) {
387: puts("Illegal use of \"message list\"");
388: break;
389: }
390: /*
391: * remove leading blanks.
392: */
393: while (isspace(*cp))
394: cp++;
395:
396: if (isdigit(*cp) || *cp == ':') {
397: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
398: break;
399: /* position to next space - past the message list */
400: while (!isspace(*cp))
401: cp++;
402: /* position to next non-space */
403: while (isspace(*cp))
404: cp++;
405: } else {
406: c = 0; /* no message list */
407: }
408:
409: if (c == 0) {
410: *msgvec = first(com->c_msgflag,
411: com->c_msgmask);
412: msgvec[1] = NULL;
413: }
414: if (*msgvec == NULL) {
415: puts("No applicable messages");
416: break;
417: }
418: /*
419: * Just the straight string, with
420: * leading blanks removed.
421: */
422: while (isspace(*cp))
423: cp++;
424:
425: e = (*com->c_func2)(msgvec, cp);
426: break;
427:
1.1 deraadt 428: case MSGLIST:
429: /*
430: * A message list defaulting to nearest forward
431: * legal message.
432: */
433: if (msgvec == 0) {
1.6 millert 434: puts("Illegal use of \"message list\"");
1.1 deraadt 435: break;
436: }
437: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
438: break;
439: if (c == 0) {
440: *msgvec = first(com->c_msgflag,
441: com->c_msgmask);
442: msgvec[1] = NULL;
443: }
444: if (*msgvec == NULL) {
1.6 millert 445: puts("No applicable messages");
1.1 deraadt 446: break;
447: }
448: e = (*com->c_func)(msgvec);
449: break;
450:
451: case NDMLIST:
452: /*
453: * A message list with no defaults, but no error
454: * if none exist.
455: */
456: if (msgvec == 0) {
1.6 millert 457: puts("Illegal use of \"message list\"");
1.1 deraadt 458: break;
459: }
460: if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
461: break;
462: e = (*com->c_func)(msgvec);
463: break;
464:
465: case STRLIST:
466: /*
467: * Just the straight string, with
468: * leading blanks removed.
469: */
470: while (isspace(*cp))
471: cp++;
472: e = (*com->c_func)(cp);
473: break;
474:
475: case RAWLIST:
476: /*
477: * A vector of strings, in shell style.
478: */
479: if ((c = getrawlist(cp, arglist,
1.6 millert 480: sizeof(arglist) / sizeof(*arglist))) < 0)
1.1 deraadt 481: break;
482: if (c < com->c_minargs) {
483: printf("%s requires at least %d arg(s)\n",
484: com->c_name, com->c_minargs);
485: break;
486: }
487: if (c > com->c_maxargs) {
488: printf("%s takes no more than %d arg(s)\n",
489: com->c_name, com->c_maxargs);
490: break;
491: }
492: e = (*com->c_func)(arglist);
493: break;
494:
495: case NOLIST:
496: /*
497: * Just the constant zero, for exiting,
498: * eg.
499: */
500: e = (*com->c_func)(0);
501: break;
502:
503: default:
1.15 millert 504: errx(1, "Unknown argtype");
1.1 deraadt 505: }
506:
507: out:
508: /*
509: * Exit the current source file on
510: * error.
511: */
512: if (e) {
513: if (e < 0)
1.6 millert 514: return(1);
1.1 deraadt 515: if (loading)
1.6 millert 516: return(1);
1.1 deraadt 517: if (sourcing)
518: unstack();
1.6 millert 519: return(0);
1.1 deraadt 520: }
1.3 deraadt 521: if (com == NULL)
522: return(0);
1.8 millert 523: if (value("autoprint") != NULL && com->c_argtype & P)
1.1 deraadt 524: if ((dot->m_flag & MDELETED) == 0) {
525: muvec[0] = dot - &message[0] + 1;
526: muvec[1] = 0;
527: type(muvec);
528: }
529: if (!sourcing && (com->c_argtype & T) == 0)
530: sawcom = 1;
531: return(0);
532: }
533:
534: /*
535: * Set the size of the message vector used to construct argument
536: * lists to message list functions.
537: */
538: void
1.27 ! millert 539: setmsize(int n)
1.1 deraadt 540: {
1.27 ! millert 541: size_t msize;
1.1 deraadt 542:
1.27 ! millert 543: msize = (n + 1) * sizeof(*msgvec);
! 544: if ((msgvec = realloc(msgvec, msize)) == NULL)
! 545: errx(1, "Out of memory");
! 546: memset(msgvec, 0, msize);
1.1 deraadt 547: }
548:
549: /*
550: * Find the correct command in the command table corresponding
551: * to the passed command "word"
552: */
553:
1.2 niklas 554: const struct cmd *
1.26 millert 555: lex(char *word)
1.1 deraadt 556: {
1.2 niklas 557: extern const struct cmd cmdtab[];
1.15 millert 558: const struct cmd *cp;
1.1 deraadt 559:
1.18 millert 560: if (word[0] == '#')
561: word = "#";
1.8 millert 562: for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
1.1 deraadt 563: if (isprefix(word, cp->c_name))
564: return(cp);
1.26 millert 565: return(NULL);
1.1 deraadt 566: }
567:
568: /*
569: * Determine if as1 is a valid prefix of as2.
570: * Return true if yep.
571: */
572: int
1.26 millert 573: isprefix(char *as1, char *as2)
1.1 deraadt 574: {
1.15 millert 575: char *s1, *s2;
1.1 deraadt 576:
577: s1 = as1;
578: s2 = as2;
579: while (*s1++ == *s2)
580: if (*s2++ == '\0')
581: return(1);
582: return(*--s1 == '\0');
583: }
584:
585: /*
586: * The following gets called on receipt of an interrupt. This is
587: * to abort printout of a command, mainly.
588: * Dispatching here when command() is inactive crashes rcv.
589: * Close all open files except 0, 1, 2, and the temporary.
590: * Also, unstack all source files.
591: */
592: int inithdr; /* am printing startup headers */
593:
594: void
1.26 millert 595: dointr(void)
1.1 deraadt 596: {
597:
598: noreset = 0;
599: if (!inithdr)
600: sawcom++;
601: inithdr = 0;
602: while (sourcing)
603: unstack();
604:
605: close_all_files();
606:
607: if (image >= 0) {
1.6 millert 608: (void)close(image);
1.1 deraadt 609: image = -1;
610: }
1.6 millert 611: fputs("Interrupt\n", stderr);
1.1 deraadt 612: }
613:
614: /*
615: * Announce the presence of the current Mail version,
616: * give the message count, and print a header listing.
617: */
618: void
1.26 millert 619: announce(void)
1.1 deraadt 620: {
621: int vec[2], mdot;
622:
1.6 millert 623: mdot = newfileinfo(0);
1.1 deraadt 624: vec[0] = mdot;
625: vec[1] = 0;
626: dot = &message[mdot - 1];
1.8 millert 627: if (msgCount > 0 && value("noheader") == NULL) {
1.1 deraadt 628: inithdr++;
629: headers(vec);
630: inithdr = 0;
631: }
632: }
633:
634: /*
635: * Announce information about the file we are editing.
636: * Return a likely place to set dot.
637: */
638: int
1.26 millert 639: newfileinfo(int omsgCount)
1.1 deraadt 640: {
1.15 millert 641: struct message *mp;
642: int u, n, mdot, d, s;
1.6 millert 643: char fname[PATHSIZE], zname[PATHSIZE], *ename;
1.1 deraadt 644:
1.6 millert 645: for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
1.1 deraadt 646: if (mp->m_flag & MNEW)
647: break;
648: if (mp >= &message[msgCount])
1.6 millert 649: for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
1.1 deraadt 650: if ((mp->m_flag & MREAD) == 0)
651: break;
652: if (mp < &message[msgCount])
653: mdot = mp - &message[0] + 1;
654: else
1.6 millert 655: mdot = omsgCount + 1;
1.1 deraadt 656: s = d = 0;
657: for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
658: if (mp->m_flag & MNEW)
659: n++;
660: if ((mp->m_flag & MREAD) == 0)
661: u++;
662: if (mp->m_flag & MDELETED)
663: d++;
664: if (mp->m_flag & MSAVED)
665: s++;
666: }
667: ename = mailname;
1.6 millert 668: if (getfold(fname, sizeof(fname)) >= 0) {
1.26 millert 669: strlcat(fname, "/", sizeof(fname));
1.1 deraadt 670: if (strncmp(fname, mailname, strlen(fname)) == 0) {
1.12 millert 671: (void)snprintf(zname, sizeof(zname), "+%s",
1.6 millert 672: mailname + strlen(fname));
1.1 deraadt 673: ename = zname;
674: }
675: }
676: printf("\"%s\": ", ename);
677: if (msgCount == 1)
1.6 millert 678: fputs("1 message", stdout);
1.1 deraadt 679: else
680: printf("%d messages", msgCount);
681: if (n > 0)
682: printf(" %d new", n);
683: if (u-n > 0)
684: printf(" %d unread", u);
685: if (d > 0)
686: printf(" %d deleted", d);
687: if (s > 0)
688: printf(" %d saved", s);
689: if (readonly)
1.6 millert 690: fputs(" [Read only]", stdout);
691: putchar('\n');
1.1 deraadt 692: return(mdot);
693: }
694:
695: /*
696: * Print the current version number.
697: */
698: /*ARGSUSED*/
699: int
1.26 millert 700: pversion(void *v)
1.1 deraadt 701: {
1.26 millert 702: extern const char version[];
1.1 deraadt 703:
704: printf("Version %s\n", version);
705: return(0);
706: }
707:
708: /*
709: * Load a file of user definitions.
710: */
711: void
1.26 millert 712: load(char *name)
1.1 deraadt 713: {
1.15 millert 714: FILE *in, *oldin;
1.1 deraadt 715:
716: if ((in = Fopen(name, "r")) == NULL)
717: return;
718: oldin = input;
719: input = in;
720: loading = 1;
721: sourcing = 1;
722: commands();
723: loading = 0;
724: sourcing = 0;
725: input = oldin;
1.6 millert 726: (void)Fclose(in);
1.1 deraadt 727: }