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