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@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