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