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