3e5035fbda5032e8e5dffcae4037030e00ae756f
[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.8 2017/12/02 04:32:39 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 #define OUTSIDE_OF_LIBKERN
49 #include "strlfun.inc"
50 #endif
51
52 #ifndef HAVE_CTIME
53 #ifdef TIME_WITH_SYS_TIME
54 # include <sys/time.h>
55 # include <time.h>
56 #else
57 # ifdef HAVE_SYS_TIME_H
58 #  include <sys/time.h>
59 # else
60 #  include <time.h>
61 # endif
62 #endif
63
64 typedef struct {
65         int tm_sec;             /* seconds [0-60] */
66         int tm_min;             /* minutes [0-59] */
67         int tm_hour;            /* hours [0-23] */
68         int tm_mday;            /* day of month [1-31] */
69         int tm_mon;             /* month of year - 1 [0-11] */
70         int tm_year;            /* year - 1900 */
71         int tm_wday;            /* day of week (0 = sunday) */
72 } joe_tm;
73
74 static void joe_timet2tm(joe_tm *, const time_t *);
75
76 #if !HAVE_DECL_CTIME
77 char *ctime(const time_t *);
78 #endif
79
80 /* 302 / 1000 is log10(2.0) rounded up */
81 #define T_SIGNED(t)     (((t)-1) < 0)
82 #define T_MAXLEN(t)     ((sizeof(t) * CHAR_BIT - T_SIGNED(t)) * 302 / 1000 + \
83                             /* div.trunc. */ 1 + /* minus sign */ T_SIGNED(t))
84
85 static const char joe_days[7][4] = {
86         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
87 };
88
89 static const char joe_months[12][4] = {
90         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
91         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
92 };
93
94 /*-
95  * Dimensions for the buffer, example formats:
96  * "Sun Jan  1 12:34:56 1234\n"
97  * "Sat Dec 31 12:34:56     12345\n"
98  *  <- 24 -----------------> + max.length of a year + NL + NUL
99  */
100 static char joe_ctime_buf[24 + T_MAXLEN(time_t) + 2];
101
102 char *
103 ctime(const time_t *tp)
104 {
105         int year;
106         joe_tm tm;
107
108         joe_timet2tm(&tm, tp);
109         year = (int)(tm.tm_year + 1900);
110         joe_snprintf_7(joe_ctime_buf, sizeof(joe_ctime_buf),
111             (year >= -999 && year <= 9999) ?
112             "%s %s %2d %02d:%02d:%02d %04d\n" :
113             "%s %s %2d %02d:%02d:%02d     %d\n",
114             joe_days[tm.tm_wday], joe_months[tm.tm_mon],
115             tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, year);
116         return (joe_ctime_buf);
117 }
118
119 static void
120 joe_timet2tm(joe_tm *tm, const time_t *tp)
121 {
122         int sec, day, mon;
123         time_t y;
124
125         /* convert to MJD */
126         y = *tp;
127         sec = (int)(y % 86400);
128         y /= 86400;
129         y += 40587;
130         while (sec < 0) {
131                 --y;
132                 sec += 86400;
133         }
134
135         /* calculate 400-year cycle (y) and offset in it (day) */
136         day = (int)(y % 146097);
137         y /= 146097;
138
139         /* add bias: 678881 = days between "convenient origin" and MJD 0 */
140         /* convenient origin is Wed(3) 1 March 0(fictional)/-1(real) */
141         day += 678881;
142         /* recalculate offset in cycle (Gregorian Period) */
143         y += day / 146097;
144         day %= 146097;
145
146         /* days in 400 years are cyclic, they have 20871 weeks */
147         tm->tm_wday = (day + 3) % 7;
148
149         /* calculate year from period, taking leap years into account */
150         y *= 4;
151         /* a long (Julian) century is at the end of each Gregorian Period */
152         if (day == 146096) {
153                 y += 3;
154                 day = 36524;
155         } else {
156                 y += day / 36524;
157                 day %= 36524;
158         }
159         y *= 25;
160         y += day / 1461;
161         day %= 1461;
162         y *= 4;
163
164         /* March to December, or January/February? */
165         /* a leap year is at the end of each olympiad */
166         if (day == 1460) {
167                 y += 3;
168                 day = 365;
169         } else {
170                 y += day / 365;
171                 day %= 365;
172         }
173
174         /* count days and months from 1st March using fixed-point */
175         day *= 10;
176         mon = (day + 5) / 306;
177         day = (day + 5) % 306;
178         day /= 10;
179         /* adjust for Jan/Feb offset */
180         if (mon >= 10) {
181                 mon -= 10;
182                 ++y;
183         } else {
184                 mon += 2;
185         }
186
187         /* adjust for year 0(fictional) which did not exist */
188         if (y < 1)
189                 --y;
190
191         /* fill in the values still missing */
192         tm->tm_sec = sec % 60;
193         sec /= 60;
194         tm->tm_min = sec % 60;
195         tm->tm_hour = sec / 60;
196         tm->tm_mday = day + 1;
197         tm->tm_mon = mon;
198         /*XXX truncate, for joe_snprintf doesn't know %lld portably */
199         tm->tm_year = (int)(y - 1900);
200 }
201 #endif /* ndef HAVE_CTIME */
202
203 #ifndef HAVE_POPEN
204 #include "popen.inc"
205 #endif
206
207 size_t
208 ustoc_hex(const void *us, int *dp, size_t lim)
209 {
210         unsigned char c;
211         const unsigned char *s = (const unsigned char *)us;
212         int rv = 0, rounds = 0;
213
214         while (rounds++ < 2 && lim-- > 0)
215                 if ((c = *s++) >= '0' && c <= '9')
216                         rv = (rv << 4) | (c & 15);
217                 else if ((c |= 0x20) >= 'a' && c <= 'f')
218                         rv = (rv << 4) | (c - 'a' + 10);
219                 else {
220                         --s;
221                         break;
222                 }
223
224         *dp = rv;
225         return (s - (const unsigned char *)us);
226 }
227
228 size_t
229 ustoc_oct(const void *us, int *dp, size_t lim)
230 {
231         unsigned char c;
232         const unsigned char *s = (const unsigned char *)us;
233         int rv = 0, rounds = 0;
234
235         while (rounds++ < 3 && lim-- > 0)
236                 if ((c = *s++) >= '0' && c <= '7')
237                         rv = (rv << 3) | (c & 7);
238                 else {
239                         --s;
240                         break;
241                 }
242
243         *dp = rv;
244         return (s - (const unsigned char *)us);
245 }
246
247 #define USTOL_MODEMASK  0x03
248
249 static const char ustol_wsp[] = "\t\x0B\x0C\r ";
250
251 long
252 ustol(void *us, void **dpp, int flags)
253 {
254         unsigned char c, *s = (unsigned char *)us;
255         unsigned long a = 0;
256         unsigned char *sp;
257         unsigned char neg = 0;
258
259         if (flags & USTOL_LTRIM)
260                 while ((c = *s) && strchr(ustol_wsp, c))
261                         ++s;
262
263         switch (flags & USTOL_MODEMASK) {
264         case USTOL_HEX:
265                 if (s[0] == '0' && (s[1] | 0x20) == 'x')
266                         s += 2;
267                 break;
268         case USTOL_AUTO:
269                 if (s[0] != '0')
270                         flags |= USTOL_DEC;
271                 else if ((s[1] | 0x20) != 'x')
272                         flags |= USTOL_OCT;
273                 else {
274                         flags |= USTOL_HEX;
275                         s += 2;
276                 }
277                 break;
278         }
279
280         sp = s;
281         switch (flags & USTOL_MODEMASK) {
282         case USTOL_OCT:
283                 while ((c = *s++) >= '0' && c <= '7') {
284                         /* overflow check */
285                         if (a > (ULONG_MAX >> 3))
286                                 goto err;
287                         /* accumulate trivially */
288                         a = (a << 3) | (c & 7);
289                 }
290                 break;
291         case USTOL_HEX:
292                 while ((c = *s++) >= '0') {
293                         if (c <= '9')
294                                 c &= 15;
295                         else if ((c |= 0x20) >= 'a' && c <= 'f')
296                                 c = c - 'a' + 10;
297                         else
298                                 break;
299                         /* overflow check */
300                         if (a > (ULONG_MAX >> 4))
301                                 goto err;
302                         /* accumulate trivially */
303                         a = (a << 4) | c;
304                 }
305                 break;
306         default:
307                 switch (*s) {
308                 case '-':
309                         neg = 1;
310                         /* FALLTHROUGH */
311                 case '+':
312                         sp = ++s;
313                 }
314                 while ((c = *s++) >= '0' && c <= '9') {
315                         c &= 15;
316                         if (a > (ULONG_MAX / 10))
317                                 goto err;
318                         a *= 10;
319                         if (a > (ULONG_MAX - c))
320                                 goto err;
321                         a += c;
322                 }
323                 if (neg) {
324                         if (a > (((unsigned long)(-(LONG_MIN + 1L))) + 1UL))
325                                 goto err;
326                         a = -a;
327                 } else if (a > (unsigned long)LONG_MAX)
328                         goto err;
329         }
330         /* check we had at least one digit */
331         if (--s == sp)
332                 goto err;
333
334         if (flags & USTOL_RTRIM)
335                 while ((c = *s) && strchr(ustol_wsp, c))
336                         ++s;
337
338         /* don’t check for EOS, or arrived at EOS */
339         if (!(flags & USTOL_EOS) || !*s)
340                 goto out;
341
342  err:
343         s = NULL;
344         a = 0;
345  out:
346         if (dpp)
347                 *dpp = (void *)s;
348         return ((long)a);
349 }
350
351 long
352 ustolb(void *us, void **dpp, long lower, long upper, int flags)
353 {
354         void *dp;
355         long rv;
356
357         rv = ustol(us, &dp, flags);
358         if (dp != NULL && (rv < lower || rv > upper)) {
359                 dp = NULL;
360                 rv = 0;
361         }
362         if (dpp)
363                 *dpp = dp;
364         return (rv);
365 }