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