NetBSD/usr.sbin/sushi/sushi.c

399 lines
9.5 KiB
C

/* $NetBSD: sushi.c,v 1.25 2005/06/02 09:41:12 lukem Exp $ */
/*
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Copyright (c) 2000 Tim Rightnour <garbled@NetBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <cdk/cdk.h>
#include <form.h>
#include "sushi.h"
#include "menutree.h"
#include "scandir.h"
#include "formtree.h"
#include "handlers.h"
#include "blabel.h"
MTREE_ENTRY *navigate_menu(struct cqMenu *, char *, char *);
MTREE_ENTRY *navigate_submenu(MTREE_ENTRY *);
MTREE_ENTRY *display_menu(struct cqMenu *, char *, char *);
void parse_config(void);
CDKSCREEN *cdkscreen;
int scripting;
int logging;
FILE *logfile;
FILE *script;
WINDOW *funcwin;
struct winsize ws;
nl_catd catalog;
char *lang_id;
char **searchpaths;
char *keylabel[10];
chtype keybinding[10];
int
main(int argc, char **argv)
{
char *p;
int i;
MTREE_ENTRY *mte = NULL;
catalog = catopen("sushi", 0);
if (getenv("LANG") == NULL)
lang_id = NULL;
else
lang_id = strdup(getenv("LANG"));
parse_config();
if (initscr() == NULL)
errx(1, "%s", "Cannot initialize curses");
cdkscreen = initCDKScreen(stdscr);
(void)ioctl(0, TIOCGWINSZ, &ws);
use_default_colors();
initCDKColor();
curs_set(0);
raw();
tree_init();
i = 0;
for (p = searchpaths[i]; p != NULL; i++) {
p = searchpaths[i];
scan_dir(cqMenuHeadp, p);
}
if (argc > 1) {
mte = tree_gettreebyname(cqMenuHeadp, argv[1]);
if (mte == NULL)
bailout("%s: %s", argv[1], catgets(catalog, 1, 23,
"Quickname not found in any menus"));
}
if (mte == NULL)
navigate_menu(cqMenuHeadp, "sushi_topmenu",
catgets(catalog, 4, 5, "<C></5>Sushi\n\n"));
else
navigate_submenu(mte);
destroyCDKScreen(cdkscreen);
endCDK();
curs_set(1);
endwin();
#ifdef DEBUG
tree_printtree(cqMenuHeadp);
#endif
catclose(catalog);
return (EXIT_SUCCESS);
}
static char *
next_word(char **line)
{
char *word, *p;
p = *line;
while (*++p && isspace((unsigned char)*p))
continue;
word = p;
for (; *p != '\0' && !isspace((unsigned char)*p); ++p)
continue;
*p = '\0';
*line = p;
return (word);
}
static chtype
parse_keybinding(char *key)
{
if (tolower((unsigned char)key[0]) == 'f' &&
isdigit((unsigned char)key[1]))
/* we have an F key */
return (KEY_F(atoi(key + 1)));
else if (key[0] == '^')
return ((chtype)(toupper((unsigned char)key[1]) & ~0x40));
else if (isalpha((unsigned char)key[0]))
/* we have an insane user */
return ((chtype)tolower((unsigned char)key[0]));
bailout("%s: %s", catgets(catalog, 1, 20, "Bad keybinding"), key);
/* NOTREACHED */
return (0);
}
void
parse_config(void)
{
FILE *conf;
size_t len;
int i, j;
char *p, *t, *word;
char *key, *env;
char **n;
searchpaths = NULL;
i = 0;
if ((conf = fopen("/etc/sushi.conf", "r")) != NULL) {
while ((p = fgetln(conf, &len)) != NULL) {
if (len == 1 || p[len - 1] == '#' || p[len - 1] != '\n')
continue;
p[len - 1] = '\0';
while (*p != '\0' && isspace((unsigned char)*p))
++p;
if (*p == '\0' || *p == '#')
continue;
for (t = p; *t && !isspace((unsigned char)*t); ++t)
continue;
if (*t == '\0')
continue;
*t = '\0';
word = p;
while (*++p && !isspace((unsigned char)*p))
continue;
key = strdup(word);
if (key == NULL)
err(1, "strdup");
if (strcmp(key, "searchpath") == 0) {
word = next_word(&p);
n = (char **)realloc(searchpaths,
sizeof(char *) * (i + 3));
if (n == NULL)
err(1, "realloc");
searchpaths = n;
searchpaths[i] = strdup(word);
if (searchpaths[i] == NULL)
err(1, "strdup");
if ((t = strpbrk(searchpaths[i], "\r\n")))
*t = '\0';
i++;
} else if (strcmp(key, "bind") == 0) {
word = next_word(&p);
word++;
j = atoi(word);
j--;
/* now get the character */
word = next_word(&p);
keybinding[j] = parse_keybinding(word);
/* now get the string */
word = next_word(&p);
keylabel[j] = strdup(word);
if (keylabel[j] == NULL)
err(1, "strdup");
} else if (strcmp(key, "env") == 0) {
env = next_word(&p);
word = next_word(&p);
if (*word == '\0')
errx(1, "Invalid env value");
setenv(env, word, 1);
} else {
errx(1, "%s: %s", catgets(catalog, 1, 21,
"Bad keyword in config file"), key);
}
free(key);
} /* while */
}
if (searchpaths == NULL) {
searchpaths = (char **)malloc(sizeof(char *) * 6);
if (searchpaths == NULL)
err(1, "malloc");
searchpaths[0] = "/usr/share/sushi";
searchpaths[1] = "/usr/pkg/share/sushi";
searchpaths[2] = "/usr/X11R6/share/sushi";
searchpaths[3] = "/etc/sushi";
i = 4;
}
if (getenv("HOME") != NULL) {
searchpaths[i] = (char *)malloc(sizeof(char) * PATH_MAX);
if (searchpaths[i] == NULL)
err(1, "malloc");
strlcpy(searchpaths[i], getenv("HOME"), PATH_MAX);
strlcat(searchpaths[i], "/sushi", PATH_MAX);
i++;
}
searchpaths[i] = NULL;
}
MTREE_ENTRY *
navigate_menu(struct cqMenu *cqm, char *basedir, char *ititle)
{
MTREE_ENTRY *mte;
char title[90];
mte = display_menu(cqm, ititle, basedir);
if (mte != NULL) {
if (!TREE_ISEMPTY(&mte->cqSubMenuHead)) {
snprintf(title, sizeof(title), "<C></5>%s\n\n",
mte->itemname);
navigate_menu(&mte->cqSubMenuHead, mte->path, title);
} else
while (handle_endpoint(mte->path) != 0)
continue;
navigate_menu(cqm, basedir, ititle);
}
return (mte);
}
MTREE_ENTRY *
navigate_submenu(MTREE_ENTRY *mte)
{
char title[90];
MTREE_ENTRY *lastmte;
lastmte = mte;
if (TREE_ISEMPTY(&mte->cqSubMenuHead)) {
while (handle_endpoint(mte->path) != 0)
continue;
exit(EXIT_SUCCESS);
}
snprintf(title, sizeof(title), "<C></5>%s\n\n", mte->itemname);
mte = display_menu(&mte->cqSubMenuHead, title, mte->path);
if (mte != NULL) {
if (!TREE_ISEMPTY(&mte->cqSubMenuHead)) {
snprintf(title, sizeof(title), "<C></5>%s\n\n",
mte->itemname);
navigate_submenu(mte);
} else
while (handle_endpoint(mte->path) != 0)
continue;
navigate_submenu(lastmte);
}
return (mte);
}
MTREE_ENTRY *
display_menu(struct cqMenu *cqm, char *title, char *basedir)
{
CDKSCROLL *scrollp;
MTREE_ENTRY *mte;
char **menu;
int i, items, selection;
items = tree_entries(cqm);
if ((menu = malloc(sizeof(char *) * (items + 1))) == NULL)
bailout("malloc: %s", strerror(errno));
items = 0;
for (mte = CIRCLEQ_FIRST(cqm); mte != (void *)cqm;
mte = CIRCLEQ_NEXT(mte, cqMenuEntries)){
if (strcmp(mte->itemname, "BLANK") == 0) {
if ((menu[items] = strdup("")) == NULL)
bailout("strdup: %s", strerror(errno));
} else {
if ((menu[items] = strdup(mte->itemname)) == NULL)
bailout("strdup: %s", strerror(errno));
}
++items;
}
if (items == 0) {
destroyCDKScreen(cdkscreen);
endCDK();
curs_set(1);
endwin();
(void)fprintf(stderr, "%s\n", catgets(catalog, 1, 19,
"No menu hierarchy found"));
catclose(catalog);
exit(EXIT_FAILURE);
}
bottom_help(1);
scrollp = newCDKScroll(cdkscreen, CENTER, TOP, RIGHT,
ws.ws_row-3, ws.ws_col, title, menu, items, FALSE, A_REVERSE,
TRUE, FALSE);
if (scrollp == NULL)
bailout(catgets(catalog, 1, 16,
"Cannot allocate scroll widget"));
bind_menu(scrollp, basedir);
do {
selection = activateCDKScroll(scrollp, NULL);
mte = tree_getentry(cqm, selection);
} while (selection != -1 && strcmp(mte->filename, "BLANK") == 0);
destroyCDKScroll(scrollp);
for (i = 0; i < items; i++)
free(menu[i]);
free(menu);
return (mte);
}
void
bailout(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
destroyCDKScreen(cdkscreen);
endCDK();
curs_set(1);
fprintf(stderr, "%s: ", getprogname());
if (fmt != NULL)
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
catclose(catalog);
exit(EXIT_FAILURE);
}