xref: /illumos-gate/usr/src/cmd/acct/lib/pnpsplit.c (revision cdd3e9a818787b4def17c9f707f435885ce0ed31)
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 
48 static int	thisyear = 1970;	/* this is changed by holidays file */
49 static int	holidays[NHOLIDAYS];	/* holidays file day-of-year table */
50 
51 
52 static 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  */
61 static 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];
67 
68 /* the sec, min, hr of the day's end */
69 struct tm daysend = {
70 	.tm_sec = 0,
71 	.tm_min = 60,
72 	.tm_hour = 23
73 };
74 
75 long tmsecs(struct tm *, struct tm *);
76 
77 /*
78  * split interval of length etime, starting at start into prime/nonprime
79  * values, return as result
80  * input values in seconds
81  */
82 int
83 pnpsplit(long start, ulong_t etime, long result[2])
84 {
85 	struct tm cur, end, hours;
86 	time_t tcur, tend;
87 	long tmp;
88 	int sameday;
89 	struct hours *hp;
90 
91 	/* once holidays file is read, this is zero */
92 	if(thisyear && (checkhol() == 0)) {
93 		return(0);
94 	}
95 	tcur = start;
96 	tend = start + etime;
97 	memcpy(&end, localtime(&tend), sizeof(end));
98 	result[PRIME] = 0;
99 	result[NONPRIME] = 0;
100 
101 	while ( tcur < tend ) {	/* one iteration per day or part thereof */
102 		memcpy(&cur, localtime(&tcur), sizeof(cur));
103 		sameday = cur.tm_yday == end.tm_yday;
104 		if (ssh(&cur)) {	/* ssh:only NONPRIME */
105 			if (sameday) {
106 				result[NONPRIME] += tend-tcur;
107 
108 				break;
109 			} else {
110 				tmp = tmsecs(&cur, &daysend);
111 				result[NONPRIME] += tmp;
112 				tcur += tmp;
113 			}
114 		} else {	/* working day, PRIME or NONPRIME */
115 			for (hp = h; tmless(hp, &cur); hp++);
116 			for (; hp->h_sec >= 0; hp++) {
117 				if (sameday && tmless(&end, hp)) {
118 			/* WHCC mod, change from = to +=   3/6/86   Paul */
119 					result[hp->h_type] += tend-tcur;
120 					tcur = tend;
121 					break;	/* all done */
122 				} else {	/* time to next PRIME /NONPRIME change */
123 					hours.tm_sec = hp->h_sec;
124 					hours.tm_min = hp->h_min;
125 					hours.tm_hour = hp->h_hour;
126 					tmp = tmsecs(&cur, &hours);
127 					result[hp->h_type] += tmp;
128 					tcur += tmp;
129 					cur.tm_sec = hp->h_sec;
130 					cur.tm_min = hp->h_min;
131 					cur.tm_hour = hp->h_hour;
132 				}
133 			}
134 		}
135 	}
136 	return(1);
137 }
138 
139 /*
140  *	Starting day after Christmas, complain if holidays not yet updated.
141  *	This code is only executed once per program invocation.
142  */
143 int
144 checkhol(void)
145 {
146 	struct tm *tp;
147 	time_t t;
148 
149 	if(inithol() == 0) {
150 		fprintf(stderr, "pnpsplit: holidays table setup failed\n");
151 		thisyear = 0;
152 		holidays[0] = -1;
153 		return(0);
154 	}
155 	time(&t);
156 	tp = localtime(&t);
157 	tp->tm_year += 1900;
158 	if ((tp->tm_year == thisyear && tp->tm_yday > 359)
159 		|| tp->tm_year > thisyear)
160 		fprintf(stderr,
161 			"***UPDATE %s WITH NEW HOLIDAYS***\n", HOLFILE);
162 	thisyear = 0;	/* checkhol() will not be called again */
163 	return(1);
164 }
165 
166 /*
167  * ssh returns 1 if Sat, Sun, or Holiday
168  */
169 int
170 ssh(struct tm *ltp)
171 {
172 	int i;
173 
174 	if (ltp->tm_wday == 0 || ltp->tm_wday == 6)
175 		return(1);
176 	for (i = 0; holidays[i] >= 0; i++)
177 		if (ltp->tm_yday == holidays[i])
178 			return(1);
179 	return(0);
180 }
181 
182 /*
183  * inithol - read from an ascii file and initialize the "thisyear"
184  * variable, the times that prime and non-prime start, and the
185  * holidays array.
186  */
187 int
188 inithol(void)
189 {
190 	FILE		*holptr;
191 	char		holbuf[128];
192 	int		line = 0,
193 			holindx = 0,
194 			errflag = 0;
195 	int		pstart, npstart;
196 	int		doy;	/* day of the year */
197 	int 		month, day;
198 	char		*c;
199 
200 	if((holptr=fopen(HOLFILE, "r")) == NULL) {
201 		perror(HOLFILE);
202 		fclose(holptr);
203 		return(0);
204 	}
205 	while(fgets(holbuf, sizeof(holbuf), holptr) != NULL) {
206 		/* skip over blank lines and comments */
207 		if (holbuf[0] == '*')
208 			continue;
209 
210 		for (c = holbuf; isspace(*c); c++)
211 			/* is a space */;
212 
213 		if (*c == '\0')
214 			continue;
215 
216 		else if(++line == 1) {	/* format: year p-start np-start */
217 			if(sscanf(holbuf, "%4d %4d %4d",
218 				&thisyear, &pstart, &npstart) != 3) {
219 				fprintf(stderr,
220 					"%s: bad {yr ptime nptime} conversion\n",
221 					HOLFILE);
222 				errflag++;
223 				break;
224 			}
225 
226 			/* validate year */
227 			if(thisyear < 1970 || thisyear > 2037) {
228 				fprintf(stderr, "pnpsplit: invalid year: %d\n",
229 					thisyear);
230 				errflag++;
231 				break;
232 			}
233 
234 			/* validate prime/nonprime hours */
235 			if((! okay(pstart)) || (! okay(npstart))) {
236 				fprintf(stderr,
237 					"pnpsplit: invalid p/np hours\n");
238 				errflag++;
239 				break;
240 			}
241 
242 			/* Set up start of prime time; 2400 == 0000 */
243 			h[0].h_sec = 0;
244 			h[0].h_min = pstart%100;
245 			h[0].h_hour = (pstart/100==24) ? 0 : pstart/100;
246 			h[0].h_type = NONPRIME;
247 
248 			/* Set up start of non-prime time; 2400 == 2360 */
249 			if ((npstart/100) == 24) {
250 				h[1].h_sec = 0;
251 				h[1].h_min = 60;
252 				h[1].h_hour = 23;
253 			} else {
254 				h[1].h_sec = 0;
255 				h[1].h_min = npstart % 100;
256 				h[1].h_hour = npstart / 100;
257 			}
258 
259 			h[1].h_type = PRIME;
260 
261 			/* This is the end of the day */
262 			h[2].h_sec = 0;
263 			h[2].h_min = 60;
264 			h[2].h_hour = 23;
265 			h[2].h_type = NONPRIME;
266 
267 			/* The end of the array */
268 			h[3].h_sec = -1;
269 
270 			continue;
271 		}
272 		else if(holindx >= NHOLIDAYS) {
273 			fprintf(stderr, "pnpsplit: too many holidays, ");
274 			fprintf(stderr, "recompile with larger NHOLIDAYS\n");
275 			errflag++;
276 			break;
277 		}
278 
279 		/* Fill up holidays array from holidays file */
280 		sscanf(holbuf, "%d/%d	%*s %*s	%*[^\n]\n", &month, &day);
281 		if (month < 0 || month > 12) {
282 			fprintf(stderr, "pnpsplit: invalid month %d\n", month);
283 			errflag++;
284 			break;
285 		}
286 		if (day < 0 || day > 31) {
287 			fprintf(stderr, "pnpsplit: invalid day %d\n", day);
288 			errflag++;
289 			break;
290 		}
291 		doy = day_of_year(thisyear, month, day);
292 		holidays[holindx++] = (doy - 1);
293 	}
294 	fclose(holptr);
295 	if(!errflag && holindx < NHOLIDAYS) {
296 		holidays[holindx] = -1;
297 		return(1);
298 	}
299 	else
300 		return(0);
301 }
302 
303 /*
304  *	tmsecs returns number of seconds from t1 to t2,
305  *	times expressed in localtime format.
306  *	assumed that t1 <= t2, and are in same day.
307  */
308 
309 long
310 tmsecs(struct tm *t1, struct tm *t2)
311 {
312 	return((t2->tm_sec - t1->tm_sec) +
313 		60*(t2->tm_min - t1->tm_min) +
314 		3600L*(t2->tm_hour - t1->tm_hour));
315 }
316 
317 /*
318  *	return 1 if t1 earlier than t2 (times in localtime format)
319  *	assumed that t1 and t2 are in same day
320  */
321 
322 int
323 tmless(struct tm *t1, struct tm *t2)
324 {
325 	if (t1->tm_hour != t2->tm_hour)
326 		return(t1->tm_hour < t2->tm_hour);
327 	if (t1->tm_min != t2->tm_min)
328 		return(t1->tm_min < t2->tm_min);
329 	return(t1->tm_sec < t2->tm_sec);
330 }
331 
332 /* set day of year from month and day */
333 
334 int
335 day_of_year(int year, int month, int day)
336 {
337 	int i, leap;
338 
339 	leap = year%4 == 0 && year%100 || year%400 == 0;
340 	for (i = 1; i < month; i++)
341 		day += day_tab[leap][i];
342 	return(day);
343 }
344