1 /*
2  *  utils.c  --  miscellaneous utility functions
3  *
4  *  Copyright (C) 1998,2002 by Massimiliano Ghilardi
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <ctype.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <time.h>
21 
22 #include "defines.h"
23 #include "main.h"
24 #include "utils.h"
25 #include "list.h"
26 #include "cmd.h"
27 #include "cmd2.h"
28 #include "beam.h"
29 #include "tty.h"
30 #include "edit.h"
31 #include "eval.h"
32 #include "log.h"
33 
34 #define SAVEFILEVER 6
35 
36 static char can_suspend = 0; /* 1 if shell has job control */
37 
38 /*
39  * GH: memory-"safe" strdup
40  */
__P1(char *,s)41 char *my_strdup __P1 (char *,s)
42 {
43     if (s) {
44 	s = strdup(s);
45 	if (!s)
46 	    error = NO_MEM_ERROR;
47     }
48     return s;
49 }
50 
51 /*
52  * non-braindamaged strncpy:
53  * copy up to len chars from src to dst, then add a final \0
54  * (i.e. dst[len] = '\0')
55  */
__P3(char *,dst,char *,src,int,len)56 char *my_strncpy __P3 (char *,dst, char *,src, int,len)
57 {
58     int slen = strlen(src);
59     if (slen < len)
60 	return strcpy(dst, src);
61     memcpy(dst, src, len);
62     dst[len] = '\0';
63     return dst;
64 }
65 
66 /*
67  * Determine the printed length of a string. This can be less than the string
68  * length since it might contain escape sequences. Treated as such are
69  * "<esc> [ <non-letters> <letter>", "<esc> <non-[>", "<control-char>".
70  * This is not entirely universal but covers the most common cases (i.e. ansi)
71  */
__P1(char *,s)72 int printstrlen __P1 (char *,s)
73 {
74     int l;
75     enum { NORM, ESCAPE, BRACKET } state = NORM;
76     for (l = 0; *s; s++) {
77         switch(state) {
78 	 case NORM:
79             if (*s == '\033') {
80                 state = ESCAPE;
81             } else if ((*s & 0x80) || *s >= ' ') {
82                 l++;
83             } else if (*s == '\r')
84 		l = (l / cols) * cols;
85             break;
86 
87 	 case ESCAPE:
88             state = (*s == '[') ? BRACKET : NORM;
89             break;
90 
91 	 case BRACKET:
92             if (isalpha(*s))
93 	      state = NORM;
94             break;
95         }
96     }
97     return l;
98 }
99 
100 /*
101  * return pointer to next non-blank char
102  */
__P1(char *,p)103 char *skipspace __P1 (char *,p)
104 {
105     while (*p == ' ' || *p == '\t') p++;
106     return p;
107 }
108 
109 /*
110  * find the first valid (non-escaped)
111  * char in a string
112  */
__P2(char *,p,char,ch)113 char *first_valid __P2 (char *,p, char,ch)
114 {
115     if (*p && *p != ch) {
116 	p++;
117 	if (ch == ESC2 || ch == ESC) while (*p && *p != ch)
118 	    p++;
119 	else while (*p && ((*p != ch) || p[-1] == ESC))
120 	    p++;
121     }
122     return p;
123 }
124 
125 /*
126  * find the first regular (non-escaped, non in "" () or {} )
127  * char in a string
128  */
__P2(char *,p,char,c)129 char *first_regular __P2 (char *,p, char,c)
130 {
131     int escaped, quotes=0, paren=0, braces=0;
132 
133     while (*p && ((*p != c) || quotes || paren>0 || braces>0)) {
134 	escaped = 0;
135 	if (*p == ESC) {
136 	    while (*p == ESC)
137 		p++;
138 	    escaped = 1;
139 	}
140 	if (*p == ESC2) {
141 	    while (*p == ESC2)
142 		p++;
143 	    escaped = 0;
144 	}
145 	if (!*p)
146 	    break;
147 	if (!escaped) {
148 	    if (quotes) {
149 		if (*p == '\"')
150 		    quotes = 0;
151 	    }
152 	    else if (*p == '\"')
153 		quotes = 1;
154 	    else if (*p == ')')
155 		paren--;
156 	    else if (*p == '(')
157 		paren++;
158 	    else if (*p == '}')
159 		braces--;
160 	    else if (*p == '{')
161 		braces++;
162 	}
163 	p++;
164     }
165     return p;
166 }
167 
168 /*
169  * remove escapes (backslashes) from a string
170  */
__P2(char *,p,int,lenp)171 int memunescape __P2 (char *,p, int,lenp)
172 {
173     char *start, *s;
174     enum { NORM, ESCSINGLE, ESCAPE } state = NORM;
175 
176     if (!p || !*p)
177 	return 0;
178 
179     start = s = p;
180 
181     while (lenp) switch (state) {
182       case NORM:
183 	if (*s != ESC) {
184 	    *p++ = *s++, lenp--;
185 	    break;
186 	}
187 	state = ESCSINGLE, s++;
188 	if (!--lenp)
189 	    break;
190 	/* fallthrough */
191       case ESCSINGLE:
192       case ESCAPE:
193 	if (*s == ESC)
194 	    state = ESCAPE, *p++ = *s++, lenp--;
195 	else if (*s == ESC2)
196 	    state = NORM, *p++ = ESC, s++, lenp--;
197 	else {
198 	    if (state == ESCSINGLE && lenp >= 3 &&
199 		ISODIGIT(s[0]) && ISODIGIT(s[1]) && ISODIGIT(s[2])) {
200 
201 		*p++ = ((s[0]-'0') << 6) | ((s[1]-'0') << 3) | (s[2]-'0');
202 		s += 3, lenp -= 3;
203 	    } else
204 		*p++ = *s++, lenp--;
205 	    state = NORM;
206 	}
207 	break;
208       default:
209 	break;
210     }
211     *p = '\0';
212     return (int)(p - start);
213 }
214 
__P1(char *,s)215 void unescape __P1 (char *,s)
216 {
217     (void)memunescape(s, strlen(s));
218 }
219 
__P1(ptr,p)220 void ptrunescape __P1 (ptr,p)
221 {
222     if (!p)
223 	return;
224     p->len = memunescape(ptrdata(p), ptrlen(p));
225 }
226 
227 /*
228  * add escapes (backslashes) to src
229  */
__P4(ptr,dst,char *,src,int,srclen,int,append)230 ptr ptrmescape __P4 (ptr,dst, char *,src, int,srclen, int,append)
231 {
232     int len;
233     char *p;
234     enum { NORM, ESCAPE } state;
235 
236     if (!src || srclen <= 0) {
237 	if (!append)
238 	    ptrzero(dst);
239 	return dst;
240     }
241 
242     if (dst && append)
243 	len = ptrlen(dst);
244     else
245 	len = 0;
246 
247     dst = ptrsetlen(dst, len + srclen*4); /* worst case */
248     if (MEM_ERROR) return dst;
249 
250     dst->len = len;
251     p = ptrdata(dst) + len;
252 
253     while (srclen) {
254 	state = NORM;
255 	if (*src == ESC) {
256 	    while (srclen && *src == ESC)
257 		dst->len++, *p++ = *src++, srclen--;
258 
259 	    if (!srclen || *src == ESC2)
260 		dst->len++, *p++ = ESC2;
261 	    else
262 		state = ESCAPE;
263 	}
264 	if (!srclen) break;
265 
266 	if (*src < ' ' || *src > '~') {
267 	    sprintf(p, "\\%03o", (int)(byte)*src++);
268 	    len = strlen(p);
269 	    dst->len += len, p += len, srclen--;
270 	} else {
271 	    if (state == ESCAPE || strchr(SPECIAL_CHARS, *src))
272 		dst->len++, *p++ = ESC;
273 
274 	    dst->len++, *p++ = *src++, srclen--;
275 	}
276     }
277     *p = '\0';
278     return dst;
279 }
280 
__P3(ptr,dst,ptr,src,int,append)281 ptr ptrescape __P3 (ptr,dst, ptr,src, int,append)
282 {
283     if (!src) {
284 	if (!append)
285 	    ptrzero(dst);
286 	return dst;
287     }
288     return ptrmescape(dst, ptrdata(src), ptrlen(src), append);
289 }
290 
291 /*
292  * add escapes to protect special characters from being escaped.
293  */
__P2(char *,dst,char *,src)294 void escape_specials __P2 (char *,dst, char *,src)
295 {
296     enum { NORM, ESCAPE } state;
297     while (*src) {
298 	state = NORM;
299 	if (*src == ESC) {
300 	    while (*src == ESC)
301 		*dst++ = *src++;
302 
303 	    if (!*src || *src == ESC2)
304 		*dst++ = ESC2;
305 	    else
306 		state = ESCAPE;
307 	}
308 	if (!*src) break;
309 
310 	if (*src < ' ' || *src > '~') {
311 	    sprintf(dst, "\\%03o", (int)(byte)*src++);
312 	    dst += strlen(dst);
313 	} else {
314 	    if (state == ESCAPE || strchr(SPECIAL_CHARS, *src))
315 		*dst++ = ESC;
316 
317 	    *dst++ = *src++;
318 	}
319     }
320     *dst = '\0';
321 }
322 
323 /*
324  * match mark containing & and $ and return 1 if matched, 0 if not
325  * if 1, start and end contain the match bounds
326  * if 0, start and end are undefined on return
327  */
__P2(marknode *,mp,char *,src)328 static int match_mark __P2 (marknode *,mp, char *,src)
329 {
330     char *pat = mp->pattern;
331     char *npat=0, *npat2=0, *nsrc=0, *prm=0, *endprm=0, *tmp, c;
332     static char mpat[BUFSIZE];
333     int mbeg = mp->mbeg, mword = 0, p;
334 
335     /* shortcut for #marks without wildcards */
336     if (!mp->wild) {
337 	if ((nsrc = strstr(src, pat))) {
338 	    mp->start = nsrc;
339 	    mp->end = nsrc + strlen(pat);
340 	    return 1;
341 	}
342 	return 0;
343     }
344 
345     mp->start = NULL;
346 
347     if (ISMARKWILDCARD(*pat))
348 	mbeg = - mbeg - 1;  /* pattern starts with '&' or '$' */
349 
350     while (pat && (c = *pat)) {
351 	if (ISMARKWILDCARD(c)) {
352 	    /* & matches a string */
353 	    /* $ matches a single word */
354 	    prm = src;
355 	    if (c == '$')
356 		mword = 1;
357 	    else if (!mp->start)
358 		mp->start = src;
359 	    ++pat;
360 	}
361 
362 	npat  = first_valid(pat, '&');
363 	npat2 = first_valid(pat, '$');
364 	if (npat2 < npat) npat = npat2;
365 	if (!*npat) npat = 0;
366 
367 	if (npat) {
368 	    my_strncpy(mpat, pat, npat-pat);
369 	    /* mpat[npat - pat] = 0; */
370 	} else
371 	    strcpy(mpat, pat);
372 
373 	if (*mpat) {
374 	    nsrc = strstr(src, mpat);
375 	    if (!nsrc)
376 		return 0;
377 	    if (mbeg > 0) {
378 		if (nsrc != src)
379 		    return 0;
380 		mbeg = 0;  /* reset mbeg to stop further start match */
381 	    }
382 	    endprm = nsrc;
383 	    if (!mp->start) {
384 		if (prm)
385 		    mp->start = src;
386 		else
387 		    mp->start = nsrc;
388 	    }
389 	    mp->end = nsrc + strlen(mpat);
390 	} else if (prm)           /* end of pattern space */
391 	    mp->end = endprm = prm + strlen(prm);
392 	else
393 	    mp->end = src;
394 
395 
396 	/* post-processing of param */
397 	if (mword) {
398 	    if (prm) {
399 		if (mbeg == -1) {
400 		    if (!*pat) {
401 			/* the pattern is "$", take first word */
402 			p = - 1;
403 		    } else {
404 			/* unanchored '$' start, take last word */
405 			tmp = memrchrs(prm, endprm - prm - 1, DELIM, DELIM_LEN);
406 			if (tmp)
407 			    p = tmp - prm;
408 			else
409 			    p = -1;
410 		    }
411 		    mp->start = prm += p + 1;
412 		} else if (!*pat) {
413 		    /* '$' at end of pattern, take first word */
414 		    if ((tmp = memchrs(prm, strlen(prm), DELIM, DELIM_LEN)))
415 			mp->end = endprm = tmp;
416 		} else {
417 			/* match only if param is single-worded */
418 		    if (memchrs(prm, endprm - prm, DELIM, DELIM_LEN))
419 			return 0;
420 		}
421 	    } else
422 		return 0;
423 	}
424 	if (prm)
425 	    mbeg = mword = 0;  /* reset match flags */
426 	src = nsrc + strlen(mpat);
427 	pat = npat;
428     }
429     return 1;
430 }
431 
432 /*
433  * add marks to line. write in dst.
434  */
__P3(ptr,dst,char *,line,int,len)435 ptr ptrmaddmarks __P3 (ptr,dst, char *,line, int,len)
436 {
437     marknode *mp, *mfirst;
438     char begin[CAPLEN], end[CAPLEN], *lineend = line + len;
439     int start = 1, matchlen, matched = 0;
440 
441     ptrzero(dst);
442 
443     if (!line || len <= 0)
444 	return dst;
445 
446     for (mp = markers; mp; mp = mp->next)
447 	mp->start = NULL;
448 
449     do {
450 	mfirst = NULL;
451 	for (mp = markers; mp; mp = mp->next) {
452 	    if (mp->start && mp->start >= line)
453 		matched = 1;
454 	    else {
455 		if (!(matched = (!mp->mbeg || start) && match_mark(mp, line)))
456 		    mp->start = lineend;
457 	    }
458 	    if (matched && mp->start < lineend &&
459 		(!mfirst || mp->start < mfirst->start))
460 		mfirst = mp;
461 	}
462 
463 	if (mfirst) {
464 	    start = 0;
465 	    attr_string(mfirst->attrcode, begin, end);
466 
467 	    dst = ptrmcat(dst, line, matchlen = mfirst->start - line);
468 	    if (MEM_ERROR) break;
469 	    line += matchlen;
470 	    len -= matchlen;
471 
472 	    dst = ptrmcat(dst, begin, strlen(begin));
473 	    if (MEM_ERROR) break;
474 
475 	    dst = ptrmcat(dst, line, matchlen = mfirst->end - mfirst->start);
476 	    if (MEM_ERROR) break;
477 	    line += matchlen;
478 	    len -= matchlen;
479 
480 	    dst = ptrmcat(dst, end, strlen(end));
481 	    if (MEM_ERROR) break;
482 	}
483     } while (mfirst);
484 
485     if (!MEM_ERROR)
486 	dst = ptrmcat(dst, line, len);
487 
488     return dst;
489 }
490 
__P2(ptr,dst,ptr,line)491 ptr ptraddmarks __P2 (ptr,dst, ptr,line)
492 {
493     if (line)
494 	return ptrmaddmarks(dst, ptrdata(line), ptrlen(line));
495     ptrzero(dst);
496     return dst;
497 }
498 
499 /*
500  * write string to tty, word wrapping to next line if needed.
501  * don't print a final \n
502  */
__P1(char *,s)503 static void wrap_print __P1 (char *,s)
504 {
505     /* ls = last space in *s, lp = last space in *p */
506     char *ls, *lp, *p, c, follow = 1;
507     char buf[BUFSIZE]; /* ASSERT(cols<BUFSIZE) */
508 
509     /* l = left, m = current offset */
510     int l, m;
511     enum { NORM, ESCAPE, BRACKET } state;
512 #ifdef BUG_ANSI
513     int ansibug = 0;
514 #endif
515 
516     l = printstrlen(s);
517 #ifdef BUG_ANSI
518     if (l > cols_1 && l < (int)strlen(s))
519         ansibug = 1;
520 #endif
521 
522     while (l >= cols_1 - col0 && *s) {
523         p = buf; m = 0; state = NORM;
524         lp = ls = NULL;
525 
526         /* this scans over the remaining part of the line adding stuff to
527          * print to the buffer and tallying the length of displayed
528          * characters */
529         while (m < cols_1 - col0 && *s && *s != '\n') {
530             *p++ = c = *s++;
531             switch (state) {
532                 case NORM:
533                     if (c == ' ') {
534                         ls = s;
535                         lp = p;
536                     }
537 
538                     if (c == '\033') {
539                         state = ESCAPE;
540                     }else if ((c & 0x80) || (c >= ' ' && c <= '~')) {
541                         /* if char is hi (128+) or printable */
542                         m++, l--;
543                     }else if (c == '\r') {
544                         ls = lp = NULL;
545                         m = 0;
546                     }
547 
548                     break;
549 
550                 case ESCAPE:
551                     state = (c == '[') ? BRACKET : NORM;
552                     break;
553 
554                 case BRACKET:
555                     if (isalpha(c))
556                         state = NORM;
557                     break;
558             }
559         }
560 
561         /* Adjust offsets and stuff to last space */
562         if( ls != NULL && ls != s ) {
563             /* move s back to the last space */
564             s = ls;
565             /* null term buf[] at last space */
566             lp[ 0 ] = 0;
567         }
568 
569         follow = *s;
570 
571         *p = '\0';
572         tty_printf("%s%s", buf, follow ? "\n" : "");
573         if (follow)
574             col0 = 0;
575     }
576 
577 #ifdef BUG_ANSI
578     if (ansibug)
579         tty_printf("%s%s%s", follow ? s : "" ,
580                 tty_modenorm, tty_clreoln);
581     else
582 #endif
583         if (follow)
584             tty_puts(s);
585 }
586 
587 /*
588  * add marks to line and print.
589  * if newline, also print a final \n
590  */
__P2(char *,line,char,newline)591 void smart_print __P2 (char *,line, char,newline)
592 {
593     static ptr ptrbuf = NULL;
594     static char *buf;
595 
596     do {
597 	if (!ptrbuf) {
598 	    ptrbuf = ptrnew(PARAMLEN);
599 	    if (MEM_ERROR) break;
600 	}
601 	ptrbuf = ptrmaddmarks(ptrbuf, line, strlen(line));
602 	if (MEM_ERROR || !ptrbuf) break;
603 
604 	buf = ptrdata(ptrbuf);
605 
606 	if (opt_wrap)
607 	    wrap_print(buf);
608 	else {
609 #ifdef BUG_ANSI
610 	    int l;
611 	    l = printstrlen(buf);
612 	    if (l > cols_1 && l < ptrlen(ptrbuf))
613 		tty_printf("%s%s%s", buf, tty_modenorm, tty_clreoln);
614 	    else
615 #endif
616 		tty_printf("%s", buf);
617 	}
618     } while(0);
619 
620     if (MEM_ERROR)
621 	print_error(error);
622     else if (newline)
623 	col0 = 0, tty_putc('\n');
624 }
625 
626 /*
627  * copy first word of src into dst, and return pointer to second word of src
628  */
__P3(char *,dst,int,dstlen,char *,src)629 char *split_first_word __P3 (char *,dst, int,dstlen, char *,src)
630 {
631     char *tmp;
632     int len;
633 
634     src = skipspace(src);
635     if (!*src) {
636 	*dst='\0';
637 	return src;
638     }
639     len = strlen(src);
640 
641     tmp = memchrs(src, len, DELIM, DELIM_LEN);
642     if (tmp) {
643 	if (dstlen > tmp-src+1) dstlen = tmp-src+1;
644 	my_strncpy(dst, src, dstlen-1);
645     } else {
646 	my_strncpy(dst, src, dstlen-1);
647 	tmp = src + len;
648     }
649     if (*tmp && *tmp != CMDSEP) tmp++;
650     return tmp;
651 }
652 
__P1(int,signum)653 static void sig_pipe_handler __P1 (int,signum)
654 {
655     tty_puts("\n#broken pipe.\n");
656 }
657 
__P1(int,signum)658 static void sig_winch_handler __P1 (int,signum)
659 {
660     sig_pending = sig_winch_got = 1;
661 }
662 
__P1(int,signum)663 static void sig_chld_handler __P1 (int,signum)
664 {
665     sig_pending = sig_chld_got = 1;
666 }
667 
__P1(int,signum)668 static void sig_term_handler __P1 (int,signum)
669 {
670     tty_printf("%s\n#termination signal.\n", edattrend);
671     exit_powwow();
672 }
673 
__P1(int,signum)674 static void sig_intr_handler __P1 (int,signum)
675 {
676     if (confirm) {
677 	tty_printf("%s\n#interrupted.%s\n", edattrend, tty_clreoln);
678 	exit_powwow();
679     }
680 
681     PRINTF("%s\n#interrupted. Press again to quit%s\n", edattrend, tty_clreoln);
682     tty_flush(); /* in case we are not in mainlupe */
683     confirm = 1;
684     error = USER_BREAK;
685 
686     sig_oneshot(SIGINT, sig_intr_handler);
687 }
688 
689 /*
690  * suspend ourselves
691  */
__P1(int,signum)692 void suspend_powwow __P1 (int, signum)
693 {
694     if (can_suspend) {
695         sig_permanent(SIGTSTP, SIG_DFL);
696         sig_permanent(SIGTERM, SIG_IGN);
697         sig_permanent(SIGINT, SIG_IGN);
698 	tty_puts(edattrend);
699         tty_quit();
700 
701 	if (kill(0, SIGTSTP) < 0) {
702 	    errmsg("suspend powwow");
703 	    return;
704 	}
705 
706         signal_start();
707         tty_start();
708         tty_sig_winch_bottomhalf();  /* in case size changed meanwhile */
709     } else
710 	tty_puts("\n#I don't think your shell has job control.\n");
711     status(1);
712 }
713 
714 /*
715  * GH: Sets a signal-handler permanently (like bsd signal())
716  *     from Ilie
717  */
__P2(int,signum,function_signal,sighandler)718 function_signal sig_permanent __P2 (int,signum, function_signal,sighandler)
719 {
720     struct sigaction act;
721     function_signal oldhandler;
722 
723     if (sigaction(signum, NULL, &act))
724         return SIG_ERR;
725     oldhandler = act.sa_handler;
726     act.sa_handler = sighandler;
727 #ifdef SA_RESTART
728     act.sa_flags = SA_RESTART;
729 #else
730     act.sa_flags = 0;
731 #endif
732     if (sigaction(signum, &act, NULL))
733 	return SIG_ERR;
734     return oldhandler;
735 }
736 
737 /*
738  * One-shot only signal. Hope it will work as intended.
739  */
740 #ifdef SA_ONESHOT
__P2(int,signum,function_signal,sighandler)741 function_signal sig_oneshot __P2 (int,signum, function_signal,sighandler)
742 {
743     struct sigaction act;
744     function_signal oldhandler;
745 
746     if (sigaction(signum, NULL, &act))
747         return SIG_ERR;
748     oldhandler = act.sa_handler;
749     act.sa_handler = sighandler;
750     act.sa_flags = SA_ONESHOT;
751     if (sigaction(signum, &act, NULL))
752 	return SIG_ERR;
753     return oldhandler;
754 }
755 #endif
756 
757 
758 /*
759  * set up our signal handlers
760  */
__P0(void)761 void signal_start __P0 (void)
762 {
763     if (sig_permanent(SIGTSTP, SIG_IGN) == SIG_DFL) {
764 	sig_permanent(SIGTSTP, suspend_powwow);
765 	can_suspend = 1;
766     }
767     sig_permanent(SIGCHLD,  sig_chld_handler);
768     sig_permanent(SIGQUIT,  sig_intr_handler);
769     sig_permanent(SIGTERM,  sig_term_handler);
770     sig_permanent(SIGPIPE,  sig_pipe_handler);
771     sig_permanent(SIGWINCH, sig_winch_handler);
772     /*
773      * this must not be permanent, as we want
774      * to be able to interrupt system calls
775      */
776     sig_oneshot(SIGINT, sig_intr_handler);
777 }
778 
__P0(void)779 void sig_bottomhalf __P0 (void)
780 {
781     if (sig_chld_got)
782 	sig_chld_bottomhalf();
783     if (sig_winch_got)
784 	tty_sig_winch_bottomhalf();
785 
786     sig_pending = sig_chld_got = sig_winch_got = 0;
787 }
788 
__P1(char *,msg)789 void errmsg __P1 (char *,msg)
790 {
791     if (!msg)
792 	msg = "";
793 
794     clear_input_line(opt_compact);
795     if (!opt_compact) {
796 	tty_putc('\n');
797 	status(1);
798     }
799     if (errno == EINTR) {
800 	tty_printf("#user break: %s (%d: %s)\n",
801 		  msg, errno, strerror(errno));
802     } else if (errno) {
803 	tty_printf("#system call error: %s (%d", msg, errno);
804 	if (errno > 0)
805 	    tty_printf(": %s)\n", strerror(errno));
806 	else
807 	    tty_puts(")\n");
808     } else if (error == NO_MEM_ERROR) {
809 	tty_printf("#system call error: %s (%d", msg, ENOMEM);
810 	tty_printf(": %s)\n", strerror(ENOMEM));
811     }
812     tty_flush();
813 }
814 
815 /*
816  * print system call error message and terminate
817  */
__P1(char *,msg)818 void syserr __P1 (char *,msg)
819 {
820     if (msg && *msg) {
821 	clear_input_line(opt_compact);
822 	if (!opt_compact) {
823 	    tty_putc('\n');
824 	    /* status(1); */
825 	}
826 	tty_flush();
827 
828 	fprintf(stderr, "#powwow: fatal system call error:\n\t%s (%d", msg, errno);
829 	if (errno > 0)
830 	  fprintf(stderr, ": %s", strerror(errno));
831 	fprintf(stderr, ")\n");
832     }
833 
834 #ifdef SAVE_ON_SYSERR
835     /* Try to do an emergency save. This can wreak havoc
836      * if called from the wrong place, like
837      * read_settings() or save_settings(),
838      * thus is executed only if you add -DSAVE_ON_SYSERR
839      * to CF flags in make_it
840      */
841     (void)save_settings();
842 #else
843     tty_puts("#settings NOT saved to file.\n");
844 #endif
845 
846     tty_quit();
847     exit(1);
848 }
849 
__P1(int,n)850 static void load_missing_stuff __P1 (int,n)
851 {
852     char buf[BUFSIZE];
853 
854     if (n < 1) {
855         tty_add_walk_binds();
856         tty_puts("#default keypad settings loaded\n");
857     }
858     if (n < 2) {
859         tty_add_initial_binds();
860         tty_puts("#default editing keys settings loaded\n");
861     }
862     if (n < 5) {
863 	static char *str[] = { "compact", "debug", "echo", "info",
864 		"keyecho", "speedwalk", "wrap", 0 };
865 	int i;
866 	for (i=0; str[i]; i++) {
867 	    sprintf(buf, "#%s={#if ($(1)==\"on\") #option +%s; #else #if ($(1)==\"off\") #option -%s; #else #option %s}",
868 		    str[i], str[i], str[i], str[i]);
869 	    parse_alias(buf);
870 	}
871 	tty_printf("#compatibility aliases loaded:\n\t%s\n",
872 		 "#compact, #debug, #echo, #info, #keyecho, #speedwalk, #wrap");
873     }
874     if (n < 6) {
875 	sprintf(buf, "#lines=#setvar lines=$0");
876 	parse_alias(buf);
877 	sprintf(buf, "#settimer=#setvar timer=$0");
878 	parse_alias(buf);
879 	limit_mem = 1048576;
880 	tty_printf("#compatibility aliases loaded:\n\t%s\n", "#lines, #settimer");
881 	tty_puts("#max text/strings length set to 1048576 bytes\n\tuse \"#setvar mem\" to change it\n\n#wait...");
882 	tty_flush();
883 	sleep(1);
884 	tty_puts("ok\n");
885     }
886 }
887 
888 /*
889  * read definitions from file
890  * return > 0 if success, < 0 if fail.
891  * NEVER call syserr() from here or powwow will
892  * try to save the definition file even if it got
893  * a broken or empty one.
894  */
__P0(void)895 int read_settings __P0 (void)
896 {
897     FILE *f;
898     char *buf, *p, *cmd, old_nice = a_nice;
899     int failed = 1, n, savefilever = 0, left, len, limit_mem_hit = 0;
900     varnode **first;
901     ptr ptrbuf;
902 
903     if (!*deffile) {
904 	PRINTF("#warning: no save-file defined!\n");
905 	return 0;
906     }
907 
908     f = fopen(deffile, "r");
909     if (!f) {
910         PRINTF("#error: cannot open file \"%s\": %s\n", deffile, strerror(errno));
911 	return 0;
912     }
913 
914     ptrbuf = ptrnew(PARAMLEN);
915     if (MEM_ERROR) {
916 	print_error(error);
917 	return 0;
918     }
919     buf = ptrdata(ptrbuf);
920     left = ptrmax(ptrbuf);
921     len = 0;
922 
923     opt_info = a_nice = 0;
924 
925     for (n = 0; n < MAX_HASH; n++) {
926 	while (aliases[n])
927 	    delete_aliasnode(&aliases[n]);
928     }
929     while (actions)
930 	delete_actionnode(&actions);
931     while (prompts)
932 	delete_promptnode(&prompts);
933     while (markers)
934 	delete_marknode(&markers);
935     while (keydefs)
936 	delete_keynode(&keydefs);
937     for (n = 0; n < MAX_HASH; n++) {
938 	while (named_vars[0][n])
939 	    delete_varnode(&named_vars[0][n], 0);
940 	first = &named_vars[1][n];
941 	while (*first) {
942 	    if (is_permanent_variable(*first))
943 		first = &(*first)->next;
944 	    else
945 		delete_varnode(first, 1);
946 	}
947     }
948 
949     for (n = 0; n < NUMVAR; n++) {
950 	*var[n].num = 0;
951 	ptrdel(*var[n].str);
952 	*var[n].str = NULL;
953     }
954 
955     while (left > 0 && fgets(buf+len, left+1, f)) {
956 	/* WARNING: accessing private field ->len */
957 	len += n = strlen(buf+len);
958 	ptrbuf->len = len;
959 	left -= n;
960 
961 	/* Clear all \n prefixed with a literal backslash '\\' */
962 	while ((cmd = strstr(buf, "\\\n"))) {
963 		cmd[ 0 ] = ' ';
964 		cmd[ 1 ] = ' ';
965 	}
966 
967 	cmd = strchr(buf, '\n');
968 
969 	if (!cmd) {
970 	    if (feof(f)) {
971 		PRINTF("#error: missing newline at end of file \"%s\"\n", deffile);
972 		break;
973 	    }
974 	    /* no newline yet. increase line size and try again */
975 	    ptrbuf = ptrpad(ptrbuf, ptrlen(ptrbuf) >> 1);
976 	    if (MEM_ERROR) {
977 		limit_mem_hit = 1;
978 		print_error(error);
979 		break;
980 	    }
981 	    ptrtrunc(ptrbuf,len);
982 	    buf = ptrdata(ptrbuf);
983 	    left = ptrmax(ptrbuf) - len;
984 	    continue;
985 	}
986 	/* got a full line */
987 	*cmd = '\0';
988 	cmd = buf;
989 	left += len;
990 	len = 0;
991 
992         cmd = skipspace(cmd);
993 	if (!*cmd)
994 	    continue;
995 
996         error = 0;
997         if (*(p = first_regular(cmd, ' '))) {
998             *p++ = '\0';
999             if (!strcmp(cmd, "#savefile-version")) {
1000 		savefilever = atoi(p);
1001 		continue;
1002             }
1003 	    *--p = ' ';
1004 	}
1005 	parse_user_input(cmd, 1);
1006     }
1007 
1008     if (error)
1009 	failed = -1;
1010     else if (ferror(f) && !feof(f)) {
1011 	PRINTF("#error: cannot read file \"%s\": %s\n", deffile, strerror(errno));
1012         failed = -1;
1013     } else if (limit_mem_hit) {
1014 	PRINTF("#error: cannot load save-file: got a line longer than limit\n");
1015 	failed = -1;
1016     } else if (savefilever > SAVEFILEVER) {
1017 	PRINTF("#warning: this powwow version is too old!\n");
1018     } else if (savefilever < SAVEFILEVER) {
1019 	PRINTF("\n#warning: config file is from an older version\n");
1020 	load_missing_stuff(savefilever);
1021     }
1022 
1023     fclose(f);
1024     a_nice = old_nice;
1025 
1026     if (ptrbuf)
1027 	ptrdel(ptrbuf);
1028 
1029     return failed;
1030 }
1031 
1032 static char tmpname[BUFSIZE];
1033 
__P0(void)1034 static void fail_msg __P0 (void)
1035 {
1036     PRINTF("#error: cannot write to temporary file \"%s\": %s\n", tmpname, strerror(errno));
1037 }
1038 
1039 /*
1040  * save settings in definition file
1041  * return > 0 if success, < 0 if fail.
1042  * NEVER call syserr() from here or powwow will
1043  * enter an infinite loop!
1044  */
__P0(void)1045 int save_settings __P0 (void)
1046 {
1047     FILE *f;
1048     int l;
1049     aliasnode *alp;
1050     actionnode *acp;
1051     promptnode *ptp;
1052     marknode *mp;
1053     keynode *kp;
1054     varnode *vp;
1055     ptr pp = (ptr)0;
1056     int i, flag, failed = 1;
1057 
1058     if (REAL_ERROR) {
1059 	PRINTF("#will not save after an error!\n");
1060 	return -1;
1061     }
1062     error = 0;
1063 
1064     if (!*deffile) {
1065 	PRINTF("#warning: no save-file defined!\n");
1066 	return -1;
1067     }
1068 
1069     /*
1070      * Create a temporary file in the same directory as deffile,
1071      * and write settings there
1072      */
1073     strcpy(tmpname, deffile);
1074     l = strlen(tmpname) - 1;
1075     while (l && tmpname[l] != '/')
1076 	l--;
1077     if (l)
1078 	l++;
1079 
1080     sprintf(tmpname + l, "tmpsav%d%d", getpid(), rand() >> 8);
1081     if (!(f = fopen(tmpname, "w"))) {
1082         fail_msg();
1083         return -1;
1084     }
1085 
1086     pp = ptrnew(PARAMLEN);
1087     if (MEM_ERROR) failed = -1;
1088 
1089     failed = fprintf(f, "#savefile-version %d\n", SAVEFILEVER);
1090     if (failed > 0 && *hostname)
1091 	failed = fprintf(f, "#host %s %d\n", hostname, portnumber);
1092 
1093     if (failed > 0) {
1094 	if (delim_mode == DELIM_CUSTOM) {
1095 	    pp = ptrmescape(pp, DELIM, strlen(DELIM), 0);
1096 	    if (MEM_ERROR) failed = -1;
1097 	}
1098 	if (failed > 0)
1099 	    failed = fprintf(f, "#delim %s%s\n", delim_name[delim_mode],
1100 			     delim_mode == DELIM_CUSTOM ? ptrdata(pp) : "" );
1101     }
1102 
1103 	if (failed > 0)
1104 	    failed = fprintf(f, "#groupdelim %s\n", group_delim);
1105 
1106     if (failed > 0 && *initstr)
1107 	failed = fprintf(f, "#init =%s\n", initstr);
1108 
1109     if (failed > 0 && limit_mem)
1110 	failed = fprintf(f, "#setvar mem=%d\n", limit_mem);
1111 
1112     if (failed > 0 && (i = log_getsize()))
1113 	failed = fprintf(f, "#setvar buffer=%d\n", i);
1114 
1115     if (failed > 0) {
1116 	reverse_sortedlist((sortednode **)&sortedaliases);
1117 	for (alp = sortedaliases; alp && failed > 0; alp = alp->snext) {
1118 	    pp = ptrmescape(pp, alp->name, strlen(alp->name), 0);
1119 	    if (MEM_ERROR) { failed = -1; break; }
1120 	    failed = fprintf(f, "#alias %s%s%s=%s\n", ptrdata(pp),
1121 		alp -> group == NULL ? "" : group_delim,
1122 		alp -> group == NULL ? "" : alp -> group,
1123 		alp->subst);
1124 	}
1125 	reverse_sortedlist((sortednode **)&sortedaliases);
1126     }
1127 
1128     for (acp = actions; acp && failed > 0; acp = acp->next) {
1129 	failed = fprintf(f, "#action %c%c%s%s%s %s=%s\n",
1130 			 action_chars[acp->type], acp->active ? '+' : '-',
1131 			 acp->label,
1132 		acp -> group == NULL ? "" : group_delim,
1133 		acp -> group == NULL ? "" : acp -> group,
1134 			 acp->pattern, acp->command);
1135     }
1136 
1137     for (ptp = prompts; ptp && failed > 0; ptp = ptp->next) {
1138 	failed = fprintf(f, "#prompt %c%c%s %s=%s\n",
1139 			 action_chars[ptp->type], ptp->active ? '+' : '-',
1140 			 ptp->label, ptp->pattern, ptp->command);
1141     }
1142 
1143     for (mp = markers; mp && failed > 0; mp = mp->next) {
1144 	pp = ptrmescape(pp, mp->pattern, strlen(mp->pattern), 0);
1145 	if (MEM_ERROR) { failed = -1; break; }
1146 	failed = fprintf(f, "#mark %s%s=%s\n", mp->mbeg ? "^" : "",
1147 			     ptrdata(pp), attr_name(mp->attrcode));
1148     }
1149     /* save value of global variables */
1150 
1151     for (flag = 0, i=0; i<NUMVAR && failed > 0; i++) {
1152         if (var[i].num && *var[i].num) {    /* first check was missing!!! */
1153 	    failed = fprintf(f, "%s@%d = %ld", flag ? ", " : "#(",
1154 			     i-NUMVAR, *var[i].num);
1155             flag = 1;
1156         }
1157     }
1158     if (failed > 0 && flag) failed = fprintf(f, ")\n");
1159 
1160     for (i=0; i<NUMVAR && failed > 0; i++) {
1161         if (var[i].str && *var[i].str && ptrlen(*var[i].str)) {
1162 	    pp = ptrescape(pp, *var[i].str, 0);
1163 	    if (MEM_ERROR) { failed = -1; break; }
1164 	    failed = fprintf(f, "#($%d = \"%s\")\n", i-NUMVAR, ptrdata(pp));
1165 	}
1166     }
1167 
1168     if (failed > 0) {
1169 	reverse_sortedlist((sortednode **)&sortednamed_vars[0]);
1170 	for (flag = 0, vp = sortednamed_vars[0]; vp && failed > 0; vp = vp->snext) {
1171 	    if (vp->num) {
1172 		failed = fprintf(f, "%s@%s = %ld", flag ? ", " : "#(",
1173 				 vp->name, vp->num);
1174 		flag = 1;
1175 	    }
1176 	}
1177 	reverse_sortedlist((sortednode **)&sortednamed_vars[0]);
1178     }
1179     if (failed > 0 && flag) failed = fprintf(f, ")\n");
1180 
1181     if (failed > 0) {
1182 	reverse_sortedlist((sortednode **)&sortednamed_vars[1]);
1183 	for (vp = sortednamed_vars[1]; vp && failed > 0; vp = vp->snext) {
1184 	    if (!is_permanent_variable(vp) && vp->str && ptrlen(vp->str)) {
1185 		pp = ptrescape(pp, vp->str, 0);
1186 		if (MEM_ERROR) { failed = -1; break; }
1187 		failed = fprintf(f, "#($%s = \"%s\")\n", vp->name, ptrdata(pp));
1188 	    }
1189 	}
1190 	reverse_sortedlist((sortednode **)&sortednamed_vars[1]);
1191     }
1192 
1193     /* GH: fixed the history and word completions saves */
1194     if (failed > 0 && opt_history) {
1195 	l = (curline + 1) % MAX_HIST;
1196 	while (failed > 0 && l != curline) {
1197 	    if (hist[l] && *hist[l]) {
1198 		pp = ptrmescape(pp, hist[l], strlen(hist[l]), 0);
1199 		if (MEM_ERROR) { failed = -1; break; }
1200 		failed = fprintf(f, "#put %s\n", ptrdata(pp));
1201 	    }
1202 	    if (++l >= MAX_HIST)
1203 		l = 0;
1204 	}
1205     }
1206 
1207     if (failed > 0 && opt_words) {
1208 	int cl = 4, len;
1209 	l = wordindex;
1210 	flag = 0;
1211 	while (words[l = words[l].next].word)
1212 	    ;
1213 	while (words[l = words[l].prev].word && failed > 0) {
1214             pp = ptrmescape(pp, words[l].word, strlen(words[l].word), 0);
1215             len = ptrlen(pp) + 1;
1216             if (cl > 4 && cl + len >= 80) {
1217                 cl = 4;
1218                 failed = fprintf(f, "\n");
1219                 flag = 0;
1220             }
1221             if (failed > 0)
1222                 failed = fprintf(f, "%s %s", flag ? "" : "#add", ptrdata(pp));
1223             cl += len;
1224             flag = 1;
1225 	}
1226 	if (failed > 0 && flag)
1227 	    failed = fprintf(f, "\n");
1228     }
1229 
1230     for (kp = keydefs; kp && failed > 0; kp = kp->next) {
1231 	if (kp->funct==key_run_command)
1232 	    failed = fprintf(f, "#bind %s %s=%s\n", kp->name,
1233 			     seq_name(kp->sequence, kp->seqlen), kp->call_data);
1234 	else
1235 	    failed = fprintf(f, "#bind %s %s=%s%s%s\n", kp->name,
1236 			     seq_name(kp->sequence, kp->seqlen),
1237 			     internal_functions[lookup_edit_function(kp->funct)].name,
1238 			     kp->call_data ? " " : "",
1239 			     kp->call_data ? kp->call_data : "");
1240     }
1241 
1242     if (failed > 0)
1243         failed = print_all_options(f);
1244 
1245     fclose(f);
1246 
1247     if (error)
1248 	errmsg("malloc");
1249     else if (failed <= 0)
1250 	fail_msg();
1251     else {
1252 	failed = rename(tmpname, deffile);
1253 	if (failed == -1) {
1254 	    PRINTF("#error: cannot move temporary file \"%s\" to \"%s\": %s\n",
1255 		       tmpname, deffile, strerror(errno));
1256 	} else
1257 	    failed = 1;
1258     }
1259 
1260     if (pp)
1261 	ptrdel(pp);
1262 
1263     return failed > 0 ? 1 : -1;
1264 }
1265 
1266 /*
1267  * update "now" to current time
1268  */
__P0(void)1269 void update_now __P0 (void)
1270 {
1271     if (!now_updated) {
1272 	gettimeofday(&now, NULL);
1273 	now_updated = 1;
1274     }
1275 }
1276 
1277 /*
1278  * terminate powwow as cleanly as possible
1279  */
__P0(void)1280 void exit_powwow __P0 (void)
1281 {
1282     log_flush();
1283     if (capturefile) fclose(capturefile);
1284     if (recordfile)  fclose(recordfile);
1285     if (moviefile)   fclose(moviefile);
1286     (void)save_settings();
1287     show_stat();
1288     tty_quit();
1289     exit(0);
1290 }
1291