1 /*
2  * active file access 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 
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include "libc.h"
15 #include "news.h"
16 #include "config.h"
17 #include "active.h"
18 
19 /* ordinal numbers of fields */
20 #define CURRFIELD 2		/* current article # */
21 #define FLAGFIELD 4		/* y/n/m/x/= flag */
22 
23 /* flag field values */
24 #define FLAGOKAY 'y'		/* ordinary unmoderated group */
25 #define FLAGBAD 'n'		/* unmoderated but locally-restricted group */
26 #define FLAGMOD 'm'		/* moderated group */
27 #define FLAGNEVER 'x'		/* unwanted group: don't file in this one */
28 #define FLAGGOTO '='		/* see another group (following) instead */
29 
30 /* imports */
31 extern char *actfind();
32 extern statust actfload(), actfsync(), actfwrnum();
33 extern void persistent(void *art, int code, const char *fmt, const char *arg);
34 
35 /* exports */
36 char actrelnm[] = "active";
37 
38 /* privates */
39 static const char *actmode = "r+";
40 static FILE *actfp = NULL;
41 static struct lastngcache {
42 	char *lnc_ng;			/* newsgroup name */
43 	char *lnc_line;			/* matching active file line */
44 } lnc = { NULL, NULL };
45 
46 STATIC char *
fieldfind(ngline,fieldno)47 fieldfind(ngline, fieldno)	/* return address of field "fieldno" in ngline */
48 register char *ngline;
49 register int fieldno;
50 {
51 	register int field;
52 
53 	for (field = 1; ngline != NULL && field < fieldno; ++field) {
54 		ngline = strchr(ngline, ' ');
55 		if (ngline != NULL)
56 			ngline++;		/* point at next field */
57 	}
58 	return ngline;
59 }
60 
61 char *
findflag(ngline)62 findflag(ngline)		/* return address of flag field in ngline */
63 register char *ngline;
64 {
65 	return fieldfind(ngline, FLAGFIELD);
66 }
67 
68 /*
69  * return a pointer to the active file entry for ng
70  * (or a pointed-to group (by ``=group'')), or 0 if no entry exists.
71  * since actlook is called repeatedly for the same newsgroup,
72  * actlook caches the last newsgroup looked-up and the result.
73  */
74 char *
actlook(ang)75 actlook(ang)
76 register char *ang;
77 {
78 	register char *ngline, *ng, *flag;
79 	register int loopbreak = 100;
80 
81 	if (lnc.lnc_ng != NULL && STREQ(lnc.lnc_ng, ang))
82 		return lnc.lnc_line;
83 
84 	if (actload() != ST_OKAY)
85 		return NULL;
86 	ng = strsave(ang);
87 	while ((ngline = actfind(actfp, ng, strlen(ng))) != NULL &&
88 	    (flag = findflag(ngline)) != NULL && *flag == FLAGGOTO &&
89 	    --loopbreak > 0) {
90 		free(ng);
91 	    	ng = strsvto(flag+1, '\n');	/* follow "=ng" pointer */
92 	}
93 	if (loopbreak <= 0)			/* "infinite" loop broken */
94 		ngline = NULL;
95 
96 	nnfree(&lnc.lnc_ng);
97 	lnc.lnc_ng = ng;
98 	lnc.lnc_line = ngline;
99 	return ngline;
100 }
101 
102 /*
103  * Find the active entry for ng (or a pointed-to group (by ``=group''))
104  * and add inc to its 2nd field (highest number).
105  * Return the resultant number.
106  */
107 long
incartnum(ng,inc)108 incartnum(ng, inc)
109 char *ng;
110 int inc;
111 {
112 	char testnum[40];
113 	register char *line = actlook(ng);
114 	register long nextart = -1;
115 
116 	if (line != NULL) {
117 		register char *artnum, *pastartnum;
118 
119 		pastartnum = artnum = fieldfind(line, CURRFIELD);
120 		if (artnum == NULL)
121 			return nextart;
122 		while (isascii(*pastartnum) && isdigit(*pastartnum))
123 			++pastartnum;
124 		nextart = atol(artnum) + inc;
125 
126 		/* update active file article # in place, from nextart */
127 		errno = 0;
128 		if (pastartnum-artnum > sizeof testnum ||
129 		    !ltozan(testnum, nextart, pastartnum-artnum) ||
130 		    !ltozan(artnum, nextart, pastartnum-artnum)) {
131 			persistent(NOART, 'f',
132 			"article number too big for group `%s' active field",
133 				   ng);
134 			return -1;
135 		}
136 
137 		/* give the implementation a chance to write line to disk */
138 		if (actfwrnum(actfp, line) != ST_OKAY) {
139 			persistent(NOART, 'f', "can't update active file", "");
140 			nextart = -1;
141 		}
142 	}
143 	return nextart;
144 }
145 
146 void
actread(void)147 actread(void)				/* set mode: just reading */
148 {
149 	actmode = "r";
150 }
151 
152 /*
153  * Reload the active file cache.
154  */
155 statust
actload()156 actload()
157 {
158 	register statust status = ST_OKAY;
159 
160 	if (actfp == NULL &&
161 	    (actfp = fopenwclex(ctlfile(actrelnm), actmode)) == NULL) {
162 		persistent(NOART, '\0', "can't open %s", ctlfile(actrelnm));
163 		status |= ST_DROPPED|ST_NEEDATTN;
164 	}
165 	status |= actfload(actfp);
166 	return status;
167 }
168 
169 /*
170  * Write back to disk the active file cache, if any, and flush the
171  * last-newsgroup-cache, since it refers to the (now invalid) active file cache.
172  */
173 statust
actsync()174 actsync()
175 {
176 	register statust status = ST_OKAY;
177 
178 	if (actfp != NULL) {
179 		nnfree(&lnc.lnc_ng);
180 		lnc.lnc_ng = lnc.lnc_line = NULL;
181 		status |= actfsync(actfp);
182 		if (nfclose(actfp) == EOF || status != ST_OKAY) {
183 			persistent(NOART, 'f', "error writing `%s'",
184 				ctlfile(actrelnm));
185 			status |= ST_DROPPED|ST_NEEDATTN;
186 		}
187 	}
188 	actfp = NULL;
189 	return status;
190 }
191 
192 /*
193  * Return YES iff any group in ngs (or a pointed-to group (by ``=group''))
194  * matches thisflag.
195  */
196 boolean
isflag(ngs,thisflag)197 isflag(ngs, thisflag)
198 register char *ngs;
199 int thisflag;
200 {
201 	register char *newng, *flag, *ng;
202 	register boolean result = NO;
203 
204 	for (ng = ngs; !result && ng != NULL; ng = newng) {
205 		newng = strchr(ng, NGSEP);
206 		if (newng != NULL)
207 			*newng = '\0';		/* restored below */
208 
209 		flag = findflag(actlook(ng));
210 		if (flag != NULL && *flag == thisflag)
211 			result = YES;
212 
213 		if (newng != NULL)
214 			*newng++ = NGSEP;	/* point at next group */
215 	}
216 	return result;
217 }
218 
219 /*
220  * Are any groups in ngs moderated?
221  */
222 boolean
moderated(ngs)223 moderated(ngs)
224 register char *ngs;
225 {
226 	return isflag(ngs, FLAGMOD);
227 }
228 
229 /*
230  * Are any groups in ngs unwanted?
231  */
232 boolean
unwanted(ngs)233 unwanted(ngs)
234 register char *ngs;
235 {
236 	return isflag(ngs, FLAGNEVER);
237 }
238 
239 /*
240  * Return 0 or a malloced newsgroup name corresponding to "ong",
241  * but without an "=" flag in its active file entry.
242  * This is done by tracing the chain of "=ng" pointers (in actlook()), if any.
243  */
244 char *
realngname(ong)245 realngname(ong)
246 char *ong;
247 {
248 	register char *ngline = actlook(ong);
249 
250 	return (ngline == NULL? NULL: strsvto(ngline, ' '));
251 }
252