1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1990-2011 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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 28 static 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 35 static const char usage[] = 36 "[-?\n@(#)$Id: release (AT&T Research) 2000-01-28 $\n]" 37 USAGE_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 85 static char mon[] = "janfebmaraprmayjunjulaugsepoctnovdec"; 86 static char day[] = "sunmontuewedthufrisat"; 87 88 #if !_PACKAGE_ast 89 90 static void 91 usage() 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 99 static unsigned long 100 number(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 113 unsigned long 114 string(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 134 static unsigned long 135 date(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 193 int 194 main(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