xref: /dragonfly/gnu/usr.bin/rcs/lib/rcskeep.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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
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               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
badly_terminated()273 badly_terminated()
274 {
275           workerror("badly terminated keyword value");
276           return false;
277 }
278 
279           static int
getval(fp,target,optional)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
get0val(c,fp,target,optional)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
keepdate(fp)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
keepid(c,fp,b)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
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