1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved	*/
28
29
30/*
31 * pnpsplit splits interval into prime & nonprime portions
32 * ONLY ROUTINE THAT KNOWS ABOUT HOLIDAYS AND DEFN OF PRIME/NONPRIME
33 */
34
35#include <stdio.h>
36#include <sys/types.h>
37#include <sys/param.h>
38#include "acctdef.h"
39#include <time.h>
40#include <ctype.h>
41
42/*
43 * validate that hours and minutes of prime/non-prime read in
44 * from holidays file fall within proper boundaries.
45 * Time is expected in the form and range of 0000-2400.
46 */
47
48static int	thisyear = 1970;	/* this is changed by holidays file */
49static int	holidays[NHOLIDAYS];	/* holidays file day-of-year table */
50
51
52static int day_tab[2][13] = {
53	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
54	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
55};
56
57/*
58 *	prime(0) and nonprime(1) times during a day
59 *	for BTL, prime time is 9AM to 5PM
60 */
61static struct hours {
62	int	h_sec;		/* normally always zero */
63	int	h_min;		/* initialized from holidays file (time%100) */
64	int	h_hour;		/* initialized from holidays file (time/100) */
65	long	h_type;		/* prime/nonprime of previous period */
66} h[4];
67int	daysend[] = {0, 60, 23};	/* the sec, min, hr of the day's end */
68
69struct tm *localtime();
70long	tmsecs();
71
72/*
73 * split interval of length etime, starting at start into prime/nonprime
74 * values, return as result
75 * input values in seconds
76 */
77int
78pnpsplit(start, etime, result)
79long start, result[2];
80ulong_t etime;
81{
82	struct tm cur, end;
83	time_t tcur, tend;
84	long tmp;
85	int sameday;
86	register struct hours *hp;
87	char *memcpy();
88
89	/* once holidays file is read, this is zero */
90	if(thisyear && (checkhol() == 0)) {
91		return(0);
92	}
93	tcur = start;
94	tend = start + etime;
95	memcpy(&end, localtime(&tend), sizeof(end));
96	result[PRIME] = 0;
97	result[NONPRIME] = 0;
98
99	while ( tcur < tend ) {	/* one iteration per day or part thereof */
100		memcpy(&cur, localtime(&tcur), sizeof(cur));
101		sameday = cur.tm_yday == end.tm_yday;
102		if (ssh(&cur)) {	/* ssh:only NONPRIME */
103			if (sameday) {
104				result[NONPRIME] += tend-tcur;
105
106				break;
107			} else {
108				tmp = tmsecs(&cur, daysend);
109				result[NONPRIME] += tmp;
110				tcur += tmp;
111			}
112		} else {	/* working day, PRIME or NONPRIME */
113			for (hp = h; tmless(hp, &cur); hp++);
114			for (; hp->h_sec >= 0; hp++) {
115				if (sameday && tmless(&end, hp)) {
116			/* WHCC mod, change from = to +=   3/6/86   Paul */
117					result[hp->h_type] += tend-tcur;
118					tcur = tend;
119					break;	/* all done */
120				} else {	/* time to next PRIME /NONPRIME change */
121					tmp = tmsecs(&cur, hp);
122					result[hp->h_type] += tmp;
123					tcur += tmp;
124					cur.tm_sec = hp->h_sec;
125					cur.tm_min = hp->h_min;
126					cur.tm_hour = hp->h_hour;
127				}
128			}
129		}
130	}
131	return(1);
132}
133
134/*
135 *	Starting day after Christmas, complain if holidays not yet updated.
136 *	This code is only executed once per program invocation.
137 */
138int
139checkhol()
140{
141	register struct tm *tp;
142	time_t t;
143
144	if(inithol() == 0) {
145		fprintf(stderr, "pnpsplit: holidays table setup failed\n");
146		thisyear = 0;
147		holidays[0] = -1;
148		return(0);
149	}
150	time(&t);
151	tp = localtime(&t);
152	tp->tm_year += 1900;
153	if ((tp->tm_year == thisyear && tp->tm_yday > 359)
154		|| tp->tm_year > thisyear)
155		fprintf(stderr,
156			"***UPDATE %s WITH NEW HOLIDAYS***\n", HOLFILE);
157	thisyear = 0;	/* checkhol() will not be called again */
158	return(1);
159}
160
161/*
162 * ssh returns 1 if Sat, Sun, or Holiday
163 */
164int
165ssh(ltp)
166register struct tm *ltp;
167{
168	int i;
169
170	if (ltp->tm_wday == 0 || ltp->tm_wday == 6)
171		return(1);
172	for (i = 0; holidays[i] >= 0; i++)
173		if (ltp->tm_yday == holidays[i])
174			return(1);
175	return(0);
176}
177
178/*
179 * inithol - read from an ascii file and initialize the "thisyear"
180 * variable, the times that prime and non-prime start, and the
181 * holidays array.
182 */
183int
184inithol()
185{
186	FILE		*fopen(), *holptr;
187	char		*fgets(), holbuf[128];
188	register int	line = 0,
189			holindx = 0,
190			errflag = 0;
191	int		pstart, npstart;
192	int		doy;	/* day of the year */
193	int 		month, day;
194	char		*c;
195
196	if((holptr=fopen(HOLFILE, "r")) == NULL) {
197		perror(HOLFILE);
198		fclose(holptr);
199		return(0);
200	}
201	while(fgets(holbuf, sizeof(holbuf), holptr) != NULL) {
202		/* skip over blank lines and comments */
203		if (holbuf[0] == '*')
204			continue;
205
206		for (c = holbuf; isspace(*c); c++)
207			/* is a space */;
208
209		if (*c == '\0')
210			continue;
211
212		else if(++line == 1) {	/* format: year p-start np-start */
213			if(sscanf(holbuf, "%4d %4d %4d",
214				&thisyear, &pstart, &npstart) != 3) {
215				fprintf(stderr,
216					"%s: bad {yr ptime nptime} conversion\n",
217					HOLFILE);
218				errflag++;
219				break;
220			}
221
222			/* validate year */
223			if(thisyear < 1970 || thisyear > 2037) {
224				fprintf(stderr, "pnpsplit: invalid year: %d\n",
225					thisyear);
226				errflag++;
227				break;
228			}
229
230			/* validate prime/nonprime hours */
231			if((! okay(pstart)) || (! okay(npstart))) {
232				fprintf(stderr,
233					"pnpsplit: invalid p/np hours\n");
234				errflag++;
235				break;
236			}
237
238			/* Set up start of prime time; 2400 == 0000 */
239			h[0].h_sec = 0;
240			h[0].h_min = pstart%100;
241			h[0].h_hour = (pstart/100==24) ? 0 : pstart/100;
242			h[0].h_type = NONPRIME;
243
244			/* Set up start of non-prime time; 2400 == 2360 */
245			if ((npstart/100) == 24) {
246				h[1].h_sec = 0;
247				h[1].h_min = 60;
248				h[1].h_hour = 23;
249			} else {
250				h[1].h_sec = 0;
251				h[1].h_min = npstart % 100;
252				h[1].h_hour = npstart / 100;
253			}
254
255			h[1].h_type = PRIME;
256
257			/* This is the end of the day */
258			h[2].h_sec = 0;
259			h[2].h_min = 60;
260			h[2].h_hour = 23;
261			h[2].h_type = NONPRIME;
262
263			/* The end of the array */
264			h[3].h_sec = -1;
265
266			continue;
267		}
268		else if(holindx >= NHOLIDAYS) {
269			fprintf(stderr, "pnpsplit: too many holidays, ");
270			fprintf(stderr, "recompile with larger NHOLIDAYS\n");
271			errflag++;
272			break;
273		}
274
275		/* Fill up holidays array from holidays file */
276		sscanf(holbuf, "%d/%d	%*s %*s	%*[^\n]\n", &month, &day);
277		if (month < 0 || month > 12) {
278			fprintf(stderr, "pnpsplit: invalid month %d\n", month);
279			errflag++;
280			break;
281		}
282		if (day < 0 || day > 31) {
283			fprintf(stderr, "pnpsplit: invalid day %d\n", day);
284			errflag++;
285			break;
286		}
287		doy = day_of_year(thisyear, month, day);
288		holidays[holindx++] = (doy - 1);
289	}
290	fclose(holptr);
291	if(!errflag && holindx < NHOLIDAYS) {
292		holidays[holindx] = -1;
293		return(1);
294	}
295	else
296		return(0);
297}
298
299/*
300 *	tmsecs returns number of seconds from t1 to t2,
301 *	times expressed in localtime format.
302 *	assumed that t1 <= t2, and are in same day.
303 */
304
305long
306tmsecs(t1, t2)
307register struct tm *t1, *t2;
308{
309	return((t2->tm_sec - t1->tm_sec) +
310		60*(t2->tm_min - t1->tm_min) +
311		3600L*(t2->tm_hour - t1->tm_hour));
312}
313
314/*
315 *	return 1 if t1 earlier than t2 (times in localtime format)
316 *	assumed that t1 and t2 are in same day
317 */
318
319int
320tmless(t1, t2)
321register struct tm *t1, *t2;
322{
323	if (t1->tm_hour != t2->tm_hour)
324		return(t1->tm_hour < t2->tm_hour);
325	if (t1->tm_min != t2->tm_min)
326		return(t1->tm_min < t2->tm_min);
327	return(t1->tm_sec < t2->tm_sec);
328}
329
330/* set day of year from month and day */
331
332int
333day_of_year(year, month, day)
334{
335	int i, leap;
336
337	leap = year%4 == 0 && year%100 || year%400 == 0;
338	for (i = 1; i < month; i++)
339		day += day_tab[leap][i];
340	return(day);
341}
342