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