update from MirBSD CVS
[alioth/jupp.git] / compat.c
1 /*-
2  * Copyright © 2004, 2005, 2006, 2007, 2011, 2012, 2017
3  *      mirabilos <m@mirbsd.org>
4  *
5  * Provided that these terms and disclaimer and all copyright notices
6  * are retained or reproduced in an accompanying document, permission
7  * is granted to deal in this work without restriction, including un‐
8  * limited rights to use, publicly perform, distribute, sell, modify,
9  * merge, give away, or sublicence.
10  *
11  * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
12  * the utmost extent permitted by applicable law, neither express nor
13  * implied; without malicious intent or gross negligence. In no event
14  * may a licensor, author or contributor be held liable for indirect,
15  * direct, other damage, loss, or other issues arising in any way out
16  * of dealing in the work, even if advised of the possibility of such
17  * damage or existence of a defect, except proven that it results out
18  * of said person’s immediate fault when using the work as intended.
19  *-
20  * Compatibility and fully new utility functions for jupp.
21  *
22  * – ctime: based on mirtime from MirBSD libc; not leap second capable
23  *   src/kern/include/mirtime.h,v 1.2 2011/11/20 23:40:11 tg Exp
24  *   src/kern/c/mirtime.c,v 1.3 2011/11/20 23:40:10 tg Exp
25  * – strlcpy, strlcat: pulled in via "strlfun.inc"
26  * – popen, pclose: pulled in via "popen.inc"
27  * - ustoc_{hex,oct}, ustol: parse integers
28  */
29
30 #include "config.h"
31 #include "types.h"
32
33 __RCSID("$MirOS: contrib/code/jupp/compat.c,v 1.9 2017/12/02 17:00:53 tg Exp $");
34
35 #include <limits.h>
36 #include <string.h>
37 #include "utils.h"
38
39 #undef L_strlcat
40 #undef L_strlcpy
41 #ifndef HAVE_STRLCAT
42 #define L_strlcat
43 #endif
44 #ifndef HAVE_STRLCPY
45 #define L_strlcpy
46 #endif
47 #if defined(L_strlcat) || defined(L_strlcpy)
48 #undef __RCSID
49 #define __RCSID(x)              __IDSTRING(rcsid_strlfun_inc,x)
50 #define OUTSIDE_OF_LIBKERN
51 #include "strlfun.inc"
52 #endif
53
54 #ifndef HAVE_CTIME
55 #ifdef TIME_WITH_SYS_TIME
56 # include <sys/time.h>
57 # include <time.h>
58 #else
59 # ifdef HAVE_SYS_TIME_H
60 #  include <sys/time.h>
61 # else
62 #  include <time.h>
63 # endif
64 #endif
65
66 typedef struct {
67         int tm_sec;             /* seconds [0-60] */
68         int tm_min;             /* minutes [0-59] */
69         int tm_hour;            /* hours [0-23] */
70         int tm_mday;            /* day of month [1-31] */
71         int tm_mon;             /* month of year - 1 [0-11] */
72         int tm_year;            /* year - 1900 */
73         int tm_wday;            /* day of week (0 = sunday) */
74 } joe_tm;
75
76 static void joe_timet2tm(joe_tm *, const time_t *);
77
78 #if !HAVE_DECL_CTIME
79 char *ctime(const time_t *);
80 #endif
81
82 /* 302 / 1000 is log10(2.0) rounded up */
83 #define T_SIGNED(t)     (((t)-1) < 0)
84 #define T_MAXLEN(t)     ((sizeof(t) * CHAR_BIT - T_SIGNED(t)) * 302 / 1000 + \
85                             /* div.trunc. */ 1 + /* minus sign */ T_SIGNED(t))
86
87 static const char joe_days[7][4] = {
88         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
89 };
90
91 static const char joe_months[12][4] = {
92         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
93         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
94 };
95
96 /*-
97  * Dimensions for the buffer, example formats:
98  * "Sun Jan  1 12:34:56 1234\n"
99  * "Sat Dec 31 12:34:56     12345\n"
100  *  <- 24 -----------------> + max.length of a year + NL + NUL
101  */
102 static char joe_ctime_buf[24 + T_MAXLEN(time_t) + 2];
103
104 char *
105 ctime(const time_t *tp)
106 {
107         int year;
108         joe_tm tm;
109
110         joe_timet2tm(&tm, tp);
111         year = (int)(tm.tm_year + 1900);
112         joe_snprintf_7(joe_ctime_buf, sizeof(joe_ctime_buf),
113             (year >= -999 && year <= 9999) ?
114             "%s %s %2d %02d:%02d:%02d %04d\n" :
115             "%s %s %2d %02d:%02d:%02d     %d\n",
116             joe_days[tm.tm_wday], joe_months[tm.tm_mon],
117             tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, year);
118         return (joe_ctime_buf);
119 }
120
121 static void
122 joe_timet2tm(joe_tm *tm, const time_t *tp)
123 {
124         int sec, day, mon;
125         time_t y;
126
127         /* convert to MJD */
128         y = *tp;
129         sec = (int)(y % 86400);
130         y /= 86400;
131         y += 40587;
132         while (sec < 0) {
133                 --y;
134                 sec += 86400;
135         }
136
137         /* calculate 400-year cycle (y) and offset in it (day) */
138         day = (int)(y % 146097);
139         y /= 146097;
140
141         /* add bias: 678881 = days between "convenient origin" and MJD 0 */
142         /* convenient origin is Wed(3) 1 March 0(fictional)/-1(real) */
143         day += 678881;
144         /* recalculate offset in cycle (Gregorian Period) */
145         y += day / 146097;
146         day %= 146097;
147
148         /* days in 400 years are cyclic, they have 20871 weeks */
149         tm->tm_wday = (day + 3) % 7;
150
151         /* calculate year from period, taking leap years into account */
152         y *= 4;
153         /* a long (Julian) century is at the end of each Gregorian Period */
154         if (day == 146096) {
155                 y += 3;
156                 day = 36524;
157         } else {
158                 y += day / 36524;
159                 day %= 36524;
160         }
161         y *= 25;
162         y += day / 1461;
163         day %= 1461;
164         y *= 4;
165
166         /* March to December, or January/February? */
167         /* a leap year is at the end of each olympiad */
168         if (day == 1460) {
169                 y += 3;
170                 day = 365;
171         } else {
172                 y += day / 365;
173                 day %= 365;
174         }
175
176         /* count days and months from 1st March using fixed-point */
177         day *= 10;
178         mon = (day + 5) / 306;
179         day = (day + 5) % 306;
180         day /= 10;
181         /* adjust for Jan/Feb offset */
182         if (mon >= 10) {
183                 mon -= 10;
184                 ++y;
185         } else {
186                 mon += 2;
187         }
188
189         /* adjust for year 0(fictional) which did not exist */
190         if (y < 1)
191                 --y;
192
193         /* fill in the values still missing */
194         tm->tm_sec = sec % 60;
195         sec /= 60;
196         tm->tm_min = sec % 60;
197         tm->tm_hour = sec / 60;
198         tm->tm_mday = day + 1;
199         tm->tm_mon = mon;
200         /*XXX truncate, for joe_snprintf doesn't know %lld portably */
201         tm->tm_year = (int)(y - 1900);
202 }
203 #endif /* ndef HAVE_CTIME */
204
205 #ifndef HAVE_POPEN
206 #undef __RCSID
207 #define __RCSID(x)              __IDSTRING(rcsid_popen_inc,x)
208 #include "popen.inc"
209 #endif
210
211 size_t
212 ustoc_hex(const void *us, int *dp, size_t lim)
213 {
214         unsigned char c;
215         const unsigned char *s = (const unsigned char *)us;
216         int rv = 0, rounds = 0;
217
218         while (rounds++ < 2 && lim-- > 0)
219                 if ((c = *s++) >= '0' && c <= '9')
220                         rv = (rv << 4) | (c & 15);
221                 else if ((c |= 0x20) >= 'a' && c <= 'f')
222                         rv = (rv << 4) | (c - 'a' + 10);
223                 else {
224                         --s;
225                         break;
226                 }
227
228         *dp = rv;
229         return (s - (const unsigned char *)us);
230 }
231
232 size_t
233 ustoc_oct(const void *us, int *dp, size_t lim)
234 {
235         unsigned char c;
236         const unsigned char *s = (const unsigned char *)us;
237         int rv = 0, rounds = 0;
238
239         while (rounds++ < 3 && lim-- > 0)
240                 if ((c = *s++) >= '0' && c <= '7')
241                         rv = (rv << 3) | (c & 7);
242                 else {
243                         --s;
244                         break;
245                 }
246
247         *dp = rv;
248         return (s - (const unsigned char *)us);
249 }
250
251 #define USTOL_MODEMASK  0x03
252
253 static const char ustol_wsp[] = "\t\x0B\x0C\r ";
254
255 long
256 ustol(void *us, void **dpp, int flags)
257 {
258         unsigned char c, *s = (unsigned char *)us;
259         unsigned long a = 0;
260         unsigned char *sp;
261         unsigned char neg = 0;
262
263         if (flags & USTOL_LTRIM)
264                 while ((c = *s) && strchr(ustol_wsp, c))
265                         ++s;
266
267         switch (flags & USTOL_MODEMASK) {
268         case USTOL_HEX:
269                 if (s[0] == '0' && (s[1] | 0x20) == 'x')
270                         s += 2;
271                 break;
272         case USTOL_AUTO:
273                 if (s[0] != '0')
274                         flags |= USTOL_DEC;
275                 else if ((s[1] | 0x20) != 'x')
276                         flags |= USTOL_OCT;
277                 else {
278                         flags |= USTOL_HEX;
279                         s += 2;
280                 }
281                 break;
282         }
283
284         sp = s;
285         switch (flags & USTOL_MODEMASK) {
286         case USTOL_OCT:
287                 while ((c = *s++) >= '0' && c <= '7') {
288                         /* overflow check */
289                         if (a > (ULONG_MAX >> 3))
290                                 goto err;
291                         /* accumulate trivially */
292                         a = (a << 3) | (c & 7);
293                 }
294                 break;
295         case USTOL_HEX:
296                 while ((c = *s++) >= '0') {
297                         if (c <= '9')
298                                 c &= 15;
299                         else if ((c |= 0x20) >= 'a' && c <= 'f')
300                                 c = c - 'a' + 10;
301                         else
302                                 break;
303                         /* overflow check */
304                         if (a > (ULONG_MAX >> 4))
305                                 goto err;
306                         /* accumulate trivially */
307                         a = (a << 4) | c;
308                 }
309                 break;
310         default:
311                 switch (*s) {
312                 case '-':
313                         neg = 1;
314                         /* FALLTHROUGH */
315                 case '+':
316                         sp = ++s;
317                 }
318                 while ((c = *s++) >= '0' && c <= '9') {
319                         c &= 15;
320                         if (a > (ULONG_MAX / 10))
321                                 goto err;
322                         a *= 10;
323                         if (a > (ULONG_MAX - c))
324                                 goto err;
325                         a += c;
326                 }
327                 if (neg) {
328                         if (a > (((unsigned long)(-(LONG_MIN + 1L))) + 1UL))
329                                 goto err;
330                         a = -a;
331                 } else if (a > (unsigned long)LONG_MAX)
332                         goto err;
333         }
334         /* check we had at least one digit */
335         if (--s == sp)
336                 goto err;
337
338         if (flags & USTOL_RTRIM)
339                 while ((c = *s) && strchr(ustol_wsp, c))
340                         ++s;
341
342         /* don’t check for EOS, or arrived at EOS */
343         if (!(flags & USTOL_EOS) || !*s)
344                 goto out;
345
346  err:
347         s = NULL;
348         a = 0;
349  out:
350         if (dpp)
351                 *dpp = (void *)s;
352         return ((long)a);
353 }
354
355 long
356 ustolb(void *us, void **dpp, long lower, long upper, int flags)
357 {
358         void *dp;
359         long rv;
360
361         rv = ustol(us, &dp, flags);
362         if (dp != NULL && (rv < lower || rv > upper)) {
363                 dp = NULL;
364                 rv = 0;
365         }
366         if (dpp)
367                 *dpp = dp;
368         return (rv);
369 }