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
usage()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
number(register char * s,char ** e)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
string(register char * s,char * tab,int num,int siz,char ** e)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
date(char * s,char ** e)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
main(int argc,char ** argv)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