|
|
1.1 ! root 1: static char *rcsid = "$Header$"; ! 2: /* ! 3: * plog - record, edit, print, sort progress of a project ! 4: * ! 5: * Author: Peter J. Nicklin ! 6: */ ! 7: #include <ctype.h> ! 8: #include <signal.h> ! 9: #include <stdio.h> ! 10: #include "bin.h" ! 11: #include "date.h" ! 12: #include "from.h" ! 13: #include "getarg.h" ! 14: #include "null.h" ! 15: #include "path.h" ! 16: #include "spms.h" ! 17: #include "system.h" ! 18: #include "yesno.h" ! 19: ! 20: #define SUBJECT_MAX_FORWARD_LOOK 5 /* max no. of lines to read ahead */ ! 21: /* for "Subject: " */ ! 22: #define TITLELENGTH 72 /* length of subject title */ ! 23: /* ! 24: * type of operation to be executed by plog ! 25: */ ! 26: #define APPENDLOG 1 ! 27: #define EDITLOG 2 ! 28: #define PRINTLOG 3 ! 29: #define PRINTLOGTITLE 4 ! 30: #define SORTLOG 5 ! 31: ! 32: char *PGN = "plog"; /* program name */ ! 33: int PRINT_TITLE = YES; /* print message titles? */ ! 34: ! 35: main(argc, argv) ! 36: int argc; ! 37: char **argv; ! 38: { ! 39: extern int PPDEBUG; /* project pathname debug flag */ ! 40: char command[PATHSIZE+8]; /* shell command buffer */ ! 41: char *cwp; /* current working project */ ! 42: char *getcwp(); /* get current working project */ ! 43: char *pathcat(); /* pathname concatenation */ ! 44: char *pathname; /* regular pathname */ ! 45: char plog[PATHSIZE]; /* PROJECTLOG pathname */ ! 46: char *strcpy(); /* string copy */ ! 47: FILE *ifp; /* input file stream */ ! 48: FILE *mustfopen(); /* must open file or die */ ! 49: FILE *ofp; /* output file stream */ ! 50: FILE *popen(); /* open pipe */ ! 51: int action; /* what plog has to do */ ! 52: int decoderange(); /* decode message range */ ! 53: int naction = 0; /* number of non-default actions */ ! 54: int range[2]; /* range of messages to print */ ! 55: int sortlog(); /* sort project log by date */ ! 56: int status = 0; /* exit status */ ! 57: int xppath(); /* expand project pathname */ ! 58: PATH pathbuf; /* pathname struct buffer */ ! 59: void print(); /* print PROJECTLOG */ ! 60: void print_head(); /* print PROJECTLOG message headings */ ! 61: ! 62: action = APPENDLOG; ! 63: range[0] = range[1] = 0; ! 64: ! 65: { ! 66: register char *s; /* option pointer */ ! 67: while (--argc > 0 && (**++argv == '-' || **argv == '+')) ! 68: { ! 69: if (**argv == '-') ! 70: { ! 71: for (s = argv[0]+1; *s != '\0'; s++) ! 72: switch (*s) ! 73: { ! 74: case 'D': ! 75: PPDEBUG = YES; ! 76: break; ! 77: case 'e': ! 78: action = EDITLOG; ! 79: naction++; ! 80: break; ! 81: case 'h': ! 82: PRINT_TITLE = NO; ! 83: break; ! 84: case 'p': ! 85: action = PRINTLOG; ! 86: naction++; ! 87: if (decoderange(++s, range) == NO) ! 88: status = 1; ! 89: goto endif; ! 90: case 's': ! 91: action = SORTLOG; ! 92: naction++; ! 93: break; ! 94: default: ! 95: warn("bad option -%c", *s); ! 96: status = 1; ! 97: goto endif; ! 98: } ! 99: } ! 100: else { ! 101: for (s = argv[0]+1; *s != '\0'; s++) ! 102: switch (*s) ! 103: { ! 104: case 'h': ! 105: action = PRINTLOGTITLE; ! 106: naction++; ! 107: break; ! 108: default: ! 109: warn("bad option +%c", *s); ! 110: status = 1; ! 111: goto endif; ! 112: } ! 113: } ! 114: endif: continue; ! 115: } ! 116: } ! 117: if (status == 1 || argc > 1) ! 118: { ! 119: fatal("usage: plog [-e] [{+-}h] [-p[low[-high]]] [-s] [projectname]"); ! 120: } ! 121: if (naction > 1) ! 122: { ! 123: fatal("choose only one of -e, -p, -s, or +h options"); ! 124: } ! 125: if (argc == 0) ! 126: { ! 127: if ((cwp = getcwp()) == NULL) ! 128: fatal("no project environment"); ! 129: pathname = cwp; ! 130: } ! 131: else { ! 132: if (xppath(*argv, &pathbuf) == -1) ! 133: { ! 134: patherr(*argv); ! 135: exit(1); ! 136: } ! 137: switch (pathbuf.p_mode & P_IFMT) ! 138: { ! 139: case P_IFNEW: ! 140: case P_IFREG: ! 141: case P_IFPDIR: ! 142: fatal("%s: no such project", *argv); ! 143: case P_IFHOME: ! 144: case P_IFPROOT: ! 145: pathname = pathbuf.p_path; ! 146: break; ! 147: } ! 148: } ! 149: pathcat(plog, pathname, PROJECTLOG); ! 150: ! 151: switch (action) ! 152: { ! 153: case APPENDLOG: ! 154: printf("Mailing to %s\n", plog); ! 155: sprintf(command, "Mail %s", plog); ! 156: status = system(command); ! 157: break; ! 158: case EDITLOG: ! 159: printf("Editing %s\n", plog); ! 160: sprintf(command, "Mail -f %s", plog); ! 161: status = system(command); ! 162: break; ! 163: case PRINTLOG: ! 164: ifp = mustfopen(plog, "r"); ! 165: if (!isatty(fileno(stdout)) || (ofp = popen("more","w")) == NULL) ! 166: ofp = stdout; ! 167: ! 168: print(ifp, ofp, range); ! 169: ! 170: if (ofp != stdout) ! 171: pclose(ofp); ! 172: break; ! 173: case PRINTLOGTITLE: ! 174: ifp = mustfopen(plog, "r"); ! 175: if (!isatty(fileno(stdout)) || (ofp = popen("more","w")) == NULL) ! 176: ofp = stdout; ! 177: ! 178: print_head(ifp, ofp); ! 179: ! 180: if (ofp != stdout) ! 181: pclose(ofp); ! 182: break; ! 183: case SORTLOG: ! 184: printf("Sorting %s\n", plog); ! 185: status = sortlog(plog); ! 186: break; ! 187: } ! 188: exit(status); ! 189: } ! 190: ! 191: ! 192: ! 193: /* ! 194: * decoderange() determines the range of message numbers to be printed. ! 195: * Prints an error message and returns NO if syntax error, otherwise YES. ! 196: */ ! 197: decoderange(srange, range) ! 198: char *srange; /* range string to decode */ ! 199: int range[]; /* decoded message range */ ! 200: { ! 201: register char *s; /* range string pointer */ ! 202: register int high = 0; /* high end of message range */ ! 203: register int low = 0; /* low end of message range */ ! 204: ! 205: for (s = srange; isdigit(*s); s++) ! 206: low = 10*low + (*s - '0'); ! 207: if (*s == '-') ! 208: for (s++; isdigit(*s); s++) ! 209: high = 10*high + (*s - '0'); ! 210: if (*s != '\0') ! 211: { ! 212: warn("%s: bad message range", srange); ! 213: return(NO); ! 214: } ! 215: range[0] = low; ! 216: range[1] = high; ! 217: return(YES); ! 218: } ! 219: ! 220: ! 221: ! 222: /* ! 223: * print() copies input stream to output stream, printing a title at ! 224: * the beginning of each log entry. ! 225: */ ! 226: void ! 227: print(ifp, ofp, range) ! 228: register FILE *ifp; /* input file stream */ ! 229: register FILE *ofp; /* output file stream */ ! 230: int range[]; /* range of messages to print */ ! 231: { ! 232: register int high; /* top of range */ ! 233: register int low; /* bottom of range */ ! 234: register int msgno; /* current message number */ ! 235: char *fgets(); /* get a line from input stream */ ! 236: char linebuf[BUFSIZ]; /* input line buffer */ ! 237: FROM *isfrom(); /* is line a "From " line? */ ! 238: void print_title(); /* print message title */ ! 239: ! 240: msgno = 0; ! 241: low = range[0]; ! 242: high = range[1]; ! 243: ! 244: while (fgets(linebuf, BUFSIZ, ifp) != NULL) ! 245: if (isfrom(linebuf) != NULL) ! 246: { ! 247: msgno++; ! 248: if (msgno < low) ! 249: continue; ! 250: else if (msgno > high && high != 0) ! 251: break; ! 252: if (PRINT_TITLE == YES) ! 253: { ! 254: print_title(linebuf, ifp, ofp); ! 255: } ! 256: else { ! 257: fputs(linebuf, ofp); ! 258: } ! 259: } ! 260: else { ! 261: if (msgno < low) ! 262: continue; ! 263: fputs(linebuf, ofp); ! 264: } ! 265: } ! 266: ! 267: ! 268: ! 269: /* ! 270: * print_head() prints the log entry headings only. ! 271: */ ! 272: void ! 273: print_head(ifp, ofp) ! 274: register FILE *ifp; /* input file stream */ ! 275: register FILE *ofp; /* output file stream */ ! 276: { ! 277: register int msgno; /* current message number */ ! 278: register char *sp; /* subject field pointer */ ! 279: char *fgets(); /* get a line from input stream */ ! 280: char linebuf[BUFSIZ]; /* input line buffer */ ! 281: char *skipword(); /* skip to next word */ ! 282: char *subject; /* beginning of subject field */ ! 283: FROM *fromline; /* "From " line struct */ ! 284: FROM *isfrom(); /* is line a "From " line? */ ! 285: int i; /* read-ahead buffer counter */ ! 286: int strlen(); /* string length */ ! 287: int strncmp(); /* compare strings for n chars */ ! 288: ! 289: msgno = 0; ! 290: while (fgets(linebuf, BUFSIZ, ifp) != NULL) ! 291: if ((fromline = isfrom(linebuf)) != NULL) ! 292: { ! 293: msgno++; ! 294: fprintf(ofp, "%3d %-10s %s", msgno, fromline->from, ! 295: fromline->date); ! 296: for (i = 0; i < SUBJECT_MAX_FORWARD_LOOK; i++) ! 297: { ! 298: if (fgets(linebuf, BUFSIZ, ifp) == NULL) ! 299: break; ! 300: if (strncmp("Subject: ", linebuf, 9) == 0) ! 301: { ! 302: sp = subject = skipword(linebuf); ! 303: while (*sp != '\n' && *sp != '\0') ! 304: sp++; ! 305: *sp = '\0'; ! 306: fprintf(ofp, " \"%s\"", subject); ! 307: } ! 308: else if (isfrom(linebuf) != NULL) ! 309: { ! 310: /* "From " line not welcome here */ ! 311: fseek(ifp, (long) -strlen(linebuf), 1); ! 312: break; ! 313: } ! 314: } ! 315: putc('\n', ofp); ! 316: } ! 317: } ! 318: ! 319: ! 320: ! 321: /* ! 322: * printsubject() pretty-prints a "Subject: " field. ! 323: */ ! 324: void ! 325: printsubject(linebuf, ofp) ! 326: char *linebuf; /* line containing subject */ ! 327: register FILE *ofp; /* output file stream */ ! 328: { ! 329: register int nblank; /* number of blanks to pad title */ ! 330: char *skipword(); /* skip to next word */ ! 331: int strlen(); /* string length */ ! 332: ! 333: nblank = (TITLELENGTH - (strlen(linebuf) - 9)) / 2; ! 334: /* length of "Subject: " = 9 chars */ ! 335: while (nblank-- > 0) ! 336: putc(' ', ofp); ! 337: fputs(skipword(linebuf), ofp); ! 338: } ! 339: ! 340: ! 341: ! 342: /* ! 343: * print_title() prints a subject title between two dashed lines. ! 344: * If a "Subject:" field cannot be found within SUBJECT_MAX_FORWARD_LOOK ! 345: * lines of the "From" line, then, a single dash line is printed. ! 346: */ ! 347: #define PUTDASHLINE(fp) {int i = TITLELENGTH; \ ! 348: while (i-- > 0) putc('-', fp); putc('\n', fp);} ! 349: void ! 350: print_title(linebuf, ifp, ofp) ! 351: char linebuf[]; /* line buffer containing "From" */ ! 352: register FILE *ifp; /* input file stream */ ! 353: register FILE *ofp; /* output file stream */ ! 354: { ! 355: char *fgets(); /* get a line from input stream */ ! 356: char frombuf[BUFSIZ]; /* "From " line buffer */ ! 357: char *strcpy(); /* string copy */ ! 358: int i; /* read-ahead buffer counter */ ! 359: int strncmp(); /* compare strings for n chars */ ! 360: long ftell(); /* offset relative to file beginning */ ! 361: long markifp; /* mark position of file */ ! 362: void printsubject(); /* printprint "Subject: " field */ ! 363: ! 364: PUTDASHLINE(ofp); ! 365: ! 366: markifp = ftell(ifp); ! 367: strcpy(frombuf, linebuf); ! 368: for (i = 0; i < SUBJECT_MAX_FORWARD_LOOK; i++) ! 369: { ! 370: if (fgets(linebuf, BUFSIZ, ifp) == NULL) ! 371: break; ! 372: if (strncmp("Subject: ", linebuf, 9) == 0) ! 373: { ! 374: printsubject(linebuf, ofp); ! 375: PUTDASHLINE(ofp); ! 376: break; ! 377: } ! 378: } ! 379: fputs(frombuf, ofp); ! 380: fseek(ifp, markifp, 0); ! 381: } ! 382: ! 383: ! 384: ! 385: /* ! 386: * sortlog() sorts the project log by date. Returns status 0 if ! 387: * successful, otherwise 1. ! 388: */ ! 389: sortlog(logname) ! 390: char *logname; /* name of project log file */ ! 391: { ! 392: register FILE *ifp; /* input file stream */ ! 393: register int (*hstat)(); /* hangup status */ ! 394: register int (*istat)(); /* interrupt status */ ! 395: register int (*qstat)(); /* quit status */ ! 396: register long nc = 0; /* number of characters read */ ! 397: char *fgets(); /* get a line from input stream */ ! 398: char linebuf[BUFSIZ]; /* input line buffer */ ! 399: char *pathcat(); /* pathname concatenation */ ! 400: char *pathhead(); /* remove pathname tail */ ! 401: char *strcpy(); /* string copy */ ! 402: FILE *mustfopen(); /* must open file or die */ ! 403: FILE *ofp; /* output file stream */ ! 404: FROM *from; /* broken down "From " line */ ! 405: FROM *initfrom(); /* initialize "From " pointer array */ ! 406: FROM *isfrom(); /* is line a "From " line? */ ! 407: FROM *lastfrom; /* previous "From " line */ ! 408: FROM *savefrom(); /* save "From " lines */ ! 409: int len; /* length of input line */ ! 410: int outfrom(); /* output "From " messages */ ! 411: int parsedate(); /* parse ctime(3) generated date */ ! 412: char sortlog[PATHSIZE]; /* temporary projectlog for sorting */ ! 413: int status = 0; /* return status */ ! 414: int strlen(); /* string length */ ! 415: void sortfrom(); /* sort "From " lines */ ! 416: ! 417: ifp = mustfopen(logname, "r"); ! 418: ! 419: lastfrom = initfrom(); ! 420: ! 421: for (;;) ! 422: { ! 423: if (fgets(linebuf, BUFSIZ, ifp) == NULL) ! 424: break; ! 425: len = strlen(linebuf); ! 426: nc += len; ! 427: if ((from = isfrom(linebuf)) != NULL) ! 428: { ! 429: if (parsedate(from->date, &from->bdt) == NO) ! 430: { ! 431: warn("%s: bad date", from->date); ! 432: status = 1; ! 433: } ! 434: else { ! 435: from->m_seek = nc - len; ! 436: lastfrom->m_len = from->m_seek - lastfrom->m_seek; ! 437: if ((lastfrom = savefrom(from)) == NULL) ! 438: { ! 439: warn("out of memory"); ! 440: return(1); ! 441: } ! 442: } ! 443: } ! 444: } ! 445: lastfrom->m_len = nc - lastfrom->m_seek; ! 446: if (status > 0) ! 447: return(status); ! 448: sortfrom(); ! 449: pathcat(sortlog, pathhead(strcpy(sortlog, logname)), "temp_log"); ! 450: if (FILEXIST(sortlog)) ! 451: { ! 452: warn("%s sort in progress - try later", PROJECTLOG); ! 453: return(1); ! 454: } ! 455: ! 456: hstat = signal(SIGHUP, SIG_IGN); ! 457: istat = signal(SIGINT, SIG_IGN); ! 458: qstat = signal(SIGQUIT, SIG_IGN); ! 459: ! 460: ofp = mustfopen(sortlog, "w"); ! 461: if (outfrom(ifp, ofp) == YES) ! 462: { ! 463: fclose(ifp); ! 464: fclose(ofp); ! 465: RENAME(sortlog, logname); ! 466: } ! 467: else { ! 468: warn("write error in %s: sort failed", sortlog); ! 469: fclose(ifp); ! 470: fclose(ofp); ! 471: unlink(sortlog); ! 472: status = 1; ! 473: } ! 474: ! 475: signal(SIGINT, hstat); ! 476: signal(SIGINT, istat); ! 477: signal(SIGQUIT, qstat); ! 478: ! 479: return(status); ! 480: } ! 481: ! 482: ! 483: ! 484: /* ! 485: * skipword() skips a liberal (blank, tab delimited) word and returns a ! 486: * pointer to the next word. ! 487: */ ! 488: char * ! 489: skipword(bp) ! 490: register char *bp; /* buffer pointer */ ! 491: { ! 492: for (; *bp != '\0' && isspace(*bp); bp++) ! 493: continue; ! 494: for (; *bp != '\0' && !isspace(*bp); bp++) ! 495: continue; ! 496: for (; *bp != '\0' && isspace(*bp); bp++) ! 497: continue; ! 498: return(bp); ! 499: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.