/* setopts.c (c) copyright 1986 (Dan Heller) */ #include "mush.h" #include "bindings.h" static void insert_option(list, opt, order) struct options **list, *opt; int order; /* Insert in sorted order? */ { while (*list && (!order || (strcmp((*list)->option, opt->option) < 1))) list = &((*list)->next); opt->next = *list; *list = opt; } /* add an option indicated by "set option[=value]" or by "alias name alias" * function is recursive, so multilists get appended accordingly */ add_option(list, argv) register struct options **list; register char **argv; { register struct options *tmp; register char *option, *value = NULL; if (!(option = *argv)) return 1; /* check for one of three forms: * option=value option= value option = value */ if (value = index(option, '=')) { if (value == option) { print("No variable specified\n"); return 0; } /* "option=value" strip into option="option" value="value" */ *value++ = 0; /* option is now a null terminated `option' */ if (*value || (value = *++argv)) { /* "option= value" */ ++argv; } } else if (*++argv && !strcmp(*argv, "=")) { if (value = *++argv) /* "option = value" */ ++argv; } /* check for internal vars that can't be set this way */ if (*list == set_options && check_internal(option)) { print("You can't change %s with \"set\".\n", option); return 0; } /* check to see if option is already set by attempting to unset it */ if (un_set(list, option) == -1) return 0; /* now make a new option struct and set fields */ if (!(tmp = (struct options *)calloc((unsigned)1,sizeof(struct options)))) { error("calloc"); return -1; } tmp->option = savestr(option); tmp->value = savestr(value); /* strdup handles the NULL case */ insert_option(list, tmp, (list != &own_hdrs)); /* check for options which must have values or are used frequently */ if (*list == set_options) { #if defined(CURSES) || defined(SUNTOOL) if (!strcmp(tmp->option, "no_reverse")) turnoff(glob_flags, REV_VIDEO); else #endif /* CURSES || SUNTOOL */ #ifdef SUNTOOL if (!strcmp(tmp->option, "tool_help")) if (tmp->value && *(tmp->value)) strdup(tool_help, tmp->value); else { int n = 0; char *p = getpath(TOOL_HELP, &n); if (n) strdup(tool_help, "tool_help"); else strdup(tool_help, p); strdup(tmp->value, tool_help); } else #endif /* SUNTOOL */ if (!strcmp(tmp->option, "cmd_help")) if (tmp->value && *(tmp->value)) strdup(cmd_help, tmp->value); else { int n = 0; /* don't ignore no such file or directory */ char *p = getpath(COMMAND_HELP, &n); if (n) strdup(cmd_help, "cmd_help"); else strdup(cmd_help, p); strdup(tmp->value, cmd_help); } else if (!strcmp(tmp->option, "prompt")) prompt = (tmp->value)? tmp->value : DEF_PROMPT; else if (!strcmp(tmp->option, "warning")) turnon(glob_flags, WARNING); else if (!strcmp(tmp->option, "mil_time")) turnon(glob_flags, MIL_TIME); #ifndef MSG_SEPARATOR else if (!strcmp(tmp->option, "date_received")) turnon(glob_flags, DATE_RECV); #endif /* MSG_SEPARATOR */ else if (!strcmp(tmp->option, "escape")) { escape = (tmp->value)? tmp->value : DEF_ESCAPE; escape[1] = 0; /* only one character, please */ } else if (!strcmp(tmp->option, "hdr_format")) hdr_format = (tmp->value)? tmp->value : DEF_HDR_FMT; else if (!strcmp(tmp->option, "crt")) { if (!istool) crt = (tmp->value)? max(atoi(tmp->value), 2): 18; } else if (!strcmp(tmp->option, "screen")) { screen = (tmp->value)? max(atoi(tmp->value), 1): 18; #ifdef CURSES if (iscurses && screen > LINES-2) screen = LINES-2; #endif /* CURSES */ } else if (!strcmp(tmp->option, "wrapcolumn")) { char wval[16]; wrapcolumn = (tmp->value && *(tmp->value))? max(atoi(tmp->value), 0): 78; #ifdef CURSES /* Use COLS-2 because of silly terminals like vt100 */ if (iscurses && wrapcolumn > COLS - 2) wrapcolumn = COLS - 2; #endif /* CURSES */ xfree(tmp->value); tmp->value = savestr(sprintf(wval, "%d", wrapcolumn)); } else if (!strcmp(tmp->option, "history")) init_history((value && *value)? atoi(value) : 1); else if (!strcmp(tmp->option, "realname")) { char *new[4]; new[1] = "NAME"; new[2] = tmp->value; new[3] = NULL; (void) Setenv(3, new); /* new[0] is ignored */ } else if (!strcmp(tmp->option, "known_hosts")) { register char *p; int n; /* in case user separated with commas */ for (p = index(tmp->value, ','); p; p = index(p+1, ',')) *p = ' '; free_vec(known_hosts); known_hosts = mk_argv(tmp->value, &n, FALSE); } else if (!strcmp(tmp->option, "hostname")) { register char *p; int n; /* in case user separated with commas */ for (p = index(tmp->value, ','); p; p = index(p+1, ',')) *p = ' '; free_vec(ourname); ourname = mk_argv(tmp->value, &n, FALSE); } else if (!strcmp(tmp->option, "complete")) { if (value && *value) { m_xlate(value); /* use the original, don't change tmp->value */ complete = value[0]; complist = value[1]; } else { tmp->value = savestr("\\E\\CD"); complete = '\033'; complist = '\004'; } } } if (*argv) return add_option(list, argv); return 1; } /* * If str is NULL, just print options and their values. Note that numerical * values are not converted to int upon return. If str is not NULL * return the string that matched, else return NULL; */ char * do_set(list, str) register struct options *list; register char *str; { register struct options *opts; if (!str) (void) do_pager(NULL, TRUE); /* page using internal pager */ for (opts = list; opts; opts = opts->next) if (!str) { (void) do_pager(opts->option, FALSE); if (opts->value && *opts->value) { (void) do_pager(" \t", FALSE); (void) do_pager(opts->value, FALSE); } if (do_pager("\n", FALSE) == EOF) break; } else { if (strcmp(str, opts->option)) continue; if (opts->value) return opts->value; else return ""; } if (!str) (void) do_pager(NULL, FALSE); /* terminate internal pager */ /* if we still haven't matched, check for environment vars */ if (str && list == set_options) { register int N, n; for (N = 0; environ[N]; N++) { char *p = index(environ[N], '='); if (p) *p = 0; n = lcase_strncmp(str, environ[N], -1); if (p) *p = '='; if (!n) return p+1; } } return NULL; } /* * unset the variable described by p in the list "list". * if the variable isn't set, then return 0, else return 1. */ un_set(list, p) register struct options **list; register char *p; { register struct options *opts = *list, *tmp; if (!list || !*list || !p || !*p) return 0; if (*list == set_options) { #if defined(CURSES) || defined(SUNTOOL) if (!strcmp(p, "no_reverse")) turnon(glob_flags, REV_VIDEO); else #endif /* CURSES || SUNTOOL */ if (!strcmp(p, "prompt")) prompt = DEF_PROMPT; else if (!strcmp(p, "warning")) turnoff(glob_flags, WARNING); else if (!strcmp(p, "mil_time")) turnoff(glob_flags, MIL_TIME); #ifndef MSG_SEPARATOR else if (!strcmp(p, "date_received")) turnoff(glob_flags, DATE_RECV); #endif /* MSG_SEPARATOR */ else if (!strcmp(p, "escape")) escape = DEF_ESCAPE; else if (!strcmp(p, "hdr_format")) hdr_format = DEF_HDR_FMT; else if (!strcmp(p, "crt")) crt = 18; else if (!strcmp(p, "screen")) { screen = 18; #ifdef CURSES if (iscurses && screen > LINES-2) screen = LINES-2; #endif /* CURSES */ } else #ifdef SUNTOOL if (!strcmp(p, "tool_help")) { int n = 0; char *p2 = getpath(TOOL_HELP, &n); if (n) strdup(tool_help, "tool_help"); else strdup(tool_help, p2); } else #endif /* SUNTOOL */ if (!strcmp(p, "cmd_help")) { int n = 0; /* don't ignore no such file or directory */ char *p2 = getpath(COMMAND_HELP, &n); if (n) strdup(cmd_help, "cmd_help"); else strdup(cmd_help, p2); } else if (!strcmp(p, "wrapcolumn")) wrapcolumn = 0; else if (!strcmp(p, "history")) init_history(1); else if (!strcmp(p, "known_hosts")) { free_vec(known_hosts); known_hosts = DUBL_NULL; } else if (!strcmp(p, "hostname")) { free_vec(ourname); ourname = DUBL_NULL; } else if (ison(glob_flags, IS_GETTING) && !strcmp(p, "edit_hdrs")) { wprint("You must finish this letter first.\n"); return -1; } else if (!strcmp(p, "complete")) complete = complist = 0; #ifdef SUNTOOL else if (!strcmp(p, "compose_icon")) { if (ison(glob_flags, IS_GETTING)) { wprint("You must finish this letter first.\n"); return -1; } else /* destroy compose frame so that it is recreated * later as the proper type (base frame or subframe). */ if (compose_frame) destroy_compose(); } #endif } if (!strcmp(p, opts->option)) { *list = (*list)->next; xfree (opts->option); if (opts->value) xfree(opts->value); xfree((char *)opts); return 1; } for ( ; opts->next; opts = opts->next) if (!strcmp(p, opts->next->option)) { tmp = opts->next; opts->next = opts->next->next; xfree (tmp->option); if (tmp->value) xfree(tmp->value); xfree ((char *)tmp); return 1; } return 0; } /* The functions below return 0 since they don't affect * messages. */ set(n, argv, list) register int n; register char **argv; char *list; { void list_to_str(); char firstchar = **argv; register char *cmd = *argv; register struct options **optlist; char buf[BUFSIZ]; if (*cmd == 'u') cmd += 2; if (*++argv && !strcmp(*argv, "-?")) return help(0, (*cmd == 'i')? "ignore": "set", cmd_help); if (*argv && **argv == '?') { int incurses; if (!strcmp(*argv, "?all")) { if (incurses = iscurses) /* assign and compare to TRUE */ clr_bot_line(), iscurses = FALSE; (void) do_pager(NULL, TRUE); /* start internal pager */ for (n = 0; variable_stuff(n, NULL, buf); n++) if (do_pager(strcat(buf, "\n"), FALSE) == EOF) break; (void) do_pager(NULL, FALSE); /* terminate pager */ iscurses = incurses; } else { /* May return null if variable not set. */ (void) variable_stuff(0, (*argv)+1, buf); print("%s\n", buf); } return 0; } if (firstchar == 'u') { if (!*argv) { print("%s what?\n", cmd); return -1; } else { optlist = (*cmd == 'i')? &ignore_hdr : &set_options; do if (!strcmp(*argv, "*")) { while (*optlist) (void) un_set(optlist, (*optlist)->option); #ifdef SUNTOOL if (*cmd != 'i') opts_panel_item(NULL); #endif /* SUNTOOL */ } else if (!un_set(optlist, *argv) && do_set(set_options, "warning")) print("un%s: %s not set\n", (*cmd == 'i')? "ignore" : "set", *argv); #ifdef SUNTOOL else if (*cmd != 'i') opts_panel_item(*argv); #endif /* SUNTOOL */ while (*++argv); #ifdef SUNTOOL if (*cmd == 'i' && istool > 1) update_list_textsw(&ignore_hdr); #endif /* SUNTOOL */ } return 0; } if (!*argv) { (void) do_set((*cmd == 'i')? ignore_hdr: set_options, NULL); return 0; } /* * Check for input redirection. If so, set the variable to the ascii * value of the current msg_list. */ if (ison(glob_flags, IS_PIPE)) { char *newargv[4]; if (*cmd == 'i') { print("You can't pipe to the \"%s\" command.\n", cmd); return -1; } if (newargv[0] = index(argv[0], '=')) *newargv[0] = 0; list_to_str(list, buf); if (!buf[0] && !do_set(set_options, argv[0])) { return 0; } newargv[0] = argv[0]; newargv[1] = "="; newargv[2] = buf; newargv[3] = NULL; (void) add_option(&set_options, newargv); return 0; } /* * finally, just set the variable the user requested. */ (void) add_option((*cmd == 'i')? &ignore_hdr: &set_options, argv); #ifdef SUNTOOL if (istool > 1) if (*cmd == 'i') update_list_textsw(&ignore_hdr); else opts_panel_item(argv[0]); #endif /* SUNTOOL */ return 0; } /* * The alts list is a list of hostnames or pathnames where the user * has an account. If he doesn't specify "metoo", then when replying * to mail, if his address is listed, it will be removed. The syntax * is compatible with ucb Mail in that just hostnames can be used. * However, there is an added feature that mush provides which another * login name or path to another login can be specified by preceding the * path or login with a ! * "argv" may be a file pointer to write the data into by use of save_opts() */ alts(argc, argv) register char **argv; { char buf[BUFSIZ], *p; /* check here first because a 0 argc means to write it to a file */ if (argc <= 1) { int n; if (!alternates) return 0; if (argc == 0) (void) fprintf((FILE *)argv, "alts "); for (n = 0; alternates[n]; n++) { p = 0; buf[0] = 0; (void) strcpy(&buf[1], alternates[n]); if (buf[1] != '*') (void) reverse(&buf[1]); if ((p = rindex(&buf[1], '!')) && !lcase_strncmp(p+1, login, -1)) *p = 0; else if (buf[1] != '*') buf[0] = '!'; if (argc == 0) (void) fprintf((FILE *)argv, "%s ", *buf? buf : &buf[1]); else wprint("%s ", *buf? buf : &buf[1]); if (p) *p = '!'; } if (argc == 0) (void) fputc('\n', (FILE *)argv); else wprint("\n"); return 0; } if (argc-- && *++argv && !strcmp(*argv, "-?")) return help(0, "alts", cmd_help); free_vec(alternates); if (alternates = (char **)calloc((unsigned)argc+1, sizeof(char *))) while (argc-- > 0) { if (argv[argc][0] == '!') alternates[argc] = savestr(reverse(&argv[argc][1])); else if (argv[argc][0] == '*') { alternates[argc] = savestr(argv[argc]); } else { if (index(argv[argc], '@')) bang_form(buf, argv[argc]); else { p = buf + Strcpy(buf, argv[argc]); *p++ = '!', p += Strcpy(p, login); } alternates[argc] = savestr(reverse(buf)); } } return 0; } save_opts(cnt, argv) char **argv; { char file[MAXPATHLEN], *tmp; register FILE *fp; if (cnt && *++argv && !strcmp(*argv, "-?")) return help(0, "source", cmd_help); if (cnt && *argv) (void) strcpy(file, *argv); else if ((tmp = getenv("MUSHRC")) || (tmp = getenv("MAILRC"))) (void) strcpy(file, tmp); else { char *home = do_set(set_options, "home"); if (!home || !*home) home = ALTERNATE_HOME; /* if .mushrc doesn't exist, check .mailrc. If neither, force .mushrc */ if (Access(sprintf(file, "%s/%s", home, MAILRC), F_OK) && Access(sprintf(file, "%s/%s", home, ALTERNATE_RC), F_OK)) (void) sprintf(file, "%s/%s", home, MAILRC); } cnt = 1; tmp = getpath(file, &cnt); if (cnt) { if (cnt == -1) { print("%s: %s\n", file, tmp); return -1; } else { print("%s is a directory.\n", tmp); return -2; } } /* See if the file exists and confirm overwrite */ if (!Access(tmp, F_OK)) { int overwrite = TRUE; char buf[BUFSIZ]; if (!istool) { print("\"%s\" exists. Overwrite? ", trim_filename(tmp)); if (Getstr(buf, 3, 0) <= 0 || lower(*buf) != 'y') overwrite = FALSE; } #ifdef SUNTOOL else { sprintf(buf, "\"%s\" exists. Overwrite? ", trim_filename(tmp)); overwrite = ask(buf); } #endif /* SUNTOOL */ if (!overwrite) { print("\"%s\" unchanged.\n", tmp); return -3; } } if (!(fp = fopen(tmp, "w"))) { error("Can't open %s", file); return -1; } save_list("basic variable settings", set_options, "set", '=', fp); save_list("mail headers for outgoing mail", own_hdrs, "my_hdr", 0, fp); save_list("aliases", aliases, "alias", 0, fp); (void) alts(0, (char **)fp); save_list("headers to ignore", ignore_hdr, "ignore", ' ', fp); save_list("command abbreviations", functions, "cmd", ' ', fp); save_list("command macros for function keys", fkeys, "fkey", ' ', fp); #ifdef CURSES save_cmd("curses mode key bindings", cmd_map, "bind", 1, fp); #endif /* CURSES */ save_cmd("line mode mappings", line_map, "map", 0, fp); save_cmd("composition mode mappings", bang_map, "map!", 0, fp); (void) fclose(fp); print("All variables and options saved in %s\n", trim_filename(tmp)); return 0; } char * quoteit(str, in_quotes, fix_vars) char *str; int in_quotes; /* Type of quote the string is in, if any */ int fix_vars; /* Variables will be expanded, so quote $ */ { #define other_quote(x) ((x == '"')? '\'' : '"') static char *buf; static int bufsiz; char *s = str, *d; int len = str? strlen(str) : 0, was_magic = FALSE; if (!len) return str; if (bufsiz < 2 * len) { xfree(buf); buf = malloc(bufsiz = 2 * len); } if (!buf) return NULL; for (d = buf; *d = *s; d++, s++) { if ((*s == '\'' || *s == '"') && (was_magic || in_quotes != other_quote(*s))) { if (was_magic) { if (*s == '\'') continue; /* else '"' */ ++d; was_magic = FALSE; } if (in_quotes == *s) *d++ = *s; *d = other_quote(*s); *++d = *s; *++d = other_quote(*s); if (in_quotes == *s) *++d = *s; } else if (*s == '$' && fix_vars && (was_magic || in_quotes != '\'')) { if (was_magic || in_quotes == '"') { *d++ = '"'; was_magic = FALSE; } *d = '\''; *++d = *s; *++d = '\''; if (in_quotes == '"') *++d = in_quotes; } else if (fix_vars && isspace(*s)) { /* backslash-newline may get stripped when fix_vars */ if (*s == '\n' || *s == '\r') { *d = '\\'; *++d = *s; *++d = ' '; /* XXX not perfect, but ... */ } else if (!was_magic && !in_quotes) { *d = '"'; *++d = *s; was_magic = TRUE; } } else if (!in_quotes && index("#;|~", *s)) { if (*s == '~' && !fix_vars) { /* !fix_vars implies !fix_tildes */ if (was_magic) { *d = '"'; *++d = *s; was_magic = FALSE; } } else if (!was_magic && (s == str || *s != '~')) { *d = '"'; *++d = *s; was_magic = TRUE; } } } if (was_magic) { *d = '"'; *++d = '\0'; } return buf; #undef other_quote } save_list(title, list, command, equals, fp) struct options *list; register char *command, *title, equals; register FILE *fp; { register struct options *opts; register char *p; if (!list) return; (void) fprintf(fp, "#\n# %s\n#\n", title); for (opts = list; opts; opts = opts->next) { if (list == set_options && !strcmp(opts->option, "cwd")) continue; /* don't print $cwd */ (void) fprintf(fp, "%s %s", command, opts->option); if (opts->value && *opts->value) { register char *quote; if (!equals) quote = NO_STRING; else if (p = any(opts->value, "\"'")) if (*p == '\'') quote = "\""; else quote = "'"; else if (!any(opts->value, " \t;|")) quote = NO_STRING; else quote = "'"; (void) fputc(equals? equals: ' ', fp); (void) fprintf(fp, "%s%s%s", quote, quoteit(opts->value, quote, TRUE), quote); } (void) fputc('\n', fp); } } extern struct cmd_map map_func_names[]; save_cmd(title, list, command, equals, fp) struct cmd_map *list; register char *command, *title; register int equals; register FILE *fp; { register struct cmd_map *opts; register char *p; char buf[MAX_MACRO_LEN * 2]; if (!list) return; (void) fprintf(fp, "#\n# %s\n#\n", title); for (opts = list; opts; opts = opts->m_next) { register char *quote; if ((p = any(opts->m_str, "\"'")) && *p == '\'') quote = "\""; else quote = "'"; (void) fprintf(fp, "%s %s%s%s", command, quote, quoteit(ctrl_strcpy(buf, opts->m_str, TRUE), quote, TRUE), quote); if (equals && map_func_names[opts->m_cmd].m_str) (void) fprintf(fp, " %s", map_func_names[opts->m_cmd].m_str); if (opts->x_str && *opts->x_str) { if ((p = any(opts->x_str, "\"'")) && *p == '\'') quote = "\""; else quote = "'"; (void) fprintf(fp, " %s%s%s", quote, quoteit(ctrl_strcpy(buf, opts->x_str, TRUE), quote, TRUE), quote); } (void) fputc('\n', fp); } } /* * do_alias handles aliases, header settings, functions, and fkeys. * since they're all handled in the same manner, the same routine is * used. argv[0] determines which to use. * alias is given here as an example * * alias identify all aliases * alias name identify alias * alias name arg1 arg2 arg3... -> name="arg1 arg2 arg3"; call add_option * unalias arg1 [arg2 arg3 ... ] unalias args * * same is true for dealing with your own headers. * (also the expand command) */ do_alias(argc, argv) register char **argv; { register char *cmd = *argv, *p; struct options **list; char firstchar = *cmd, buf[HDRSIZ]; if (argc == 0) return 0 - in_pipe(); if (firstchar == 'u') firstchar = cmd[2]; if (*++argv && !strcmp(*argv, "-?")) { /* doesn't apply for fkeys */ register char *help_str; if (firstchar == 'a' || firstchar == 'e') help_str = "alias"; else if (firstchar == 'c') help_str = "cmd"; else if (firstchar == 'f') help_str = "fkey"; else help_str = "my_hdr"; return help(0, help_str, cmd_help); } if (firstchar == 'a') list = &aliases; else if (firstchar == 'c') list = &functions; else if (firstchar == 'f') list = &fkeys; else list = &own_hdrs; if (*cmd == 'u') { if (!*argv) { print("%s what?\n", cmd); return -1; /* unset a list separated by spaces or ',' */ } else while (*argv) { if (!strcmp(*argv, "*")) /* unset everything */ while (*list) (void) un_set(list, (*list)->option); else if (!un_set(list, *argv)) print("\"%s\" isn't set\n", *argv); argv++; } #ifdef SUNTOOL if (istool > 1) update_list_textsw(list); #endif /* SUNTOOL */ return 0; } if (!*argv && *cmd != 'e') { /* just type out all the aliases or own_hdrs */ (void) do_set(*list, NULL); return 0; } if (*cmd == 'e') { /* command was "expand" (aliases only) */ if (!*argv) { print("expand which alias?\n"); return -1; } else do { print("%s: ", *argv); if (p = alias_to_address(*argv)) print("%s\n", p); } while (*++argv); return 0; } /* at this point, *argv now points to a variable name ... * check for hdr -- if so, *argv better end with a ':' (check *p) */ if (list == &own_hdrs && !(p = index(*argv, ':'))) { print("header labels must end with a ':' (%s)\n", *argv); return -1; } if (!argv[1] && !index(*argv, '=')) if (p = do_set(*list, *argv)) print("%s\n", p); else print("%s is not set\n", *argv); else { char *tmpargv[2]; (void) argv_to_string(buf, argv); if ((p = any(buf, " \t=")) && *p != '=') *p = '='; /* if we're setting an alias, enforce the insertion of commas * between each well-formed address. */ if (list == &aliases) fix_up_addr(p+1); tmpargv[0] = buf; tmpargv[1] = NULL; (void) add_option(list, tmpargv); #ifdef SUNTOOL if (istool > 1) update_list_textsw(list); #endif /* SUNTOOL */ } return 0; }