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