Do not use non-compiler-builtin alloca(3)s..
[s-mailx.git] / aux.c
blobfcfb73d9ae98ac9435310a3ad668404f2e320b82
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "rcv.h"
41 #include "extern.h"
43 #include <sys/stat.h>
44 #include <utime.h>
45 #include <time.h>
46 #include <termios.h>
47 #include <ctype.h>
48 #ifdef HAVE_WCTYPE_H
49 # include <wctype.h>
50 #endif
51 #ifdef HAVE_WCWIDTH
52 # include <wchar.h>
53 #endif
54 #include <errno.h>
55 #include <sys/stat.h>
56 #include <unistd.h>
57 #include <time.h>
58 #include <dirent.h>
59 #include <fcntl.h>
60 #include <limits.h>
61 #include <stdarg.h>
63 #ifdef USE_MD5
64 # include "md5.h"
65 #endif
68 * Mail -- a mail program
70 * Auxiliary functions.
74 * Lazy vsprintf wrapper.
76 #ifndef HAVE_SNPRINTF
77 int
78 snprintf(char *str, size_t size, const char *format, ...)
80 va_list ap;
81 int ret;
83 va_start(ap, format);
84 ret = vsprintf(str, format, ap);
85 va_end(ap);
86 return ret;
88 #endif
91 * Announce a fatal error and die.
93 void
94 panic(const char *format, ...)
96 va_list ap;
98 va_start(ap, format);
99 fprintf(stderr, catgets(catd, CATSET, 1, "panic: "));
100 vfprintf(stderr, format, ap);
101 va_end(ap);
102 fprintf(stderr, catgets(catd, CATSET, 2, "\n"));
103 fflush(stderr);
104 abort();
107 void
108 holdint(void)
110 sigset_t set;
112 sigemptyset(&set);
113 sigaddset(&set, SIGINT);
114 sigprocmask(SIG_BLOCK, &set, NULL);
117 void
118 relseint(void)
120 sigset_t set;
122 sigemptyset(&set);
123 sigaddset(&set, SIGINT);
124 sigprocmask(SIG_UNBLOCK, &set, NULL);
128 * Touch the named message by setting its MTOUCH flag.
129 * Touched messages have the effect of not being sent
130 * back to the system mailbox on exit.
132 void
133 touch(struct message *mp)
136 mp->m_flag |= MTOUCH;
137 if ((mp->m_flag & MREAD) == 0)
138 mp->m_flag |= MREAD|MSTATUS;
142 * Test to see if the passed file name is a directory.
143 * Return true if it is.
145 int
146 is_dir(char *name)
148 struct stat sbuf;
150 if (stat(name, &sbuf) < 0)
151 return(0);
152 return(S_ISDIR(sbuf.st_mode));
156 * Count the number of arguments in the given string raw list.
158 int
159 argcount(char **argv)
161 char **ap;
163 for (ap = argv; *ap++ != NULL;)
165 return ap - argv - 1;
169 * Copy a string, lowercasing it as we go.
171 void
172 i_strcpy(char *dest, const char *src, int size)
174 char *max;
176 max=dest+size-1;
177 while (dest<=max) {
178 *dest++ = lowerconv(*src & 0377);
179 if (*src++ == '\0')
180 break;
184 char *
185 i_strdup(const char *src)
187 int sz;
188 char *dest;
190 sz = strlen(src) + 1;
191 dest = salloc(sz);
192 i_strcpy(dest, src, sz);
193 return dest;
197 * Convert a string to lowercase, in-place and with multibyte-aware.
199 void
200 makelow(char *cp)
202 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
203 if (mb_cur_max > 1) {
204 char *tp = cp;
205 wchar_t wc;
206 int len;
208 while (*cp) {
209 len = mbtowc(&wc, cp, mb_cur_max);
210 if (len < 0)
211 *tp++ = *cp++;
212 else {
213 wc = towlower(wc);
214 if (wctomb(tp, wc) == len)
215 tp += len, cp += len;
216 else
217 *tp++ = *cp++;
220 } else
221 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
224 *cp = tolower(*cp & 0377);
225 while (*cp++);
229 int
230 substr(const char *str, const char *sub)
232 const char *cp, *backup;
234 cp = sub;
235 backup = str;
236 while (*str && *cp) {
237 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
238 if (mb_cur_max > 1) {
239 wchar_t c, c2;
240 int sz;
242 if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0)
243 goto singlebyte;
244 cp += sz;
245 if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0)
246 goto singlebyte;
247 str += sz;
248 c = towupper(c);
249 c2 = towupper(c2);
250 if (c != c2) {
251 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
252 backup += sz;
253 str = backup;
254 } else
255 str = ++backup;
256 cp = sub;
258 } else
259 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
261 int c, c2;
263 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
264 singlebyte:
265 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
266 c = *cp++ & 0377;
267 if (islower(c))
268 c = toupper(c);
269 c2 = *str++ & 0377;
270 if (islower(c2))
271 c2 = toupper(c2);
272 if (c != c2) {
273 str = ++backup;
274 cp = sub;
278 return *cp == '\0';
281 char *
282 colalign(const char *cp, int col, int fill)
284 int n, sz;
285 char *nb, *np;
287 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
288 while (*cp) {
289 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
290 if (mb_cur_max > 1) {
291 wchar_t wc;
293 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
294 n = sz = 1;
295 } else {
296 if ((n = wcwidth(wc)) < 0)
297 n = 1;
299 } else
300 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
302 n = sz = 1;
304 if (n > col)
305 break;
306 col -= n;
307 if (sz == 1 && spacechar(*cp&0377)) {
308 *np++ = ' ';
309 cp++;
310 } else
311 while (sz--)
312 *np++ = *cp++;
314 if (fill)
315 while (col-- > 0)
316 *np++ = ' ';
317 *np = '\0';
318 return nb;
321 void
322 try_pager(FILE *fp)
324 long lines = 0;
325 int c;
326 char *cp;
328 fflush(fp);
329 rewind(fp);
330 while ((c = getc(fp)) != EOF)
331 if (c == '\n')
332 lines++;
333 rewind(fp);
334 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL &&
335 lines > (*cp ? atol(cp) : scrnheight))
336 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
337 else
338 while ((c = getc(fp)) != EOF)
339 putchar(c);
343 * The following code deals with input stacking to do source
344 * commands. All but the current file pointer are saved on
345 * the stack.
348 static int ssp; /* Top of file stack */
349 struct sstack {
350 FILE *s_file; /* File we were in. */
351 enum condition s_cond; /* Saved state of conditionals */
352 int s_loading; /* Loading .mailrc, etc. */
353 #define SSTACK 20
354 } sstack[SSTACK];
357 * Pushdown current input file and switch to a new one.
358 * Set the global flag "sourcing" so that others will realize
359 * that they are no longer reading from a tty (in all probability).
361 int
362 source(void *v)
364 char **arglist = v;
365 FILE *fi;
366 char *cp;
368 if ((cp = file_expand(*arglist)) == NULL)
369 return (1);
370 if ((fi = Fopen(cp, "r")) == NULL) {
371 perror(cp);
372 return (1);
374 if (ssp >= SSTACK - 1) {
375 fprintf(stderr, tr(3, "Too much \"sourcing\" going on.\n"));
376 Fclose(fi);
377 return (1);
379 sstack[ssp].s_file = input;
380 sstack[ssp].s_cond = cond;
381 sstack[ssp].s_loading = loading;
382 ssp++;
383 loading = 0;
384 cond = CANY;
385 input = fi;
386 sourcing++;
387 return(0);
391 * Pop the current input back to the previous level.
392 * Update the "sourcing" flag as appropriate.
394 int
395 unstack(void)
397 if (ssp <= 0) {
398 printf(catgets(catd, CATSET, 4,
399 "\"Source\" stack over-pop.\n"));
400 sourcing = 0;
401 return(1);
403 Fclose(input);
404 if (cond != CANY)
405 printf(catgets(catd, CATSET, 5, "Unmatched \"if\"\n"));
406 ssp--;
407 cond = sstack[ssp].s_cond;
408 loading = sstack[ssp].s_loading;
409 input = sstack[ssp].s_file;
410 if (ssp == 0)
411 sourcing = loading;
412 return(0);
416 * Touch the indicated file.
417 * This is nifty for the shell.
419 void
420 alter(char *name)
422 struct stat sb;
423 struct utimbuf utb;
425 if (stat(name, &sb))
426 return;
427 utb.actime = time((time_t *)0) + 1;
428 utb.modtime = sb.st_mtime;
429 utime(name, &utb);
433 * Examine the passed line buffer and
434 * return true if it is all blanks and tabs.
436 int
437 blankline(char *linebuf)
439 char *cp;
441 for (cp = linebuf; *cp; cp++)
442 if (!blankchar(*cp & 0377))
443 return(0);
444 return(1);
448 * Are any of the characters in the two strings the same?
450 int
451 anyof(char const*s1, char const*s2)
454 while (*s1)
455 if (strchr(s2, *s1++))
456 return 1;
457 return 0;
461 * Determine if as1 is a valid prefix of as2.
462 * Return true if yep.
464 int
465 is_prefix(const char *as1, const char *as2)
467 const char *s1, *s2;
469 s1 = as1;
470 s2 = as2;
471 while (*s1++ == *s2)
472 if (*s2++ == '\0')
473 return(1);
474 return(*--s1 == '\0');
477 char *
478 last_at_before_slash(const char *sp)
480 const char *cp;
482 for (cp = sp; *cp; cp++)
483 if (*cp == '/')
484 break;
485 while (cp > sp && *--cp != '@');
486 return *cp == '@' ? (char *)cp : NULL;
489 enum protocol
490 which_protocol(const char *name)
492 register const char *cp;
493 char *np;
494 size_t sz;
495 struct stat st;
496 enum protocol p;
498 if (name[0] == '%' && name[1] == ':')
499 name += 2;
500 for (cp = name; *cp && *cp != ':'; cp++)
501 if (!alnumchar(*cp&0377))
502 goto file;
503 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
504 if (strncmp(name, "pop3://", 7) == 0)
505 #ifdef USE_POP3
506 return PROTO_POP3;
507 #else
508 fprintf(stderr, catgets(catd, CATSET, 216,
509 "No POP3 support compiled in.\n"));
510 #endif
511 if (strncmp(name, "pop3s://", 8) == 0)
512 #ifdef USE_SSL
513 return PROTO_POP3;
514 #else /* !USE_SSL */
515 fprintf(stderr, catgets(catd, CATSET, 225,
516 "No SSL support compiled in.\n"));
517 #endif /* !USE_SSL */
518 if (strncmp(name, "imap://", 7) == 0)
519 #ifdef USE_IMAP
520 return PROTO_IMAP;
521 #else
522 fprintf(stderr, catgets(catd, CATSET, 269,
523 "No IMAP support compiled in.\n"));
524 #endif
525 if (strncmp(name, "imaps://", 8) == 0)
526 #ifdef USE_SSL
527 return PROTO_IMAP;
528 #else /* !USE_SSL */
529 fprintf(stderr, catgets(catd, CATSET, 225,
530 "No SSL support compiled in.\n"));
531 #endif /* !USE_SSL */
532 return PROTO_UNKNOWN;
533 } else {
534 file: p = PROTO_FILE;
535 np = ac_alloc((sz = strlen(name)) + 5);
536 strcpy(np, name);
537 if (stat(name, &st) == 0) {
538 if (S_ISDIR(st.st_mode)) {
539 strcpy(&np[sz], "/tmp");
540 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
541 strcpy(&np[sz], "/new");
542 if (stat(np, &st) == 0 &&
543 S_ISDIR(st.st_mode)) {
544 strcpy(&np[sz], "/cur");
545 if (stat(np, &st) == 0 &&
546 S_ISDIR(st.st_mode))
547 p = PROTO_MAILDIR;
551 } else {
552 strcpy(&np[sz], ".gz");
553 if (stat(np, &st) < 0) {
554 strcpy(&np[sz], ".bz2");
555 if (stat(np, &st) < 0) {
556 if ((cp = value("newfolders")) != 0 &&
557 strcmp(cp, "maildir") == 0)
558 p = PROTO_MAILDIR;
562 ac_free(np);
563 return p;
567 const char *
568 protfile(const char *xcp)
570 const char *cp = xcp;
571 int state = 0;
573 while (*cp) {
574 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
575 cp += 3;
576 state = 1;
578 if (cp[0] == '/' && state == 1)
579 return &cp[1];
580 if (cp[0] == '/')
581 return xcp;
582 cp++;
584 return cp;
587 char *
588 protbase(const char *cp)
590 char *n = salloc(strlen(cp) + 1);
591 char *np = n;
593 while (*cp) {
594 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
595 *np++ = *cp++;
596 *np++ = *cp++;
597 *np++ = *cp++;
598 } else if (cp[0] == '/')
599 break;
600 else
601 *np++ = *cp++;
603 *np = '\0';
604 return n;
607 int
608 disconnected(const char *file)
610 char *cp, *cq, *vp;
611 int vs, r;
613 if (value("disconnected"))
614 return 1;
615 cp = protbase(file);
616 if (strncmp(cp, "imap://", 7) == 0)
617 cp += 7;
618 else if (strncmp(cp, "imaps://", 8) == 0)
619 cp += 8;
620 else
621 return 0;
622 if ((cq = strchr(cp, ':')) != NULL)
623 *cq = '\0';
624 vp = ac_alloc(vs = strlen(cp) + 14);
625 snprintf(vp, vs, "disconnected-%s", cp);
626 r = value(vp) != NULL;
627 ac_free(vp);
628 return r;
631 unsigned
632 pjw(const char *cp)
634 unsigned h = 0, g;
636 cp--;
637 while (*++cp) {
638 h = (h << 4 & 0xffffffff) + (*cp&0377);
639 if ((g = h & 0xf0000000) != 0) {
640 h = h ^ g >> 24;
641 h = h ^ g;
644 return h;
647 long
648 nextprime(long n)
650 const long primes[] = {
651 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
652 131071, 262139, 524287, 1048573, 2097143, 4194301,
653 8388593, 16777213, 33554393, 67108859, 134217689,
654 268435399, 536870909, 1073741789, 2147483647
656 long mprime = 7;
657 size_t i;
659 for (i = 0; i < sizeof primes / sizeof *primes; i++)
660 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
661 n < 262144 ? n*2 : n))
662 break;
663 if (i == sizeof primes / sizeof *primes)
664 mprime = n; /* not so prime, but better than failure */
665 return mprime;
668 #define Hexchar(n) ((n)>9 ? (n)-10+'A' : (n)+'0')
669 #define hexchar(n) ((n)>9 ? (n)-10+'a' : (n)+'0')
671 char *
672 strenc(const char *cp)
674 char *n, *np;
676 np = n = salloc(strlen(cp) * 3 + 1);
677 while (*cp) {
678 if (alnumchar(*cp&0377) || *cp == '_' || *cp == '@' ||
679 (np > n && (*cp == '.' || *cp == '-' ||
680 *cp == ':')))
681 *np++ = *cp;
682 else {
683 *np++ = '%';
684 *np++ = Hexchar((*cp&0xf0) >> 4);
685 *np++ = Hexchar(*cp&0x0f);
687 cp++;
689 *np = '\0';
690 return n;
693 char *
694 strdec(const char *cp)
696 char *n, *np;
698 np = n = salloc(strlen(cp) + 1);
699 while (*cp) {
700 if (cp[0] == '%' && cp[1] && cp[2]) {
701 *np = (int)(cp[1]>'9'?cp[1]-'A'+10:cp[1]-'0') << 4;
702 *np++ |= cp[2]>'9'?cp[2]-'A'+10:cp[2]-'0';
703 cp += 3;
704 } else
705 *np++ = *cp++;
707 *np = '\0';
708 return n;
711 #ifdef USE_MD5
712 char *
713 md5tohex(const void *vp)
715 char *hex;
716 const char *cp = vp;
717 int i;
719 hex = salloc(33);
720 for (i = 0; i < 16; i++) {
721 hex[2*i] = hexchar((cp[i]&0xf0) >> 4);
722 hex[2*i+1] = hexchar(cp[i]&0x0f);
724 hex[32] = '\0';
725 return hex;
728 char *
729 cram_md5_string(const char *user, const char *pass, const char *b64)
731 struct str in, out;
732 char digest[16], *cp, *sp, *rp, *xp;
733 int ss, rs;
735 in.s = (char *)b64;
736 in.l = strlen(in.s);
737 mime_fromb64(&in, &out, 0);
738 hmac_md5((unsigned char *)out.s, out.l,
739 (unsigned char *)pass, strlen(pass),
740 digest);
741 free(out.s);
742 xp = md5tohex(digest);
743 sp = ac_alloc(ss = strlen(user) + strlen(xp) + 2);
744 snprintf(sp, ss, "%s %s", user, xp);
745 cp = strtob64(sp);
746 ac_free(sp);
747 rp = salloc(rs = strlen(cp) + 3);
748 snprintf(rp, rs, "%s\r\n", cp);
749 free(cp);
750 return rp;
752 #endif /* USE_MD5 */
754 char *
755 getuser(void)
757 char *line = NULL, *user;
758 size_t linesize = 0;
760 if (is_a_tty[0]) {
761 fputs("User: ", stdout);
762 fflush(stdout);
764 if (readline(stdin, &line, &linesize) == 0) {
765 if (line)
766 free(line);
767 return NULL;
769 user = savestr(line);
770 free(line);
771 return user;
774 char *
775 getpassword(struct termios *otio, int *reset_tio, const char *query)
777 struct termios tio;
778 char *line = NULL, *pass;
779 size_t linesize = 0;
780 int i;
782 if (is_a_tty[0]) {
783 fputs(query ? query : "Password:", stdout);
784 fflush(stdout);
785 tcgetattr(0, &tio);
786 *otio = tio;
787 tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
788 *reset_tio = 1;
789 tcsetattr(0, TCSAFLUSH, &tio);
791 i = readline(stdin, &line, &linesize);
792 if (is_a_tty[0]) {
793 fputc('\n', stdout);
794 tcsetattr(0, TCSADRAIN, otio);
796 *reset_tio = 0;
797 if (i < 0) {
798 if (line)
799 free(line);
800 return NULL;
802 pass = savestr(line);
803 free(line);
804 return pass;
807 void
808 transflags(struct message *omessage, long omsgCount, int transparent)
810 struct message *omp, *nmp, *newdot, *newprevdot;
811 int hf;
813 omp = omessage;
814 nmp = message;
815 newdot = message;
816 newprevdot = NULL;
817 while (omp < &omessage[omsgCount] &&
818 nmp < &message[msgCount]) {
819 if (dot && nmp->m_uid == dot->m_uid)
820 newdot = nmp;
821 if (prevdot && nmp->m_uid == prevdot->m_uid)
822 newprevdot = nmp;
823 if (omp->m_uid == nmp->m_uid) {
824 hf = nmp->m_flag & MHIDDEN;
825 if (transparent && mb.mb_type == MB_IMAP)
826 omp->m_flag &= ~MHIDDEN;
827 *nmp++ = *omp++;
828 if (transparent && mb.mb_type == MB_CACHE)
829 nmp[-1].m_flag |= hf;
830 } else if (omp->m_uid < nmp->m_uid)
831 omp++;
832 else
833 nmp++;
835 dot = newdot;
836 setdot(newdot);
837 prevdot = newprevdot;
838 free(omessage);
841 char *
842 getrandstring(size_t length)
844 static unsigned char nodedigest[16];
845 static pid_t pid;
846 int fd = -1;
847 char *data;
848 char *cp, *rp;
849 size_t i;
850 #ifdef USE_MD5
851 MD5_CTX ctx;
852 #else
853 size_t j;
854 #endif
856 data = salloc(length);
857 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
858 length != (size_t)read(fd, data, length)) {
859 if (pid == 0) {
860 pid = getpid();
861 srand(pid);
862 cp = nodename(0);
863 #ifdef USE_MD5
864 MD5Init(&ctx);
865 MD5Update(&ctx, (unsigned char *)cp, strlen(cp));
866 MD5Final(nodedigest, &ctx);
867 #else
868 /* In that case it's only used for boundaries and
869 * Message-Id:s so that srand(3) should suffice */
870 j = strlen(cp) + 1;
871 for (i = 0; i < sizeof(nodedigest); ++i)
872 nodedigest[i] = (unsigned char)(
873 cp[i % j] ^ rand());
874 #endif
876 for (i = 0; i < length; i++)
877 data[i] = (char)
878 ((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
879 nodedigest[i % sizeof nodedigest]);
881 if (fd > 0)
882 close(fd);
883 cp = memtob64(data, length);
884 rp = salloc(length+1);
885 strncpy(rp, cp, length)[length] = '\0';
886 free(cp);
887 return rp;
890 void
891 out_of_memory(void)
893 panic("no memory");
896 void *
897 smalloc(size_t s)
899 void *p;
901 if (s == 0)
902 s = 1;
903 if ((p = malloc(s)) == NULL)
904 out_of_memory();
905 return p;
908 void *
909 srealloc(void *v, size_t s)
911 void *r;
913 if (s == 0)
914 s = 1;
915 if (v == NULL)
916 return smalloc(s);
917 if ((r = realloc(v, s)) == NULL)
918 out_of_memory();
919 return r;
922 void *
923 scalloc(size_t nmemb, size_t size)
925 void *vp;
927 if (size == 0)
928 size = 1;
929 if ((vp = calloc(nmemb, size)) == NULL)
930 out_of_memory();
931 return vp;
934 char *
935 sstpcpy(char *dst, const char *src)
937 while ((*dst = *src++) != '\0')
938 dst++;
939 return dst;
942 char *
943 sstrdup(const char *cp)
945 char *dp;
947 if (cp) {
948 dp = smalloc(strlen(cp) + 1);
949 strcpy(dp, cp);
950 return dp;
951 } else
952 return NULL;
955 enum okay
956 makedir(const char *name)
958 int e;
959 struct stat st;
961 if (mkdir(name, 0700) < 0) {
962 e = errno;
963 if ((e == EEXIST || e == ENOSYS) &&
964 stat(name, &st) == 0 &&
965 (st.st_mode&S_IFMT) == S_IFDIR)
966 return OKAY;
967 return STOP;
969 return OKAY;
972 #ifdef HAVE_FCHDIR
973 enum okay
974 cwget(struct cw *cw)
976 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
977 return STOP;
978 if (fchdir(cw->cw_fd) < 0) {
979 close(cw->cw_fd);
980 return STOP;
982 return OKAY;
985 enum okay
986 cwret(struct cw *cw)
988 if (fchdir(cw->cw_fd) < 0)
989 return STOP;
990 return OKAY;
993 void
994 cwrelse(struct cw *cw)
996 close(cw->cw_fd);
998 #else /* !HAVE_FCHDIR */
999 enum okay
1000 cwget(struct cw *cw)
1002 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
1003 return STOP;
1004 return OKAY;
1007 enum okay
1008 cwret(struct cw *cw)
1010 if (chdir(cw->cw_wd) < 0)
1011 return STOP;
1012 return OKAY;
1015 /*ARGSUSED*/
1016 void
1017 cwrelse(struct cw *cw)
1020 #endif /* !HAVE_FCHDIR */
1022 void
1023 makeprint(struct str *in, struct str *out)
1025 static int print_all_chars = -1;
1026 char *inp, *outp;
1027 size_t msz, dist;
1029 out->s = smalloc(msz = in->l + 1);
1030 if (print_all_chars == -1)
1031 print_all_chars = value("print-all-chars") != NULL;
1032 if (print_all_chars) {
1033 memcpy(out->s, in->s, in->l);
1034 out->l = in->l;
1035 out->s[out->l] = '\0';
1036 return;
1038 inp = in->s;
1039 outp = out->s;
1040 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
1041 if (mb_cur_max > 1) {
1042 wchar_t wc;
1043 char mb[MB_LEN_MAX+1];
1044 int i, n;
1045 out->l = 0;
1046 while (inp < &in->s[in->l]) {
1047 if (*inp & 0200)
1048 n = mbtowc(&wc, inp, &in->s[in->l] - inp);
1049 else {
1050 wc = *inp;
1051 n = 1;
1053 if (n < 0) {
1054 mbtowc(&wc, NULL, mb_cur_max);
1055 wc = utf8 ? 0xFFFD : '?';
1056 n = 1;
1057 } else if (n == 0)
1058 n = 1;
1059 inp += n;
1060 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
1061 wc != '\b' && wc != '\t') {
1062 if ((wc & ~(wchar_t)037) == 0)
1063 wc = utf8 ? 0x2400 | wc : '?';
1064 else if (wc == 0177)
1065 wc = utf8 ? 0x2421 : '?';
1066 else
1067 wc = utf8 ? 0x2426 : '?';
1069 if ((n = wctomb(mb, wc)) <= 0)
1070 continue;
1071 out->l += n;
1072 if (out->l >= msz - 1) {
1073 dist = outp - out->s;
1074 out->s = srealloc(out->s, msz += 32);
1075 outp = &out->s[dist];
1077 for (i = 0; i < n; i++)
1078 *outp++ = mb[i];
1080 } else
1081 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
1083 int c;
1084 while (inp < &in->s[in->l]) {
1085 c = *inp++ & 0377;
1086 if (!isprint(c) && c != '\n' && c != '\r' &&
1087 c != '\b' && c != '\t')
1088 c = '?';
1089 *outp++ = c;
1091 out->l = in->l;
1093 out->s[out->l] = '\0';
1096 char *
1097 prstr(const char *s)
1099 struct str in, out;
1100 char *rp;
1102 in.s = (char *)s;
1103 in.l = strlen(s);
1104 makeprint(&in, &out);
1105 rp = salloc(out.l + 1);
1106 memcpy(rp, out.s, out.l);
1107 rp[out.l] = '\0';
1108 free(out.s);
1109 return rp;
1113 prout(const char *s, size_t sz, FILE *fp)
1115 struct str in, out;
1116 int n;
1118 in.s = (char *)s;
1119 in.l = sz;
1120 makeprint(&in, &out);
1121 n = fwrite(out.s, 1, out.l, fp);
1122 free(out.s);
1123 return n;
1127 * Print out a Unicode character or a substitute for it.
1130 putuc(int u, int c, FILE *fp)
1132 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
1133 if (utf8 && u & ~(wchar_t)0177) {
1134 char mb[MB_LEN_MAX];
1135 int i, n, r = 0;
1136 if ((n = wctomb(mb, u)) > 0) {
1137 for (i = 0; i < n; i++)
1138 r += putc(mb[i] & 0377, fp) != EOF;
1139 return r;
1140 } else if (n == 0)
1141 return putc('\0', fp) != EOF;
1142 else
1143 return 0;
1144 } else
1145 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
1146 return putc(c, fp) != EOF;
1150 * Locale-independent character class functions.
1152 int
1153 asccasecmp(const char *s1, const char *s2)
1155 register int cmp;
1158 if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0)
1159 return cmp;
1160 while (*s1++ != '\0' && *s2++ != '\0');
1161 return 0;
1165 ascncasecmp(const char *s1, const char *s2, size_t sz)
1167 register int cmp;
1168 size_t i = 1;
1170 if (sz == 0)
1171 return 0;
1173 if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0)
1174 return cmp;
1175 while (i++ < sz && *s1++ != '\0' && *s2++ != '\0');
1176 return 0;
1179 char *
1180 asccasestr(const char *haystack, const char *xneedle)
1182 char *needle, *NEEDLE;
1183 int i, sz;
1185 sz = strlen(xneedle);
1186 if (sz == 0)
1187 return (char *)haystack;
1188 needle = ac_alloc(sz);
1189 NEEDLE = ac_alloc(sz);
1190 for (i = 0; i < sz; i++) {
1191 needle[i] = lowerconv(xneedle[i]&0377);
1192 NEEDLE[i] = upperconv(xneedle[i]&0377);
1194 while (*haystack) {
1195 if (*haystack == *needle || *haystack == *NEEDLE) {
1196 for (i = 1; i < sz; i++)
1197 if (haystack[i] != needle[i] &&
1198 haystack[i] != NEEDLE[i])
1199 break;
1200 if (i == sz)
1201 return (char *)haystack;
1203 haystack++;
1205 return NULL;
1208 const unsigned char class_char[] = {
1209 /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */
1210 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1211 /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */
1212 C_CNTRL,C_BLANK,C_WHITE,C_SPACE,C_SPACE,C_SPACE,C_CNTRL,C_CNTRL,
1213 /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */
1214 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1215 /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */
1216 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1217 /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */
1218 C_BLANK,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1219 /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */
1220 C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1221 /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */
1222 C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,
1223 /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */
1224 C_DIGIT,C_DIGIT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1225 /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */
1226 C_PUNCT,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1227 /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */
1228 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1229 /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */
1230 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1231 /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */
1232 C_UPPER,C_UPPER,C_UPPER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1233 /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */
1234 C_PUNCT,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1235 /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */
1236 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1237 /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */
1238 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1239 /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */
1240 C_LOWER,C_LOWER,C_LOWER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_CNTRL