1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 static char *sccsid = "@(#)list.c 5.2 (Berkeley) 06/21/85"; 9 #endif not lint 10 11 #include "rcv.h" 12 #include <ctype.h> 13 14 /* 15 * Mail -- a mail program 16 * 17 * Message list handling. 18 */ 19 20 /* 21 * Convert the user string of message numbers and 22 * store the numbers into vector. 23 * 24 * Returns the count of messages picked up or -1 on error. 25 */ 26 27 getmsglist(buf, vector, flags) 28 char *buf; 29 int *vector; 30 { 31 register int *ip; 32 register struct message *mp; 33 34 if (markall(buf, flags) < 0) 35 return(-1); 36 ip = vector; 37 for (mp = &message[0]; mp < &message[msgCount]; mp++) 38 if (mp->m_flag & MMARK) 39 *ip++ = mp - &message[0] + 1; 40 *ip = NULL; 41 return(ip - vector); 42 } 43 44 /* 45 * Mark all messages that the user wanted from the command 46 * line in the message structure. Return 0 on success, -1 47 * on error. 48 */ 49 50 /* 51 * Bit values for colon modifiers. 52 */ 53 54 #define CMNEW 01 /* New messages */ 55 #define CMOLD 02 /* Old messages */ 56 #define CMUNREAD 04 /* Unread messages */ 57 #define CMDELETED 010 /* Deleted messages */ 58 #define CMREAD 020 /* Read messages */ 59 60 /* 61 * The following table describes the letters which can follow 62 * the colon and gives the corresponding modifier bit. 63 */ 64 65 struct coltab { 66 char co_char; /* What to find past : */ 67 int co_bit; /* Associated modifier bit */ 68 int co_mask; /* m_status bits to mask */ 69 int co_equal; /* ... must equal this */ 70 } coltab[] = { 71 'n', CMNEW, MNEW, MNEW, 72 'o', CMOLD, MNEW, 0, 73 'u', CMUNREAD, MREAD, 0, 74 'd', CMDELETED, MDELETED, MDELETED, 75 'r', CMREAD, MREAD, MREAD, 76 0, 0, 0, 0 77 }; 78 79 static int lastcolmod; 80 81 markall(buf, f) 82 char buf[]; 83 { 84 register char **np; 85 register int i; 86 register struct message *mp; 87 char *namelist[NMLSIZE], *bufp; 88 int tok, beg, mc, star, other, valdot, colmod, colresult; 89 90 valdot = dot - &message[0] + 1; 91 colmod = 0; 92 for (i = 1; i <= msgCount; i++) 93 unmark(i); 94 bufp = buf; 95 mc = 0; 96 np = &namelist[0]; 97 scaninit(); 98 tok = scan(&bufp); 99 star = 0; 100 other = 0; 101 beg = 0; 102 while (tok != TEOL) { 103 switch (tok) { 104 case TNUMBER: 105 number: 106 if (star) { 107 printf("No numbers mixed with *\n"); 108 return(-1); 109 } 110 mc++; 111 other++; 112 if (beg != 0) { 113 if (check(lexnumber, f)) 114 return(-1); 115 for (i = beg; i <= lexnumber; i++) 116 if ((message[i - 1].m_flag & MDELETED) == f) 117 mark(i); 118 beg = 0; 119 break; 120 } 121 beg = lexnumber; 122 if (check(beg, f)) 123 return(-1); 124 tok = scan(&bufp); 125 regret(tok); 126 if (tok != TDASH) { 127 mark(beg); 128 beg = 0; 129 } 130 break; 131 132 case TPLUS: 133 if (beg != 0) { 134 printf("Non-numeric second argument\n"); 135 return(-1); 136 } 137 if (valdot < msgCount) 138 mark(valdot+1); 139 else { 140 printf("Referencing beyond EOF\n"); 141 return(-1); 142 } 143 break; 144 145 case TDASH: 146 if (beg == 0) { 147 if (valdot > 1) 148 mark(valdot-1); 149 else { 150 printf("Referencing before 1\n"); 151 return(-1); 152 } 153 } 154 break; 155 156 case TSTRING: 157 if (beg != 0) { 158 printf("Non-numeric second argument\n"); 159 return(-1); 160 } 161 other++; 162 if (lexstring[0] == ':') { 163 colresult = evalcol(lexstring[1]); 164 if (colresult == 0) { 165 printf("Unknown colon modifier \"%s\"\n", 166 lexstring); 167 return(-1); 168 } 169 colmod |= colresult; 170 } 171 else 172 *np++ = savestr(lexstring); 173 break; 174 175 case TDOLLAR: 176 case TUP: 177 case TDOT: 178 lexnumber = metamess(lexstring[0], f); 179 if (lexnumber == -1) 180 return(-1); 181 goto number; 182 183 case TSTAR: 184 if (other) { 185 printf("Can't mix \"*\" with anything\n"); 186 return(-1); 187 } 188 star++; 189 break; 190 } 191 tok = scan(&bufp); 192 } 193 lastcolmod = colmod; 194 *np = NOSTR; 195 mc = 0; 196 if (star) { 197 for (i = 0; i < msgCount; i++) 198 if ((message[i].m_flag & MDELETED) == f) { 199 mark(i+1); 200 mc++; 201 } 202 if (mc == 0) { 203 printf("No applicable messages.\n"); 204 return(-1); 205 } 206 return(0); 207 } 208 209 /* 210 * If no numbers were given, mark all of the messages, 211 * so that we can unmark any whose sender was not selected 212 * if any user names were given. 213 */ 214 215 if ((np > namelist || colmod != 0) && mc == 0) 216 for (i = 1; i <= msgCount; i++) 217 if ((message[i-1].m_flag & (MSAVED|MDELETED)) == f) 218 mark(i); 219 220 /* 221 * If any names were given, go through and eliminate any 222 * messages whose senders were not requested. 223 */ 224 225 if (np > namelist) { 226 for (i = 1; i <= msgCount; i++) { 227 for (mc = 0, np = &namelist[0]; *np != NOSTR; np++) 228 if (**np == '/') { 229 if (matchsubj(*np, i)) { 230 mc++; 231 break; 232 } 233 } 234 else { 235 if (sender(*np, i)) { 236 mc++; 237 break; 238 } 239 } 240 if (mc == 0) 241 unmark(i); 242 } 243 244 /* 245 * Make sure we got some decent messages. 246 */ 247 248 mc = 0; 249 for (i = 1; i <= msgCount; i++) 250 if (message[i-1].m_flag & MMARK) { 251 mc++; 252 break; 253 } 254 if (mc == 0) { 255 printf("No applicable messages from {%s", 256 namelist[0]); 257 for (np = &namelist[1]; *np != NOSTR; np++) 258 printf(", %s", *np); 259 printf("}\n"); 260 return(-1); 261 } 262 } 263 264 /* 265 * If any colon modifiers were given, go through and 266 * unmark any messages which do not satisfy the modifiers. 267 */ 268 269 if (colmod != 0) { 270 for (i = 1; i <= msgCount; i++) { 271 register struct coltab *colp; 272 273 mp = &message[i - 1]; 274 for (colp = &coltab[0]; colp->co_char; colp++) 275 if (colp->co_bit & colmod) 276 if ((mp->m_flag & colp->co_mask) 277 != colp->co_equal) 278 unmark(i); 279 280 } 281 for (mp = &message[0]; mp < &message[msgCount]; mp++) 282 if (mp->m_flag & MMARK) 283 break; 284 if (mp >= &message[msgCount]) { 285 register struct coltab *colp; 286 287 printf("No messages satisfy"); 288 for (colp = &coltab[0]; colp->co_char; colp++) 289 if (colp->co_bit & colmod) 290 printf(" :%c", colp->co_char); 291 printf("\n"); 292 return(-1); 293 } 294 } 295 return(0); 296 } 297 298 /* 299 * Turn the character after a colon modifier into a bit 300 * value. 301 */ 302 evalcol(col) 303 { 304 register struct coltab *colp; 305 306 if (col == 0) 307 return(lastcolmod); 308 for (colp = &coltab[0]; colp->co_char; colp++) 309 if (colp->co_char == col) 310 return(colp->co_bit); 311 return(0); 312 } 313 314 /* 315 * Check the passed message number for legality and proper flags. 316 */ 317 318 check(mesg, f) 319 { 320 register struct message *mp; 321 322 if (mesg < 1 || mesg > msgCount) { 323 printf("%d: Invalid message number\n", mesg); 324 return(-1); 325 } 326 mp = &message[mesg-1]; 327 if ((mp->m_flag & MDELETED) != f) { 328 printf("%d: Inappropriate message\n", mesg); 329 return(-1); 330 } 331 return(0); 332 } 333 334 /* 335 * Scan out the list of string arguments, shell style 336 * for a RAWLIST. 337 */ 338 339 getrawlist(line, argv) 340 char line[]; 341 char **argv; 342 { 343 register char **ap, *cp, *cp2; 344 char linebuf[BUFSIZ], quotec; 345 346 ap = argv; 347 cp = line; 348 while (*cp != '\0') { 349 while (any(*cp, " \t")) 350 cp++; 351 cp2 = linebuf; 352 quotec = 0; 353 if (any(*cp, "'\"")) 354 quotec = *cp++; 355 if (quotec == 0) 356 while (*cp != '\0' && !any(*cp, " \t")) 357 *cp2++ = *cp++; 358 else { 359 while (*cp != '\0' && *cp != quotec) 360 *cp2++ = *cp++; 361 if (*cp != '\0') 362 cp++; 363 } 364 *cp2 = '\0'; 365 if (cp2 == linebuf) 366 break; 367 *ap++ = savestr(linebuf); 368 } 369 *ap = NOSTR; 370 return(ap-argv); 371 } 372 373 /* 374 * scan out a single lexical item and return its token number, 375 * updating the string pointer passed **p. Also, store the value 376 * of the number or string scanned in lexnumber or lexstring as 377 * appropriate. In any event, store the scanned `thing' in lexstring. 378 */ 379 380 struct lex { 381 char l_char; 382 char l_token; 383 } singles[] = { 384 '$', TDOLLAR, 385 '.', TDOT, 386 '^', TUP, 387 '*', TSTAR, 388 '-', TDASH, 389 '+', TPLUS, 390 '(', TOPEN, 391 ')', TCLOSE, 392 0, 0 393 }; 394 395 scan(sp) 396 char **sp; 397 { 398 register char *cp, *cp2; 399 register int c; 400 register struct lex *lp; 401 int quotec; 402 403 if (regretp >= 0) { 404 copy(stringstack[regretp], lexstring); 405 lexnumber = numberstack[regretp]; 406 return(regretstack[regretp--]); 407 } 408 cp = *sp; 409 cp2 = lexstring; 410 c = *cp++; 411 412 /* 413 * strip away leading white space. 414 */ 415 416 while (any(c, " \t")) 417 c = *cp++; 418 419 /* 420 * If no characters remain, we are at end of line, 421 * so report that. 422 */ 423 424 if (c == '\0') { 425 *sp = --cp; 426 return(TEOL); 427 } 428 429 /* 430 * If the leading character is a digit, scan 431 * the number and convert it on the fly. 432 * Return TNUMBER when done. 433 */ 434 435 if (isdigit(c)) { 436 lexnumber = 0; 437 while (isdigit(c)) { 438 lexnumber = lexnumber*10 + c - '0'; 439 *cp2++ = c; 440 c = *cp++; 441 } 442 *cp2 = '\0'; 443 *sp = --cp; 444 return(TNUMBER); 445 } 446 447 /* 448 * Check for single character tokens; return such 449 * if found. 450 */ 451 452 for (lp = &singles[0]; lp->l_char != 0; lp++) 453 if (c == lp->l_char) { 454 lexstring[0] = c; 455 lexstring[1] = '\0'; 456 *sp = cp; 457 return(lp->l_token); 458 } 459 460 /* 461 * We've got a string! Copy all the characters 462 * of the string into lexstring, until we see 463 * a null, space, or tab. 464 * If the lead character is a " or ', save it 465 * and scan until you get another. 466 */ 467 468 quotec = 0; 469 if (any(c, "'\"")) { 470 quotec = c; 471 c = *cp++; 472 } 473 while (c != '\0') { 474 if (c == quotec) 475 break; 476 if (quotec == 0 && any(c, " \t")) 477 break; 478 if (cp2 - lexstring < STRINGLEN-1) 479 *cp2++ = c; 480 c = *cp++; 481 } 482 if (quotec && c == 0) 483 fprintf(stderr, "Missing %c\n", quotec); 484 *sp = --cp; 485 *cp2 = '\0'; 486 return(TSTRING); 487 } 488 489 /* 490 * Unscan the named token by pushing it onto the regret stack. 491 */ 492 493 regret(token) 494 { 495 if (++regretp >= REGDEP) 496 panic("Too many regrets"); 497 regretstack[regretp] = token; 498 lexstring[STRINGLEN-1] = '\0'; 499 stringstack[regretp] = savestr(lexstring); 500 numberstack[regretp] = lexnumber; 501 } 502 503 /* 504 * Reset all the scanner global variables. 505 */ 506 507 scaninit() 508 { 509 regretp = -1; 510 } 511 512 /* 513 * Find the first message whose flags & m == f and return 514 * its message number. 515 */ 516 517 first(f, m) 518 { 519 register int mesg; 520 register struct message *mp; 521 522 mesg = dot - &message[0] + 1; 523 f &= MDELETED; 524 m &= MDELETED; 525 for (mp = dot; mp < &message[msgCount]; mp++) { 526 if ((mp->m_flag & m) == f) 527 return(mesg); 528 mesg++; 529 } 530 mesg = dot - &message[0]; 531 for (mp = dot-1; mp >= &message[0]; mp--) { 532 if ((mp->m_flag & m) == f) 533 return(mesg); 534 mesg--; 535 } 536 return(NULL); 537 } 538 539 /* 540 * See if the passed name sent the passed message number. Return true 541 * if so. 542 */ 543 544 sender(str, mesg) 545 char *str; 546 { 547 register struct message *mp; 548 register char *cp; 549 550 mp = &message[mesg-1]; 551 cp = nameof(mp, 0); 552 return(icequal(cp, str)); 553 } 554 555 /* 556 * See if the given string matches inside the subject field of the 557 * given message. For the purpose of the scan, we ignore case differences. 558 * If it does, return true. The string search argument is assumed to 559 * have the form "/search-string." If it is of the form "/," we use the 560 * previous search string. 561 */ 562 563 char lastscan[128]; 564 565 matchsubj(str, mesg) 566 char *str; 567 { 568 register struct message *mp; 569 register char *cp, *cp2, *backup; 570 571 str++; 572 if (strlen(str) == 0) 573 str = lastscan; 574 else 575 strcpy(lastscan, str); 576 mp = &message[mesg-1]; 577 578 /* 579 * Now look, ignoring case, for the word in the string. 580 */ 581 582 cp = str; 583 cp2 = hfield("subject", mp); 584 if (cp2 == NOSTR) 585 return(0); 586 backup = cp2; 587 while (*cp2) { 588 if (*cp == 0) 589 return(1); 590 if (raise(*cp++) != raise(*cp2++)) { 591 cp2 = ++backup; 592 cp = str; 593 } 594 } 595 return(*cp == 0); 596 } 597 598 /* 599 * Mark the named message by setting its mark bit. 600 */ 601 602 mark(mesg) 603 { 604 register int i; 605 606 i = mesg; 607 if (i < 1 || i > msgCount) 608 panic("Bad message number to mark"); 609 message[i-1].m_flag |= MMARK; 610 } 611 612 /* 613 * Unmark the named message. 614 */ 615 616 unmark(mesg) 617 { 618 register int i; 619 620 i = mesg; 621 if (i < 1 || i > msgCount) 622 panic("Bad message number to unmark"); 623 message[i-1].m_flag &= ~MMARK; 624 } 625 626 /* 627 * Return the message number corresponding to the passed meta character. 628 */ 629 630 metamess(meta, f) 631 { 632 register int c, m; 633 register struct message *mp; 634 635 c = meta; 636 switch (c) { 637 case '^': 638 /* 639 * First 'good' message left. 640 */ 641 for (mp = &message[0]; mp < &message[msgCount]; mp++) 642 if ((mp->m_flag & MDELETED) == f) 643 return(mp - &message[0] + 1); 644 printf("No applicable messages\n"); 645 return(-1); 646 647 case '$': 648 /* 649 * Last 'good message left. 650 */ 651 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--) 652 if ((mp->m_flag & MDELETED) == f) 653 return(mp - &message[0] + 1); 654 printf("No applicable messages\n"); 655 return(-1); 656 657 case '.': 658 /* 659 * Current message. 660 */ 661 m = dot - &message[0] + 1; 662 if ((dot->m_flag & MDELETED) != f) { 663 printf("%d: Inappropriate message\n", m); 664 return(-1); 665 } 666 return(m); 667 668 default: 669 printf("Unknown metachar (%c)\n", c); 670 return(-1); 671 } 672 } 673