aboutsummaryrefslogtreecommitdiffstats
path: root/utils.c
diff options
context:
space:
mode:
authorSteve Slaven <bpk@hoopajoo.net>2005-03-12 00:27:55 (GMT)
committerSteve Slaven <bpk@hoopajoo.net>2005-03-12 00:27:55 (GMT)
commit77b250bfb63a28a8fe8a8da67de7354bce6e61ff (patch)
treee28f72c18305016c19c608f5fce4432529e7673d /utils.c
parent5dfb1906b299bf7c8a1ee3ba5cd1c9ea40648d89 (diff)
downloadpowwow-77b250bfb63a28a8fe8a8da67de7354bce6e61ff.zip
powwow-77b250bfb63a28a8fe8a8da67de7354bce6e61ff.tar.gz
powwow-77b250bfb63a28a8fe8a8da67de7354bce6e61ff.tar.bz2
Initial revisionv1.2.7
Diffstat (limited to 'utils.c')
-rw-r--r--utils.c1279
1 files changed, 1279 insertions, 0 deletions
diff --git a/utils.c b/utils.c
new file mode 100644
index 0000000..8ec96eb
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,1279 @@
+/*
+ * utils.c -- miscellaneous utility functions
+ *
+ * Copyright (C) 1998,2002 by Massimiliano Ghilardi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+
+#include "defines.h"
+#include "main.h"
+#include "utils.h"
+#include "list.h"
+#include "cmd.h"
+#include "cmd2.h"
+#include "beam.h"
+#include "tty.h"
+#include "edit.h"
+#include "eval.h"
+#include "log.h"
+
+#define SAVEFILEVER 6
+
+static char can_suspend = 0; /* 1 if shell has job control */
+
+/*
+ * GH: memory-"safe" strdup
+ */
+char *my_strdup __P1 (char *,s)
+{
+ if (s) {
+ s = strdup(s);
+ if (!s)
+ error = NO_MEM_ERROR;
+ }
+ return s;
+}
+
+/*
+ * non-braindamaged strncpy:
+ * copy up to len chars from src to dst, then add a final \0
+ * (i.e. dst[len] = '\0')
+ */
+char *my_strncpy __P3 (char *,dst, char *,src, int,len)
+{
+ int slen = strlen(src);
+ if (slen < len)
+ return strcpy(dst, src);
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ return dst;
+}
+
+/*
+ * Determine the printed length of a string. This can be less than the string
+ * length since it might contain escape sequences. Treated as such are
+ * "<esc> [ <non-letters> <letter>", "<esc> <non-[>", "<control-char>".
+ * This is not entirely universal but covers the most common cases (i.e. ansi)
+ */
+int printstrlen __P1 (char *,s)
+{
+ int l;
+ enum { NORM, ESCAPE, BRACKET } state = NORM;
+ for (l = 0; *s; s++) {
+ switch(state) {
+ case NORM:
+ if (*s == '\033') {
+ state = ESCAPE;
+ } else if ((*s & 0x80) || *s >= ' ') {
+ l++;
+ } else if (*s == '\r')
+ l = (l / cols) * cols;
+ break;
+
+ case ESCAPE:
+ state = (*s == '[') ? BRACKET : NORM;
+ break;
+
+ case BRACKET:
+ if (isalpha(*s))
+ state = NORM;
+ break;
+ }
+ }
+ return l;
+}
+
+/*
+ * return pointer to next non-blank char
+ */
+char *skipspace __P1 (char *,p)
+{
+ while (*p == ' ' || *p == '\t') p++;
+ return p;
+}
+
+/*
+ * find the first valid (non-escaped)
+ * char in a string
+ */
+char *first_valid __P2 (char *,p, char,ch)
+{
+ if (*p && *p != ch) {
+ p++;
+ if (ch == ESC2 || ch == ESC) while (*p && *p != ch)
+ p++;
+ else while (*p && ((*p != ch) || p[-1] == ESC))
+ p++;
+ }
+ return p;
+}
+
+/*
+ * find the first regular (non-escaped, non in "" () or {} )
+ * char in a string
+ */
+char *first_regular __P2 (char *,p, char,c)
+{
+ int escaped, quotes=0, paren=0, braces=0;
+
+ while (*p && ((*p != c) || quotes || paren>0 || braces>0)) {
+ escaped = 0;
+ if (*p == ESC) {
+ while (*p == ESC)
+ p++;
+ escaped = 1;
+ }
+ if (*p == ESC2) {
+ while (*p == ESC2)
+ p++;
+ escaped = 0;
+ }
+ if (!*p)
+ break;
+ if (!escaped) {
+ if (quotes) {
+ if (*p == '\"')
+ quotes = 0;
+ }
+ else if (*p == '\"')
+ quotes = 1;
+ else if (*p == ')')
+ paren--;
+ else if (*p == '(')
+ paren++;
+ else if (*p == '}')
+ braces--;
+ else if (*p == '{')
+ braces++;
+ }
+ p++;
+ }
+ return p;
+}
+
+/*
+ * remove escapes (backslashes) from a string
+ */
+int memunescape __P2 (char *,p, int,lenp)
+{
+ char *start, *s;
+ enum { NORM, ESCSINGLE, ESCAPE } state = NORM;
+
+ if (!p || !*p)
+ return 0;
+
+ start = s = p;
+
+ while (lenp) switch (state) {
+ case NORM:
+ if (*s != ESC) {
+ *p++ = *s++, lenp--;
+ break;
+ }
+ state = ESCSINGLE, s++;
+ if (!--lenp)
+ break;
+ /* fallthrough */
+ case ESCSINGLE:
+ case ESCAPE:
+ if (*s == ESC)
+ state = ESCAPE, *p++ = *s++, lenp--;
+ else if (*s == ESC2)
+ state = NORM, *p++ = ESC, s++, lenp--;
+ else {
+ if (state == ESCSINGLE && lenp >= 3 &&
+ ISODIGIT(s[0]) && ISODIGIT(s[1]) && ISODIGIT(s[2])) {
+
+ *p++ = ((s[0]-'0') << 6) | ((s[1]-'0') << 3) | (s[2]-'0');
+ s += 3, lenp -= 3;
+ } else
+ *p++ = *s++, lenp--;
+ state = NORM;
+ }
+ break;
+ default:
+ break;
+ }
+ *p = '\0';
+ return (int)(p - start);
+}
+
+void unescape __P1 (char *,s)
+{
+ (void)memunescape(s, strlen(s));
+}
+
+void ptrunescape __P1 (ptr,p)
+{
+ if (!p)
+ return;
+ p->len = memunescape(ptrdata(p), ptrlen(p));
+}
+
+/*
+ * add escapes (backslashes) to src
+ */
+ptr ptrmescape __P4 (ptr,dst, char *,src, int,srclen, int,append)
+{
+ int len;
+ char *p;
+ enum { NORM, ESCAPE } state;
+
+ if (!src || srclen <= 0) {
+ if (!append)
+ ptrzero(dst);
+ return dst;
+ }
+
+ if (dst && append)
+ len = ptrlen(dst);
+ else
+ len = 0;
+
+ dst = ptrsetlen(dst, len + srclen*4); /* worst case */
+ if (MEM_ERROR) return dst;
+
+ dst->len = len;
+ p = ptrdata(dst) + len;
+
+ while (srclen) {
+ state = NORM;
+ if (*src == ESC) {
+ while (srclen && *src == ESC)
+ dst->len++, *p++ = *src++, srclen--;
+
+ if (!srclen || *src == ESC2)
+ dst->len++, *p++ = ESC2;
+ else
+ state = ESCAPE;
+ }
+ if (!srclen) break;
+
+ if (*src < ' ' || *src > '~') {
+ sprintf(p, "\\%03o", (int)(byte)*src++);
+ len = strlen(p);
+ dst->len += len, p += len, srclen--;
+ } else {
+ if (state == ESCAPE || strchr(SPECIAL_CHARS, *src))
+ dst->len++, *p++ = ESC;
+
+ dst->len++, *p++ = *src++, srclen--;
+ }
+ }
+ *p = '\0';
+ return dst;
+}
+
+ptr ptrescape __P3 (ptr,dst, ptr,src, int,append)
+{
+ if (!src) {
+ if (!append)
+ ptrzero(dst);
+ return dst;
+ }
+ return ptrmescape(dst, ptrdata(src), ptrlen(src), append);
+}
+
+/*
+ * add escapes to protect special characters from being escaped.
+ */
+void escape_specials __P2 (char *,dst, char *,src)
+{
+ enum { NORM, ESCAPE } state;
+ while (*src) {
+ state = NORM;
+ if (*src == ESC) {
+ while (*src == ESC)
+ *dst++ = *src++;
+
+ if (!*src || *src == ESC2)
+ *dst++ = ESC2;
+ else
+ state = ESCAPE;
+ }
+ if (!*src) break;
+
+ if (*src < ' ' || *src > '~') {
+ sprintf(dst, "\\%03o", (int)(byte)*src++);
+ dst += strlen(dst);
+ } else {
+ if (state == ESCAPE || strchr(SPECIAL_CHARS, *src))
+ *dst++ = ESC;
+
+ *dst++ = *src++;
+ }
+ }
+ *dst = '\0';
+}
+
+/*
+ * match mark containing & and $ and return 1 if matched, 0 if not
+ * if 1, start and end contain the match bounds
+ * if 0, start and end are undefined on return
+ */
+static int match_mark __P2 (marknode *,mp, char *,src)
+{
+ char *pat = mp->pattern;
+ char *npat=0, *npat2=0, *nsrc=0, *prm=0, *endprm=0, *tmp, c;
+ static char mpat[BUFSIZE];
+ int mbeg = mp->mbeg, mword = 0, p;
+
+ /* shortcut for #marks without wildcards */
+ if (!mp->wild) {
+ if ((nsrc = strstr(src, pat))) {
+ mp->start = nsrc;
+ mp->end = nsrc + strlen(pat);
+ return 1;
+ }
+ return 0;
+ }
+
+ mp->start = NULL;
+
+ if (ISMARKWILDCARD(*pat))
+ mbeg = - mbeg - 1; /* pattern starts with '&' or '$' */
+
+ while (pat && (c = *pat)) {
+ if (ISMARKWILDCARD(c)) {
+ /* & matches a string */
+ /* $ matches a single word */
+ prm = src;
+ if (c == '$')
+ mword = 1;
+ else if (!mp->start)
+ mp->start = src;
+ ++pat;
+ }
+
+ npat = first_valid(pat, '&');
+ npat2 = first_valid(pat, '$');
+ if (npat2 < npat) npat = npat2;
+ if (!*npat) npat = 0;
+
+ if (npat) {
+ my_strncpy(mpat, pat, npat-pat);
+ /* mpat[npat - pat] = 0; */
+ } else
+ strcpy(mpat, pat);
+
+ if (*mpat) {
+ nsrc = strstr(src, mpat);
+ if (!nsrc)
+ return 0;
+ if (mbeg > 0) {
+ if (nsrc != src)
+ return 0;
+ mbeg = 0; /* reset mbeg to stop further start match */
+ }
+ endprm = nsrc;
+ if (!mp->start) {
+ if (prm)
+ mp->start = src;
+ else
+ mp->start = nsrc;
+ }
+ mp->end = nsrc + strlen(mpat);
+ } else if (prm) /* end of pattern space */
+ mp->end = endprm = prm + strlen(prm);
+ else
+ mp->end = src;
+
+
+ /* post-processing of param */
+ if (mword) {
+ if (prm) {
+ if (mbeg == -1) {
+ if (!*pat) {
+ /* the pattern is "$", take first word */
+ p = - 1;
+ } else {
+ /* unanchored '$' start, take last word */
+ tmp = memrchrs(prm, endprm - prm - 1, DELIM, DELIM_LEN);
+ if (tmp)
+ p = tmp - prm;
+ else
+ p = -1;
+ }
+ mp->start = prm += p + 1;
+ } else if (!*pat) {
+ /* '$' at end of pattern, take first word */
+ if ((tmp = memchrs(prm, strlen(prm), DELIM, DELIM_LEN)))
+ mp->end = endprm = tmp;
+ } else {
+ /* match only if param is single-worded */
+ if (memchrs(prm, endprm - prm, DELIM, DELIM_LEN))
+ return 0;
+ }
+ } else
+ return 0;
+ }
+ if (prm)
+ mbeg = mword = 0; /* reset match flags */
+ src = nsrc + strlen(mpat);
+ pat = npat;
+ }
+ return 1;
+}
+
+/*
+ * add marks to line. write in dst.
+ */
+ptr ptrmaddmarks __P3 (ptr,dst, char *,line, int,len)
+{
+ marknode *mp, *mfirst;
+ char begin[CAPLEN], end[CAPLEN], *lineend = line + len;
+ int start = 1, matchlen, matched = 0;
+
+ ptrzero(dst);
+
+ if (!line || len <= 0)
+ return dst;
+
+ for (mp = markers; mp; mp = mp->next)
+ mp->start = NULL;
+
+ do {
+ mfirst = NULL;
+ for (mp = markers; mp; mp = mp->next) {
+ if (mp->start && mp->start >= line)
+ matched = 1;
+ else {
+ if (!(matched = (!mp->mbeg || start) && match_mark(mp, line)))
+ mp->start = lineend;
+ }
+ if (matched && mp->start < lineend &&
+ (!mfirst || mp->start < mfirst->start))
+ mfirst = mp;
+ }
+
+ if (mfirst) {
+ start = 0;
+ attr_string(mfirst->attrcode, begin, end);
+
+ dst = ptrmcat(dst, line, matchlen = mfirst->start - line);
+ if (MEM_ERROR) break;
+ line += matchlen;
+ len -= matchlen;
+
+ dst = ptrmcat(dst, begin, strlen(begin));
+ if (MEM_ERROR) break;
+
+ dst = ptrmcat(dst, line, matchlen = mfirst->end - mfirst->start);
+ if (MEM_ERROR) break;
+ line += matchlen;
+ len -= matchlen;
+
+ dst = ptrmcat(dst, end, strlen(end));
+ if (MEM_ERROR) break;
+ }
+ } while (mfirst);
+
+ if (!MEM_ERROR)
+ dst = ptrmcat(dst, line, len);
+
+ return dst;
+}
+
+ptr ptraddmarks __P2 (ptr,dst, ptr,line)
+{
+ if (line)
+ return ptrmaddmarks(dst, ptrdata(line), ptrlen(line));
+ ptrzero(dst);
+ return dst;
+}
+
+/*
+ * write string to tty, wrapping to next line if needed.
+ * don't print a final \n
+ */
+static void wrap_print __P1 (char *,s)
+{
+ char *p, c, follow = 1;
+ char buf[BUFSIZE]; /* ASSERT(cols<BUFSIZE) */
+ int l, m;
+ enum { NORM, ESCAPE, BRACKET } state;
+#ifdef BUG_ANSI
+ int ansibug = 0;
+#endif
+
+ l = printstrlen(s);
+#ifdef BUG_ANSI
+ if (l > cols_1 && l < (int)strlen(s))
+ ansibug = 1;
+#endif
+
+ while (l >= cols_1 - col0) {
+ p = buf; m = 0; state = NORM;
+
+ while (m < cols_1 - col0 && *s && *s != '\n') {
+ *p++ = c = *s++;
+ switch (state) {
+ case NORM:
+ if (c == '\033')
+ state = ESCAPE;
+ else if ((c & 0x80) || (c >= ' ' && c <= '~'))
+ m++, l--;
+ else if (c == '\r')
+ m = 0;
+ break;
+ case ESCAPE:
+ state = (c == '[') ? BRACKET : NORM;
+ break;
+ case BRACKET:
+ if (isalpha(c))
+ state = NORM;
+ break;
+ }
+ }
+
+ follow = *s;
+
+ *p = '\0';
+ tty_printf("%s%s", buf, follow ? "\n" : "");
+ if (follow)
+ col0 = 0;
+ }
+#ifdef BUG_ANSI
+ if (ansibug)
+ tty_printf("%s%s%s", follow ? s : "" ,
+ tty_modenorm, tty_clreoln);
+ else
+#endif
+ if (follow)
+ tty_puts(s);
+}
+
+/*
+ * add marks to line and print.
+ * if newline, also print a final \n
+ */
+void smart_print __P2 (char *,line, char,newline)
+{
+ static ptr ptrbuf = NULL;
+ static char *buf;
+
+ do {
+ if (!ptrbuf) {
+ ptrbuf = ptrnew(PARAMLEN);
+ if (MEM_ERROR) break;
+ }
+ ptrbuf = ptrmaddmarks(ptrbuf, line, strlen(line));
+ if (MEM_ERROR || !ptrbuf) break;
+
+ buf = ptrdata(ptrbuf);
+
+ if (opt_wrap)
+ wrap_print(buf);
+ else {
+#ifdef BUG_ANSI
+ int l;
+ l = printstrlen(buf);
+ if (l > cols_1 && l < ptrlen(ptrbuf))
+ tty_printf("%s%s%s", buf, tty_modenorm, tty_clreoln);
+ else
+#endif
+ tty_printf("%s", buf);
+ }
+ } while(0);
+
+ if (MEM_ERROR)
+ print_error(error);
+ else if (newline)
+ col0 = 0, tty_putc('\n');
+}
+
+/*
+ * copy first word of src into dst, and return pointer to second word of src
+ */
+char *split_first_word __P3 (char *,dst, int,dstlen, char *,src)
+{
+ char *tmp;
+ int len;
+
+ src = skipspace(src);
+ if (!*src) {
+ *dst='\0';
+ return src;
+ }
+ len = strlen(src);
+
+ tmp = memchrs(src, len, DELIM, DELIM_LEN);
+ if (tmp) {
+ if (dstlen > tmp-src+1) dstlen = tmp-src+1;
+ my_strncpy(dst, src, dstlen-1);
+ } else {
+ my_strncpy(dst, src, dstlen-1);
+ tmp = src + len;
+ }
+ if (*tmp && *tmp != CMDSEP) tmp++;
+ return tmp;
+}
+
+static void sig_pipe_handler __P1 (int,signum)
+{
+ tty_puts("\n#broken pipe.\n");
+}
+
+static void sig_winch_handler __P1 (int,signum)
+{
+ sig_pending = sig_winch_got = 1;
+}
+
+static void sig_chld_handler __P1 (int,signum)
+{
+ sig_pending = sig_chld_got = 1;
+}
+
+static void sig_term_handler __P1 (int,signum)
+{
+ tty_printf("%s\n#termination signal.\n", edattrend);
+ exit_powwow();
+}
+
+static void sig_intr_handler __P1 (int,signum)
+{
+ if (confirm) {
+ tty_printf("%s\n#interrupted.%s\n", edattrend, tty_clreoln);
+ exit_powwow();
+ }
+
+ PRINTF("%s\n#interrupted. Press again to quit%s\n", edattrend, tty_clreoln);
+ tty_flush(); /* in case we are not in mainlupe */
+ confirm = 1;
+ error = USER_BREAK;
+
+ sig_oneshot(SIGINT, sig_intr_handler);
+}
+
+/*
+ * suspend ourselves
+ */
+void suspend_powwow __P1 (int, signum)
+{
+ if (can_suspend) {
+ sig_permanent(SIGTSTP, SIG_DFL);
+ sig_permanent(SIGTERM, SIG_IGN);
+ sig_permanent(SIGINT, SIG_IGN);
+ tty_puts(edattrend);
+ tty_quit();
+
+ if (kill(0, SIGTSTP) < 0) {
+ errmsg("suspend powwow");
+ return;
+ }
+
+ signal_start();
+ tty_start();
+ tty_sig_winch_bottomhalf(); /* in case size changed meanwhile */
+ } else
+ tty_puts("\n#I don't think your shell has job control.\n");
+ status(1);
+}
+
+/*
+ * GH: Sets a signal-handler permanently (like bsd signal())
+ * from Ilie
+ */
+function_signal sig_permanent __P2 (int,signum, function_signal,sighandler)
+{
+ struct sigaction act;
+ function_signal oldhandler;
+
+ if (sigaction(signum, NULL, &act))
+ return SIG_ERR;
+ oldhandler = act.sa_handler;
+ act.sa_handler = sighandler;
+#ifdef SA_RESTART
+ act.sa_flags = SA_RESTART;
+#else
+ act.sa_flags = 0;
+#endif
+ if (sigaction(signum, &act, NULL))
+ return SIG_ERR;
+ return oldhandler;
+}
+
+/*
+ * One-shot only signal. Hope it will work as intended.
+ */
+#ifdef SA_ONESHOT
+function_signal sig_oneshot __P2 (int,signum, function_signal,sighandler)
+{
+ struct sigaction act;
+ function_signal oldhandler;
+
+ if (sigaction(signum, NULL, &act))
+ return SIG_ERR;
+ oldhandler = act.sa_handler;
+ act.sa_handler = sighandler;
+ act.sa_flags = SA_ONESHOT;
+ if (sigaction(signum, &act, NULL))
+ return SIG_ERR;
+ return oldhandler;
+}
+#endif
+
+
+/*
+ * set up our signal handlers
+ */
+void signal_start __P0 (void)
+{
+ if (sig_permanent(SIGTSTP, SIG_IGN) == SIG_DFL) {
+ sig_permanent(SIGTSTP, suspend_powwow);
+ can_suspend = 1;
+ }
+ sig_permanent(SIGCHLD, sig_chld_handler);
+ sig_permanent(SIGQUIT, sig_intr_handler);
+ sig_permanent(SIGTERM, sig_term_handler);
+ sig_permanent(SIGPIPE, sig_pipe_handler);
+ sig_permanent(SIGWINCH, sig_winch_handler);
+ /*
+ * this must not be permanent, as we want
+ * to be able to interrupt system calls
+ */
+ sig_oneshot(SIGINT, sig_intr_handler);
+}
+
+void sig_bottomhalf __P0 (void)
+{
+ if (sig_chld_got)
+ sig_chld_bottomhalf();
+ if (sig_winch_got)
+ tty_sig_winch_bottomhalf();
+
+ sig_pending = sig_chld_got = sig_winch_got = 0;
+}
+
+void errmsg __P1 (char *,msg)
+{
+ if (!msg)
+ msg = "";
+
+ clear_input_line(opt_compact);
+ if (!opt_compact) {
+ tty_putc('\n');
+ status(1);
+ }
+ if (errno == EINTR) {
+ tty_printf("#user break: %s (%d: %s)\n",
+ msg, errno, strerror(errno));
+ } else if (errno) {
+ tty_printf("#system call error: %s (%d", msg, errno);
+ if (errno > 0)
+ tty_printf(": %s)\n", strerror(errno));
+ else
+ tty_puts(")\n");
+ } else if (error == NO_MEM_ERROR) {
+ tty_printf("#system call error: %s (%d", msg, ENOMEM);
+ tty_printf(": %s)\n", strerror(ENOMEM));
+ }
+ tty_flush();
+}
+
+/*
+ * print system call error message and terminate
+ */
+void syserr __P1 (char *,msg)
+{
+ if (msg && *msg) {
+ clear_input_line(opt_compact);
+ if (!opt_compact) {
+ tty_putc('\n');
+ /* status(1); */
+ }
+ tty_flush();
+
+ fprintf(stderr, "#powwow: fatal system call error:\n\t%s (%d", msg, errno);
+ if (errno > 0)
+ fprintf(stderr, ": %s", strerror(errno));
+ fprintf(stderr, ")\n");
+ }
+
+#ifdef SAVE_ON_SYSERR
+ /* Try to do an emergency save. This can wreak havoc
+ * if called from the wrong place, like
+ * read_settings() or save_settings(),
+ * thus is executed only if you add -DSAVE_ON_SYSERR
+ * to CF flags in make_it
+ */
+ (void)save_settings();
+#else
+ tty_puts("#settings NOT saved to file.\n");
+#endif
+
+ tty_quit();
+ exit(1);
+}
+
+static void load_missing_stuff __P1 (int,n)
+{
+ char buf[BUFSIZE];
+
+ if (n < 1) {
+ tty_add_walk_binds();
+ tty_puts("#default keypad settings loaded\n");
+ }
+ if (n < 2) {
+ tty_add_initial_binds();
+ tty_puts("#default editing keys settings loaded\n");
+ }
+ if (n < 5) {
+ static char *str[] = { "compact", "debug", "echo", "info",
+ "keyecho", "speedwalk", "wrap", 0 };
+ int i;
+ for (i=0; str[i]; i++) {
+ sprintf(buf, "#%s={#if ($(1)==\"on\") #option +%s; #else #if ($(1)==\"off\") #option -%s; #else #option %s}",
+ str[i], str[i], str[i], str[i]);
+ parse_alias(buf);
+ }
+ tty_printf("#compatibility aliases loaded:\n\t%s\n",
+ "#compact, #debug, #echo, #info, #keyecho, #speedwalk, #wrap");
+ }
+ if (n < 6) {
+ sprintf(buf, "#lines=#setvar lines=$0");
+ parse_alias(buf);
+ sprintf(buf, "#settimer=#setvar timer=$0");
+ parse_alias(buf);
+ limit_mem = 1048576;
+ tty_printf("#compatibility aliases loaded:\n\t%s\n", "#lines, #settimer");
+ tty_puts("#max text/strings length set to 1048576 bytes\n\tuse `#setvar mem' to change it\n\n#wait...");
+ tty_flush();
+ sleep(1);
+ tty_puts("ok\n");
+ }
+}
+
+/*
+ * read definitions from file
+ * return > 0 if success, < 0 if fail.
+ * NEVER call syserr() from here or powwow will
+ * try to save the definition file even if it got
+ * a broken or empty one.
+ */
+int read_settings __P0 (void)
+{
+ FILE *f;
+ char *buf, *p, *cmd, old_nice = a_nice;
+ int failed = 1, n, savefilever = 0, left, len, limit_mem_hit = 0;
+ varnode **first;
+ ptr ptrbuf;
+
+ if (!*deffile) {
+ PRINTF("#warning: no save-file defined!\n");
+ return 0;
+ }
+
+ f = fopen(deffile, "r");
+ if (!f) {
+ PRINTF("#error: cannot open file `%s': %s\n", deffile, strerror(errno));
+ return 0;
+ }
+
+ ptrbuf = ptrnew(PARAMLEN);
+ if (MEM_ERROR) {
+ print_error(error);
+ return 0;
+ }
+ buf = ptrdata(ptrbuf);
+ left = ptrmax(ptrbuf);
+ len = 0;
+
+ echo_int = a_nice = 0;
+
+ for (n = 0; n < MAX_HASH; n++) {
+ while (aliases[n])
+ delete_aliasnode(&aliases[n]);
+ }
+ while (actions)
+ delete_actionnode(&actions);
+ while (prompts)
+ delete_promptnode(&prompts);
+ while (markers)
+ delete_marknode(&markers);
+ while (keydefs)
+ delete_keynode(&keydefs);
+ for (n = 0; n < MAX_HASH; n++) {
+ while (named_vars[0][n])
+ delete_varnode(&named_vars[0][n], 0);
+ first = &named_vars[1][n];
+ while (*first) {
+ if (is_permanent_variable(*first))
+ first = &(*first)->next;
+ else
+ delete_varnode(first, 1);
+ }
+ }
+
+ for (n = 0; n < NUMVAR; n++) {
+ *var[n].num = 0;
+ ptrdel(*var[n].str);
+ *var[n].str = NULL;
+ }
+
+ while (left > 0 && fgets(buf+len, left+1, f)) {
+ /* WARNING: accessing private field ->len */
+ len += n = strlen(buf+len);
+ ptrbuf->len = len;
+ left -= n;
+
+ /* Clear all \n prefixed with a literal backslash '\\' */
+ while( cmd = strstr( buf, "\\\n" ) ) {
+ cmd[ 0 ] = ' ';
+ cmd[ 1 ] = ' ';
+ }
+
+ cmd = strchr(buf, '\n');
+
+ if (!cmd) {
+ if (feof(f)) {
+ PRINTF("#error: missing newline at end of file `%s'\n", deffile);
+ break;
+ }
+ /* no newline yet. increase line size and try again */
+ ptrbuf = ptrpad(ptrbuf, ptrlen(ptrbuf) >> 1);
+ if (MEM_ERROR) {
+ limit_mem_hit = 1;
+ print_error(error);
+ break;
+ }
+ ptrtrunc(ptrbuf,len);
+ buf = ptrdata(ptrbuf);
+ left = ptrmax(ptrbuf) - len;
+ continue;
+ }
+ /* got a full line */
+ *cmd = '\0';
+ cmd = buf;
+ left += len;
+ len = 0;
+
+ cmd = skipspace(cmd);
+ if (!*cmd)
+ continue;
+
+ error = 0;
+ if (*(p = first_regular(cmd, ' '))) {
+ *p++ = '\0';
+ if (!strcmp(cmd, "#savefile-version")) {
+ savefilever = atoi(p);
+ continue;
+ }
+ *--p = ' ';
+ }
+ parse_user_input(cmd, 1);
+ }
+
+ if (error)
+ failed = -1;
+ else if (ferror(f) && !feof(f)) {
+ PRINTF("#error: cannot read file `%s': %s\n", deffile, strerror(errno));
+ failed = -1;
+ } else if (limit_mem_hit) {
+ PRINTF("#error: cannot load save-file: got a line longer than limit\n");
+ failed = -1;
+ } else if (savefilever > SAVEFILEVER) {
+ PRINTF("#warning: this powwow version is too old!\n");
+ } else if (savefilever < SAVEFILEVER) {
+ PRINTF("\n#warning: config file is from an older version\n");
+ load_missing_stuff(savefilever);
+ }
+
+ fclose(f);
+ a_nice = old_nice;
+
+ if (ptrbuf)
+ ptrdel(ptrbuf);
+
+ return failed;
+}
+
+static char tmpname[BUFSIZE];
+
+static void fail_msg __P0 (void)
+{
+ PRINTF("#error: cannot write to temporary file `%s': %s\n", tmpname, strerror(errno));
+}
+
+/*
+ * save settings in definition file
+ * return > 0 if success, < 0 if fail.
+ * NEVER call syserr() from here or powwow will
+ * enter an infinite loop!
+ */
+int save_settings __P0 (void)
+{
+ FILE *f;
+ int l;
+ aliasnode *alp;
+ actionnode *acp;
+ promptnode *ptp;
+ marknode *mp;
+ keynode *kp;
+ varnode *vp;
+ ptr pp = (ptr)0;
+ extern char *full_options_string;
+ int i, flag, failed = 1;
+
+ if (REAL_ERROR) {
+ PRINTF("#will not save after an error!\n");
+ return -1;
+ }
+ error = 0;
+
+ if (!*deffile) {
+ PRINTF("#warning: no save-file defined!\n");
+ return -1;
+ }
+
+ /*
+ * Create a temporary file in the same directory as deffile,
+ * and write settings there
+ */
+ strcpy(tmpname, deffile);
+ l = strlen(tmpname) - 1;
+ while (l && tmpname[l] != '/')
+ l--;
+ if (l)
+ l++;
+
+ sprintf(tmpname + l, "tmpsav%d%d", getpid(), rand() >> 8);
+ if (!(f = fopen(tmpname, "w"))) {
+ fail_msg();
+ return -1;
+ }
+
+ pp = ptrnew(PARAMLEN);
+ if (MEM_ERROR) failed = -1;
+
+ failed = fprintf(f, "#savefile-version %d\n", SAVEFILEVER);
+ if (failed > 0 && *hostname)
+ failed = fprintf(f, "#host %s %d\n", hostname, portnumber);
+
+ if (failed > 0) {
+ if (delim_mode == DELIM_CUSTOM) {
+ pp = ptrmescape(pp, DELIM, strlen(DELIM), 0);
+ if (MEM_ERROR) failed = -1;
+ }
+ if (failed > 0)
+ failed = fprintf(f, "#delim %s%s\n", delim_name[delim_mode],
+ delim_mode == DELIM_CUSTOM ? ptrdata(pp) : "" );
+ }
+
+ if (failed > 0 && *initstr)
+ failed = fprintf(f, "#init =%s\n", initstr);
+
+ if (failed > 0 && limit_mem)
+ failed = fprintf(f, "#setvar mem=%d\n", limit_mem);
+
+ if (failed > 0 && (i = log_getsize()))
+ failed = fprintf(f, "#setvar buffer=%d\n", i);
+
+ if (failed > 0) {
+ reverse_sortedlist((sortednode **)&sortedaliases);
+ for (alp = sortedaliases; alp && failed > 0; alp = alp->snext) {
+ pp = ptrmescape(pp, alp->name, strlen(alp->name), 0);
+ if (MEM_ERROR) { failed = -1; break; }
+ failed = fprintf(f, "#alias %s%s%s=%s\n", ptrdata(pp),
+ alp -> group == NULL ? "" : "@",
+ alp -> group == NULL ? "" : alp -> group,
+ alp->subst);
+ }
+ reverse_sortedlist((sortednode **)&sortedaliases);
+ }
+
+ for (acp = actions; acp && failed > 0; acp = acp->next) {
+ failed = fprintf(f, "#action %c%c%s%s%s %s=%s\n",
+ action_chars[acp->type], acp->active ? '+' : '-',
+ acp->label,
+ acp -> group == NULL ? "" : "@",
+ acp -> group == NULL ? "" : acp -> group,
+ acp->pattern, acp->command);
+ }
+
+ for (ptp = prompts; ptp && failed > 0; ptp = ptp->next) {
+ failed = fprintf(f, "#prompt %c%c%s %s=%s\n",
+ action_chars[ptp->type], ptp->active ? '+' : '-',
+ ptp->label, ptp->pattern, ptp->command);
+ }
+
+ for (mp = markers; mp && failed > 0; mp = mp->next) {
+ pp = ptrmescape(pp, mp->pattern, strlen(mp->pattern), 0);
+ if (MEM_ERROR) { failed = -1; break; }
+ failed = fprintf(f, "#mark %s%s=%s\n", mp->mbeg ? "^" : "",
+ ptrdata(pp), attr_name(mp->attrcode));
+ }
+ /* save value of global variables */
+
+ for (flag = 0, i=0; i<NUMVAR && failed > 0; i++) {
+ if (var[i].num && *var[i].num) { /* first check was missing!!! */
+ failed = fprintf(f, "%s@%d = %ld", flag ? ", " : "#(",
+ i-NUMVAR, *var[i].num);
+ flag = 1;
+ }
+ }
+ if (failed > 0 && flag) failed = fprintf(f, ")\n");
+
+ for (i=0; i<NUMVAR && failed > 0; i++) {
+ if (var[i].str && *var[i].str && ptrlen(*var[i].str)) {
+ pp = ptrescape(pp, *var[i].str, 0);
+ if (MEM_ERROR) { failed = -1; break; }
+ failed = fprintf(f, "#($%d = \"%s\")\n", i-NUMVAR, ptrdata(pp));
+ }
+ }
+
+ if (failed > 0) {
+ reverse_sortedlist((sortednode **)&sortednamed_vars[0]);
+ for (flag = 0, vp = sortednamed_vars[0]; vp && failed > 0; vp = vp->snext) {
+ if (vp->num) {
+ failed = fprintf(f, "%s@%s = %ld", flag ? ", " : "#(",
+ vp->name, vp->num);
+ flag = 1;
+ }
+ }
+ reverse_sortedlist((sortednode **)&sortednamed_vars[0]);
+ }
+ if (failed > 0 && flag) failed = fprintf(f, ")\n");
+
+ if (failed > 0) {
+ reverse_sortedlist((sortednode **)&sortednamed_vars[1]);
+ for (vp = sortednamed_vars[1]; vp && failed > 0; vp = vp->snext) {
+ if (!is_permanent_variable(vp) && vp->str && ptrlen(vp->str)) {
+ pp = ptrescape(pp, vp->str, 0);
+ if (MEM_ERROR) { failed = -1; break; }
+ failed = fprintf(f, "#($%s = \"%s\")\n", vp->name, ptrdata(pp));
+ }
+ }
+ reverse_sortedlist((sortednode **)&sortednamed_vars[1]);
+ }
+
+ /* GH: fixed the history and word completions saves */
+ if (failed > 0 && opt_history) {
+ l = (curline + 1) % MAX_HIST;
+ while (failed > 0 && l != curline) {
+ if (hist[l] && *hist[l]) {
+ pp = ptrmescape(pp, hist[l], strlen(hist[l]), 0);
+ if (MEM_ERROR) { failed = -1; break; }
+ failed = fprintf(f, "#put %s\n", ptrdata(pp));
+ }
+ if (++l >= MAX_HIST)
+ l = 0;
+ }
+ }
+
+ if (failed > 0 && opt_words) {
+ int cl = 4, len;
+ l = wordindex;
+ flag = 0;
+ while (words[l = words[l].next].word)
+ ;
+ while (words[l = words[l].prev].word && failed > 0) {
+ if (~words[l].flags & WORD_RETAIN) {
+ pp = ptrmescape(pp, words[l].word, strlen(words[l].word), 0);
+ len = ptrlen(pp) + 1;
+ if (cl > 4 && cl + len >= 80) {
+ cl = 4;
+ failed = fprintf(f, "\n");
+ flag = 0;
+ }
+ if (failed > 0)
+ failed = fprintf(f, "%s %s", flag ? "" : "#add", ptrdata(pp));
+ cl += len;
+ flag = 1;
+ }
+ }
+ if (failed > 0 && flag)
+ failed = fprintf(f, "\n");
+ }
+
+ for (kp = keydefs; kp && failed > 0; kp = kp->next) {
+ if (kp->funct==key_run_command)
+ failed = fprintf(f, "#bind %s %s=%s\n", kp->name,
+ seq_name(kp->sequence, kp->seqlen), kp->call_data);
+ else
+ failed = fprintf(f, "#bind %s %s=%s%s%s\n", kp->name,
+ seq_name(kp->sequence, kp->seqlen),
+ internal_functions[lookup_edit_function(kp->funct)].name,
+ kp->call_data ? " " : "",
+ kp->call_data ? kp->call_data : "");
+ }
+
+ if (failed > 0)
+ failed =
+ fprintf(f, full_options_string,
+ opt_exit ? '+' : '-',
+ opt_history ? '+' : '-',
+ opt_words ? '+' : '-',
+ opt_compact ? '+' : '-',
+ opt_debug ? '+' : '-',
+ echo_ext ? '+' : '-',
+ echo_int ? '+' : '-',
+ echo_key ? '+' : '-',
+ opt_speedwalk ? '+' : '-',
+ opt_wrap ? '+' : '-',
+ opt_autoprint ? '+' : '-',
+ opt_reprint ? '+' : '-',
+ opt_sendsize ? '+' : '-',
+ opt_autoclear ? '+' : '-',
+ "\n");
+
+ fclose(f);
+
+ if (error)
+ errmsg("malloc");
+ else if (failed <= 0)
+ fail_msg();
+ else {
+ failed = rename(tmpname, deffile);
+ if (failed == -1) {
+ PRINTF("#error: cannot move temporary file `%s' to `%s': %s\n",
+ tmpname, deffile, strerror(errno));
+ } else
+ failed = 1;
+ }
+
+ if (pp)
+ ptrdel(pp);
+
+ return failed > 0 ? 1 : -1;
+}
+
+/*
+ * update `now' to current time
+ */
+void update_now __P0 (void)
+{
+ if (!now_updated) {
+ gettimeofday(&now, NULL);
+ now_updated = 1;
+ }
+}
+
+/*
+ * terminate powwow as cleanly as possible
+ */
+void exit_powwow __P0 (void)
+{
+ log_flush();
+ if (capturefile) fclose(capturefile);
+ if (recordfile) fclose(recordfile);
+ if (moviefile) fclose(moviefile);
+ (void)save_settings();
+ show_stat();
+ tty_quit();
+ exit(0);
+}