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