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