1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1994-2011 AT&T * 5* and is licensed under the * 6* Common Public License, Version 1.0 * 7* by AT&T * 8* * 9* A copy of the License is available at * 10* http://www.opensource.org/licenses/cpl1.0.txt * 11* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12* * 13* Information and Software Systems Research * 14* AT&T Research * 15* Florham Park NJ * 16* * 17* Glenn Fowler <gsf@research.att.com> * 18* * 19***********************************************************************/ 20#pragma prototyped 21 22/* 23 * release -- list recent release changes 24 * 25 * coded for portability 26 */ 27 28static char id[] = "\n@(#)$Id: release (AT&T Research) 2000-01-28 $\0\n"; 29 30#if _PACKAGE_ast 31 32#include <ast.h> 33#include <error.h> 34 35static const char usage[] = 36"[-?\n@(#)$Id: release (AT&T Research) 2000-01-28 $\n]" 37USAGE_LICENSE 38"[+NAME?release - list recent changes]" 39"[+DESCRIPTION?\brelease\b lists the changes within the date range specified" 40" by the \b--from\b and \b--to\b options. The input files are assumed to" 41" contain date tag lines of the form [\acc\a]]\ayy-mm-dd\a [ \atext\a ]]" 42" (or \bdate\b(1) default format), where \acc\a is determined by a Y2K" 43" window year of 69 (we can produce an example coding dated 1991 - this" 44" can be patented?, how about 1+1=2?.) The date tag lines are followed by" 45" \areadme\a text in reverse chronological order (newer entries at the" 46" top of the file.) If no selection options are spcified then all" 47" changes are listed. If no \afile\a operands are specified then the" 48" standard input is read.]" 49"[+?The entries for each \afile\a are annotated with the file directory name.]" 50"[f:from?Entries older than \adate\a are omitted.]:[date]" 51"[r:release?List all changes that include the first \acount\a release marks." 52" A release mark has a date tag followed by optional space and at least" 53" three \b-\b characters. Changes from release mark \acount\a+1 are not" 54" listed. If there are no release marks then the date range is used;" 55" if there is at least one release mark then the date range is ignored" 56" and at most \acount\a release marks will be listed.]#[count]" 57"[t:to?Entries newer than \adate\a are omitted.]:[date]" 58"[V?Print the program version and exit.]" 59 60"\n" 61"\n[ file ... ]\n" 62"\n" 63 64"[+SEE ALSO?\bpackage\b(1)]" 65; 66 67#else 68 69#define elementsof(x) ((int)(sizeof(x)/sizeof(x[0]))) 70 71#define NiL ((char*)0) 72 73#endif 74 75#include <stdio.h> 76#include <unistd.h> 77#include <ctype.h> 78#include <sys/types.h> 79 80#if !_PACKAGE_ast && defined(__STDC__) 81#include <stdlib.h> 82#include <string.h> 83#endif 84 85static char mon[] = "janfebmaraprmayjunjulaugsepoctnovdec"; 86static char day[] = "sunmontuewedthufrisat"; 87 88#if !_PACKAGE_ast 89 90static void 91usage() 92{ 93 fprintf(stderr, "Usage: release [-V] [-h hi-date] [-l lo-date] [-r count] [ file ...]\n"); 94 exit(2); 95} 96 97#endif 98 99static unsigned long 100number(register char* s, char** e) 101{ 102 unsigned long q = 0; 103 104 while (isspace(*s)) 105 s++; 106 while (isdigit(*s)) 107 q = q * 10 + *s++ - '0'; 108 if (e) 109 *e = s; 110 return q; 111} 112 113unsigned long 114string(register char* s, char* tab, int num, int siz, char** e) 115{ 116 register int i; 117 register int j; 118 char buf[16]; 119 120 while (isspace(*s)) 121 s++; 122 for (i = 0; i < siz; i++) 123 buf[i] = isupper(s[i]) ? tolower(s[i]) : s[i]; 124 for (i = 0; i < num; i += siz) 125 for (j = 0; j < siz && buf[j] == tab[j+i]; j++) 126 if (j == (siz - 1)) 127 { 128 *e = s + siz; 129 return i / siz + 1; 130 } 131 return 0; 132} 133 134static unsigned long 135date(char* s, char** e) 136{ 137 char* t; 138 unsigned long y; 139 unsigned long m; 140 unsigned long d; 141 142 if (isdigit(*s)) 143 { 144 y = number(s, &t); 145 if (*t != '-') 146 return 0; 147 switch (t - s) 148 { 149 case 2: 150 y += 1900; 151 if (y <= 1969) 152 y += 100; 153 break; 154 case 4: 155 if (y < 1969) 156 return 0; 157 break; 158 } 159 if (!(m = number(++t, &s))) 160 return 0; 161 if ((s - t) != 2 || *s != '-' || m < 1 || m > 12) 162 return 0; 163 if (!(d = number(++s, &t))) 164 return 0; 165 if ((t - s) != 2 || d < 1 || d > 31) 166 return 0; 167 } 168 else 169 { 170 if (string(s, day, elementsof(day), 3, &t)) 171 s = t; 172 if (!(m = string(s, mon, elementsof(mon), 3, &t))) 173 return 0; 174 if (!(d = number(t, &s))) 175 return 0; 176 for (y = 1969; *s; s++) 177 if ((y = number(s, &t)) && (t - s) == 4) 178 { 179 if (y < 1969) 180 return 0; 181 break; 182 } 183 } 184 if (e) 185 { 186 while (isspace(*t)) 187 t++; 188 *e = t; 189 } 190 return ((y - 1969) * 13 + m) * 32 + d; 191} 192 193int 194main(int argc, char** argv) 195{ 196 register char* s; 197 register char* u; 198 register char* v; 199 char* p; 200 char* e; 201 int i; 202 unsigned long t; 203 unsigned long lo; 204 unsigned long hi; 205 int mk; 206 FILE* f; 207 char buf[1024]; 208 209 mk = 0; 210 lo = hi = 0; 211#if _PACKAGE_ast 212 error_info.id = "release"; 213 for (;;) 214 { 215 switch (optget(argv, usage)) 216 { 217 case 'f': 218 if (!(lo = date(opt_info.arg, &e)) || *e) 219 { 220 error(2, "%s: invalid from date [%s]", opt_info.arg, e); 221 return 1; 222 } 223 continue; 224 case 'r': 225 mk = opt_info.num + 1; 226 continue; 227 case 't': 228 if (!(hi = date(opt_info.arg, &e)) || *e) 229 { 230 error(2, "%s: invalid to date [%s]", opt_info.arg, e); 231 return 1; 232 } 233 continue; 234 case 'V': 235 sfprintf(sfstdout, "%s\n", id + 10); 236 return 0; 237 case '?': 238 error(ERROR_USAGE|4, "%s", opt_info.arg); 239 continue; 240 case ':': 241 error(2, "%s", opt_info.arg); 242 continue; 243 } 244 break; 245 } 246 if (error_info.errors) 247 error(ERROR_USAGE|4, "%s", optusage(NiL)); 248 argv += opt_info.index; 249#else 250 while ((s = *++argv) && *s == '-' && *(s + 1)) 251 { 252 if (*(s + 1) == '-') 253 { 254 if (!*(s + 2)) 255 { 256 argv++; 257 break; 258 } 259 usage(); 260 break; 261 } 262 for (;;) 263 { 264 switch (i = *++s) 265 { 266 case 0: 267 break; 268 case 'f': 269 case 't': 270 if (!*(v = ++s) && !(v = *++argv)) 271 { 272 s = "??"; 273 continue; 274 } 275 if (!(t = date(v, &e)) || *e) 276 { 277 fprintf(stderr, "release: -%c%s: invalid date [%s]\n", i, s, e); 278 return 1; 279 } 280 switch (i) 281 { 282 case 'f': 283 lo = t; 284 break; 285 case 't': 286 hi = t; 287 break; 288 } 289 break; 290 case 'r': 291 if (!*(v = ++s) && !(v = *++argv)) 292 { 293 s = "??"; 294 continue; 295 } 296 mk = number(v, &e) + 1; 297 if (*e) 298 { 299 fprintf(stderr, "release: -%c%s: invalid count\n", i, s); 300 return 1; 301 } 302 break; 303 case 'V': 304 fprintf(stdout, "%s\n", id + 10); 305 return 0; 306 default: 307 fprintf(stderr, "release: -%c: unknown option\n", i); 308 /*FALLTHROUGH*/ 309 case '?': 310 usage(); 311 break; 312 } 313 break; 314 } 315 } 316#endif 317 do 318 { 319 if (!(p = *argv++) || !*p || *p == '-' && !*(p + 1)) 320 { 321 argv--; 322 p = ""; 323 f = stdin; 324 } 325 else if (!(f = fopen(p, "r"))) 326 { 327 fprintf(stderr, "release: %s: cannot read", p); 328 return 1; 329 } 330 while (s = fgets(buf, sizeof(buf), f)) 331 { 332 if (t = date(s, &e)) 333 { 334 if (mk && e[0] == '-' && e[1] == '-' && e[2] == '-' && !--mk) 335 break; 336 if (t < lo) 337 break; 338 if (hi && t > hi) 339 continue; 340 if (p) 341 { 342 if (*p) 343 { 344 for (u = v = p; *p; p++) 345 if (*p == '/') 346 { 347 v = u; 348 u = p + 1; 349 } 350 printf("\n:::::::: "); 351 while ((i = *v++) && i != '/') 352 fputc(i, stdout); 353 printf(" ::::::::\n\n"); 354 } 355 p = 0; 356 } 357 } 358 if (!p) 359 fputs(s, stdout); 360 } 361 if (f == stdin) 362 break; 363 fclose(f); 364 } while (*argv); 365 return 0; 366} 367