xref: /NextBSD/gnu/usr.bin/rcs/lib/rcskeep.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /* Extract RCS keyword string values from working files.  */
2 
3 /* Copyright 1982, 1988, 1989 Walter Tichy
4    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5    Distributed under license by the Free Software Foundation, Inc.
6 
7 This file is part of RCS.
8 
9 RCS is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13 
14 RCS is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RCS; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 Report problems and direct all questions to:
25 
26     rcs-bugs@cs.purdue.edu
27 
28 */
29 
30 /*
31  * Revision 5.10  1995/06/16 06:19:24  eggert
32  * Update FSF address.
33  *
34  * Revision 5.9  1995/06/01 16:23:43  eggert
35  * (getoldkeys): Don't panic if a Name: is empty.
36  *
37  * Revision 5.8  1994/03/17 14:05:48  eggert
38  * Remove lint.
39  *
40  * Revision 5.7  1993/11/09 17:40:15  eggert
41  * Use simpler timezone parsing strategy now that we're using ISO 8601 format.
42  *
43  * Revision 5.6  1993/11/03 17:42:27  eggert
44  * Scan for Name keyword.  Improve quality of diagnostics.
45  *
46  * Revision 5.5  1992/07/28  16:12:44  eggert
47  * Statement macro names now end in _.
48  *
49  * Revision 5.4  1991/08/19  03:13:55  eggert
50  * Tune.
51  *
52  * Revision 5.3  1991/04/21  11:58:25  eggert
53  * Shorten names to keep them distinct on shortname hosts.
54  *
55  * Revision 5.2  1990/10/04  06:30:20  eggert
56  * Parse time zone offsets; future RCS versions may output them.
57  *
58  * Revision 5.1  1990/09/20  02:38:56  eggert
59  * ci -k now checks dates more thoroughly.
60  *
61  * Revision 5.0  1990/08/22  08:12:53  eggert
62  * Retrieve old log message if there is one.
63  * Don't require final newline.
64  * Remove compile-time limits; use malloc instead.  Tune.
65  * Permit dates past 1999/12/31.  Ansify and Posixate.
66  *
67  * Revision 4.6  89/05/01  15:12:56  narten
68  * changed copyright header to reflect current distribution rules
69  *
70  * Revision 4.5  88/08/09  19:13:03  eggert
71  * Remove lint and speed up by making FILE *fp local, not global.
72  *
73  * Revision 4.4  87/12/18  11:44:21  narten
74  * more lint cleanups (Guy Harris)
75  *
76  * Revision 4.3  87/10/18  10:35:50  narten
77  * Updating version numbers. Changes relative to 1.1 actually relative
78  * to 4.1
79  *
80  * Revision 1.3  87/09/24  14:00:00  narten
81  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
82  * warnings)
83  *
84  * Revision 1.2  87/03/27  14:22:29  jenkins
85  * Port to suns
86  *
87  * Revision 4.1  83/05/10  16:26:44  wft
88  * Added new markers Id and RCSfile; extraction added.
89  * Marker matching with trymatch().
90  *
91  * Revision 3.2  82/12/24  12:08:26  wft
92  * added missing #endif.
93  *
94  * Revision 3.1  82/12/04  13:22:41  wft
95  * Initial revision.
96  *
97  */
98 
99 #include  "rcsbase.h"
100 
101 libId(keepId, "$FreeBSD$")
102 
103 static int badly_terminated P((void));
104 static int checknum P((char const*));
105 static int get0val P((int,RILE*,struct buf*,int));
106 static int getval P((RILE*,struct buf*,int));
107 static int keepdate P((RILE*));
108 static int keepid P((int,RILE*,struct buf*));
109 static int keeprev P((RILE*));
110 
111 int prevkeys;
112 struct buf prevauthor, prevdate, prevname, prevrev, prevstate;
113 
114 	int
getoldkeys(fp)115 getoldkeys(fp)
116 	register RILE *fp;
117 /* Function: Tries to read keyword values for author, date,
118  * revision number, and state out of the file fp.
119  * If fp is null, workname is opened and closed instead of using fp.
120  * The results are placed into
121  * prevauthor, prevdate, prevname, prevrev, prevstate.
122  * Aborts immediately if it finds an error and returns false.
123  * If it returns true, it doesn't mean that any of the
124  * values were found; instead, check to see whether the corresponding arrays
125  * contain the empty string.
126  */
127 {
128     register int c;
129     char keyword[keylength+1];
130     register char * tp;
131     int needs_closing;
132     int prevname_found;
133 
134     if (prevkeys)
135 	return true;
136 
137     needs_closing = false;
138     if (!fp) {
139 	if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) {
140 	    eerror(workname);
141 	    return false;
142 	}
143 	needs_closing = true;
144     }
145 
146     /* initialize to empty */
147     bufscpy(&prevauthor, "");
148     bufscpy(&prevdate, "");
149     bufscpy(&prevname, "");  prevname_found = 0;
150     bufscpy(&prevrev, "");
151     bufscpy(&prevstate, "");
152 
153     c = '\0'; /* anything but KDELIM */
154     for (;;) {
155         if ( c==KDELIM) {
156 	    do {
157 		/* try to get keyword */
158 		tp = keyword;
159 		for (;;) {
160 		    Igeteof_(fp, c, goto ok;)
161 		    switch (c) {
162 			default:
163 			    if (keyword+keylength <= tp)
164 				break;
165 			    *tp++ = c;
166 			    continue;
167 
168 			case '\n': case KDELIM: case VDELIM:
169 			    break;
170 		    }
171 		    break;
172 		}
173 	    } while (c==KDELIM);
174             if (c!=VDELIM) continue;
175 	    *tp = c;
176 	    Igeteof_(fp, c, break;)
177 	    switch (c) {
178 		case ' ': case '\t': break;
179 		default: continue;
180 	    }
181 
182 	    switch (trymatch(keyword)) {
183             case Author:
184 		if (!keepid(0, fp, &prevauthor))
185 		    return false;
186 		c = 0;
187                 break;
188             case Date:
189 		if (!(c = keepdate(fp)))
190 		    return false;
191                 break;
192             case Header:
193             case Id:
194 	    case LocalId:
195 		if (!(
196 		      getval(fp, (struct buf*)0, false) &&
197 		      keeprev(fp) &&
198 		      (c = keepdate(fp)) &&
199 		      keepid(c, fp, &prevauthor) &&
200 		      keepid(0, fp, &prevstate)
201 		))
202 		    return false;
203 		/* Skip either ``who'' (new form) or ``Locker: who'' (old).  */
204 		if (getval(fp, (struct buf*)0, true) &&
205 		    getval(fp, (struct buf*)0, true))
206 			c = 0;
207 		else if (nerror)
208 			return false;
209 		else
210 			c = KDELIM;
211 		break;
212             case Locker:
213 		(void) getval(fp, (struct buf*)0, false);
214 		c = 0;
215 		break;
216             case Log:
217             case RCSfile:
218             case Source:
219 		if (!getval(fp, (struct buf*)0, false))
220 		    return false;
221 		c = 0;
222                 break;
223 	    case Name:
224 		if (getval(fp, &prevname, false)) {
225 		    if (*prevname.string)
226 			checkssym(prevname.string);
227 		    prevname_found = 1;
228 		}
229 		c = 0;
230 		break;
231             case Revision:
232 		if (!keeprev(fp))
233 		    return false;
234 		c = 0;
235                 break;
236             case State:
237 		if (!keepid(0, fp, &prevstate))
238 		    return false;
239 		c = 0;
240                 break;
241             default:
242                continue;
243             }
244 	    if (!c)
245 		Igeteof_(fp, c, c=0;)
246 	    if (c != KDELIM) {
247 		workerror("closing %c missing on keyword", KDELIM);
248 		return false;
249 	    }
250 	    if (prevname_found &&
251 		*prevauthor.string && *prevdate.string &&
252 		*prevrev.string && *prevstate.string
253 	    )
254                 break;
255         }
256 	Igeteof_(fp, c, break;)
257     }
258 
259  ok:
260     if (needs_closing)
261 	Ifclose(fp);
262     else
263 	Irewind(fp);
264     prevkeys = true;
265     return true;
266 }
267 
268 	static int
badly_terminated()269 badly_terminated()
270 {
271 	workerror("badly terminated keyword value");
272 	return false;
273 }
274 
275 	static int
getval(fp,target,optional)276 getval(fp, target, optional)
277 	register RILE *fp;
278 	struct buf *target;
279 	int optional;
280 /* Reads a keyword value from FP into TARGET.
281  * Returns true if one is found, false otherwise.
282  * Does not modify target if it is 0.
283  * Do not report an error if OPTIONAL is set and KDELIM is found instead.
284  */
285 {
286 	int c;
287 	Igeteof_(fp, c, return badly_terminated();)
288 	return get0val(c, fp, target, optional);
289 }
290 
291 	static int
get0val(c,fp,target,optional)292 get0val(c, fp, target, optional)
293 	register int c;
294 	register RILE *fp;
295 	struct buf *target;
296 	int optional;
297 /* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly.
298  * Same as getval, except C is the lookahead character.
299  */
300 {   register char * tp;
301     char const *tlim;
302     register int got1;
303 
304     if (target) {
305 	bufalloc(target, 1);
306 	tp = target->string;
307 	tlim = tp + target->size;
308     } else
309 	tlim = tp = 0;
310     got1 = false;
311     for (;;) {
312 	switch (c) {
313 	    default:
314 		got1 = true;
315 		if (tp) {
316 		    *tp++ = c;
317 		    if (tlim <= tp)
318 			tp = bufenlarge(target, &tlim);
319 		}
320 		break;
321 
322 	    case ' ':
323 	    case '\t':
324 		if (tp) {
325 		    *tp = 0;
326 #		    ifdef KEEPTEST
327 			VOID printf("getval: %s\n", target);
328 #		    endif
329 		}
330 		return got1;
331 
332 	    case KDELIM:
333 		if (!got1 && optional)
334 		    return false;
335 		/* fall into */
336 	    case '\n':
337 	    case 0:
338 		return badly_terminated();
339 	}
340 	Igeteof_(fp, c, return badly_terminated();)
341     }
342 }
343 
344 
345 	static int
keepdate(fp)346 keepdate(fp)
347 	RILE *fp;
348 /* Function: reads a date prevdate; checks format
349  * Return 0 on error, lookahead character otherwise.
350  */
351 {
352     struct buf prevday, prevtime;
353     register int c;
354 
355     c = 0;
356     bufautobegin(&prevday);
357     if (getval(fp,&prevday,false)) {
358 	bufautobegin(&prevtime);
359 	if (getval(fp,&prevtime,false)) {
360 	    Igeteof_(fp, c, c=0;)
361 	    if (c) {
362 		register char const *d = prevday.string, *t = prevtime.string;
363 		bufalloc(&prevdate, strlen(d) + strlen(t) + 9);
364 		VOID sprintf(prevdate.string, "%s%s %s%s",
365 		    /* Parse dates put out by old versions of RCS.  */
366 		      isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2])
367 		    ? "19" : "",
368 		    d, t,
369 		    strchr(t,'-') || strchr(t,'+')  ?  ""  :  "+0000"
370 		);
371 	    }
372 	}
373 	bufautoend(&prevtime);
374     }
375     bufautoend(&prevday);
376     return c;
377 }
378 
379 	static int
keepid(c,fp,b)380 keepid(c, fp, b)
381 	int c;
382 	RILE *fp;
383 	struct buf *b;
384 /* Get previous identifier from C+FP into B.  */
385 {
386 	if (!c)
387 	    Igeteof_(fp, c, return false;)
388 	if (!get0val(c, fp, b, false))
389 	    return false;
390 	checksid(b->string);
391 	return !nerror;
392 }
393 
394 	static int
keeprev(fp)395 keeprev(fp)
396 	RILE *fp;
397 /* Get previous revision from FP into prevrev.  */
398 {
399 	return getval(fp,&prevrev,false) && checknum(prevrev.string);
400 }
401 
402 
403 	static int
checknum(s)404 checknum(s)
405 	char const *s;
406 {
407     register char const *sp;
408     register int dotcount = 0;
409     for (sp=s; ; sp++) {
410 	switch (*sp) {
411 	    case 0:
412 		if (dotcount & 1)
413 		    return true;
414 		else
415 		    break;
416 
417 	    case '.':
418 		dotcount++;
419 		continue;
420 
421 	    default:
422 		if (isdigit(*sp))
423 		    continue;
424 		break;
425 	}
426 	break;
427     }
428     workerror("%s is not a revision number", s);
429     return false;
430 }
431 
432 
433 
434 #ifdef KEEPTEST
435 
436 /* Print the keyword values found.  */
437 
438 char const cmdid[] ="keeptest";
439 
440 	int
main(argc,argv)441 main(argc, argv)
442 int  argc; char  *argv[];
443 {
444         while (*(++argv)) {
445 		workname = *argv;
446 		getoldkeys((RILE*)0);
447                 VOID printf("%s:  revision: %s, date: %s, author: %s, name: %s, state: %s\n",
448 			    *argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string);
449 	}
450 	exitmain(EXIT_SUCCESS);
451 }
452 #endif
453