1 /*
2 * news sys file reading functions
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 #include <string.h>
9 #include <errno.h>
10 #include "fixerrno.h"
11 #include <sys/types.h>
12 #include <sys/stat.h>
13
14 #include "libc.h"
15 #include "fgetmfs.h"
16 #include "news.h"
17 #include "config.h"
18 #include "batchnames.h"
19 #include "ngmatch.h"
20 #include "system.h"
21
22 #ifndef CMDPFX
23 #define CMDPFX "uux - -r -z " /* prefix of default command */
24 #endif
25 #define CMDSFX "!rnews" /* suffix of same */
26
27 /* imports */
28 extern boolean donesys();
29 extern struct system *mysysincache();
30 extern void rewsys(), remmysys(), advcurrsys(), setupsys();
31
32 /* exports */
33 struct system *firstsys = NULL; /* cache: 1st sys of in-core sys file */
34 struct system *currsys = NULL; /* current system */
35
36 /* private */
37 static FILE *fp = NULL; /* stream for ctlfile(filerelname) */
38 static char filerelname[] = "sys"; /* filename relative to $NEWSCTL */
39 static struct parsing { /* parsing state */
40 char *next;
41 } parsing;
42
43 struct system *
oursys()44 oursys() /* return our sys entry */
45 {
46 register struct system *sys = mysysincache();
47 static struct system fakesys;
48
49 if (sys == NULL) {
50 register char *host = hostname();
51
52 rewsys(fp);
53 while ((sys = nextsys()) != NULL && !STREQ(sys->sy_name, host))
54 ;
55 if (sys == NULL) {
56 /* no entry: cook one up; no need to malloc members */
57 sys = &fakesys;
58 sys->sy_name = host;
59 sys->sy_excl = NULL;
60 sys->sy_ngs = strdup("all");
61 sys->sy_distr = sys->sy_ngs;
62 sys->sy_flags = 0;
63 sys->sy_lochops = 0;
64 sys->sy_cmd = strdup("");
65 sys->sy_trngs = ngparse(strsave(sys->sy_ngs));
66 sys->sy_trdistr = sys->sy_trngs;
67 sys->sy_next = NULL;
68 }
69 remmysys(sys); /* for future reference */
70 }
71 return sys;
72 }
73
74 /*
75 * replace "delim" in "field" with a NUL and return the address of the byte
76 * after the NUL (the address of the second subfield), or NULL if no
77 * "delim" was present.
78 */
79 STATIC char *
reparse(field,delim)80 reparse(field, delim)
81 char *field;
82 register int delim;
83 {
84 register char *delimp;
85
86 STRCHR(field, delim, delimp);
87 if (delimp != NULL)
88 *delimp++ = '\0';
89 return delimp;
90 }
91
92 /*
93 * parse up to a colon, NUL it out and
94 * return NULL or ptr. to byte after colon.
95 */
96 STATIC char *
parsecolon(line)97 parsecolon(line)
98 char *line;
99 {
100 return reparse(line, ':');
101 }
102
103 /*
104 * Parse "next" to colon into malloced storage, return its ptr
105 * (freed iff on a small system, in readsys(), see freecursys()).
106 */
107 STATIC char *
parse()108 parse()
109 {
110 register char *curr = parsing.next;
111
112 if (curr != NULL)
113 parsing.next = parsecolon(curr);
114 return strsave(curr == NULL? "": curr);
115 }
116
117 /*
118 * Parse sys file flags into sysp.
119 */
120 STATIC void
parseflags(flags,sysp)121 parseflags(flags, sysp)
122 register char *flags;
123 register struct system *sysp;
124 {
125 sysp->sy_flags = 0;
126 sysp->sy_lochops = 0; /* default L value */
127 errno = 0;
128 for (; *flags != '\0'; flags++)
129 switch (*flags) {
130 case 'A':
131 errunlock("A news format not supported", "");
132 /* NOTREACHED */
133 case 'B': /* mostly harmless */
134 break;
135 case 'f':
136 sysp->sy_flags |= FLG_BATCH|FLG_SZBATCH;
137 break;
138 case 'F':
139 sysp->sy_flags |= FLG_BATCH;
140 break;
141 case 'I': /* NNTP hook: write msgids, !files */
142 sysp->sy_flags |= FLG_BATCH|FLG_IHAVE;
143 break;
144 case 'L': /* Ln */
145 sysp->sy_flags |= FLG_LOCAL;
146 sysp->sy_lochops = 0;
147 while (isascii(flags[1]) && isdigit(flags[1])) {
148 sysp->sy_lochops *= 10;
149 sysp->sy_lochops += *++flags - '0';
150 }
151 break;
152 case 'm': /* send only moderated groups */
153 sysp->sy_flags |= FLG_MOD;
154 break;
155 case 'N':
156 errunlock(
157 "The N flag is a wasteful old kludge; see the I flag instead.", "");
158 /* NOTREACHED */
159 case 'n': /* NNTP hook: write files+msgids */
160 sysp->sy_flags |= FLG_BATCH|FLG_NBATCH;
161 break;
162 case 'u': /* send only unmoderated groups */
163 sysp->sy_flags |= FLG_UNMOD;
164 break;
165 case 'U': /* mostly harmless */
166 break;
167 case 'H': /* write history entry? sorry */
168 case 'S': /* duplicate the work of the shell. bzzt */
169 case 'M': /* multicast: obs., see batcher */
170 case 'O': /* multicast: obs., see batcher */
171 default:
172 errunlock("unknown sys flag `%s' given", flags);
173 /* NOTREACHED */
174 }
175 }
176
177 /* if s contains whitespace, complain and exit */
178 void
badspace(s,part,sysp)179 badspace(s, part, sysp)
180 const char *s, *part;
181 struct system *sysp;
182 {
183 if (spacein(s)) {
184 char *err = str3save("whitespace in ", part,
185 " of sys entry for system name `%s'");
186
187 errunlock(err, sysp->sy_name);
188 /* NOTREACHED */
189 }
190 }
191
192 /*
193 * Parse (and modify) sysline into *currsys, which is malloced here
194 * and freed iff on a small system, in readsys(), see freecursys().
195 *
196 * Side-effect: sysline has a trailing newline removed.
197 */
198 STATIC void
parsesysln(sysline)199 parsesysln(sysline)
200 register char *sysline;
201 {
202 register struct system *sysp =(struct system *)nemalloc(sizeof *sysp);
203 char *flagstring;
204
205 trim(sysline);
206 parsing.next = sysline;
207 sysp->sy_name = parse();
208 sysp->sy_ngs = parse();
209 flagstring = parse();
210 sysp->sy_cmd = parse();
211 errno = 0;
212 badspace(sysp->sy_name, "system name (or exclusions)", sysp);
213 badspace(sysp->sy_ngs, "newsgroups (or distributions)", sysp);
214 badspace(flagstring, "flags", sysp);
215 /* could check for extra fields here */
216
217 parseflags(flagstring, sysp);
218 free(flagstring); /* malloced by parse */
219 sysp->sy_next = NULL;
220
221 /* reparse for embedded slashes */
222 sysp->sy_excl = reparse(sysp->sy_name, '/');
223 sysp->sy_distr = reparse(sysp->sy_ngs, '/');
224 /*
225 * N.B.: ngparse will chew up sy_ngs. not copying sy_ngs saves
226 * a lot of memory when you have a big sys file.
227 */
228 sysp->sy_trngs = ngparse(sysp->sy_ngs);
229 if (sysp->sy_distr == NULL) { /* default distr is ngs... */
230 sysp->sy_distr = sysp->sy_ngs;
231 sysp->sy_trdistr = sysp->sy_trngs;
232 } else {
233 if (strchr(sysp->sy_distr, '/') != NULL)
234 errunlock(
235 "slash in distribution subfield of sys entry for system name `%s'",
236 sysp->sy_name);
237 /* N.B.: ngparse will chew up sy_distr. */
238 sysp->sy_trdistr = ngparse(sysp->sy_distr);
239 }
240
241 sysdeflt(sysp); /* fill in any defaults */
242
243 /* stash *sysp away on the tail of the current list of systems */
244 *(firstsys == NULL? &firstsys: &currsys->sy_next) = sysp;
245 currsys = sysp;
246 }
247
248 /*
249 * On small systems, read one entry; else read whole sys file (done once only).
250 * Ignores '#' comments and blank lines; uses cfgetms to read possibly-
251 * continued lines of arbitrary length.
252 */
253 STATIC void
readsys()254 readsys()
255 {
256 register char *sysline, *nonblank;
257
258 setupsys(fp); /* reuse currsys or rewind sys file */
259 while ((sysline = cfgetms(fp)) != NULL) {
260 nonblank = skipsp(sysline);
261 if (nonblank[0] != '#' && nonblank[0] != '\n')
262 parsesysln(nonblank);
263 free(sysline);
264 if (donesys()) /* early exit if on disk (small) */
265 return;
266 }
267 (void) nfclose(fp);
268 fp = NULL;
269 rewsys(fp);
270 }
271
272 /*
273 * Return the next sys entry, which may span multiple lines.
274 * Returned pointer points at a struct whose lifetime (and that of its
275 * members) is not promised to last beyond the next call to nextsys();
276 * copy it and its pointed-to strings if you need them for longer.
277 *
278 * Note that readsys() reads one entry on small systems, but the entire
279 * sys file on big systems, so the common code in this file is subtle.
280 */
281 struct system *
nextsys()282 nextsys()
283 {
284 struct system *retsys;
285
286 if (firstsys == NULL && fp == NULL)
287 if ((fp = fopenwclex(ctlfile(filerelname), "r")) == NULL)
288 return NULL;
289 if (fp != NULL && firstsys == NULL)
290 readsys();
291 retsys = currsys;
292 advcurrsys();
293 return retsys;
294 }
295
296 STATIC void
newartfile(sysp,file)297 newartfile(sysp, file) /* replace sysp->sy_cmd with artfile(file) */
298 register struct system *sysp;
299 register char *file;
300 {
301 free(sysp->sy_cmd); /* malloced by parse */
302 #ifdef BATCHSPOOL /* UUNET special */
303 sysp->sy_cmd = str3save(BATCHSPOOL, SFNDELIM, file);
304 #else
305 sysp->sy_cmd = strsave(artfile(file));
306 #endif
307 free(file);
308 }
309
310 /*
311 * fill in defaults in sysp.
312 *
313 * expand a name of "ME" to hostname().
314 * If an empty batch file name was given, supply a default
315 * ($NEWSARTS/BTCHPFX system BTCHSFX).
316 * Prepend $NEWSARTS/BTCHDIR to relative file names.
317 * If an empty command was given, supply a default (uux - -r -z system!rnews).
318 * (This *is* yucky and uucp-version-dependent.)
319 */
320 void
sysdeflt(sysp)321 sysdeflt(sysp)
322 register struct system *sysp;
323 {
324 if (STREQ(sysp->sy_name, "ME")) {
325 free(sysp->sy_name); /* malloced by parse */
326 sysp->sy_name = strsave(hostname());
327 }
328 if (sysp->sy_flags&FLG_BATCH && sysp->sy_cmd[0] == '\0') {
329 register char *deffile =
330 str3save(BTCHPFX, sysp->sy_name, BTCHSFX);
331
332 /* frees old sysp->sy_cmd, deffile */
333 newartfile(sysp, deffile);
334 } else if (sysp->sy_flags&FLG_BATCH && sysp->sy_cmd[0] != FNDELIM) {
335 register char *absfile = str3save(BTCHDIR, sysp->sy_cmd, "");
336
337 /* frees old sysp->sy_cmd, absfile */
338 newartfile(sysp, absfile);
339 } else if (!(sysp->sy_flags&FLG_BATCH) && sysp->sy_cmd[0] == '\0') {
340 free(sysp->sy_cmd); /* malloced by parse */
341 sysp->sy_cmd = str3save(CMDPFX, sysp->sy_name, CMDSFX);
342 }
343 }
344
345 void
rewndsys()346 rewndsys()
347 {
348 rewsys(fp);
349 }
350