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