1 /* $NetBSD: rcskeep.c,v 1.2 2016/01/14 04:22:39 christos Exp $ */
2
3 /* Extract RCS keyword string values from working files. */
4
5 /* Copyright 1982, 1988, 1989 Walter Tichy
6 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
7 Distributed under license by the Free Software Foundation, Inc.
8
9 This file is part of RCS.
10
11 RCS is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2, or (at your option)
14 any later version.
15
16 RCS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with RCS; see the file COPYING.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26 Report problems and direct all questions to:
27
28 rcs-bugs@cs.purdue.edu
29
30 */
31
32 /*
33 * Log: rcskeep.c,v
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, "Id: rcskeep.c,v 5.10 1995/06/16 06:19:24 eggert 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
getoldkeys(fp)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 #ifdef LOCALID
198 case LocalId:
199 #endif
200 if (!(
201 getval(fp, (struct buf*)0, false) &&
202 keeprev(fp) &&
203 (c = keepdate(fp)) &&
204 keepid(c, fp, &prevauthor) &&
205 keepid(0, fp, &prevstate)
206 ))
207 return false;
208 /* Skip either ``who'' (new form) or ``Locker: who'' (old). */
209 if (getval(fp, (struct buf*)0, true) &&
210 getval(fp, (struct buf*)0, true))
211 c = 0;
212 else if (nerror)
213 return false;
214 else
215 c = KDELIM;
216 break;
217 case Locker:
218 (void) getval(fp, (struct buf*)0, false);
219 c = 0;
220 break;
221 case Log:
222 case RCSfile:
223 case Source:
224 if (!getval(fp, (struct buf*)0, false))
225 return false;
226 c = 0;
227 break;
228 case Name:
229 if (getval(fp, &prevname, false)) {
230 if (*prevname.string)
231 checkssym(prevname.string);
232 prevname_found = 1;
233 }
234 c = 0;
235 break;
236 case Revision:
237 if (!keeprev(fp))
238 return false;
239 c = 0;
240 break;
241 case State:
242 if (!keepid(0, fp, &prevstate))
243 return false;
244 c = 0;
245 break;
246 default:
247 continue;
248 }
249 if (!c)
250 Igeteof_(fp, c, c=0;)
251 if (c != KDELIM) {
252 workerror("closing %c missing on keyword", KDELIM);
253 return false;
254 }
255 if (prevname_found &&
256 *prevauthor.string && *prevdate.string &&
257 *prevrev.string && *prevstate.string
258 )
259 break;
260 }
261 Igeteof_(fp, c, break;)
262 }
263
264 ok:
265 if (needs_closing)
266 Ifclose(fp);
267 else
268 Irewind(fp);
269 prevkeys = true;
270 return true;
271 }
272
273 static int
badly_terminated()274 badly_terminated()
275 {
276 workerror("badly terminated keyword value");
277 return false;
278 }
279
280 static int
getval(fp,target,optional)281 getval(fp, target, optional)
282 register RILE *fp;
283 struct buf *target;
284 int optional;
285 /* Reads a keyword value from FP into TARGET.
286 * Returns true if one is found, false otherwise.
287 * Does not modify target if it is 0.
288 * Do not report an error if OPTIONAL is set and KDELIM is found instead.
289 */
290 {
291 int c;
292 Igeteof_(fp, c, return badly_terminated();)
293 return get0val(c, fp, target, optional);
294 }
295
296 static int
get0val(c,fp,target,optional)297 get0val(c, fp, target, optional)
298 register int c;
299 register RILE *fp;
300 struct buf *target;
301 int optional;
302 /* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly.
303 * Same as getval, except C is the lookahead character.
304 */
305 { register char * tp;
306 char const *tlim;
307 register int got1;
308
309 if (target) {
310 bufalloc(target, 1);
311 tp = target->string;
312 tlim = tp + target->size;
313 } else
314 tlim = tp = 0;
315 got1 = false;
316 for (;;) {
317 switch (c) {
318 default:
319 got1 = true;
320 if (tp) {
321 *tp++ = c;
322 if (tlim <= tp)
323 tp = bufenlarge(target, &tlim);
324 }
325 break;
326
327 case ' ':
328 case '\t':
329 if (tp) {
330 *tp = 0;
331 # ifdef KEEPTEST
332 VOID printf("getval: %s\n", target);
333 # endif
334 }
335 return got1;
336
337 case KDELIM:
338 if (!got1 && optional)
339 return false;
340 /* fall into */
341 case '\n':
342 case 0:
343 return badly_terminated();
344 }
345 Igeteof_(fp, c, return badly_terminated();)
346 }
347 }
348
349
350 static int
keepdate(fp)351 keepdate(fp)
352 RILE *fp;
353 /* Function: reads a date prevdate; checks format
354 * Return 0 on error, lookahead character otherwise.
355 */
356 {
357 struct buf prevday, prevtime;
358 register int c;
359
360 c = 0;
361 bufautobegin(&prevday);
362 if (getval(fp,&prevday,false)) {
363 bufautobegin(&prevtime);
364 if (getval(fp,&prevtime,false)) {
365 Igeteof_(fp, c, c=0;)
366 if (c) {
367 register char const *d = prevday.string, *t = prevtime.string;
368 bufalloc(&prevdate, strlen(d) + strlen(t) + 9);
369 VOID sprintf(prevdate.string, "%s%s %s%s",
370 /* Parse dates put out by old versions of RCS. */
371 isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2])
372 ? "19" : "",
373 d, t,
374 strchr(t,'-') || strchr(t,'+') ? "" : "+0000"
375 );
376 }
377 }
378 bufautoend(&prevtime);
379 }
380 bufautoend(&prevday);
381 return c;
382 }
383
384 static int
keepid(c,fp,b)385 keepid(c, fp, b)
386 int c;
387 RILE *fp;
388 struct buf *b;
389 /* Get previous identifier from C+FP into B. */
390 {
391 if (!c)
392 Igeteof_(fp, c, return false;)
393 if (!get0val(c, fp, b, false))
394 return false;
395 checksid(b->string);
396 return !nerror;
397 }
398
399 static int
keeprev(fp)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
checknum(s)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
main(argc,argv)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