Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / gnu / usr.bin / rcs / lib / rcskeep.c
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