|
|
1.1 root 1: #ifndef lint
2: static char *sccsid = "@(#)newnews.c 1.5 (Berkeley) 3/20/86";
3: #endif
4:
5: #include "common.h"
6:
7: long gmt_to_local();
8:
9: /*
10: * NEWNEWS newsgroups date time ["GMT"] [<distributions>]
11: *
12: * Return the message-id's of any news articles past
13: * a certain date and time, within the specified distributions.
14: *
15: */
16:
17: newnews(argc, argv)
18: int argc;
19: char *argv[];
20: {
21: char *cp, *ngp;
22: char *key;
23: char datebuf[32];
24: char line[MAX_STRLEN];
25: char **distlist, **nglist, **histlist;
26: int distcount, ngcount, histcount;
27: int all;
28: FILE *fp;
29: long date;
30: long dtol();
31: char *ltod();
32:
33: if (argc < 4) {
34: printf("%d NEWNEWS requires at least three arguments.\r\n",
35: ERR_CMDSYN);
36: (void) fflush(stdout);
37: return;
38: }
39:
40: all = streql(argv[1], "*");
41: if (!all) {
42: ngcount = get_nglist(&nglist, argv[1]);
43: if (ngcount == 0) {
44: printf("%d Bogus newsgroup specifier: %s\r\n",
45: ERR_CMDSYN);
46: (void) fflush(stdout);
47: return;
48: }
49: }
50:
51: /* YYMMDD HHMMSS */
52: if (strlen(argv[2]) != 6 || strlen(argv[3]) != 6) {
53: printf("%d Date/time must be in form YYMMDD HHMMSS.\r\n",
54: ERR_CMDSYN);
55: (void) fflush(stdout);
56: return;
57: }
58:
59: (void) strcpy(datebuf, argv[2]);
60: (void) strcat(datebuf, argv[3]);
61:
62: argc -= 4;
63: argv += 4;
64:
65: /*
66: * Flame on. The history file is not stored in GMT, but
67: * in local time. So we have to convert GMT to local time
68: * if we're given GMT, otherwise we need only chop off the
69: * the seconds. Such braindamage.
70: */
71:
72: key = datebuf; /* Unless they specify GMT */
73:
74: if (argc > 0) {
75: if (streql(*argv, "GMT")) { /* Which we handle here */
76: date = dtol(datebuf);
77: if (date < 0) {
78: printf("%d Invalid date specification.\r\n",
79: ERR_CMDSYN);
80: (void) fflush(stdout);
81: return;
82: }
83: date = gmt_to_local(date);
84: key = ltod(date);
85: ++argv;
86: --argc;
87: }
88: }
89:
90: /* So, key now points to the local time, but we need to zap secs */
91:
92: key[10] = '\0';
93:
94: distcount = 0;
95: if (argc > 0) {
96: distcount = get_distlist(&distlist, *argv);
97: if (distcount < 0) {
98: printf("%d Bad distribution list: %s\r\n", ERR_CMDSYN,
99: *argv);
100: (void) fflush(stdout);
101: return;
102: }
103: }
104:
105: fp = fopen(HISTORY_FILE, "r");
106: if (fp == NULL) {
107: syslog(LOG_ERR, "newnews: fopen %s: %m", HISTORY_FILE);
108: printf("%d Cannot open history file.\r\n", ERR_FAULT);
109: (void) fflush(stdout);
110: return;
111: }
112:
113: printf("%d New news by message id follows.\r\n", OK_NEWNEWS);
114:
115: if (seekuntil(fp, key, line, sizeof (line)) < 0) {
116: printf(".\r\n");
117: (void) fflush(stdout);
118: (void) fclose(fp);
119: return;
120: }
121:
122: /*
123: * History file looks like:
124: *
125: * <[email protected]> 01/22/86 09:19 net.micro.att/899 ucb.general/2545
126: * ^--tab ^--tab ^--space ^sp\0
127: * Sometimes the newsgroups are missing; we try to be robust and
128: * ignore such bogosity. We tackle this by our usual parse routine,
129: * and break the list of articles in the history file into an argv
130: * array with one newsgroup per entry.
131: */
132:
133: do {
134: if ((cp = index(line, '\t')) == NULL)
135: continue;
136:
137: if ((ngp = index(cp+1, '\t')) == NULL) /* 2nd tab */
138: continue;
139: ++ngp; /* Points at newsgroup list */
140: if (*ngp == '\n')
141: continue;
142: histcount = get_histlist(&histlist, ngp);
143: if (histcount == 0)
144: continue;
145:
146: /*
147: * For each newsgroup on this line in the history
148: * file, check it against the newsgroup names we're given.
149: * If it matches, then see if we're hacking distributions.
150: * If so, open the file and match the distribution line.
151: */
152:
153: if (!all)
154: if (!ngmatch(nglist, ngcount, histlist, histcount))
155: continue;
156:
157: if (distcount)
158: if (!distmatch(distlist, distcount, histlist, histcount))
159: continue;
160:
161: *cp = '\0';
162: printf("%s\r\n", line);
163: } while (fgets(line, sizeof(line), fp) != NULL);
164:
165: printf(".\r\n");
166: (void) fflush(stdout);
167: (void) fclose(fp);
168: }
169:
170:
171: /*
172: * seekuntil -- seek through the history file looking for
173: * a line with date "key". Get that line, and return.
174: *
175: * Parameters: "fp" is the active file.
176: * "key" is the date, in form YYMMDDHHMM (no SS)
177: * "line" is storage for the first line we find.
178: *
179: * Returns: -1 on error, 0 otherwise.
180: *
181: * Side effects: Seeks in history file, modifies line.
182: */
183:
184: seekuntil(fp, key, line, linesize)
185: FILE *fp;
186: char *key;
187: char *line;
188: int linesize;
189: {
190: char datetime[32];
191: int c;
192: long top, bot, mid;
193:
194: bot = 0;
195: (void) fseek(fp, 0L, 2);
196: top = ftell(fp);
197: for(;;) {
198: mid = (top+bot)/2;
199: (void) fseek(fp, mid, 0);
200: do {
201: c = getc(fp);
202: mid++;
203: } while (c != EOF && c!='\n');
204: if (!getword(fp, datetime, line, linesize)) {
205: return (-1);
206: }
207: switch (compare(key, datetime)) {
208: case -2:
209: case -1:
210: case 0:
211: if (top <= mid)
212: break;
213: top = mid;
214: continue;
215: case 1:
216: case 2:
217: bot = mid;
218: continue;
219: }
220: break;
221: }
222: (void) fseek(fp, bot, 0);
223: while(ftell(fp) < top) {
224: if (!getword(fp, datetime, line, linesize)) {
225: return (-1);
226: }
227: switch(compare(key, datetime)) {
228: case -2:
229: case -1:
230: case 0:
231: break;
232: case 1:
233: case 2:
234: continue;
235: }
236: break;
237: }
238:
239: return (0);
240: }
241:
242: compare(s, t)
243: register char *s, *t;
244: {
245: for (; *s == *t; s++, t++)
246: if (*s == 0)
247: return(0);
248: return (*s == 0 ? -1:
249: *t == 0 ? 1:
250: *s < *t ? -2:
251: 2);
252: }
253:
254: getword(fp, w, line, linesize)
255: FILE *fp;
256: char *w;
257: char *line;
258: int linesize;
259: {
260: register char *cp;
261:
262: if (fgets(line, linesize, fp) == NULL)
263: return (0);
264: if (cp = index(line, '\t')) {
265: /*
266: * The following gross hack is present because the history file date
267: * format is braindamaged. They like "mm/dd/yy hh:mm", which is useless
268: * for relative comparisons of dates using something like atoi() or
269: * strcmp. So, this changes their format into yymmddhhmm. Sigh.
270: *
271: * 12345678901234 ("x" for cp[x])
272: * mm/dd/yy hh:mm (their lousy representation)
273: * yymmddhhmm (our good one)
274: * 0123456789 ("x" for w[x])
275: */
276: *cp = '\0';
277: (void) strncpy(w, cp+1, 15);
278: w[0] = cp[7]; /* Years */
279: w[1] = cp[8];
280: w[2] = cp[1]; /* Months */
281: w[3] = cp[2];
282: w[4] = cp[4]; /* Days */
283: w[5] = cp[5];
284: w[6] = cp[10]; /* Hours */
285: w[7] = cp[11];
286: w[8] = cp[13]; /* Minutes */
287: w[9] = cp[14];
288: w[10] = '\0';
289: } else
290: w[0] = '\0';
291: return (1);
292: }
293:
294: /*
295: * ngmatch -- match a list of newsgroups, with possible wildcard
296: * expansion (i.e., *) with a list of newsgroups.
297: * Both the newsgroups we're to match against (regexps) and
298: * the list of newsgroups for this line in the history file are
299: * in argc/argv format.
300: *
301: * Parameters: "nglist" is the list of group specifiers to match
302: * against.
303: * "ngcount" is the number of groups in nglist.
304: * "matchlist" is the list of newsgroups to match against.
305: * "matchcount" is number of groups in matchlist.
306: *
307: * Returns: 1 if the named newsgroup is in the list.
308: * 0 otherwise.
309: *
310: * Side effects: Terminates \n on end of grlist.
311: *
312: * Note: This ain't the same routine as "ngmatch"
313: * in the normal news software, although it
314: * probably should be.
315: */
316:
317: ngmatch(nglist, ngcount, matchlist, matchcount)
318: char **nglist;
319: int ngcount;
320: char **matchlist;
321: int matchcount;
322: {
323: int i, j;
324: int match;
325: register char *cp;
326:
327: match = 0;
328:
329: for (i = 0; i < matchcount; ++i) {
330: if (cp = index(matchlist[i], '/'))
331: *cp = '\0';
332: for (j = 0; j < ngcount; ++j) {
333: if (nglist[j][0] == '!') { /* Handle negation */
334: if (restreql(nglist[j]+1, matchlist[i]))
335: return (0); /* Hit a matching '!' */
336: } else {
337: if (restreql(nglist[j], matchlist[i]))
338: match = 1;
339: }
340: }
341: }
342:
343: return (match);
344: }
345:
346:
347: /*
348: * restreql -- regular expression string equivalnce routine,
349: * but not really full fledged. Thanks and a tip of the hat to
350: * Nick Lai, <[email protected]> for this time saving device.
351: *
352: * Parameters: "w" is an asterisk-broadened regexp,
353: * "s" is a non-regexp string.
354: * Returns: 1 if match, 0 otherwise.
355: *
356: * Side effects: None.
357: */
358:
359: restreql(w, s)
360: register char *w;
361: register char *s;
362: {
363:
364: while (*s && *w) {
365: switch (*w) {
366: case '*':
367: for (w++; *s; s++)
368: if (restreql(w, s))
369: return 1;
370: break;
371: default:
372: if (*w != *s)
373: return 0;
374: w++, s++;
375: break;
376: }
377: }
378: if (*s)
379: return 0;
380: while (*w)
381: if (*w++ != '*')
382: return 0;
383:
384: return 1;
385: }
386:
387:
388: /*
389: * distmatch -- see if a file matches a set of distributions.
390: * We have to do this by (yech!) opening the file, finding
391: * the Distribution: line, if it has one, and seeing if the
392: * things match.
393: *
394: * Parameters: "distlist" is the distribution list
395: * we want.
396: * "distcount" is the count of distributions in it.
397: * "grouplist" is the list of groups (articles)
398: * for this line of the history file.
399: * "groupcount" is the count of groups in it.
400: *
401: * Returns: 1 if the article is in the given distribution.
402: * 0 otherwise.
403: */
404:
405: distmatch(distlist, distcount, grouplist, groupcount)
406: char *distlist[];
407: int distcount;
408: char *grouplist[];
409: int groupcount;
410: {
411: register char *cp;
412: register FILE *fp;
413: register int i, j;
414: char buf[MAX_STRLEN];
415:
416: fp = fopen(grouplist[0], "r");
417: if (fp == NULL) {
418: syslog(LOG_ERR, "distmatch: fopen %s: %m", buf);
419: return (0);
420: }
421:
422: while (fgets(buf, sizeof (buf), fp) != NULL) {
423: cp = index(buf, '\n');
424: if (cp)
425: *cp = '\0';
426: if (buf[0] == '\0') /* End of header */
427: break;
428: cp = index(buf, ':');
429: if (cp == NULL)
430: continue;
431: *cp = '\0';
432: if (streql(buf, "distribution")) {
433: for (i = 0; i < distcount; ++i) {
434: if (streql(cp + 2, distlist[i])) {
435: (void) fclose(fp);
436: return (1);
437: }
438: }
439: return (0);
440: }
441: }
442:
443: (void) fclose(fp);
444:
445: /*
446: * We've finished the header with no distribution field.
447: * So we'll assume that the distribution is the characters
448: * up to the first dot in the newsgroup name.
449: */
450:
451: for (i = 0; i < groupcount; ++i) {
452: cp = index(grouplist[i], '.');
453: if (cp)
454: *cp = '\0';
455: for (j = 0; j < distcount; ++i)
456: if (streql(grouplist[i], distlist[i]))
457: return (1);
458: }
459:
460: return (0);
461: }
462:
463:
464: /*
465: * get_histlist -- return a nicely set up array of newsgroups
466: * (actually, net.foo.bar/article_num) along with a count.
467: *
468: * Parameters: "array" is storage for our array,
469: * set to point at some static data.
470: * "list" is the history file newsgroup list.
471: *
472: * Returns: Number of group specs found.
473: *
474: * Side effects: Changes static data area.
475: */
476:
477: get_histlist(array, list)
478: char ***array;
479: char *list;
480: {
481: register int histcount;
482: register char *cp;
483: static char **hist_list = (char **) NULL;
484:
485: cp = index(list, '\n');
486: if (cp)
487: *cp-- = '\0';
488: histcount = parsit(list, &hist_list);
489: *array = hist_list;
490: return (histcount);
491: }
492:
493:
494: /*
495: * get_nglist -- return a nicely set up array of newsgroups
496: * along with a count, when given an NNTP-spec newsgroup list
497: * in the form ng1,ng2,ng...
498: *
499: * Parameters: "array" is storage for our array,
500: * set to point at some static data.
501: * "list" is the NNTP newsgroup list.
502: *
503: * Returns: Number of group specs found.
504: *
505: * Side effects: Changes static data area.
506: */
507:
508: get_nglist(array, list)
509: char ***array;
510: char *list;
511: {
512: register char *cp;
513: register int ngcount;
514: static char **ng_list = (char **) NULL;
515:
516: for (cp = list; *cp != '\0'; ++cp)
517: if (*cp == ',')
518: *cp = ' ';
519: ngcount = parsit(list, &ng_list);
520: *array = ng_list;
521: return (ngcount);
522: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.