Home | History | Annotate | Line # | Download | only in src
      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 (at) 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
    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
    274 badly_terminated()
    275 {
    276 	workerror("badly terminated keyword value");
    277 	return false;
    278 }
    279 
    280 	static int
    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
    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
    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
    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
    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