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 458