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