Annotation of src/usr.bin/mail/lex.c, Revision 1.22
1.22 ! millert 1: /* $OpenBSD: lex.c,v 1.21 2000/07/06 06:24:21 deraadt 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.6 millert 39: static char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/20/95";
1.3 deraadt 40: #else
1.22 ! millert 41: static char rcsid[] = "$OpenBSD: lex.c,v 1.21 2000/07/06 06:24:21 deraadt 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
67: setfile(name)
68: char *name;
69: {
70: FILE *ibuf;
1.11 millert 71: int i, fd;
1.1 deraadt 72: struct stat stb;
73: char isedit = *name != '%';
74: char *who = name[1] ? name + 1 : myname;
1.12 millert 75: char tempname[PATHSIZE];
1.1 deraadt 76: static int shudclob;
77:
1.8 millert 78: if ((name = expand(name)) == NULL)
1.6 millert 79: return(-1);
1.1 deraadt 80:
81: if ((ibuf = Fopen(name, "r")) == NULL) {
82: if (!isedit && errno == ENOENT)
83: goto nomail;
1.20 millert 84: warn("%s", name);
1.1 deraadt 85: return(-1);
86: }
87:
88: if (fstat(fileno(ibuf), &stb) < 0) {
1.6 millert 89: warn("fstat");
90: (void)Fclose(ibuf);
91: return(-1);
1.1 deraadt 92: }
93:
94: switch (stb.st_mode & S_IFMT) {
95: case S_IFDIR:
1.6 millert 96: (void)Fclose(ibuf);
1.1 deraadt 97: errno = EISDIR;
1.20 millert 98: warn("%s", name);
1.6 millert 99: return(-1);
1.1 deraadt 100:
101: case S_IFREG:
102: break;
103:
104: default:
1.6 millert 105: (void)Fclose(ibuf);
1.1 deraadt 106: errno = EINVAL;
1.20 millert 107: warn("%s", name);
1.6 millert 108: return(-1);
1.1 deraadt 109: }
110:
111: /*
112: * Looks like all will be well. We must now relinquish our
113: * hold on the current set of stuff. Must hold signals
114: * while we are reading the new file, else we will ruin
115: * the message[] data structure.
116: */
117:
118: holdsigs();
119: if (shudclob)
120: quit();
121:
122: /*
123: * Copy the messages into /tmp
124: * and set pointers.
125: */
126:
127: readonly = 0;
128: if ((i = open(name, 1)) < 0)
129: readonly++;
130: else
1.6 millert 131: (void)close(i);
1.1 deraadt 132: if (shudclob) {
1.6 millert 133: (void)fclose(itf);
134: (void)fclose(otf);
1.1 deraadt 135: }
136: shudclob = 1;
137: edit = isedit;
138: strcpy(prevfile, mailname);
1.14 millert 139: if (name != mailname) {
140: strncpy(mailname, name, sizeof(mailname) - 1);
141: mailname[sizeof(mailname) - 1] = '\0';
142: }
1.1 deraadt 143: mailsize = fsize(ibuf);
1.11 millert 144: (void)snprintf(tempname, sizeof(tempname),
145: "%s/mail.RxXXXXXXXXXX", tmpdir);
146: if ((fd = mkstemp(tempname)) == -1 ||
147: (otf = fdopen(fd, "w")) == NULL)
1.20 millert 148: err(1, "%s", tempname);
1.7 millert 149: (void)fcntl(fileno(otf), F_SETFD, 1);
1.11 millert 150: if ((itf = fopen(tempname, "r")) == NULL)
1.20 millert 151: err(1, "%s", tempname);
1.7 millert 152: (void)fcntl(fileno(itf), F_SETFD, 1);
1.12 millert 153: (void)rm(tempname);
1.6 millert 154: setptr(ibuf, 0);
1.1 deraadt 155: setmsize(msgCount);
1.6 millert 156: /*
157: * New mail may have arrived while we were reading
158: * the mail file, so reset mailsize to be where
159: * we really are in the file...
160: */
161: mailsize = ftell(ibuf);
162: (void)Fclose(ibuf);
1.1 deraadt 163: relsesigs();
164: sawcom = 0;
165: if (!edit && msgCount == 0) {
166: nomail:
167: fprintf(stderr, "No mail for %s\n", who);
1.6 millert 168: return(-1);
1.1 deraadt 169: }
170: return(0);
171: }
172:
1.6 millert 173: /*
174: * Incorporate any new mail that has arrived since we first
175: * started reading mail.
176: */
177: int
178: incfile()
179: {
180: int newsize;
181: int omsgCount = msgCount;
182: FILE *ibuf;
183:
184: ibuf = Fopen(mailname, "r");
185: if (ibuf == NULL)
186: return(-1);
187: holdsigs();
1.10 millert 188: if (!spool_lock())
189: return(-1);
1.6 millert 190: newsize = fsize(ibuf);
1.9 millert 191: /* make sure mail box has grown and is non-empty */
192: if (newsize == 0 || newsize <= mailsize) {
1.10 millert 193: spool_unlock();
1.9 millert 194: relsesigs();
195: return(newsize == mailsize ? 0 : -1);
196: }
1.6 millert 197: setptr(ibuf, mailsize);
198: setmsize(msgCount);
199: mailsize = ftell(ibuf);
200: (void)Fclose(ibuf);
1.10 millert 201: spool_unlock();
1.6 millert 202: relsesigs();
203: return(msgCount - omsgCount);
204: }
205:
206:
1.1 deraadt 207: int *msgvec;
208: int reset_on_stop; /* do a reset() if stopped */
209:
210: /*
211: * Interpret user commands one by one. If standard input is not a tty,
212: * print no prompt.
213: */
214: void
215: commands()
216: {
1.15 millert 217: int n, eofloop = 0;
1.1 deraadt 218: char linebuf[LINESIZE];
1.3 deraadt 219: #if __GNUC__
1.8 millert 220: /* Avoid siglongjmp clobbering */
1.7 millert 221: (void)&eofloop;
1.3 deraadt 222: #endif
1.1 deraadt 223:
224: if (!sourcing) {
225: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1.13 millert 226: (void)signal(SIGINT, intr);
1.1 deraadt 227: if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1.13 millert 228: (void)signal(SIGHUP, hangup);
229: (void)signal(SIGTSTP, stop);
230: (void)signal(SIGTTOU, stop);
231: (void)signal(SIGTTIN, stop);
1.1 deraadt 232: }
233: setexit();
234: for (;;) {
235: /*
236: * Print the prompt, if needed. Clear out
237: * string space, and flush the output.
238: */
1.8 millert 239: if (!sourcing && value("interactive") != NULL) {
240: if ((value("autoinc") != NULL) && (incfile() > 0))
1.6 millert 241: puts("New mail has arrived.");
1.1 deraadt 242: reset_on_stop = 1;
1.21 deraadt 243: printf("%s", prompt);
1.1 deraadt 244: }
245: fflush(stdout);
246: sreset();
247: /*
248: * Read a line of commands from the current input
249: * and handle end of file specially.
250: */
251: n = 0;
252: for (;;) {
253: if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
254: if (n == 0)
255: n = -1;
256: break;
257: }
258: if ((n = strlen(linebuf)) == 0)
259: break;
260: n--;
261: if (linebuf[n] != '\\')
262: break;
263: linebuf[n++] = ' ';
264: }
265: reset_on_stop = 0;
266: if (n < 0) {
267: /* eof */
268: if (loading)
269: break;
270: if (sourcing) {
271: unstack();
272: continue;
273: }
1.8 millert 274: if (value("interactive") != NULL &&
275: value("ignoreeof") != NULL &&
1.1 deraadt 276: ++eofloop < 25) {
1.6 millert 277: puts("Use \"quit\" to quit.");
1.1 deraadt 278: continue;
279: }
280: break;
281: }
282: eofloop = 0;
283: if (execute(linebuf, 0))
284: break;
285: }
286: }
287:
288: /*
289: * Execute a single command.
290: * Command functions return 0 for success, 1 for error, and -1
291: * for abort. A 1 or -1 aborts a load or source. A -1 aborts
292: * the interactive command loop.
293: * Contxt is non-zero if called while composing mail.
294: */
295: int
296: execute(linebuf, contxt)
297: char linebuf[];
298: int contxt;
299: {
300: char word[LINESIZE];
301: char *arglist[MAXARGC];
1.15 millert 302: char *cp, *cp2;
303: int c, muvec[2];
1.1 deraadt 304: int e = 1;
305:
1.22 ! millert 306: com = NULL;
! 307:
1.1 deraadt 308: /*
309: * Strip the white space away from the beginning
310: * of the command, then scan out a word, which
311: * consists of anything except digits and white space.
312: *
313: * Handle ! escapes differently to get the correct
314: * lexical conventions.
315: */
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:
340: if (sourcing && *word == '\0')
341: return(0);
342: com = lex(word);
343: if (com == NONE) {
344: printf("Unknown command: \"%s\"\n", word);
345: goto out;
346: }
347:
348: /*
349: * See if we should execute the command -- if a conditional
350: * we always execute it, otherwise, check the state of cond.
351: */
352:
353: if ((com->c_argtype & F) == 0)
1.3 deraadt 354: if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
1.1 deraadt 355: return(0);
356:
357: /*
358: * Process the arguments to the command, depending
359: * on the type he expects. Default to an error.
360: * If we are sourcing an interactive command, it's
361: * an error.
362: */
363:
364: if (!rcvmode && (com->c_argtype & M) == 0) {
365: printf("May not execute \"%s\" while sending\n",
366: com->c_name);
367: goto out;
368: }
369: if (sourcing && com->c_argtype & I) {
370: printf("May not execute \"%s\" while sourcing\n",
371: com->c_name);
372: goto out;
373: }
374: if (readonly && com->c_argtype & W) {
375: printf("May not execute \"%s\" -- message file is read only\n",
376: com->c_name);
377: goto out;
378: }
379: if (contxt && com->c_argtype & R) {
380: printf("Cannot recursively invoke \"%s\"\n", com->c_name);
381: goto out;
382: }
383: switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
1.22 ! millert 384: case MSGLIST|STRLIST:
! 385: /*
! 386: * A message list defaulting to nearest forward
! 387: * legal message.
! 388: */
! 389: if (msgvec == 0) {
! 390: puts("Illegal use of \"message list\"");
! 391: break;
! 392: }
! 393: /*
! 394: * remove leading blanks.
! 395: */
! 396: while (isspace(*cp))
! 397: cp++;
! 398:
! 399: if (isdigit(*cp) || *cp == ':') {
! 400: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
! 401: break;
! 402: /* position to next space - past the message list */
! 403: while (!isspace(*cp))
! 404: cp++;
! 405: /* position to next non-space */
! 406: while (isspace(*cp))
! 407: cp++;
! 408: } else {
! 409: c = 0; /* no message list */
! 410: }
! 411:
! 412: if (c == 0) {
! 413: *msgvec = first(com->c_msgflag,
! 414: com->c_msgmask);
! 415: msgvec[1] = NULL;
! 416: }
! 417: if (*msgvec == NULL) {
! 418: puts("No applicable messages");
! 419: break;
! 420: }
! 421: /*
! 422: * Just the straight string, with
! 423: * leading blanks removed.
! 424: */
! 425: while (isspace(*cp))
! 426: cp++;
! 427:
! 428: e = (*com->c_func2)(msgvec, cp);
! 429: break;
! 430:
1.1 deraadt 431: case MSGLIST:
432: /*
433: * A message list defaulting to nearest forward
434: * legal message.
435: */
436: if (msgvec == 0) {
1.6 millert 437: puts("Illegal use of \"message list\"");
1.1 deraadt 438: break;
439: }
440: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
441: break;
442: if (c == 0) {
443: *msgvec = first(com->c_msgflag,
444: com->c_msgmask);
445: msgvec[1] = NULL;
446: }
447: if (*msgvec == NULL) {
1.6 millert 448: puts("No applicable messages");
1.1 deraadt 449: break;
450: }
451: e = (*com->c_func)(msgvec);
452: break;
453:
454: case NDMLIST:
455: /*
456: * A message list with no defaults, but no error
457: * if none exist.
458: */
459: if (msgvec == 0) {
1.6 millert 460: puts("Illegal use of \"message list\"");
1.1 deraadt 461: break;
462: }
463: if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
464: break;
465: e = (*com->c_func)(msgvec);
466: break;
467:
468: case STRLIST:
469: /*
470: * Just the straight string, with
471: * leading blanks removed.
472: */
473: while (isspace(*cp))
474: cp++;
475: e = (*com->c_func)(cp);
476: break;
477:
478: case RAWLIST:
479: /*
480: * A vector of strings, in shell style.
481: */
482: if ((c = getrawlist(cp, arglist,
1.6 millert 483: sizeof(arglist) / sizeof(*arglist))) < 0)
1.1 deraadt 484: break;
485: if (c < com->c_minargs) {
486: printf("%s requires at least %d arg(s)\n",
487: com->c_name, com->c_minargs);
488: break;
489: }
490: if (c > com->c_maxargs) {
491: printf("%s takes no more than %d arg(s)\n",
492: com->c_name, com->c_maxargs);
493: break;
494: }
495: e = (*com->c_func)(arglist);
496: break;
497:
498: case NOLIST:
499: /*
500: * Just the constant zero, for exiting,
501: * eg.
502: */
503: e = (*com->c_func)(0);
504: break;
505:
506: default:
1.15 millert 507: errx(1, "Unknown argtype");
1.1 deraadt 508: }
509:
510: out:
511: /*
512: * Exit the current source file on
513: * error.
514: */
515: if (e) {
516: if (e < 0)
1.6 millert 517: return(1);
1.1 deraadt 518: if (loading)
1.6 millert 519: return(1);
1.1 deraadt 520: if (sourcing)
521: unstack();
1.6 millert 522: return(0);
1.1 deraadt 523: }
1.3 deraadt 524: if (com == NULL)
525: return(0);
1.8 millert 526: if (value("autoprint") != NULL && com->c_argtype & P)
1.1 deraadt 527: if ((dot->m_flag & MDELETED) == 0) {
528: muvec[0] = dot - &message[0] + 1;
529: muvec[1] = 0;
530: type(muvec);
531: }
532: if (!sourcing && (com->c_argtype & T) == 0)
533: sawcom = 1;
534: return(0);
535: }
536:
537: /*
538: * Set the size of the message vector used to construct argument
539: * lists to message list functions.
540: */
541: void
542: setmsize(sz)
543: int sz;
544: {
545:
546: if (msgvec != 0)
1.7 millert 547: (void)free(msgvec);
548: msgvec = (int *)calloc(sz + 1, sizeof(*msgvec));
1.1 deraadt 549: }
550:
551: /*
552: * Find the correct command in the command table corresponding
553: * to the passed command "word"
554: */
555:
1.2 niklas 556: const struct cmd *
1.1 deraadt 557: lex(word)
558: char word[];
559: {
1.2 niklas 560: extern const struct cmd cmdtab[];
1.15 millert 561: const struct cmd *cp;
1.1 deraadt 562:
1.18 millert 563: if (word[0] == '#')
564: word = "#";
1.8 millert 565: for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
1.1 deraadt 566: if (isprefix(word, cp->c_name))
567: return(cp);
568: return(NONE);
569: }
570:
571: /*
572: * Determine if as1 is a valid prefix of as2.
573: * Return true if yep.
574: */
575: int
576: isprefix(as1, as2)
577: char *as1, *as2;
578: {
1.15 millert 579: char *s1, *s2;
1.1 deraadt 580:
581: s1 = as1;
582: s2 = as2;
583: while (*s1++ == *s2)
584: if (*s2++ == '\0')
585: return(1);
586: return(*--s1 == '\0');
587: }
588:
589: /*
590: * The following gets called on receipt of an interrupt. This is
591: * to abort printout of a command, mainly.
592: * Dispatching here when command() is inactive crashes rcv.
593: * Close all open files except 0, 1, 2, and the temporary.
594: * Also, unstack all source files.
595: */
596:
597: int inithdr; /* am printing startup headers */
598:
599: /*ARGSUSED*/
600: void
601: intr(s)
602: int s;
603: {
604:
605: noreset = 0;
606: if (!inithdr)
607: sawcom++;
608: inithdr = 0;
609: while (sourcing)
610: unstack();
611:
612: close_all_files();
613:
614: if (image >= 0) {
1.6 millert 615: (void)close(image);
1.1 deraadt 616: image = -1;
617: }
1.6 millert 618: fputs("Interrupt\n", stderr);
1.1 deraadt 619: reset(0);
620: }
621:
622: /*
623: * When we wake up after ^Z, reprint the prompt.
624: */
625: void
626: stop(s)
627: int s;
628: {
629: sig_t old_action = signal(s, SIG_DFL);
1.3 deraadt 630: sigset_t nset;
1.1 deraadt 631:
1.13 millert 632: (void)sigemptyset(&nset);
633: (void)sigaddset(&nset, s);
634: (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
635: (void)kill(0, s);
636: (void)sigprocmask(SIG_BLOCK, &nset, NULL);
637: (void)signal(s, old_action);
1.1 deraadt 638: if (reset_on_stop) {
639: reset_on_stop = 0;
640: reset(0);
641: }
642: }
643:
644: /*
645: * Branch here on hangup signal and simulate "exit".
646: */
647: /*ARGSUSED*/
648: void
649: hangup(s)
650: int s;
651: {
652:
653: /* nothing to do? */
654: exit(1);
655: }
656:
657: /*
658: * Announce the presence of the current Mail version,
659: * give the message count, and print a header listing.
660: */
661: void
662: announce()
663: {
664: int vec[2], mdot;
665:
1.6 millert 666: mdot = newfileinfo(0);
1.1 deraadt 667: vec[0] = mdot;
668: vec[1] = 0;
669: dot = &message[mdot - 1];
1.8 millert 670: if (msgCount > 0 && value("noheader") == NULL) {
1.1 deraadt 671: inithdr++;
672: headers(vec);
673: inithdr = 0;
674: }
675: }
676:
677: /*
678: * Announce information about the file we are editing.
679: * Return a likely place to set dot.
680: */
681: int
1.6 millert 682: newfileinfo(omsgCount)
683: int omsgCount;
1.1 deraadt 684: {
1.15 millert 685: struct message *mp;
686: int u, n, mdot, d, s;
1.6 millert 687: char fname[PATHSIZE], zname[PATHSIZE], *ename;
1.1 deraadt 688:
1.6 millert 689: for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
1.1 deraadt 690: if (mp->m_flag & MNEW)
691: break;
692: if (mp >= &message[msgCount])
1.6 millert 693: for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
1.1 deraadt 694: if ((mp->m_flag & MREAD) == 0)
695: break;
696: if (mp < &message[msgCount])
697: mdot = mp - &message[0] + 1;
698: else
1.6 millert 699: mdot = omsgCount + 1;
1.1 deraadt 700: s = d = 0;
701: for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
702: if (mp->m_flag & MNEW)
703: n++;
704: if ((mp->m_flag & MREAD) == 0)
705: u++;
706: if (mp->m_flag & MDELETED)
707: d++;
708: if (mp->m_flag & MSAVED)
709: s++;
710: }
711: ename = mailname;
1.6 millert 712: if (getfold(fname, sizeof(fname)) >= 0) {
713: strncat(fname, "/", sizeof(fname) - strlen(fname) - 1);
1.1 deraadt 714: if (strncmp(fname, mailname, strlen(fname)) == 0) {
1.12 millert 715: (void)snprintf(zname, sizeof(zname), "+%s",
1.6 millert 716: mailname + strlen(fname));
1.1 deraadt 717: ename = zname;
718: }
719: }
720: printf("\"%s\": ", ename);
721: if (msgCount == 1)
1.6 millert 722: fputs("1 message", stdout);
1.1 deraadt 723: else
724: printf("%d messages", msgCount);
725: if (n > 0)
726: printf(" %d new", n);
727: if (u-n > 0)
728: printf(" %d unread", u);
729: if (d > 0)
730: printf(" %d deleted", d);
731: if (s > 0)
732: printf(" %d saved", s);
733: if (readonly)
1.6 millert 734: fputs(" [Read only]", stdout);
735: putchar('\n');
1.1 deraadt 736: return(mdot);
737: }
738:
739: /*
740: * Print the current version number.
741: */
742:
743: /*ARGSUSED*/
744: int
1.3 deraadt 745: pversion(v)
746: void *v;
1.1 deraadt 747: {
748: extern char *version;
749:
750: printf("Version %s\n", version);
751: return(0);
752: }
753:
754: /*
755: * Load a file of user definitions.
756: */
757: void
758: load(name)
759: char *name;
760: {
1.15 millert 761: FILE *in, *oldin;
1.1 deraadt 762:
763: if ((in = Fopen(name, "r")) == NULL)
764: return;
765: oldin = input;
766: input = in;
767: loading = 1;
768: sourcing = 1;
769: commands();
770: loading = 0;
771: sourcing = 0;
772: input = oldin;
1.6 millert 773: (void)Fclose(in);
1.1 deraadt 774: }