xref: /dragonfly/gnu/usr.bin/rcs/lib/rcskeep.c (revision 0db87cb7)
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             }
250 	    if (c != KDELIM) {
251 		workerror("closing %c missing on keyword", KDELIM);
252 		return false;
253 	    }
254 	    if (prevname_found &&
255 		*prevauthor.string && *prevdate.string &&
256 		*prevrev.string && *prevstate.string
257 	    )
258                 break;
259         }
260 	Igeteof_(fp, c, break;)
261     }
262 
263  ok:
264     if (needs_closing)
265 	Ifclose(fp);
266     else
267 	Irewind(fp);
268     prevkeys = true;
269     return true;
270 }
271 
272 	static int
273 badly_terminated()
274 {
275 	workerror("badly terminated keyword value");
276 	return false;
277 }
278 
279 	static int
280 getval(fp, target, optional)
281 	register RILE *fp;
282 	struct buf *target;
283 	int optional;
284 /* Reads a keyword value from FP into TARGET.
285  * Returns true if one is found, false otherwise.
286  * Does not modify target if it is 0.
287  * Do not report an error if OPTIONAL is set and KDELIM is found instead.
288  */
289 {
290 	int c;
291 	Igeteof_(fp, c, return badly_terminated();)
292 	return get0val(c, fp, target, optional);
293 }
294 
295 	static int
296 get0val(c, fp, target, optional)
297 	register int c;
298 	register RILE *fp;
299 	struct buf *target;
300 	int optional;
301 /* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly.
302  * Same as getval, except C is the lookahead character.
303  */
304 {   register char * tp;
305     char const *tlim;
306     register int got1;
307 
308     if (target) {
309 	bufalloc(target, 1);
310 	tp = target->string;
311 	tlim = tp + target->size;
312     } else
313 	tlim = tp = 0;
314     got1 = false;
315     for (;;) {
316 	switch (c) {
317 	    default:
318 		got1 = true;
319 		if (tp) {
320 		    *tp++ = c;
321 		    if (tlim <= tp)
322 			tp = bufenlarge(target, &tlim);
323 		}
324 		break;
325 
326 	    case ' ':
327 	    case '\t':
328 		if (tp) {
329 		    *tp = 0;
330 #		    ifdef KEEPTEST
331 			VOID printf("getval: %s\n", target);
332 #		    endif
333 		}
334 		return got1;
335 
336 	    case KDELIM:
337 		if (!got1 && optional)
338 		    return false;
339 		/* fall into */
340 	    case '\n':
341 	    case 0:
342 		return badly_terminated();
343 	}
344 	Igeteof_(fp, c, return badly_terminated();)
345     }
346 }
347 
348 
349 	static int
350 keepdate(fp)
351 	RILE *fp;
352 /* Function: reads a date prevdate; checks format
353  * Return 0 on error, lookahead character otherwise.
354  */
355 {
356     struct buf prevday, prevtime;
357     register int c;
358 
359     c = 0;
360     bufautobegin(&prevday);
361     if (getval(fp,&prevday,false)) {
362 	bufautobegin(&prevtime);
363 	if (getval(fp,&prevtime,false)) {
364 	    Igeteof_(fp, c, c=0;)
365 	    if (c) {
366 		register char const *d = prevday.string, *t = prevtime.string;
367 		bufalloc(&prevdate, strlen(d) + strlen(t) + 9);
368 		VOID sprintf(prevdate.string, "%s%s %s%s",
369 		    /* Parse dates put out by old versions of RCS.  */
370 		      isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2])
371 		    ? "19" : "",
372 		    d, t,
373 		    strchr(t,'-') || strchr(t,'+')  ?  ""  :  "+0000"
374 		);
375 	    }
376 	}
377 	bufautoend(&prevtime);
378     }
379     bufautoend(&prevday);
380     return c;
381 }
382 
383 	static int
384 keepid(c, fp, b)
385 	int c;
386 	RILE *fp;
387 	struct buf *b;
388 /* Get previous identifier from C+FP into B.  */
389 {
390 	if (!c) {
391 	    Igeteof_(fp, c, return false;)
392 	}
393 	if (!get0val(c, fp, b, false))
394 	    return false;
395 	checksid(b->string);
396 	return !nerror;
397 }
398 
399 	static int
400 keeprev(fp)
401 	RILE *fp;
402 /* Get previous revision from FP into prevrev.  */
403 {
404 	return getval(fp,&prevrev,false) && checknum(prevrev.string);
405 }
406 
407 
408 	static int
409 checknum(s)
410 	char const *s;
411 {
412     register char const *sp;
413     register int dotcount = 0;
414     for (sp=s; ; sp++) {
415 	switch (*sp) {
416 	    case 0:
417 		if (dotcount & 1)
418 		    return true;
419 		else
420 		    break;
421 
422 	    case '.':
423 		dotcount++;
424 		continue;
425 
426 	    default:
427 		if (isdigit(*sp))
428 		    continue;
429 		break;
430 	}
431 	break;
432     }
433     workerror("%s is not a revision number", s);
434     return false;
435 }
436 
437 
438 
439 #ifdef KEEPTEST
440 
441 /* Print the keyword values found.  */
442 
443 char const cmdid[] ="keeptest";
444 
445 	int
446 main(argc, argv)
447 int  argc; char  *argv[];
448 {
449         while (*(++argv)) {
450 		workname = *argv;
451 		getoldkeys((RILE*)0);
452                 VOID printf("%s:  revision: %s, date: %s, author: %s, name: %s, state: %s\n",
453 			    *argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string);
454 	}
455 	exitmain(EXIT_SUCCESS);
456 }
457 #endif
458