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 if (c != KDELIM) { 250 workerror("closing %c missing on keyword", KDELIM); 251 return false; 252 } 253 if (prevname_found && 254 *prevauthor.string && *prevdate.string && 255 *prevrev.string && *prevstate.string 256 ) 257 break; 258 } 259 Igeteof_(fp, c, break;) 260 } 261 262 ok: 263 if (needs_closing) 264 Ifclose(fp); 265 else 266 Irewind(fp); 267 prevkeys = true; 268 return true; 269 } 270 271 static int 272 badly_terminated() 273 { 274 workerror("badly terminated keyword value"); 275 return false; 276 } 277 278 static int 279 getval(fp, target, optional) 280 register RILE *fp; 281 struct buf *target; 282 int optional; 283 /* Reads a keyword value from FP into TARGET. 284 * Returns true if one is found, false otherwise. 285 * Does not modify target if it is 0. 286 * Do not report an error if OPTIONAL is set and KDELIM is found instead. 287 */ 288 { 289 int c; 290 Igeteof_(fp, c, return badly_terminated();) 291 return get0val(c, fp, target, optional); 292 } 293 294 static int 295 get0val(c, fp, target, optional) 296 register int c; 297 register RILE *fp; 298 struct buf *target; 299 int optional; 300 /* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly. 301 * Same as getval, except C is the lookahead character. 302 */ 303 { register char * tp; 304 char const *tlim; 305 register int got1; 306 307 if (target) { 308 bufalloc(target, 1); 309 tp = target->string; 310 tlim = tp + target->size; 311 } else 312 tlim = tp = 0; 313 got1 = false; 314 for (;;) { 315 switch (c) { 316 default: 317 got1 = true; 318 if (tp) { 319 *tp++ = c; 320 if (tlim <= tp) 321 tp = bufenlarge(target, &tlim); 322 } 323 break; 324 325 case ' ': 326 case '\t': 327 if (tp) { 328 *tp = 0; 329 # ifdef KEEPTEST 330 VOID printf("getval: %s\n", target); 331 # endif 332 } 333 return got1; 334 335 case KDELIM: 336 if (!got1 && optional) 337 return false; 338 /* fall into */ 339 case '\n': 340 case 0: 341 return badly_terminated(); 342 } 343 Igeteof_(fp, c, return badly_terminated();) 344 } 345 } 346 347 348 static int 349 keepdate(fp) 350 RILE *fp; 351 /* Function: reads a date prevdate; checks format 352 * Return 0 on error, lookahead character otherwise. 353 */ 354 { 355 struct buf prevday, prevtime; 356 register int c; 357 358 c = 0; 359 bufautobegin(&prevday); 360 if (getval(fp,&prevday,false)) { 361 bufautobegin(&prevtime); 362 if (getval(fp,&prevtime,false)) { 363 Igeteof_(fp, c, c=0;) 364 if (c) { 365 register char const *d = prevday.string, *t = prevtime.string; 366 bufalloc(&prevdate, strlen(d) + strlen(t) + 9); 367 VOID sprintf(prevdate.string, "%s%s %s%s", 368 /* Parse dates put out by old versions of RCS. */ 369 isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2]) 370 ? "19" : "", 371 d, t, 372 strchr(t,'-') || strchr(t,'+') ? "" : "+0000" 373 ); 374 } 375 } 376 bufautoend(&prevtime); 377 } 378 bufautoend(&prevday); 379 return c; 380 } 381 382 static int 383 keepid(c, fp, b) 384 int c; 385 RILE *fp; 386 struct buf *b; 387 /* Get previous identifier from C+FP into B. */ 388 { 389 if (!c) 390 Igeteof_(fp, c, return false;) 391 if (!get0val(c, fp, b, false)) 392 return false; 393 checksid(b->string); 394 return !nerror; 395 } 396 397 static int 398 keeprev(fp) 399 RILE *fp; 400 /* Get previous revision from FP into prevrev. */ 401 { 402 return getval(fp,&prevrev,false) && checknum(prevrev.string); 403 } 404 405 406 static int 407 checknum(s) 408 char const *s; 409 { 410 register char const *sp; 411 register int dotcount = 0; 412 for (sp=s; ; sp++) { 413 switch (*sp) { 414 case 0: 415 if (dotcount & 1) 416 return true; 417 else 418 break; 419 420 case '.': 421 dotcount++; 422 continue; 423 424 default: 425 if (isdigit(*sp)) 426 continue; 427 break; 428 } 429 break; 430 } 431 workerror("%s is not a revision number", s); 432 return false; 433 } 434 435 436 437 #ifdef KEEPTEST 438 439 /* Print the keyword values found. */ 440 441 char const cmdid[] ="keeptest"; 442 443 int 444 main(argc, argv) 445 int argc; char *argv[]; 446 { 447 while (*(++argv)) { 448 workname = *argv; 449 getoldkeys((RILE*)0); 450 VOID printf("%s: revision: %s, date: %s, author: %s, name: %s, state: %s\n", 451 *argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string); 452 } 453 exitmain(EXIT_SUCCESS); 454 } 455 #endif 456