rcskeep.c revision 10
1/* 2 * RCS keyword extraction 3 */ 4/***************************************************************************** 5 * main routine: getoldkeys() 6 * Testprogram: define KEEPTEST 7 ***************************************************************************** 8 */ 9 10/* Copyright (C) 1982, 1988, 1989 Walter Tichy 11 Copyright 1990, 1991 by Paul Eggert 12 Distributed under license by the Free Software Foundation, Inc. 13 14This file is part of RCS. 15 16RCS is free software; you can redistribute it and/or modify 17it under the terms of the GNU General Public License as published by 18the Free Software Foundation; either version 2, or (at your option) 19any later version. 20 21RCS is distributed in the hope that it will be useful, 22but WITHOUT ANY WARRANTY; without even the implied warranty of 23MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24GNU General Public License for more details. 25 26You should have received a copy of the GNU General Public License 27along with RCS; see the file COPYING. If not, write to 28the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 29 30Report problems and direct all questions to: 31 32 rcs-bugs@cs.purdue.edu 33 34*/ 35 36 37 38/* $Log: rcskeep.c,v $ 39 * Revision 5.4 1991/08/19 03:13:55 eggert 40 * Tune. 41 * 42 * Revision 5.3 1991/04/21 11:58:25 eggert 43 * Shorten names to keep them distinct on shortname hosts. 44 * 45 * Revision 5.2 1990/10/04 06:30:20 eggert 46 * Parse time zone offsets; future RCS versions may output them. 47 * 48 * Revision 5.1 1990/09/20 02:38:56 eggert 49 * ci -k now checks dates more thoroughly. 50 * 51 * Revision 5.0 1990/08/22 08:12:53 eggert 52 * Retrieve old log message if there is one. 53 * Don't require final newline. 54 * Remove compile-time limits; use malloc instead. Tune. 55 * Permit dates past 1999/12/31. Ansify and Posixate. 56 * 57 * Revision 4.6 89/05/01 15:12:56 narten 58 * changed copyright header to reflect current distribution rules 59 * 60 * Revision 4.5 88/08/09 19:13:03 eggert 61 * Remove lint and speed up by making FILE *fp local, not global. 62 * 63 * Revision 4.4 87/12/18 11:44:21 narten 64 * more lint cleanups (Guy Harris) 65 * 66 * Revision 4.3 87/10/18 10:35:50 narten 67 * Updating version numbers. Changes relative to 1.1 actually relative 68 * to 4.1 69 * 70 * Revision 1.3 87/09/24 14:00:00 narten 71 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 72 * warnings) 73 * 74 * Revision 1.2 87/03/27 14:22:29 jenkins 75 * Port to suns 76 * 77 * Revision 4.1 83/05/10 16:26:44 wft 78 * Added new markers Id and RCSfile; extraction added. 79 * Marker matching with trymatch(). 80 * 81 * Revision 3.2 82/12/24 12:08:26 wft 82 * added missing #endif. 83 * 84 * Revision 3.1 82/12/04 13:22:41 wft 85 * Initial revision. 86 * 87 */ 88 89/* 90#define KEEPTEST 91*/ 92/* Testprogram; prints out the keyword values found. */ 93 94#include "rcsbase.h" 95 96libId(keepId, "$Id: rcskeep.c,v 5.4 1991/08/19 03:13:55 eggert Exp $") 97 98static int checknum P((char const*,int)); 99static int getval P((RILE*,struct buf*,int)); 100static int get0val P((int,RILE*,struct buf*,int)); 101static int keepdate P((RILE*)); 102static int keepid P((int,RILE*,struct buf*)); 103static int keeprev P((RILE*)); 104 105int prevkeys; 106struct buf prevauthor, prevdate, prevrev, prevstate; 107 108 int 109getoldkeys(fp) 110 register RILE *fp; 111/* Function: Tries to read keyword values for author, date, 112 * revision number, and state out of the file fp. 113 * If FNAME is nonnull, it is opened and closed instead of using FP. 114 * The results are placed into 115 * prevauthor, prevdate, prevrev, prevstate. 116 * Aborts immediately if it finds an error and returns false. 117 * If it returns true, it doesn't mean that any of the 118 * values were found; instead, check to see whether the corresponding arrays 119 * contain the empty string. 120 */ 121{ 122 register int c; 123 char keyword[keylength+1]; 124 register char * tp; 125 int needs_closing; 126 127 if (prevkeys) 128 return true; 129 130 needs_closing = false; 131 if (!fp) { 132 if (!(fp = Iopen(workfilename, FOPEN_R_WORK, (struct stat*)0))) { 133 eerror(workfilename); 134 return false; 135 } 136 needs_closing = true; 137 } 138 139 /* initialize to empty */ 140 bufscpy(&prevauthor, ""); 141 bufscpy(&prevdate, ""); 142 bufscpy(&prevrev, ""); 143 bufscpy(&prevstate, ""); 144 145 c = '\0'; /* anything but KDELIM */ 146 for (;;) { 147 if ( c==KDELIM) { 148 do { 149 /* try to get keyword */ 150 tp = keyword; 151 for (;;) { 152 Igeteof(fp, c, goto ok;); 153 switch (c) { 154 default: 155 if (keyword+keylength <= tp) 156 break; 157 *tp++ = c; 158 continue; 159 160 case '\n': case KDELIM: case VDELIM: 161 break; 162 } 163 break; 164 } 165 } while (c==KDELIM); 166 if (c!=VDELIM) continue; 167 *tp = c; 168 Igeteof(fp, c, break;); 169 switch (c) { 170 case ' ': case '\t': break; 171 default: continue; 172 } 173 174 switch (trymatch(keyword)) { 175 case Author: 176 if (!keepid(0, fp, &prevauthor)) 177 return false; 178 c = 0; 179 break; 180 case Date: 181 if (!(c = keepdate(fp))) 182 return false; 183 break; 184 case Header: 185 case Id: 186 if (!( 187 getval(fp, (struct buf*)nil, false) && 188 keeprev(fp) && 189 (c = keepdate(fp)) && 190 keepid(c, fp, &prevauthor) && 191 keepid(0, fp, &prevstate) 192 )) 193 return false; 194 /* Skip either ``who'' (new form) or ``Locker: who'' (old). */ 195 if (getval(fp, (struct buf*)nil, true) && 196 getval(fp, (struct buf*)nil, true)) 197 c = 0; 198 else if (nerror) 199 return false; 200 else 201 c = KDELIM; 202 break; 203 case Locker: 204 case Log: 205 case RCSfile: 206 case Source: 207 if (!getval(fp, (struct buf*)nil, false)) 208 return false; 209 c = 0; 210 break; 211 case Revision: 212 if (!keeprev(fp)) 213 return false; 214 c = 0; 215 break; 216 case State: 217 if (!keepid(0, fp, &prevstate)) 218 return false; 219 c = 0; 220 break; 221 default: 222 continue; 223 } 224 if (!c) 225 Igeteof(fp, c, c=0;); 226 if (c != KDELIM) { 227 error("closing %c missing on keyword", KDELIM); 228 return false; 229 } 230 if (*prevauthor.string && *prevdate.string && *prevrev.string && *prevstate.string) { 231 break; 232 } 233 } 234 Igeteof(fp, c, break;); 235 } 236 237 ok: 238 if (needs_closing) 239 Ifclose(fp); 240 else 241 Irewind(fp); 242 prevkeys = true; 243 return true; 244} 245 246 static int 247badly_terminated() 248{ 249 error("badly terminated keyword value"); 250 return false; 251} 252 253 static int 254getval(fp, target, optional) 255 register RILE *fp; 256 struct buf *target; 257 int optional; 258/* Reads a keyword value from FP into TARGET. 259 * Returns true if one is found, false otherwise. 260 * Does not modify target if it is nil. 261 * Do not report an error if OPTIONAL is set and KDELIM is found instead. 262 */ 263{ 264 int c; 265 Igeteof(fp, c, return badly_terminated();); 266 return get0val(c, fp, target, optional); 267} 268 269 static int 270get0val(c, fp, target, optional) 271 register int c; 272 register RILE *fp; 273 struct buf *target; 274 int optional; 275/* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly. 276 * Same as getval, except C is the lookahead character. 277 */ 278{ register char * tp; 279 char const *tlim; 280 register int got1; 281 282 if (target) { 283 bufalloc(target, 1); 284 tp = target->string; 285 tlim = tp + target->size; 286 } else 287 tlim = tp = 0; 288 got1 = false; 289 for (;;) { 290 switch (c) { 291 default: 292 got1 = true; 293 if (tp) { 294 *tp++ = c; 295 if (tlim <= tp) 296 tp = bufenlarge(target, &tlim); 297 } 298 break; 299 300 case ' ': 301 case '\t': 302 if (tp) { 303 *tp = 0; 304# ifdef KEEPTEST 305 VOID printf("getval: %s\n", target); 306# endif 307 } 308 if (!got1) 309 error("too much white space in keyword value"); 310 return got1; 311 312 case KDELIM: 313 if (!got1 && optional) 314 return false; 315 /* fall into */ 316 case '\n': 317 case 0: 318 return badly_terminated(); 319 } 320 Igeteof(fp, c, return badly_terminated();); 321 } 322} 323 324 325 static int 326keepdate(fp) 327 RILE *fp; 328/* Function: reads a date prevdate; checks format 329 * Return 0 on error, lookahead character otherwise. 330 */ 331{ 332 struct buf prevday, prevtime, prevzone; 333 register char const *p; 334 register int c; 335 336 c = 0; 337 bufautobegin(&prevday); 338 if (getval(fp,&prevday,false)) { 339 bufautobegin(&prevtime); 340 if (getval(fp,&prevtime,false)) { 341 bufautobegin(&prevzone); 342 bufscpy(&prevzone, ""); 343 Igeteof(fp, c, c=0;); 344 if (c=='-' || c=='+') 345 if (!get0val(c,fp,&prevzone,false)) 346 c = 0; 347 else 348 Igeteof(fp, c, c=0;); 349 if (c) { 350 p = prevday.string; 351 bufalloc(&prevdate, strlen(p) + strlen(prevtime.string) + strlen(prevzone.string) + 5); 352 VOID sprintf(prevdate.string, "%s%s %s %s", 353 /* Parse dates put out by old versions of RCS. */ 354 isdigit(p[0]) && isdigit(p[1]) && p[2]=='/' ? "19" : "", 355 p, prevtime.string, prevzone.string 356 ); 357 } 358 bufautoend(&prevzone); 359 } 360 bufautoend(&prevtime); 361 } 362 bufautoend(&prevday); 363 return c; 364} 365 366 static int 367keepid(c, fp, b) 368 int c; 369 RILE *fp; 370 struct buf *b; 371/* Get previous identifier from C+FP into B. */ 372{ 373 if (!c) 374 Igeteof(fp, c, return false;); 375 if (!get0val(c, fp, b, false)) 376 return false; 377 checksid(b->string); 378 return true; 379} 380 381 static int 382keeprev(fp) 383 RILE *fp; 384/* Get previous revision from FP into prevrev. */ 385{ 386 return getval(fp,&prevrev,false) && checknum(prevrev.string,-1); 387} 388 389 390 static int 391checknum(sp,fields) 392 register char const *sp; 393 int fields; 394{ register int dotcount; 395 dotcount=0; 396 while(*sp) { 397 if (*sp=='.') dotcount++; 398 else if (!isdigit(*sp)) return false; 399 sp++; 400 } 401 return fields<0 ? dotcount&1 : dotcount==fields; 402} 403 404 405 406#ifdef KEEPTEST 407 408char const cmdid[] ="keeptest"; 409 410 int 411main(argc, argv) 412int argc; char *argv[]; 413{ 414 while (*(++argv)) { 415 workfilename = *argv; 416 getoldkeys((RILE*)0); 417 VOID printf("%s: revision: %s, date: %s, author: %s, state: %s\n", 418 *argv, prevrev.string, prevdate.string, prevauthor.string, prevstate.string); 419 } 420 exitmain(EXIT_SUCCESS); 421} 422#endif 423