rcskeep.c revision 11894
193787Sdes/* Extract RCS keyword string values from working files. */ 293787Sdes 393787Sdes/* Copyright 1982, 1988, 1989 Walter Tichy 493787Sdes Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 593787Sdes Distributed under license by the Free Software Foundation, Inc. 693787Sdes 793787SdesThis file is part of RCS. 893787Sdes 993787SdesRCS is free software; you can redistribute it and/or modify 1093787Sdesit under the terms of the GNU General Public License as published by 1193787Sdesthe Free Software Foundation; either version 2, or (at your option) 1293787Sdesany later version. 1393787Sdes 1494691SdesRCS is distributed in the hope that it will be useful, 1594691Sdesbut WITHOUT ANY WARRANTY; without even the implied warranty of 1694691SdesMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1794691SdesGNU General Public License for more details. 1894691Sdes 1993787SdesYou should have received a copy of the GNU General Public License 2093787Sdesalong with RCS; see the file COPYING. 2193787SdesIf not, write to the Free Software Foundation, 2293787Sdes59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2393787Sdes 2493787SdesReport problems and direct all questions to: 2593787Sdes 2693787Sdes rcs-bugs@cs.purdue.edu 2793787Sdes 2893787Sdes*/ 2993787Sdes 3093787Sdes/* 31 * $Log: rcskeep.c,v $ 32 * Revision 5.10 1995/06/16 06:19:24 eggert 33 * Update FSF address. 34 * 35 * Revision 5.9 1995/06/01 16:23:43 eggert 36 * (getoldkeys): Don't panic if a Name: is empty. 37 * 38 * Revision 5.8 1994/03/17 14:05:48 eggert 39 * Remove lint. 40 * 41 * Revision 5.7 1993/11/09 17:40:15 eggert 42 * Use simpler timezone parsing strategy now that we're using ISO 8601 format. 43 * 44 * Revision 5.6 1993/11/03 17:42:27 eggert 45 * Scan for Name keyword. Improve quality of diagnostics. 46 * 47 * Revision 5.5 1992/07/28 16:12:44 eggert 48 * Statement macro names now end in _. 49 * 50 * Revision 5.4 1991/08/19 03:13:55 eggert 51 * Tune. 52 * 53 * Revision 5.3 1991/04/21 11:58:25 eggert 54 * Shorten names to keep them distinct on shortname hosts. 55 * 56 * Revision 5.2 1990/10/04 06:30:20 eggert 57 * Parse time zone offsets; future RCS versions may output them. 58 * 59 * Revision 5.1 1990/09/20 02:38:56 eggert 60 * ci -k now checks dates more thoroughly. 61 * 62 * Revision 5.0 1990/08/22 08:12:53 eggert 63 * Retrieve old log message if there is one. 64 * Don't require final newline. 65 * Remove compile-time limits; use malloc instead. Tune. 66 * Permit dates past 1999/12/31. Ansify and Posixate. 67 * 68 * Revision 4.6 89/05/01 15:12:56 narten 69 * changed copyright header to reflect current distribution rules 70 * 71 * Revision 4.5 88/08/09 19:13:03 eggert 72 * Remove lint and speed up by making FILE *fp local, not global. 73 * 74 * Revision 4.4 87/12/18 11:44:21 narten 75 * more lint cleanups (Guy Harris) 76 * 77 * Revision 4.3 87/10/18 10:35:50 narten 78 * Updating version numbers. Changes relative to 1.1 actually relative 79 * to 4.1 80 * 81 * Revision 1.3 87/09/24 14:00:00 narten 82 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 83 * warnings) 84 * 85 * Revision 1.2 87/03/27 14:22:29 jenkins 86 * Port to suns 87 * 88 * Revision 4.1 83/05/10 16:26:44 wft 89 * Added new markers Id and RCSfile; extraction added. 90 * Marker matching with trymatch(). 91 * 92 * Revision 3.2 82/12/24 12:08:26 wft 93 * added missing #endif. 94 * 95 * Revision 3.1 82/12/04 13:22:41 wft 96 * Initial revision. 97 * 98 */ 99 100#include "rcsbase.h" 101 102libId(keepId, "$Id: rcskeep.c,v 5.10 1995/06/16 06:19:24 eggert Exp $") 103 104static int badly_terminated P((void)); 105static int checknum P((char const*)); 106static int get0val P((int,RILE*,struct buf*,int)); 107static int getval P((RILE*,struct buf*,int)); 108static int keepdate P((RILE*)); 109static int keepid P((int,RILE*,struct buf*)); 110static int keeprev P((RILE*)); 111 112int prevkeys; 113struct buf prevauthor, prevdate, prevname, prevrev, prevstate; 114 115 int 116getoldkeys(fp) 117 register RILE *fp; 118/* Function: Tries to read keyword values for author, date, 119 * revision number, and state out of the file fp. 120 * If fp is null, workname is opened and closed instead of using fp. 121 * The results are placed into 122 * prevauthor, prevdate, prevname, prevrev, prevstate. 123 * Aborts immediately if it finds an error and returns false. 124 * If it returns true, it doesn't mean that any of the 125 * values were found; instead, check to see whether the corresponding arrays 126 * contain the empty string. 127 */ 128{ 129 register int c; 130 char keyword[keylength+1]; 131 register char * tp; 132 int needs_closing; 133 int prevname_found; 134 135 if (prevkeys) 136 return true; 137 138 needs_closing = false; 139 if (!fp) { 140 if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) { 141 eerror(workname); 142 return false; 143 } 144 needs_closing = true; 145 } 146 147 /* initialize to empty */ 148 bufscpy(&prevauthor, ""); 149 bufscpy(&prevdate, ""); 150 bufscpy(&prevname, ""); prevname_found = 0; 151 bufscpy(&prevrev, ""); 152 bufscpy(&prevstate, ""); 153 154 c = '\0'; /* anything but KDELIM */ 155 for (;;) { 156 if ( c==KDELIM) { 157 do { 158 /* try to get keyword */ 159 tp = keyword; 160 for (;;) { 161 Igeteof_(fp, c, goto ok;) 162 switch (c) { 163 default: 164 if (keyword+keylength <= tp) 165 break; 166 *tp++ = c; 167 continue; 168 169 case '\n': case KDELIM: case VDELIM: 170 break; 171 } 172 break; 173 } 174 } while (c==KDELIM); 175 if (c!=VDELIM) continue; 176 *tp = c; 177 Igeteof_(fp, c, break;) 178 switch (c) { 179 case ' ': case '\t': break; 180 default: continue; 181 } 182 183 switch (trymatch(keyword)) { 184 case Author: 185 if (!keepid(0, fp, &prevauthor)) 186 return false; 187 c = 0; 188 break; 189 case Date: 190 if (!(c = keepdate(fp))) 191 return false; 192 break; 193 case Header: 194 case Id: 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