3 /* Algorithm for finding a group:
5 * Start at listentry 0.
7 * keep going through list until one of the following:
8 * if character in string matches in list entry,
9 * then jump to that listentry's `p' and go on to the next character
10 * if we encounter a null character (ie, default, end of this list)
11 * then we go to the listentry's `p' and use the `gi' member of
12 * the union as the group info struct
13 * If we run out of characters,
14 * keep going through the list until we find a default or an if-terminate-here
15 * use the listentry's `p', and the `gi' member of that entry's union.
17 * The listentry pointingto's are indices into the array of listentry unions.
26 #include <sys/types.h>
27 #include <arpa/inet.h>
28 #include <netinet/in.h>
30 #include "nntp-merge.h"
32 /* Conceptually we have one array with all the stuff in it. However,
33 * this is wasteful of space (we want the lookup algorithm and its
34 * data to fit in 1K). Conceptually the array is one of
37 * unsigned short n_to_go_to_if_char_matches_p;
38 * struct groupinfo *groupinfo_to_use_if_char_does_not_match;
40 * The groupinfo pointer is in the array element corresponding to the
41 * null character at the end of the list.
42 * Conceptually `end of string' is just another character, 1, except
43 * that when you find it in the table you use the groupinfo member rather
44 * then n_to_go member.
50 struct tlistentry *next;
54 struct groupinfo *ifdef, *ifterm;
57 void *xmalloc(size_t s) {
60 if (!r) die("malloc failed");
64 char *xstrdup(const char *s) {
66 r= xmalloc(strlen(s)+1);
71 struct permission *permissions;
72 struct permission *restrictpost;
73 struct serverinfo *servers;
74 static struct serverinfo **lastserver;
77 static unsigned short *lisp=0;
78 static struct groupinfo **lisg=0;
83 static struct tnode *newemptytnode(void) {
84 static struct tnode *r;
86 r= xmalloc(sizeof(struct tnode));
93 static void setmissingdefaults(struct tnode *n, struct groupinfo *gi) {
94 struct tlistentry *tle;
95 if (n->ifdef) gi= n->ifdef; else n->ifdef= gi;
96 for (tle= n->l; tle; tle= tle->next) setmissingdefaults(tle->p,gi);
99 static void syntax(const char *m) {
100 printf("400 syntax error in config file, line %d: %s\r\n",line,m);
104 static void countup(struct tnode *n) {
105 struct tlistentry *tle;
109 for (tle= n->l; tle; tle= tle->next) nrequired++;
110 if (n->ifterm) nrequired++;
113 for (tle= n->l; tle; tle= tle->next) countup(tle->p);
116 static void fillin(struct tnode *n) {
117 struct tlistentry *tle;
121 for (tle= n->l; tle; tle= tle->next) {
123 lisp[i]= tle->p->lip;
136 static void posscopy(int *needcopyp, struct groupinfo **gip) {
137 struct groupinfo *gi;
138 if (!*needcopyp) return;
139 gi= xmalloc(sizeof(struct groupinfo));
146 struct permission *findpermit(const char *name) {
147 struct permission *pi;
149 for (pi= permissions; pi && strcmp(name,pi->name); pi= pi->next);
153 static struct permission *findmakepermit(const char *name) {
154 struct permission *pi;
156 if (!strcmp(name,"*")) return 0;
157 pi= findpermit(name); if (pi) return pi;
159 pi= xmalloc(sizeof(struct permission));
160 pi->name= xstrdup(name);
162 pi->next= permissions;
167 static struct serverinfo *findserver(const char *name) {
168 struct serverinfo *search;
170 for (search= servers;
171 search && strcmp(search->nickname,name);
172 search= search->next);
174 if (!search) syntax("unknown server nickname");
178 static struct tnode *ttree;
180 void readconfig(void) {
182 struct groupinfo *gi;
185 struct tlistentry *tle;
186 char buf[MAX_COMMAND], *p, *q;
187 struct serverinfo *si;
188 int c, i, insearchser;
191 file= fopen("nntp-merge.conf","r");
192 if (!file) die("unable to open config file");
194 gi= xmalloc(sizeof(struct groupinfo));
202 ttree= newemptytnode();
205 lastserver= &servers;
206 while ((c= getc(file)) != EOF) {
208 if (c == '\n') continue;
210 while ((c= getc(file)) != EOF && c != '\n');
214 if (!gi->readfrom) syntax("config file has groups before readfrom specs");
215 do { c= getc(file); } while (c != EOF && c != '\n' && isspace(c));
216 if (c == '\n') continue;
219 while ((c= getc(file)) != EOF && !isspace(c) && c != '*') {
220 if (c == '\1') syntax("group name in config file has ^A character");
221 for (tle= tcur->l; tle && tle->c != c; tle= tle->next);
223 tle= xmalloc(sizeof(struct tlistentry));
226 tle->p= newemptytnode();
236 while (c != EOF && c != '\n' && isspace(c)) c= getc(file);
237 if (c != EOF && (isdigit(c) || c == '-')) {
238 posscopy(&needcopy,&gi);
240 if (fscanf(file,"%d",&gi->offset) != 1) die("fscanf in group offset");
241 } else if (gi->offset) {
242 posscopy(&needcopy,&gi);
248 while (c != EOF && c != '\n') {
250 if (!isspace(c)) syntax("stuff after * or space (and offset?) in group");
254 if (!fgets(buf,sizeof(buf),file)) break;
255 p= strchr(buf,'\n'); if (p) *p= 0;
256 p= strtok(buf," \t");
257 if (!strcmp(p,"read")) {
258 p= strtok(0," \t"); if (!p) syntax("missing server after read");
259 posscopy(&needcopy,&gi);
260 gi->readfrom= findserver(p);
261 } else if (!strcmp(p,"post")) {
262 posscopy(&needcopy,&gi);
263 for (i=0; (p= strtok(0," \t")); i++) {
264 if (i >= sizeof(gi->postto)/sizeof(gi->postto[0])-1)
265 syntax("too many servers after post");
266 gi->postto[i]= findserver(p);
269 } else if (!strcmp(p,"server") || !strcmp(p,"server-nosearch")) {
270 insearchser= !strcmp(p,"server");
271 p= strtok(0," \t"); if (!p) syntax("missing server name");
272 for (si= servers; si && strcmp(si->nickname,p); si= si->next);
273 if (si) syntax("server nickname redefined");
274 si= xmalloc(sizeof(struct serverinfo));
276 si->searchthis= insearchser;
277 si->nickname= xstrdup(p);
279 p= strtok(0," \t"); if (!p) syntax("missing server host:port/command");
281 si->hostname= xstrdup(p);
285 q= strchr(p,':'); if (!q || !*q) syntax("missing :port in server");
287 si->hostname= xstrdup(p);
288 portt= strtoul(q,&q,10);
289 if (portt > USHRT_MAX || *q) syntax("bad :port in server");
293 while ((p= strtok(0," \t"))) {
294 if (strcmp(p,"mode-reader")) syntax("bad option on server");
295 si->send= "MODE READER";
297 si->rfile= si->wfile= 0;
299 lastserver= &si->next;
300 } else if (!strcmp(p,"permit")) {
301 posscopy(&needcopy,&gi);
303 if (!p) syntax("missing read-only group after permit");
304 gi->readonlyto= findmakepermit(p);
306 if (!p) syntax("missing read-write group after permit");
307 gi->restrictto= findmakepermit(p);
308 } else if (!strcmp(p,"myfqdn")) {
309 posscopy(&needcopy,&gi);
311 if (!p) syntax("missing fqdn after myfqdn");
314 } else if (!strcmp(p,"xref")) {
315 posscopy(&needcopy,&gi);
317 if (!p) syntax("missing fqdn after myfqdn");
320 } else if (!strcmp(p,"fetch") ||
321 !strcmp(p,"believe") ||
322 !strcmp(p,"minreaddays") ||
323 !strcmp(p,"maxperuser") ||
324 !strcmp(p,"extrarc") ||
325 !strcmp(p,"ignorerc")) {
326 /* These aren't for us, we ignore them. */
328 syntax("unknown thing in file");
332 if (ferror(file)) die("failed to read config file");
333 if (!ttree->ifdef) syntax("missing default newsgroup entry");
336 setmissingdefaults(ttree,0);
338 /* Now we count the total number of entries we need in our table. */
341 lisc= xmalloc(nrequired);
342 lisp= xmalloc(nrequired*sizeof(unsigned short));
343 lisg= xmalloc(nrequired*sizeof(struct groupinfo*));
347 struct groupinfo *findgroup(const char *name) {
348 /* Null or ':' terminated */
350 unsigned short cnode;
353 while ((c= *name++) && c != ':' && !isspace(c)) {
354 while ((d= lisc[cnode]) && d != c) cnode++;
355 if (d) { cnode= lisp[cnode]; continue; }
358 while ((d= lisc[cnode]) && d != '\1') cnode++;