|
|
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.