1
2
3
4
5
6
7
8
9
10#include "toys.h"
11
12
13
14
15
16void xstrncpy(char *dest, char *src, size_t size)
17{
18 if (strlen(src)+1 > size) error_exit("'%s' > %ld bytes", src, (long)size);
19 strcpy(dest, src);
20}
21
22void xstrncat(char *dest, char *src, size_t size)
23{
24 long len = strlen(dest);
25
26 if (len+strlen(src)+1 > size)
27 error_exit("'%s%s' > %ld bytes", dest, src, (long)size);
28 strcpy(dest+len, src);
29}
30
31
32
33
34
35
36
37
38void _xexit(void)
39{
40 if (toys.rebound) siglongjmp(*toys.rebound, 1);
41
42 _exit(toys.exitval);
43}
44
45void xexit(void)
46{
47
48 while (toys.xexit) {
49 struct arg_list *al = llist_pop(&toys.xexit);
50
51
52
53 ((void (*)(int))(al->arg))(0);
54
55 free(al);
56 }
57 xflush(1);
58 _xexit();
59}
60
61void *xmmap(void *addr, size_t length, int prot, int flags, int fd, off_t off)
62{
63 void *ret = mmap(addr, length, prot, flags, fd, off);
64 if (ret == MAP_FAILED) perror_exit("mmap");
65 return ret;
66}
67
68
69void *xmalloc(size_t size)
70{
71 void *ret = malloc(size);
72 if (!ret) error_exit("xmalloc(%ld)", (long)size);
73
74 return ret;
75}
76
77
78void *xzalloc(size_t size)
79{
80 void *ret = xmalloc(size);
81 memset(ret, 0, size);
82 return ret;
83}
84
85
86
87void *xrealloc(void *ptr, size_t size)
88{
89 ptr = realloc(ptr, size);
90 if (!ptr) error_exit("xrealloc");
91
92 return ptr;
93}
94
95
96char *xstrndup(char *s, size_t n)
97{
98 char *ret = strndup(s, n);
99
100 if (!ret) error_exit("xstrndup");
101
102 return ret;
103}
104
105
106char *xstrdup(char *s)
107{
108 return xstrndup(s, strlen(s));
109}
110
111void *xmemdup(void *s, long len)
112{
113 void *ret = xmalloc(len);
114 memcpy(ret, s, len);
115
116 return ret;
117}
118
119
120char *xmprintf(char *format, ...)
121{
122 va_list va, va2;
123 int len;
124 char *ret;
125
126 va_start(va, format);
127 va_copy(va2, va);
128
129
130 len = vsnprintf(0, 0, format, va);
131 len++;
132 va_end(va);
133
134
135 ret = xmalloc(len);
136 vsnprintf(ret, len, format, va2);
137 va_end(va2);
138
139 return ret;
140}
141
142
143void xflush(int flush)
144{
145 if ((flush && fflush(0)) || ferror(stdout))
146 if (!toys.exitval) perror_msg("write");
147}
148
149void xprintf(char *format, ...)
150{
151 va_list va;
152 va_start(va, format);
153
154 vprintf(format, va);
155 va_end(va);
156 xflush(0);
157}
158
159
160void xputsl(char *s, int len)
161{
162 xflush(1);
163 xwrite(1, s, len);
164}
165
166
167void xputsn(char *s)
168{
169 xputsl(s, strlen(s));
170}
171
172
173void xputs(char *s)
174{
175 puts(s);
176 xflush(0);
177}
178
179void xputc(char c)
180{
181 if (EOF == fputc(c, stdout)) perror_exit("write");
182 xflush(0);
183}
184
185
186
187void xvdaemon(void)
188{
189 int fd;
190
191
192 if (toys.stacktop) {
193 xpopen_both(0, 0);
194 _exit(0);
195 }
196
197
198 setsid();
199 close(0);
200 xopen_stdio("/dev/null", O_RDWR);
201 dup2(0, 1);
202 if (-1 != (fd = open("/dev/tty", O_RDONLY))) {
203 ioctl(fd, TIOCNOTTY);
204 close(fd);
205 }
206 dup2(0, 2);
207}
208
209
210
211
212
213pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid)
214{
215 if (pid == -1) perror_exit("vfork");
216
217
218 if (!pid) toys.stacktop = 0;
219
220 return pid;
221}
222
223
224
225void xexec(char **argv)
226{
227
228 if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE && toys.stacktop && **argv != '/')
229 toy_exec(argv);
230 execvp(argv[0], argv);
231
232 toys.exitval = 126+(errno == ENOENT);
233 perror_msg("exec %s", argv[0]);
234 if (!toys.stacktop) _exit(toys.exitval);
235 xexit();
236}
237
238
239
240
241
242
243
244
245pid_t xpopen_setup(char **argv, int *pipes, void (*callback)(char **argv))
246{
247 int cestnepasun[4], pid;
248
249
250 memset(cestnepasun, 0, sizeof(cestnepasun));
251 if (pipes) for (pid = 0; pid < 2; pid++) {
252 if (pipes[pid] != -1) continue;
253 if (pipe(cestnepasun+(2*pid))) perror_exit("pipe");
254 }
255
256 if (!(pid = CFG_TOYBOX_FORK ? xfork() : XVFORK())) {
257
258
259 if (pipes) {
260
261
262
263
264 if (cestnepasun[2]) {
265 close(cestnepasun[2]);
266 pipes[1] = cestnepasun[3];
267 }
268
269
270 if (cestnepasun[1]) {
271 close(cestnepasun[1]);
272 pipes[0] = cestnepasun[0];
273 }
274
275
276 if (!pipes[1]) pipes[1] = dup(0);
277
278
279 if (pipes[0]) {
280 dup2(pipes[0], 0);
281 close(pipes[0]);
282 }
283
284
285 if (pipes[1] != 1) {
286 dup2(pipes[1], 1);
287 close(pipes[1]);
288 }
289 }
290 if (callback) callback(argv);
291 if (argv) xexec(argv);
292
293
294 if (CFG_TOYBOX_FORK) {
295 toy_init(toys.which, toys.argv);
296 toys.stacktop = 0;
297 toys.which->toy_main();
298 xexit();
299
300
301 } else {
302 char *s = "/proc/self/exe";
303
304
305
306 **toys.argv |= 0x80;
307 execv(s, toys.argv);
308 if ((s = getenv("_"))) execv(s, toys.argv);
309 perror_msg_raw(s);
310
311 _exit(127);
312 }
313 }
314
315
316 if (!CFG_TOYBOX_FORK) **toys.argv &= 0x7f;
317
318 if (pipes) {
319 if (cestnepasun[1]) {
320 pipes[0] = cestnepasun[1];
321 close(cestnepasun[0]);
322 }
323 if (cestnepasun[2]) {
324 pipes[1] = cestnepasun[2];
325 close(cestnepasun[3]);
326 }
327 }
328
329 return pid;
330}
331
332pid_t xpopen_both(char **argv, int *pipes)
333{
334 return xpopen_setup(argv, pipes, 0);
335}
336
337
338
339int xwaitpid(pid_t pid)
340{
341 int status;
342
343 while (-1 == waitpid(pid, &status, 0) && errno == EINTR);
344
345 return WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+128;
346}
347
348int xpclose_both(pid_t pid, int *pipes)
349{
350 if (pipes) {
351 close(pipes[0]);
352 close(pipes[1]);
353 }
354
355 return xwaitpid(pid);
356}
357
358
359pid_t xpopen(char **argv, int *pipe, int isstdout)
360{
361 int pipes[2], pid;
362
363 pipes[0] = isstdout ? 0 : -1;
364 pipes[1] = isstdout ? -1 : 1;
365 pid = xpopen_both(argv, pipes);
366 *pipe = pid ? pipes[!!isstdout] : -1;
367
368 return pid;
369}
370
371int xpclose(pid_t pid, int pipe)
372{
373 close(pipe);
374
375 return xpclose_both(pid, 0);
376}
377
378
379int xrun(char **argv)
380{
381 return xpclose_both(xpopen_both(argv, 0), 0);
382}
383
384void xaccess(char *path, int flags)
385{
386 if (access(path, flags)) perror_exit("Can't access '%s'", path);
387}
388
389
390void xunlink(char *path)
391{
392 if (unlink(path)) perror_exit("unlink '%s'", path);
393}
394
395
396
397
398int xcreate_stdio(char *path, int flags, int mode)
399{
400 int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode);
401
402 if (fd == -1) ((flags&WARN_ONLY) ? perror_msg_raw : perror_exit_raw)(path);
403 return fd;
404}
405
406
407int xopen_stdio(char *path, int flags)
408{
409 return xcreate_stdio(path, flags, 0);
410}
411
412void xpipe(int *pp)
413{
414 if (pipe(pp)) perror_exit("xpipe");
415}
416
417void xclose(int fd)
418{
419 if (close(fd)) perror_exit("xclose");
420}
421
422int xdup(int fd)
423{
424 if (fd != -1) {
425 fd = dup(fd);
426 if (fd == -1) perror_exit("xdup");
427 }
428 return fd;
429}
430
431
432
433int notstdio(int fd)
434{
435 if (fd<0) return fd;
436
437 while (fd<3) {
438 int fd2 = xdup(fd);
439
440 close(fd);
441 xopen_stdio("/dev/null", O_RDWR);
442 fd = fd2;
443 }
444
445 return fd;
446}
447
448void xrename(char *from, char *to)
449{
450 if (rename(from, to)) perror_exit("rename %s -> %s", from, to);
451}
452
453int xtempfile(char *name, char **tempname)
454{
455 int fd;
456
457 *tempname = xmprintf("%s%s", name, "XXXXXX");
458 if(-1 == (fd = mkstemp(*tempname))) error_exit("no temp file");
459
460 return fd;
461}
462
463
464int xcreate(char *path, int flags, int mode)
465{
466 return notstdio(xcreate_stdio(path, flags, mode));
467}
468
469
470int xopen(char *path, int flags)
471{
472 return notstdio(xopen_stdio(path, flags));
473}
474
475
476int openro(char *path, int flags)
477{
478 if (!strcmp(path, "-")) return 0;
479
480 return xopen(path, flags^WARN_ONLY);
481}
482
483
484int xopenro(char *path)
485{
486 return openro(path, O_RDONLY|WARN_ONLY);
487}
488
489FILE *xfdopen(int fd, char *mode)
490{
491 FILE *f = fdopen(fd, mode);
492
493 if (!f) perror_exit("xfdopen");
494
495 return f;
496}
497
498
499FILE *xfopen(char *path, char *mode)
500{
501 FILE *f = fopen(path, mode);
502 if (!f) perror_exit("No file %s", path);
503 return f;
504}
505
506
507size_t xread(int fd, void *buf, size_t len)
508{
509 ssize_t ret = read(fd, buf, len);
510 if (ret < 0) perror_exit("xread");
511
512 return ret;
513}
514
515void xreadall(int fd, void *buf, size_t len)
516{
517 if (len != readall(fd, buf, len)) perror_exit("xreadall");
518}
519
520
521
522
523
524void xwrite(int fd, void *buf, size_t len)
525{
526 if (len != writeall(fd, buf, len)) perror_exit("xwrite");
527}
528
529
530
531off_t xlseek(int fd, off_t offset, int whence)
532{
533 offset = lseek(fd, offset, whence);
534 if (offset<0) perror_exit("lseek");
535
536 return offset;
537}
538
539char *xgetcwd(void)
540{
541 char *buf = getcwd(NULL, 0);
542 if (!buf) perror_exit("xgetcwd");
543
544 return buf;
545}
546
547void xstat(char *path, struct stat *st)
548{
549 if(stat(path, st)) perror_exit("Can't stat %s", path);
550}
551
552
553
554
555
556char *xabspath(char *path, int exact)
557{
558 struct string_list *todo, *done = 0;
559 int try = 9999, dirfd = open("/", O_PATH), missing = 0;
560 char *ret;
561
562
563 if (*path != '/') {
564 char *temp = xgetcwd();
565
566 splitpath(path, splitpath(temp, &todo));
567 free(temp);
568 } else splitpath(path, &todo);
569
570
571 while (todo) {
572 struct string_list *new = llist_pop(&todo), **tail;
573 ssize_t len;
574
575
576 if (!try--) {
577 errno = ELOOP;
578 goto error;
579 }
580
581
582 if (!strcmp(new->str, ".") || !strcmp(new->str, "..")) {
583 int x = new->str[1];
584
585 free(new);
586 if (!x) continue;
587 if (done) free(llist_pop(&done));
588 len = 0;
589
590 if (missing) missing--;
591 else {
592 if (-1 == (x = openat(dirfd, "..", O_PATH))) goto error;
593 close(dirfd);
594 dirfd = x;
595 }
596 continue;
597 }
598
599
600 if (exact == -2 && !todo) len = 0;
601 else len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
602 if (len>4095) goto error;
603
604
605 if (len<1) {
606 int fd;
607
608 new->next = done;
609 done = new;
610 if (errno == EINVAL && !todo) break;
611 if (errno == ENOENT && exact<0) {
612 missing++;
613 continue;
614 }
615 if (errno != EINVAL && (exact || todo)) goto error;
616
617 fd = openat(dirfd, new->str, O_PATH);
618 if (fd == -1 && (exact || todo || errno != ENOENT)) goto error;
619 close(dirfd);
620 dirfd = fd;
621 continue;
622 }
623
624
625 libbuf[len] = 0;
626 if (*libbuf == '/') {
627 llist_traverse(done, free);
628 done=0;
629 close(dirfd);
630 dirfd = open("/", O_PATH);
631 }
632 free(new);
633
634
635 tail = splitpath(libbuf, &new);
636
637
638 if (new) {
639 *tail = todo;
640 todo = new;
641 }
642 }
643 close(dirfd);
644
645
646
647
648 try = 2;
649 while (done) {
650 struct string_list *temp = llist_pop(&done);
651
652 if (todo) try++;
653 try += strlen(temp->str);
654 temp->next = todo;
655 todo = temp;
656 }
657
658
659
660 ret = xmalloc(try);
661 *ret = '/';
662 ret [try = 1] = 0;
663 while (todo) {
664 if (try>1) ret[try++] = '/';
665 try = stpcpy(ret+try, todo->str) - ret;
666 free(llist_pop(&todo));
667 }
668
669 return ret;
670
671error:
672 close(dirfd);
673 llist_traverse(todo, free);
674 llist_traverse(done, free);
675
676 return 0;
677}
678
679void xchdir(char *path)
680{
681 if (chdir(path)) perror_exit("chdir '%s'", path);
682}
683
684void xchroot(char *path)
685{
686 if (chroot(path)) error_exit("chroot '%s'", path);
687 xchdir("/");
688}
689
690struct passwd *xgetpwuid(uid_t uid)
691{
692 struct passwd *pwd = getpwuid(uid);
693 if (!pwd) error_exit("bad uid %ld", (long)uid);
694 return pwd;
695}
696
697struct group *xgetgrgid(gid_t gid)
698{
699 struct group *group = getgrgid(gid);
700
701 if (!group) perror_exit("gid %ld", (long)gid);
702 return group;
703}
704
705unsigned xgetuid(char *name)
706{
707 struct passwd *up = getpwnam(name);
708 char *s = 0;
709 long uid;
710
711 if (up) return up->pw_uid;
712
713 uid = estrtol(name, &s, 10);
714 if (!errno && s && !*s && uid>=0 && uid<=UINT_MAX) return uid;
715
716 error_exit("bad user '%s'", name);
717}
718
719unsigned xgetgid(char *name)
720{
721 struct group *gr = getgrnam(name);
722 char *s = 0;
723 long gid;
724
725 if (gr) return gr->gr_gid;
726
727 gid = estrtol(name, &s, 10);
728 if (!errno && s && !*s && gid>=0 && gid<=UINT_MAX) return gid;
729
730 error_exit("bad group '%s'", name);
731}
732
733struct passwd *xgetpwnam(char *name)
734{
735 struct passwd *up = getpwnam(name);
736
737 if (!up) perror_exit("user '%s'", name);
738 return up;
739}
740
741struct group *xgetgrnam(char *name)
742{
743 struct group *gr = getgrnam(name);
744
745 if (!gr) perror_exit("group '%s'", name);
746 return gr;
747}
748
749
750
751
752void xsetuser(struct passwd *pwd)
753{
754 if (initgroups(pwd->pw_name, pwd->pw_gid) || setgid(pwd->pw_uid)
755 || setuid(pwd->pw_uid)) perror_exit("xsetuser '%s'", pwd->pw_name);
756}
757
758
759
760char *xreadlink(char *name)
761{
762 int len, size = 0;
763 char *buf = 0;
764
765
766 for(;;) {
767 size +=64;
768 buf = xrealloc(buf, size);
769 len = readlink(name, buf, size);
770
771 if (len<0) {
772 free(buf);
773 return 0;
774 }
775 if (len<size) {
776 buf[len]=0;
777 return buf;
778 }
779 }
780}
781
782char *xreadfile(char *name, char *buf, off_t len)
783{
784 if (!(buf = readfile(name, buf, len))) perror_exit("Bad '%s'", name);
785
786 return buf;
787}
788
789
790
791int xioctl(int fd, int request, void *data)
792{
793 int rc;
794
795 errno = 0;
796 rc = ioctl(fd, request, data);
797 if (rc == -1 && errno) perror_exit("ioctl %x", request);
798
799 return rc;
800}
801
802
803
804void xpidfile(char *name)
805{
806 char pidfile[256], spid[32];
807 int i, fd;
808 pid_t pid;
809
810 sprintf(pidfile, "/var/run/%s.pid", name);
811
812 for (i=0; i<3; i++) {
813 fd = open(pidfile, O_CREAT|O_EXCL|O_WRONLY, 0644);
814 if (fd != -1) break;
815
816
817 fd = open(pidfile, O_RDONLY);
818 if (fd == -1) continue;
819
820
821 spid[xread(fd, spid, sizeof(spid)-1)] = 0;
822 close(fd);
823 pid = atoi(spid);
824 if (pid < 1 || (kill(pid, 0) && errno == ESRCH)) unlink(pidfile);
825
826
827 }
828
829 if (i == 3) error_exit("xpidfile %s", name);
830
831 xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
832 close(fd);
833}
834
835
836long long xsendfile_len(int in, int out, long long bytes)
837{
838 long long len = sendfile_len(in, out, bytes, 0);
839
840 if (bytes != -1 && bytes != len) {
841 if (out == 1 && len<0) xexit();
842 error_exit("short %s", (len<0) ? "write" : "read");
843 }
844
845 return len;
846}
847
848
849void xsendfile_pad(int in, int out, long long len)
850{
851 len -= xsendfile_len(in, out, len);
852 if (len) {
853 perror_msg("short read");
854 memset(libbuf, 0, sizeof(libbuf));
855 while (len) {
856 int i = len>sizeof(libbuf) ? sizeof(libbuf) : len;
857
858 xwrite(out, libbuf, i);
859 len -= i;
860 }
861 }
862}
863
864
865long long xsendfile(int in, int out)
866{
867 return xsendfile_len(in, out, -1);
868}
869
870double xstrtod(char *s)
871{
872 char *end;
873 double d;
874
875 errno = 0;
876 d = strtod(s, &end);
877 if (!errno && *end) errno = E2BIG;
878 if (errno) perror_exit("strtod %s", s);
879
880 return d;
881}
882
883
884long xparsetime(char *arg, long zeroes, long *fraction)
885{
886 long l, fr = 0, mask = 1;
887 char *end;
888
889 if (*arg != '.' && !isdigit(*arg)) error_exit("Not a number '%s'", arg);
890 l = strtoul(arg, &end, 10);
891 if (*end == '.') {
892 end++;
893 while (zeroes--) {
894 fr *= 10;
895 mask *= 10;
896 if (isdigit(*end)) fr += *end++-'0';
897 }
898 while (isdigit(*end)) end++;
899 }
900
901
902 if (*end) {
903 int ismhd[]={1,60,3600,86400}, i = stridx("smhd", *end);
904
905 if (i == -1 || *(end+1)) error_exit("Unknown suffix '%s'", end);
906 l *= ismhd[i];
907 fr *= ismhd[i];
908 l += fr/mask;
909 fr %= mask;
910 }
911 if (fraction) *fraction = fr;
912
913 return l;
914}
915
916long long xparsemillitime(char *arg)
917{
918 long l, ll;
919
920 l = xparsetime(arg, 3, &ll);
921
922 return (l*1000LL)+ll;
923}
924
925
926
927
928void xregcomp(regex_t *preg, char *regex, int cflags)
929{
930 int rc;
931
932
933
934 if (!*regex) {
935 regex = "()";
936 cflags |= REG_EXTENDED;
937 }
938
939 if ((rc = regcomp(preg, regex, cflags))) {
940 regerror(rc, preg, libbuf, sizeof(libbuf));
941 error_exit("bad regex: %s", libbuf);
942 }
943}
944
945char *xtzset(char *new)
946{
947 char *old = getenv("TZ");
948
949 if (old) old = xstrdup(old);
950 if (new ? setenv("TZ", new, 1) : unsetenv("TZ")) perror_exit("setenv");
951 tzset();
952
953 return old;
954}
955
956
957void xsignal_flags(int signal, void *handler, int flags)
958{
959 struct sigaction *sa = (void *)libbuf;
960
961 memset(sa, 0, sizeof(struct sigaction));
962 sa->sa_handler = handler;
963 sa->sa_flags = flags;
964
965 if (sigaction(signal, sa, 0)) perror_exit("xsignal %d", signal);
966}
967
968void xsignal(int signal, void *handler)
969{
970 xsignal_flags(signal, handler, 0);
971}
972
973
974time_t xvali_date(struct tm *tm, char *str)
975{
976 time_t t;
977
978 if (tm && (unsigned)tm->tm_sec<=60 && (unsigned)tm->tm_min<=59
979 && (unsigned)tm->tm_hour<=23 && tm->tm_mday && (unsigned)tm->tm_mday<=31
980 && (unsigned)tm->tm_mon<=11 && (t = mktime(tm)) != -1) return t;
981
982 error_exit("bad date %s", str);
983}
984
985
986void xparsedate(char *str, time_t *t, unsigned *nano, int endian)
987{
988 struct tm tm;
989 time_t now = *t;
990 int len = 0, i = 0;
991
992
993 char *s = str, *p, *oldtz = 0, *formats[] = {"%Y-%m-%d %T", "%Y-%m-%dT%T",
994 "%a %b %e %H:%M:%S %Z %Y",
995 "%H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d", "%H:%M", "%m%d%H%M",
996 endian ? "%m%d%H%M%y" : "%y%m%d%H%M",
997 endian ? "%m%d%H%M%C%y" : "%C%y%m%d%H%M"};
998
999 *nano = 0;
1000
1001
1002 if (*str == '@') {
1003 long long ll;
1004
1005
1006
1007 sscanf(s, "@%lld%n", &ll, &len);
1008 if (s[len]=='.') {
1009 s += len+1;
1010 for (len = 0; len<9; len++) {
1011 *nano *= 10;
1012 if (isdigit(*s)) *nano += *s++-'0';
1013 }
1014 }
1015 *t = ll;
1016 if (!s[len]) return;
1017 xvali_date(0, str);
1018 }
1019
1020
1021 for (i = 0; i<ARRAY_LEN(formats); i++) {
1022 localtime_r(&now, &tm);
1023 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1024 tm.tm_isdst = -endian;
1025
1026 if ((p = strptime(s, formats[i], &tm))) {
1027
1028 if (*p == '.') {
1029 p++;
1030
1031 if (i>2) {
1032 len = 0;
1033 sscanf(p, "%2u%n", &tm.tm_sec, &len);
1034 p += len;
1035 }
1036
1037 for (len = 0; len<9; len++) {
1038 *nano *= 10;
1039 if (isdigit(*p)) *nano += *p++-'0';
1040 }
1041 }
1042
1043
1044 if (*p && strchr("Z+-", *p)) {
1045 unsigned hh, mm = 0, len;
1046 char *tz, sign = *p++;
1047
1048 if (sign == 'Z') tz = "UTC0";
1049 else if (sscanf(p, "%2u%2u%n", &hh, &mm, &len) == 2
1050 || sscanf(p, "%2u%n:%2u%n", &hh, &len, &mm, &len) > 0)
1051 {
1052
1053 sprintf(tz = libbuf, "UTC%c%02d:%02d", "+-"[sign=='+'], hh, mm);
1054 p += len;
1055 } else continue;
1056
1057 if (!oldtz) {
1058 oldtz = getenv("TZ");
1059 if (oldtz) oldtz = xstrdup(oldtz);
1060 }
1061 setenv("TZ", tz, 1);
1062 }
1063
1064 if (!*p) break;
1065 }
1066 }
1067
1068
1069 *t = xvali_date((i!=ARRAY_LEN(formats)) ? &tm : 0, str);
1070
1071 if (oldtz) setenv("TZ", oldtz, 1);
1072 free(oldtz);
1073}
1074
1075char *xgetline(FILE *fp, int *len)
1076{
1077 char *new = 0;
1078 size_t linelen = 0;
1079 long ll;
1080
1081 errno = 0;
1082 if (1>(ll = getline(&new, &linelen, fp))) {
1083 if (errno && errno != EINTR) perror_msg("getline");
1084 new = 0;
1085 } else if (new[ll-1] == '\n') new[--ll] = 0;
1086 if (len) *len = ll;
1087
1088 return new;
1089}
1090
1091time_t xmktime(struct tm *tm, int utc)
1092{
1093 char *old_tz = utc ? xtzset("UTC0") : 0;
1094 time_t result;
1095
1096 if ((result = mktime(tm)) < 0) error_exit("mktime");
1097 if (utc) {
1098 free(xtzset(old_tz));
1099 free(old_tz);
1100 }
1101 return result;
1102}
1103