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