1 1.47 andvar /* $NetBSD: lex.c,v 1.47 2024/08/17 19:18:10 andvar Exp $ */ 2 1.7 christos 3 1.1 cgd /* 4 1.4 deraadt * Copyright (c) 1980, 1993 5 1.4 deraadt * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.23 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.11 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.7 christos #if 0 35 1.8 tls static char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/20/95"; 36 1.7 christos #else 37 1.47 andvar __RCSID("$NetBSD: lex.c,v 1.47 2024/08/17 19:18:10 andvar Exp $"); 38 1.7 christos #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.30 christos #include <assert.h> 42 1.37 christos #include <util.h> 43 1.30 christos 44 1.1 cgd #include "rcv.h" 45 1.4 deraadt #include "extern.h" 46 1.28 christos #ifdef USE_EDITLINE 47 1.26 christos #include "complete.h" 48 1.26 christos #endif 49 1.30 christos #include "format.h" 50 1.37 christos #include "sig.h" 51 1.30 christos #include "thread.h" 52 1.26 christos 53 1.1 cgd /* 54 1.1 cgd * Mail -- a mail program 55 1.1 cgd * 56 1.1 cgd * Lexical processing of commands. 57 1.1 cgd */ 58 1.1 cgd 59 1.30 christos static const char *prompt = DEFAULT_PROMPT; 60 1.30 christos static int *msgvec; 61 1.37 christos static int inithdr; /* Am printing startup headers. */ 62 1.37 christos static jmp_buf jmpbuf; /* The reset jmpbuf */ 63 1.37 christos static int reset_on_stop; /* To do job control longjmp. */ 64 1.37 christos 65 1.37 christos #ifdef DEBUG_FILE_LEAK 66 1.37 christos struct glue { 67 1.37 christos struct glue *next; 68 1.37 christos int niobs; 69 1.37 christos FILE *iobs; 70 1.37 christos }; 71 1.37 christos extern struct glue __sglue; 72 1.37 christos 73 1.37 christos static int open_fd_cnt; 74 1.37 christos static int open_fp_cnt; 75 1.37 christos 76 1.37 christos static int 77 1.37 christos file_count(void) 78 1.37 christos { 79 1.37 christos struct glue *gp; 80 1.37 christos FILE *fp; 81 1.37 christos int n; 82 1.37 christos int cnt; 83 1.37 christos 84 1.37 christos cnt = 0; 85 1.37 christos for (gp = &__sglue; gp; gp = gp->next) { 86 1.37 christos for (fp = gp->iobs, n = gp->niobs; --n >= 0; fp++) 87 1.37 christos if (fp->_flags) 88 1.37 christos cnt++; 89 1.37 christos } 90 1.37 christos return cnt; 91 1.37 christos } 92 1.37 christos 93 1.37 christos static int 94 1.37 christos fds_count(void) 95 1.37 christos { 96 1.37 christos int maxfd; 97 1.37 christos int cnt; 98 1.37 christos int fd; 99 1.37 christos 100 1.37 christos maxfd = fcntl(0, F_MAXFD); 101 1.37 christos if (maxfd == -1) { 102 1.37 christos warn("fcntl"); 103 1.37 christos return -1; 104 1.37 christos } 105 1.30 christos 106 1.37 christos cnt = 0; 107 1.37 christos for (fd = 0; fd <= maxfd; fd++) { 108 1.37 christos struct stat sb; 109 1.37 christos 110 1.37 christos if (fstat(fd, &sb) != -1) 111 1.37 christos cnt++; 112 1.37 christos else if (errno != EBADF 113 1.37 christos #ifdef BROKEN_CLONE_STAT /* see PRs 37878 and 37550 */ 114 1.37 christos && errno != EOPNOTSUPP 115 1.37 christos #endif 116 1.37 christos ) 117 1.37 christos warn("fstat(%d): errno=%d", fd, errno); 118 1.37 christos } 119 1.37 christos return cnt; 120 1.37 christos } 121 1.37 christos 122 1.37 christos static void 123 1.37 christos file_leak_init(void) 124 1.37 christos { 125 1.37 christos open_fd_cnt = fds_count(); 126 1.37 christos open_fp_cnt = file_count(); 127 1.37 christos } 128 1.37 christos 129 1.37 christos static void 130 1.37 christos file_leak_check(void) 131 1.37 christos { 132 1.37 christos if (open_fp_cnt != file_count() || 133 1.37 christos open_fd_cnt != fds_count()) { 134 1.37 christos (void)printf("FILE LEAK WARNING: " 135 1.37 christos "fp-count: %d (%d) " 136 1.37 christos "fd-count: %d (%d) max-fd: %d\n", 137 1.37 christos file_count(), open_fp_cnt, 138 1.37 christos fds_count(), open_fd_cnt, 139 1.37 christos fcntl(0, F_MAXFD)); 140 1.37 christos } 141 1.37 christos } 142 1.37 christos #endif /* DEBUG_FILE_LEAK */ 143 1.30 christos 144 1.42 christos static void 145 1.42 christos update_mailname(const char *name) 146 1.42 christos { 147 1.42 christos char tbuf[PATHSIZE]; 148 1.42 christos size_t l; 149 1.42 christos 150 1.43 christos /* Don't realpath(3) if it's only an update request */ 151 1.43 christos if (name != NULL && realpath(name, mailname) == NULL) { 152 1.42 christos warn("Can't canonicalize `%s'", name); 153 1.42 christos return; 154 1.42 christos } 155 1.42 christos 156 1.42 christos if (getfold(tbuf, sizeof(tbuf)) >= 0) { 157 1.42 christos l = strlen(tbuf); 158 1.42 christos if (l < sizeof(tbuf) - 1) 159 1.42 christos tbuf[l++] = '/'; 160 1.42 christos if (strncmp(tbuf, mailname, l) == 0) { 161 1.42 christos char const *sep = "", *cp = mailname + l; 162 1.42 christos 163 1.42 christos l = strlen(cp); 164 1.42 christos if (l >= sizeof(displayname)) { 165 1.42 christos cp += l; 166 1.42 christos cp -= sizeof(displayname) - 5; 167 1.42 christos sep = "..."; 168 1.42 christos } 169 1.42 christos (void)snprintf(displayname, sizeof(displayname), 170 1.46 mrg "+%s%.*s", sep, 171 1.46 mrg (int)(sizeof(displayname) - 1 - strlen(sep)), cp); 172 1.42 christos return; 173 1.42 christos } 174 1.42 christos } 175 1.42 christos 176 1.42 christos l = strlen(mailname); 177 1.42 christos if (l < sizeof(displayname)) 178 1.42 christos strcpy(displayname, mailname); 179 1.42 christos else { 180 1.42 christos l -= sizeof(displayname) - 4 - sizeof(displayname) / 3; 181 1.42 christos (void)snprintf(displayname, sizeof(displayname), "%.*s...%s", 182 1.42 christos (int)sizeof(displayname) / 3, mailname, mailname + l); 183 1.42 christos } 184 1.42 christos } 185 1.42 christos 186 1.30 christos /* 187 1.30 christos * Set the size of the message vector used to construct argument 188 1.30 christos * lists to message list functions. 189 1.30 christos */ 190 1.30 christos static void 191 1.30 christos setmsize(int sz) 192 1.30 christos { 193 1.30 christos if (msgvec != 0) 194 1.30 christos free(msgvec); 195 1.35 christos msgvec = ecalloc((size_t) (sz + 1), sizeof(*msgvec)); 196 1.30 christos } 197 1.1 cgd 198 1.1 cgd /* 199 1.1 cgd * Set up editing on the given file name. 200 1.1 cgd * If the first character of name is %, we are considered to be 201 1.1 cgd * editing the file, otherwise we are reading our mail which has 202 1.47 andvar * significance for mbox and so forth. 203 1.1 cgd */ 204 1.30 christos PUBLIC int 205 1.24 christos setfile(const char *name) 206 1.1 cgd { 207 1.1 cgd FILE *ibuf; 208 1.20 wiz int i, fd; 209 1.1 cgd struct stat stb; 210 1.30 christos char isedit = *name != '%' || getuserid(myname) != (int)getuid(); 211 1.24 christos const char *who = name[1] ? name + 1 : myname; 212 1.1 cgd static int shudclob; 213 1.20 wiz char tempname[PATHSIZE]; 214 1.1 cgd 215 1.18 wiz if ((name = expand(name)) == NULL) 216 1.1 cgd return -1; 217 1.1 cgd 218 1.44 christos if ((ibuf = Fopen(name, "ref")) == NULL) { 219 1.1 cgd if (!isedit && errno == ENOENT) 220 1.1 cgd goto nomail; 221 1.39 christos warn("Can't open `%s'", name); 222 1.30 christos return -1; 223 1.1 cgd } 224 1.1 cgd 225 1.1 cgd if (fstat(fileno(ibuf), &stb) < 0) { 226 1.22 wiz warn("fstat"); 227 1.25 christos (void)Fclose(ibuf); 228 1.30 christos return -1; 229 1.1 cgd } 230 1.1 cgd 231 1.1 cgd switch (stb.st_mode & S_IFMT) { 232 1.1 cgd case S_IFDIR: 233 1.25 christos (void)Fclose(ibuf); 234 1.1 cgd errno = EISDIR; 235 1.22 wiz warn("%s", name); 236 1.30 christos return -1; 237 1.1 cgd 238 1.1 cgd case S_IFREG: 239 1.1 cgd break; 240 1.1 cgd 241 1.1 cgd default: 242 1.25 christos (void)Fclose(ibuf); 243 1.1 cgd errno = EINVAL; 244 1.22 wiz warn("%s", name); 245 1.30 christos return -1; 246 1.1 cgd } 247 1.1 cgd 248 1.1 cgd /* 249 1.1 cgd * Looks like all will be well. We must now relinquish our 250 1.1 cgd * hold on the current set of stuff. Must hold signals 251 1.1 cgd * while we are reading the new file, else we will ruin 252 1.1 cgd * the message[] data structure. 253 1.1 cgd */ 254 1.1 cgd 255 1.37 christos sig_check(); 256 1.37 christos sig_hold(); 257 1.1 cgd if (shudclob) 258 1.37 christos quit(jmpbuf); 259 1.1 cgd 260 1.1 cgd /* 261 1.1 cgd * Copy the messages into /tmp 262 1.1 cgd * and set pointers. 263 1.1 cgd */ 264 1.1 cgd 265 1.1 cgd readonly = 0; 266 1.33 christos if ((i = open(name, O_WRONLY)) < 0) 267 1.1 cgd readonly++; 268 1.1 cgd else 269 1.25 christos (void)close(i); 270 1.1 cgd if (shudclob) { 271 1.25 christos (void)fclose(itf); 272 1.25 christos (void)fclose(otf); 273 1.1 cgd } 274 1.1 cgd shudclob = 1; 275 1.1 cgd edit = isedit; 276 1.25 christos (void)strcpy(prevfile, mailname); 277 1.43 christos update_mailname(name != mailname ? name : NULL); 278 1.1 cgd mailsize = fsize(ibuf); 279 1.20 wiz (void)snprintf(tempname, sizeof(tempname), 280 1.20 wiz "%s/mail.RxXXXXXXXXXX", tmpdir); 281 1.20 wiz if ((fd = mkstemp(tempname)) == -1 || 282 1.44 christos (otf = fdopen(fd, "wef")) == NULL) 283 1.39 christos err(EXIT_FAILURE, "Can't create tmp file `%s'", tempname); 284 1.44 christos if ((itf = fopen(tempname, "ref")) == NULL) 285 1.39 christos err(EXIT_FAILURE, "Can't create tmp file `%s'", tempname); 286 1.25 christos (void)rm(tempname); 287 1.25 christos setptr(ibuf, (off_t)0); 288 1.30 christos setmsize(get_abs_msgCount()); 289 1.8 tls /* 290 1.9 mikel * New mail may have arrived while we were reading 291 1.9 mikel * the mail file, so reset mailsize to be where 292 1.8 tls * we really are in the file... 293 1.8 tls */ 294 1.8 tls mailsize = ftell(ibuf); 295 1.25 christos (void)Fclose(ibuf); 296 1.37 christos sig_release(); 297 1.37 christos sig_check(); 298 1.1 cgd sawcom = 0; 299 1.30 christos if (!edit && get_abs_msgCount() == 0) { 300 1.1 cgd nomail: 301 1.25 christos (void)fprintf(stderr, "No mail for %s\n", who); 302 1.1 cgd return -1; 303 1.1 cgd } 304 1.30 christos return 0; 305 1.1 cgd } 306 1.1 cgd 307 1.8 tls /* 308 1.8 tls * Incorporate any new mail that has arrived since we first 309 1.8 tls * started reading mail. 310 1.8 tls */ 311 1.30 christos PUBLIC int 312 1.17 wiz incfile(void) 313 1.8 tls { 314 1.25 christos off_t newsize; 315 1.30 christos int omsgCount; 316 1.8 tls FILE *ibuf; 317 1.36 christos int rval; 318 1.8 tls 319 1.30 christos omsgCount = get_abs_msgCount(); 320 1.30 christos 321 1.44 christos ibuf = Fopen(mailname, "ref"); 322 1.8 tls if (ibuf == NULL) 323 1.8 tls return -1; 324 1.37 christos sig_check(); 325 1.37 christos sig_hold(); 326 1.8 tls newsize = fsize(ibuf); 327 1.36 christos if (newsize == 0 || /* mail box is now empty??? */ 328 1.36 christos newsize < mailsize) { /* mail box has shrunk??? */ 329 1.36 christos rval = -1; 330 1.36 christos goto done; 331 1.36 christos } 332 1.36 christos if (newsize == mailsize) { 333 1.36 christos rval = 0; /* no new mail */ 334 1.36 christos goto done; 335 1.36 christos } 336 1.30 christos setptr(ibuf, mailsize); /* read in new mail */ 337 1.30 christos setmsize(get_abs_msgCount()); /* get the new message count */ 338 1.8 tls mailsize = ftell(ibuf); 339 1.36 christos rval = get_abs_msgCount() - omsgCount; 340 1.36 christos done: 341 1.25 christos (void)Fclose(ibuf); 342 1.37 christos sig_release(); 343 1.37 christos sig_check(); 344 1.36 christos return rval; 345 1.8 tls } 346 1.8 tls 347 1.29 christos /* 348 1.29 christos * Return a pointer to the comment character, respecting quoting as 349 1.29 christos * done in getrawlist(). The comment character is ignored inside 350 1.29 christos * quotes. 351 1.29 christos */ 352 1.29 christos static char * 353 1.29 christos comment_char(char *line) 354 1.29 christos { 355 1.29 christos char *p; 356 1.29 christos char quotec; 357 1.29 christos quotec = '\0'; 358 1.29 christos for (p = line; *p; p++) { 359 1.29 christos if (quotec != '\0') { 360 1.29 christos if (*p == quotec) 361 1.29 christos quotec = '\0'; 362 1.29 christos } 363 1.29 christos else if (*p == '"' || *p == '\'') 364 1.29 christos quotec = *p; 365 1.29 christos else if (*p == COMMENT_CHAR) 366 1.29 christos return p; 367 1.29 christos } 368 1.29 christos return NULL; 369 1.29 christos } 370 1.29 christos 371 1.30 christos /* 372 1.30 christos * Signal handler is hooked by setup_piping(). 373 1.30 christos * Respond to a broken pipe signal -- 374 1.30 christos * probably caused by quitting more. 375 1.1 cgd */ 376 1.30 christos static jmp_buf pipestop; 377 1.30 christos 378 1.30 christos /*ARGSUSED*/ 379 1.40 joerg __dead static void 380 1.37 christos lex_brokpipe(int signo) 381 1.1 cgd { 382 1.37 christos 383 1.37 christos longjmp(pipestop, signo); 384 1.30 christos } 385 1.1 cgd 386 1.30 christos /* 387 1.30 christos * Check the command line for any requested piping or redirection, 388 1.30 christos * depending on the value of 'c'. If "enable-pipes" is set, search 389 1.30 christos * the command line (cp) for the first occurrence of the character 'c' 390 1.30 christos * that is not in a quote or (parenthese) group. 391 1.30 christos */ 392 1.30 christos PUBLIC char * 393 1.30 christos shellpr(char *cp) 394 1.30 christos { 395 1.30 christos int quotec; 396 1.30 christos int level; 397 1.30 christos 398 1.30 christos if (cp == NULL || value(ENAME_ENABLE_PIPES) == NULL) 399 1.30 christos return NULL; 400 1.30 christos 401 1.30 christos level = 0; 402 1.30 christos quotec = 0; 403 1.30 christos for (/*EMPTY*/; *cp != '\0'; cp++) { 404 1.30 christos if (quotec) { 405 1.30 christos if (*cp == quotec) 406 1.30 christos quotec = 0; 407 1.30 christos if (*cp == '\\' && 408 1.30 christos (cp[1] == quotec || cp[1] == '\\')) 409 1.30 christos cp++; 410 1.30 christos } 411 1.30 christos else { 412 1.30 christos switch (*cp) { 413 1.30 christos case '|': 414 1.30 christos case '>': 415 1.30 christos if (level == 0) 416 1.30 christos return cp; 417 1.30 christos break; 418 1.30 christos case '(': 419 1.30 christos level++; 420 1.1 cgd break; 421 1.30 christos case ')': 422 1.30 christos level--; 423 1.1 cgd break; 424 1.30 christos case '"': 425 1.30 christos case '\'': 426 1.30 christos quotec = *cp; 427 1.1 cgd break; 428 1.30 christos default: 429 1.1 cgd break; 430 1.1 cgd } 431 1.30 christos } 432 1.30 christos } 433 1.30 christos return NULL; 434 1.30 christos } 435 1.30 christos 436 1.37 christos static int 437 1.37 christos do_paging(const char *cmd, int c_pipe) 438 1.37 christos { 439 1.37 christos char *cp, *p; 440 1.37 christos 441 1.37 christos if (value(ENAME_PAGER_OFF) != NULL) 442 1.37 christos return 0; 443 1.37 christos 444 1.37 christos if (c_pipe & C_PIPE_PAGER) 445 1.37 christos return 1; 446 1.37 christos 447 1.37 christos if (c_pipe & C_PIPE_CRT && value(ENAME_CRT) != NULL) 448 1.37 christos return 1; 449 1.37 christos 450 1.37 christos if ((cp = value(ENAME_PAGE_ALSO)) == NULL) 451 1.37 christos return 0; 452 1.37 christos 453 1.37 christos if ((p = strcasestr(cp, cmd)) == NULL) 454 1.37 christos return 0; 455 1.37 christos 456 1.37 christos if (p != cp && p[-1] != ',' && !is_WSP(p[-1])) 457 1.37 christos return 0; 458 1.37 christos 459 1.37 christos p += strlen(cmd); 460 1.37 christos 461 1.37 christos return (*p == '\0' || *p == ',' || is_WSP(*p)); 462 1.37 christos } 463 1.37 christos 464 1.30 christos /* 465 1.30 christos * Setup any pipe or redirection that the command line indicates. 466 1.30 christos * If none, then setup the pager unless "pager-off" is defined. 467 1.30 christos */ 468 1.30 christos static FILE *fp_stop = NULL; 469 1.30 christos static int oldfd1 = -1; 470 1.37 christos static sig_t old_sigpipe; 471 1.37 christos 472 1.30 christos static int 473 1.37 christos setup_piping(const char *cmd, char *cmdline, int c_pipe) 474 1.30 christos { 475 1.30 christos FILE *fout; 476 1.30 christos FILE *last_file; 477 1.30 christos char *cp; 478 1.30 christos 479 1.37 christos sig_check(); 480 1.37 christos 481 1.30 christos last_file = last_registered_file(0); 482 1.30 christos 483 1.30 christos fout = NULL; 484 1.30 christos if ((cp = shellpr(cmdline)) != NULL) { 485 1.30 christos char c; 486 1.30 christos c = *cp; 487 1.30 christos *cp = '\0'; 488 1.30 christos cp++; 489 1.30 christos 490 1.30 christos if (c == '|') { 491 1.41 christos if ((fout = Popen(cp, "we")) == NULL) { 492 1.30 christos warn("Popen: %s", cp); 493 1.30 christos return -1; 494 1.26 christos } 495 1.30 christos } 496 1.30 christos else { 497 1.30 christos const char *mode; 498 1.30 christos assert(c == '>'); 499 1.41 christos mode = *cp == '>' ? "ae" : "we"; 500 1.30 christos if (*cp == '>') 501 1.30 christos cp++; 502 1.30 christos 503 1.34 christos cp = skip_WSP(cp); 504 1.30 christos if ((fout = Fopen(cp, mode)) == NULL) { 505 1.30 christos warn("Fopen: %s", cp); 506 1.30 christos return -1; 507 1.1 cgd } 508 1.1 cgd } 509 1.30 christos 510 1.30 christos } 511 1.37 christos else if (do_paging(cmd, c_pipe)) { 512 1.30 christos const char *pager; 513 1.30 christos pager = value(ENAME_PAGER); 514 1.30 christos if (pager == NULL || *pager == '\0') 515 1.30 christos pager = _PATH_MORE; 516 1.30 christos 517 1.41 christos if ((fout = Popen(pager, "we")) == NULL) { 518 1.30 christos warn("Popen: %s", pager); 519 1.30 christos return -1; 520 1.30 christos } 521 1.30 christos } 522 1.30 christos 523 1.30 christos if (fout) { 524 1.37 christos old_sigpipe = sig_signal(SIGPIPE, lex_brokpipe); 525 1.30 christos (void)fflush(stdout); 526 1.33 christos if ((oldfd1 = dup(STDOUT_FILENO)) == -1) 527 1.30 christos err(EXIT_FAILURE, "dup failed"); 528 1.33 christos if (dup2(fileno(fout), STDOUT_FILENO) == -1) 529 1.30 christos err(EXIT_FAILURE, "dup2 failed"); 530 1.30 christos fp_stop = last_file; 531 1.30 christos } 532 1.30 christos return 0; 533 1.30 christos } 534 1.30 christos 535 1.30 christos /* 536 1.30 christos * This will close any piping started by setup_piping(). 537 1.30 christos */ 538 1.30 christos static void 539 1.30 christos close_piping(void) 540 1.30 christos { 541 1.37 christos sigset_t oset; 542 1.37 christos struct sigaction osa; 543 1.37 christos 544 1.30 christos if (oldfd1 != -1) { 545 1.30 christos (void)fflush(stdout); 546 1.33 christos if (fileno(stdout) != oldfd1 && 547 1.33 christos dup2(oldfd1, STDOUT_FILENO) == -1) 548 1.30 christos err(EXIT_FAILURE, "dup2 failed"); 549 1.30 christos 550 1.37 christos (void)sig_ignore(SIGPIPE, &osa, &oset); 551 1.37 christos 552 1.30 christos close_top_files(fp_stop); 553 1.30 christos fp_stop = NULL; 554 1.30 christos (void)close(oldfd1); 555 1.30 christos oldfd1 = -1; 556 1.37 christos 557 1.37 christos (void)sig_signal(SIGPIPE, old_sigpipe); 558 1.37 christos (void)sig_restore(SIGPIPE, &osa, &oset); 559 1.1 cgd } 560 1.37 christos sig_check(); 561 1.1 cgd } 562 1.1 cgd 563 1.1 cgd /* 564 1.30 christos * Determine if as1 is a valid prefix of as2. 565 1.30 christos * Return true if yep. 566 1.30 christos */ 567 1.30 christos static int 568 1.30 christos isprefix(char *as1, const char *as2) 569 1.30 christos { 570 1.30 christos char *s1; 571 1.30 christos const char *s2; 572 1.30 christos 573 1.30 christos s1 = as1; 574 1.30 christos s2 = as2; 575 1.30 christos while (*s1++ == *s2) 576 1.30 christos if (*s2++ == '\0') 577 1.30 christos return 1; 578 1.30 christos return *--s1 == '\0'; 579 1.30 christos } 580 1.30 christos 581 1.30 christos /* 582 1.30 christos * Find the correct command in the command table corresponding 583 1.30 christos * to the passed command "word" 584 1.30 christos */ 585 1.30 christos PUBLIC const struct cmd * 586 1.30 christos lex(char word[]) 587 1.30 christos { 588 1.30 christos const struct cmd *cp; 589 1.30 christos 590 1.30 christos for (cp = &cmdtab[0]; cp->c_name != NULL; cp++) 591 1.30 christos if (isprefix(word, cp->c_name)) 592 1.30 christos return cp; 593 1.30 christos return NULL; 594 1.30 christos } 595 1.30 christos 596 1.30 christos PUBLIC char * 597 1.30 christos get_cmdname(char *buf) 598 1.30 christos { 599 1.30 christos char *cp; 600 1.30 christos char *cmd; 601 1.30 christos size_t len; 602 1.30 christos 603 1.30 christos for (cp = buf; *cp; cp++) 604 1.30 christos if (strchr(" \t0123456789$^.:/-+*'\">|", *cp) != NULL) 605 1.30 christos break; 606 1.33 christos /* XXX - Don't miss the pipe command! */ 607 1.33 christos if (cp == buf && *cp == '|') 608 1.33 christos cp++; 609 1.30 christos len = cp - buf + 1; 610 1.30 christos cmd = salloc(len); 611 1.30 christos (void)strlcpy(cmd, buf, len); 612 1.30 christos return cmd; 613 1.30 christos } 614 1.30 christos 615 1.30 christos /* 616 1.1 cgd * Execute a single command. 617 1.1 cgd * Command functions return 0 for success, 1 for error, and -1 618 1.1 cgd * for abort. A 1 or -1 aborts a load or source. A -1 aborts 619 1.1 cgd * the interactive command loop. 620 1.32 christos * execute_contxt_e is in extern.h. 621 1.1 cgd */ 622 1.30 christos PUBLIC int 623 1.32 christos execute(char linebuf[], enum execute_contxt_e contxt) 624 1.1 cgd { 625 1.30 christos char *word; 626 1.1 cgd char *arglist[MAXARGC]; 627 1.38 apb const struct cmd * volatile com = NULL; 628 1.30 christos char *volatile cp; 629 1.37 christos int retval; 630 1.11 lukem int c; 631 1.45 mrg volatile int e = 1; 632 1.1 cgd 633 1.1 cgd /* 634 1.1 cgd * Strip the white space away from the beginning 635 1.1 cgd * of the command, then scan out a word, which 636 1.1 cgd * consists of anything except digits and white space. 637 1.1 cgd * 638 1.1 cgd * Handle ! escapes differently to get the correct 639 1.1 cgd * lexical conventions. 640 1.1 cgd */ 641 1.1 cgd 642 1.34 christos cp = skip_space(linebuf); 643 1.1 cgd if (*cp == '!') { 644 1.1 cgd if (sourcing) { 645 1.25 christos (void)printf("Can't \"!\" while sourcing\n"); 646 1.1 cgd goto out; 647 1.1 cgd } 648 1.28 christos (void)shell(cp + 1); 649 1.30 christos return 0; 650 1.1 cgd } 651 1.30 christos 652 1.30 christos word = get_cmdname(cp); 653 1.30 christos cp += strlen(word); 654 1.1 cgd 655 1.1 cgd /* 656 1.1 cgd * Look up the command; if not found, bitch. 657 1.1 cgd * Normally, a blank command would map to the 658 1.1 cgd * first command in the table; while sourcing, 659 1.1 cgd * however, we ignore blank lines to eliminate 660 1.1 cgd * confusion. 661 1.1 cgd */ 662 1.1 cgd 663 1.1 cgd if (sourcing && *word == '\0') 664 1.30 christos return 0; 665 1.1 cgd com = lex(word); 666 1.19 wiz if (com == NULL) { 667 1.25 christos (void)printf("Unknown command: \"%s\"\n", word); 668 1.1 cgd goto out; 669 1.1 cgd } 670 1.1 cgd 671 1.1 cgd /* 672 1.1 cgd * See if we should execute the command -- if a conditional 673 1.1 cgd * we always execute it, otherwise, check the state of cond. 674 1.1 cgd */ 675 1.1 cgd 676 1.30 christos if ((com->c_argtype & F) == 0 && (cond & CSKIP)) 677 1.30 christos return 0; 678 1.1 cgd 679 1.1 cgd /* 680 1.1 cgd * Process the arguments to the command, depending 681 1.1 cgd * on the type he expects. Default to an error. 682 1.1 cgd * If we are sourcing an interactive command, it's 683 1.1 cgd * an error. 684 1.1 cgd */ 685 1.1 cgd 686 1.30 christos if (mailmode == mm_sending && (com->c_argtype & M) == 0) { 687 1.25 christos (void)printf("May not execute \"%s\" while sending\n", 688 1.1 cgd com->c_name); 689 1.1 cgd goto out; 690 1.1 cgd } 691 1.1 cgd if (sourcing && com->c_argtype & I) { 692 1.25 christos (void)printf("May not execute \"%s\" while sourcing\n", 693 1.1 cgd com->c_name); 694 1.1 cgd goto out; 695 1.1 cgd } 696 1.1 cgd if (readonly && com->c_argtype & W) { 697 1.25 christos (void)printf("May not execute \"%s\" -- message file is read only\n", 698 1.1 cgd com->c_name); 699 1.1 cgd goto out; 700 1.1 cgd } 701 1.32 christos if (contxt == ec_composing && com->c_argtype & R) { 702 1.25 christos (void)printf("Cannot recursively invoke \"%s\"\n", com->c_name); 703 1.1 cgd goto out; 704 1.1 cgd } 705 1.30 christos 706 1.30 christos if (!sourcing && com->c_pipe && value(ENAME_INTERACTIVE) != NULL) { 707 1.37 christos 708 1.37 christos sig_check(); 709 1.30 christos if (setjmp(pipestop)) 710 1.30 christos goto out; 711 1.30 christos 712 1.37 christos if (setup_piping(com->c_name, cp, com->c_pipe) == -1) 713 1.30 christos goto out; 714 1.30 christos } 715 1.30 christos switch (com->c_argtype & ARGTYPE_MASK) { 716 1.1 cgd case MSGLIST: 717 1.1 cgd /* 718 1.1 cgd * A message list defaulting to nearest forward 719 1.1 cgd * legal message. 720 1.1 cgd */ 721 1.1 cgd if (msgvec == 0) { 722 1.25 christos (void)printf("Illegal use of \"message list\"\n"); 723 1.1 cgd break; 724 1.1 cgd } 725 1.1 cgd if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 726 1.1 cgd break; 727 1.1 cgd if (c == 0) { 728 1.30 christos *msgvec = first(com->c_msgflag, com->c_msgmask); 729 1.10 pk msgvec[1] = 0; 730 1.1 cgd } 731 1.10 pk if (*msgvec == 0) { 732 1.25 christos (void)printf("No applicable messages\n"); 733 1.1 cgd break; 734 1.1 cgd } 735 1.1 cgd e = (*com->c_func)(msgvec); 736 1.1 cgd break; 737 1.1 cgd 738 1.1 cgd case NDMLIST: 739 1.1 cgd /* 740 1.1 cgd * A message list with no defaults, but no error 741 1.1 cgd * if none exist. 742 1.1 cgd */ 743 1.1 cgd if (msgvec == 0) { 744 1.25 christos (void)printf("Illegal use of \"message list\"\n"); 745 1.1 cgd break; 746 1.1 cgd } 747 1.1 cgd if (getmsglist(cp, msgvec, com->c_msgflag) < 0) 748 1.1 cgd break; 749 1.1 cgd e = (*com->c_func)(msgvec); 750 1.1 cgd break; 751 1.1 cgd 752 1.1 cgd case STRLIST: 753 1.1 cgd /* 754 1.1 cgd * Just the straight string, with 755 1.1 cgd * leading blanks removed. 756 1.1 cgd */ 757 1.34 christos cp = skip_space(cp); 758 1.1 cgd e = (*com->c_func)(cp); 759 1.1 cgd break; 760 1.1 cgd 761 1.1 cgd case RAWLIST: 762 1.1 cgd /* 763 1.1 cgd * A vector of strings, in shell style. 764 1.1 cgd */ 765 1.37 christos if ((c = getrawlist(cp, arglist, (int)__arraycount(arglist))) < 0) 766 1.1 cgd break; 767 1.1 cgd if (c < com->c_minargs) { 768 1.25 christos (void)printf("%s requires at least %d arg(s)\n", 769 1.1 cgd com->c_name, com->c_minargs); 770 1.1 cgd break; 771 1.1 cgd } 772 1.1 cgd if (c > com->c_maxargs) { 773 1.25 christos (void)printf("%s takes no more than %d arg(s)\n", 774 1.1 cgd com->c_name, com->c_maxargs); 775 1.1 cgd break; 776 1.1 cgd } 777 1.1 cgd e = (*com->c_func)(arglist); 778 1.1 cgd break; 779 1.1 cgd 780 1.1 cgd case NOLIST: 781 1.1 cgd /* 782 1.1 cgd * Just the constant zero, for exiting, 783 1.1 cgd * eg. 784 1.1 cgd */ 785 1.1 cgd e = (*com->c_func)(0); 786 1.1 cgd break; 787 1.1 cgd 788 1.1 cgd default: 789 1.39 christos errx(EXIT_FAILURE, "Unknown argtype"); 790 1.1 cgd } 791 1.1 cgd 792 1.1 cgd out: 793 1.30 christos close_piping(); 794 1.30 christos 795 1.1 cgd /* 796 1.1 cgd * Exit the current source file on 797 1.1 cgd * error. 798 1.1 cgd */ 799 1.37 christos retval = 0; 800 1.1 cgd if (e) { 801 1.1 cgd if (e < 0) 802 1.37 christos retval = 1; 803 1.37 christos else if (loading) 804 1.37 christos retval = 1; 805 1.37 christos else if (sourcing) 806 1.25 christos (void)unstack(); 807 1.1 cgd } 808 1.37 christos else if (com != NULL) { 809 1.37 christos if (contxt != ec_autoprint && com->c_argtype & P && 810 1.37 christos value(ENAME_AUTOPRINT) != NULL && 811 1.37 christos (dot->m_flag & MDELETED) == 0) 812 1.37 christos (void)execute(__UNCONST("print ."), ec_autoprint); 813 1.37 christos if (!sourcing && (com->c_argtype & T) == 0) 814 1.37 christos sawcom = 1; 815 1.37 christos } 816 1.37 christos sig_check(); 817 1.37 christos return retval; 818 1.1 cgd } 819 1.1 cgd 820 1.1 cgd /* 821 1.1 cgd * The following gets called on receipt of an interrupt. This is 822 1.1 cgd * to abort printout of a command, mainly. 823 1.37 christos * Dispatching here when commands() is inactive crashes rcv. 824 1.1 cgd * Close all open files except 0, 1, 2, and the temporary. 825 1.1 cgd * Also, unstack all source files. 826 1.1 cgd */ 827 1.40 joerg __dead static void 828 1.37 christos lex_intr(int signo) 829 1.1 cgd { 830 1.37 christos 831 1.1 cgd noreset = 0; 832 1.1 cgd if (!inithdr) 833 1.1 cgd sawcom++; 834 1.1 cgd inithdr = 0; 835 1.1 cgd while (sourcing) 836 1.25 christos (void)unstack(); 837 1.1 cgd 838 1.30 christos close_piping(); 839 1.1 cgd close_all_files(); 840 1.1 cgd 841 1.1 cgd if (image >= 0) { 842 1.25 christos (void)close(image); 843 1.1 cgd image = -1; 844 1.1 cgd } 845 1.25 christos (void)fprintf(stderr, "Interrupt\n"); 846 1.37 christos longjmp(jmpbuf, signo); 847 1.1 cgd } 848 1.1 cgd 849 1.1 cgd /* 850 1.1 cgd * Branch here on hangup signal and simulate "exit". 851 1.1 cgd */ 852 1.1 cgd /*ARGSUSED*/ 853 1.40 joerg __dead static void 854 1.37 christos lex_hangup(int s __unused) 855 1.1 cgd { 856 1.37 christos 857 1.1 cgd /* nothing to do? */ 858 1.37 christos exit(EXIT_FAILURE); 859 1.37 christos } 860 1.37 christos 861 1.37 christos /* 862 1.37 christos * When we wake up after ^Z, reprint the prompt. 863 1.37 christos * 864 1.37 christos * NOTE: EditLine deals with the prompt and job control, so with it 865 1.37 christos * this does nothing, i.e., reset_on_stop == 0. 866 1.37 christos */ 867 1.37 christos static void 868 1.37 christos lex_stop(int signo) 869 1.37 christos { 870 1.37 christos 871 1.37 christos if (reset_on_stop) { 872 1.37 christos reset_on_stop = 0; 873 1.37 christos longjmp(jmpbuf, signo); 874 1.37 christos } 875 1.1 cgd } 876 1.1 cgd 877 1.1 cgd /* 878 1.30 christos * Interpret user commands one by one. If standard input is not a tty, 879 1.30 christos * print no prompt. 880 1.1 cgd */ 881 1.30 christos PUBLIC void 882 1.30 christos commands(void) 883 1.1 cgd { 884 1.30 christos int n; 885 1.30 christos char linebuf[LINESIZE]; 886 1.30 christos int eofloop; 887 1.30 christos 888 1.37 christos #ifdef DEBUG_FILE_LEAK 889 1.37 christos file_leak_init(); 890 1.37 christos #endif 891 1.37 christos 892 1.30 christos if (!sourcing) { 893 1.37 christos sig_check(); 894 1.37 christos 895 1.37 christos sig_hold(); 896 1.37 christos (void)sig_signal(SIGINT, lex_intr); 897 1.37 christos (void)sig_signal(SIGHUP, lex_hangup); 898 1.37 christos (void)sig_signal(SIGTSTP, lex_stop); 899 1.37 christos (void)sig_signal(SIGTTOU, lex_stop); 900 1.37 christos (void)sig_signal(SIGTTIN, lex_stop); 901 1.37 christos sig_release(); 902 1.30 christos } 903 1.37 christos 904 1.37 christos (void)setjmp(jmpbuf); /* "reset" location if we got an interrupt */ 905 1.37 christos 906 1.30 christos eofloop = 0; /* initialize this after a possible longjmp */ 907 1.30 christos for (;;) { 908 1.37 christos sig_check(); 909 1.30 christos (void)fflush(stdout); 910 1.30 christos sreset(); 911 1.30 christos /* 912 1.30 christos * Print the prompt, if needed. Clear out 913 1.30 christos * string space, and flush the output. 914 1.30 christos */ 915 1.30 christos if (!sourcing && value(ENAME_INTERACTIVE) != NULL) { 916 1.30 christos if ((prompt = value(ENAME_PROMPT)) == NULL) 917 1.30 christos prompt = DEFAULT_PROMPT; 918 1.30 christos prompt = smsgprintf(prompt, dot); 919 1.30 christos if ((value(ENAME_AUTOINC) != NULL) && (incfile() > 0)) 920 1.30 christos (void)printf("New mail has arrived.\n"); 921 1.37 christos 922 1.30 christos #ifndef USE_EDITLINE 923 1.37 christos reset_on_stop = 1; /* enable job control longjmp */ 924 1.30 christos (void)printf("%s", prompt); 925 1.30 christos #endif 926 1.30 christos } 927 1.37 christos #ifdef DEBUG_FILE_LEAK 928 1.37 christos file_leak_check(); 929 1.37 christos #endif 930 1.30 christos /* 931 1.30 christos * Read a line of commands from the current input 932 1.30 christos * and handle end of file specially. 933 1.30 christos */ 934 1.30 christos n = 0; 935 1.30 christos for (;;) { 936 1.37 christos sig_check(); 937 1.30 christos #ifdef USE_EDITLINE 938 1.30 christos if (!sourcing) { 939 1.30 christos char *line; 940 1.37 christos 941 1.37 christos line = my_gets(&elm.command, prompt, NULL); 942 1.37 christos if (line == NULL) { 943 1.30 christos if (n == 0) 944 1.30 christos n = -1; 945 1.30 christos break; 946 1.30 christos } 947 1.30 christos (void)strlcpy(linebuf, line, sizeof(linebuf)); 948 1.30 christos } 949 1.30 christos else { 950 1.37 christos if (readline(input, &linebuf[n], LINESIZE - n, 0) < 0) { 951 1.30 christos if (n == 0) 952 1.30 christos n = -1; 953 1.30 christos break; 954 1.30 christos } 955 1.30 christos } 956 1.30 christos #else /* USE_EDITLINE */ 957 1.37 christos if (readline(input, &linebuf[n], LINESIZE - n, reset_on_stop) < 0) { 958 1.30 christos if (n == 0) 959 1.30 christos n = -1; 960 1.30 christos break; 961 1.30 christos } 962 1.30 christos #endif /* USE_EDITLINE */ 963 1.37 christos if (!sourcing) 964 1.37 christos setscreensize(); /* so we can resize window */ 965 1.1 cgd 966 1.30 christos if (sourcing) { /* allow comments in source files */ 967 1.30 christos char *ptr; 968 1.30 christos if ((ptr = comment_char(linebuf)) != NULL) 969 1.30 christos *ptr = '\0'; 970 1.30 christos } 971 1.37 christos if ((n = (int)strlen(linebuf)) == 0) 972 1.30 christos break; 973 1.30 christos n--; 974 1.30 christos if (linebuf[n] != '\\') 975 1.30 christos break; 976 1.30 christos linebuf[n++] = ' '; 977 1.30 christos } 978 1.37 christos #ifndef USE_EDITLINE 979 1.37 christos sig_check(); 980 1.37 christos reset_on_stop = 0; /* disable job control longjmp */ 981 1.37 christos #endif 982 1.30 christos if (n < 0) { 983 1.37 christos char *p; 984 1.37 christos 985 1.37 christos /* eof */ 986 1.30 christos if (loading) 987 1.30 christos break; 988 1.30 christos if (sourcing) { 989 1.30 christos (void)unstack(); 990 1.30 christos continue; 991 1.30 christos } 992 1.30 christos if (value(ENAME_INTERACTIVE) != NULL && 993 1.37 christos (p = value(ENAME_IGNOREEOF)) != NULL && 994 1.37 christos ++eofloop < (*p == '\0' ? 25 : atoi(p))) { 995 1.30 christos (void)printf("Use \"quit\" to quit.\n"); 996 1.30 christos continue; 997 1.30 christos } 998 1.30 christos break; 999 1.30 christos } 1000 1.30 christos eofloop = 0; 1001 1.32 christos if (execute(linebuf, ec_normal)) 1002 1.30 christos break; 1003 1.1 cgd } 1004 1.1 cgd } 1005 1.1 cgd 1006 1.1 cgd /* 1007 1.1 cgd * Announce information about the file we are editing. 1008 1.1 cgd * Return a likely place to set dot. 1009 1.1 cgd */ 1010 1.30 christos PUBLIC int 1011 1.17 wiz newfileinfo(int omsgCount) 1012 1.1 cgd { 1013 1.11 lukem struct message *mp; 1014 1.30 christos int d, n, s, t, u, mdot; 1015 1.1 cgd 1016 1.30 christos /* 1017 1.30 christos * Figure out where to set the 'dot'. Use the first new or 1018 1.30 christos * unread message. 1019 1.30 christos */ 1020 1.30 christos for (mp = get_abs_message(omsgCount + 1); mp; 1021 1.30 christos mp = next_abs_message(mp)) 1022 1.1 cgd if (mp->m_flag & MNEW) 1023 1.1 cgd break; 1024 1.30 christos 1025 1.30 christos if (mp == NULL) 1026 1.30 christos for (mp = get_abs_message(omsgCount + 1); mp; 1027 1.30 christos mp = next_abs_message(mp)) 1028 1.1 cgd if ((mp->m_flag & MREAD) == 0) 1029 1.1 cgd break; 1030 1.30 christos if (mp != NULL) 1031 1.30 christos mdot = get_msgnum(mp); 1032 1.1 cgd else 1033 1.8 tls mdot = omsgCount + 1; 1034 1.30 christos #ifdef THREAD_SUPPORT 1035 1.30 christos /* 1036 1.30 christos * See if the message is in the current thread. 1037 1.30 christos */ 1038 1.30 christos if (mp != NULL && get_message(1) != NULL && get_message(mdot) != mp) 1039 1.30 christos mdot = 0; 1040 1.30 christos #endif 1041 1.30 christos /* 1042 1.30 christos * Scan the message array counting the new, unread, deleted, 1043 1.30 christos * and saved messages. 1044 1.30 christos */ 1045 1.30 christos d = n = s = t = u = 0; 1046 1.30 christos for (mp = get_abs_message(1); mp; mp = next_abs_message(mp)) { 1047 1.1 cgd if (mp->m_flag & MNEW) 1048 1.1 cgd n++; 1049 1.1 cgd if ((mp->m_flag & MREAD) == 0) 1050 1.1 cgd u++; 1051 1.1 cgd if (mp->m_flag & MDELETED) 1052 1.1 cgd d++; 1053 1.1 cgd if (mp->m_flag & MSAVED) 1054 1.1 cgd s++; 1055 1.30 christos if (mp->m_flag & MTAGGED) 1056 1.30 christos t++; 1057 1.1 cgd } 1058 1.30 christos /* 1059 1.30 christos * Display the statistics. 1060 1.30 christos */ 1061 1.43 christos update_mailname(NULL); 1062 1.42 christos (void)printf("\"%s\": ", displayname); 1063 1.30 christos { 1064 1.30 christos int cnt = get_abs_msgCount(); 1065 1.30 christos (void)printf("%d message%s", cnt, cnt == 1 ? "" : "s"); 1066 1.30 christos } 1067 1.1 cgd if (n > 0) 1068 1.25 christos (void)printf(" %d new", n); 1069 1.1 cgd if (u-n > 0) 1070 1.25 christos (void)printf(" %d unread", u); 1071 1.30 christos if (t > 0) 1072 1.30 christos (void)printf(" %d tagged", t); 1073 1.1 cgd if (d > 0) 1074 1.25 christos (void)printf(" %d deleted", d); 1075 1.1 cgd if (s > 0) 1076 1.25 christos (void)printf(" %d saved", s); 1077 1.1 cgd if (readonly) 1078 1.25 christos (void)printf(" [Read only]"); 1079 1.25 christos (void)printf("\n"); 1080 1.30 christos 1081 1.30 christos return mdot; 1082 1.30 christos } 1083 1.30 christos 1084 1.30 christos /* 1085 1.30 christos * Announce the presence of the current Mail version, 1086 1.30 christos * give the message count, and print a header listing. 1087 1.30 christos */ 1088 1.30 christos PUBLIC void 1089 1.30 christos announce(void) 1090 1.30 christos { 1091 1.30 christos int vec[2], mdot; 1092 1.30 christos 1093 1.30 christos mdot = newfileinfo(0); 1094 1.30 christos vec[0] = mdot; 1095 1.30 christos vec[1] = 0; 1096 1.30 christos if ((dot = get_message(mdot)) == NULL) 1097 1.30 christos dot = get_abs_message(1); /* make sure we get something! */ 1098 1.30 christos if (get_abs_msgCount() > 0 && value(ENAME_NOHEADER) == NULL) { 1099 1.30 christos inithdr++; 1100 1.30 christos (void)headers(vec); 1101 1.30 christos inithdr = 0; 1102 1.30 christos } 1103 1.1 cgd } 1104 1.1 cgd 1105 1.1 cgd /* 1106 1.1 cgd * Print the current version number. 1107 1.1 cgd */ 1108 1.1 cgd 1109 1.1 cgd /*ARGSUSED*/ 1110 1.30 christos PUBLIC int 1111 1.28 christos pversion(void *v __unused) 1112 1.1 cgd { 1113 1.25 christos (void)printf("Version %s\n", version); 1114 1.30 christos return 0; 1115 1.1 cgd } 1116 1.1 cgd 1117 1.1 cgd /* 1118 1.1 cgd * Load a file of user definitions. 1119 1.1 cgd */ 1120 1.30 christos PUBLIC void 1121 1.24 christos load(const char *name) 1122 1.1 cgd { 1123 1.11 lukem FILE *in, *oldin; 1124 1.1 cgd 1125 1.44 christos if ((in = Fopen(name, "ref")) == NULL) 1126 1.1 cgd return; 1127 1.1 cgd oldin = input; 1128 1.1 cgd input = in; 1129 1.1 cgd loading = 1; 1130 1.1 cgd sourcing = 1; 1131 1.1 cgd commands(); 1132 1.1 cgd loading = 0; 1133 1.1 cgd sourcing = 0; 1134 1.1 cgd input = oldin; 1135 1.25 christos (void)Fclose(in); 1136 1.1 cgd } 1137