From 6ecb4854b442d72249f80130d30438953fde6e18 Mon Sep 17 00:00:00 2001 From: marschap Date: Thu, 11 Oct 2007 08:17:59 +0000 Subject: [PATCH] lcdexec: parameter support using widgets; hidden behind compile time option --- ChangeLog | 1 + clients/lcdexec/lcdexec.c | 160 ++++++++++-- clients/lcdexec/lcdexec.conf | 77 +++++- clients/lcdexec/menu.c | 463 ++++++++++++++++++++++++++++++----- clients/lcdexec/menu.h | 65 ++++- docs/lcdexec.1 | 10 +- 6 files changed, 681 insertions(+), 95 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2d89a57..7df2abe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -33,6 +33,7 @@ v.0.5dev (ongoing development) * picolcd driver: overhaul USB init for more portability (M. Dolze) + curses driver: new option DrawBorder (Bruno Schwander) * avoid sending duplicate "success" messages in response to menu_add_item + + lcdexec: parameter support using widgets; hidden behind compile time option v.0.5.2 * fix switching on/off the Load screen in lcdproc client using the menu diff --git a/clients/lcdexec/lcdexec.c b/clients/lcdexec/lcdexec.c index f47b12d..c8725b3 100644 --- a/clients/lcdexec/lcdexec.c +++ b/clients/lcdexec/lcdexec.c @@ -40,6 +40,7 @@ typedef struct ProcInfo { time_t starttime; /**< start time of the process */ time_t endtime; /**< finishing time of the process */ int status; /**< exit status of the process */ + int feedback; /**< what info to show to the user */ int shown; /**< tell if the info has been shown to the user */ } ProcInfo; @@ -342,8 +343,9 @@ static int process_response(char *str) free(str2); return -1; } - if (strcmp(argv[1], "select") == 0) { - MenuEntry *exec; + if ((strcmp(argv[1], "select") == 0) || + (strcmp(argv[1], "leave") == 0)) { + MenuEntry *entry; if (argc < 3) { report(RPT_WARNING, "Server gave invalid response"); @@ -351,16 +353,85 @@ static int process_response(char *str) return -1; } - /* Find the id */ - exec = menu_find_by_id(main_menu, atoi(argv[2])); - if (exec == NULL) { + /* Find the entry by id */ + entry = menu_find_by_id(main_menu, atoi(argv[2])); + if (entry == NULL) { report(RPT_WARNING, "Could not find the item id given by the server"); free(str2); return -1; } - /* The id has been found */ - exec_command(exec); + + /* The id has been found. + * We trigger on the following conditions: + * - command entry without args + * - last arg of a command entry with args */ + if (((entry->type == MT_EXEC) && (entry->children == NULL)) || + ((entry->type & MT_ARG_ANY) && (entry->next == NULL))) { + + // last arg => get parent entry + if ((entry->type & MT_ARG_ANY) && (entry->next == NULL)) + entry = entry->parent; + + if (entry->type == MT_EXEC) + exec_command(entry); + } } +#if defined(LCDEXEC_PARAMS) + else if ((strcmp(argv[1], "plus") == 0) || + (strcmp(argv[1], "minus") == 0) || + (strcmp(argv[1], "update") == 0)) { + MenuEntry *entry; + + if (argc < 4) { + report(RPT_WARNING, "Server gave invalid response"); + free(str2); + return -1; + } + + /* Find the entry by id */ + entry = menu_find_by_id(main_menu, atoi(argv[2])); + if (entry == NULL) { + report(RPT_WARNING, "Could not find the item id given by the server"); + free(str2); + return -1; + } + + switch (entry->type) { + case MT_ARG_SLIDER: + entry->data.slider.value = atoi(argv[3]); + break; + case MT_ARG_RING: + entry->data.ring.value = atoi(argv[3]); + break; + case MT_ARG_NUMERIC: + entry->data.numeric.value = atoi(argv[3]); + break; + case MT_ARG_ALPHA: + entry->data.alpha.value = realloc(entry->data.alpha.value, + strlen(argv[3])); + strcpy(entry->data.alpha.value, argv[3]); + break; + case MT_ARG_IP: + entry->data.ip.value = realloc(entry->data.ip.value, + strlen(argv[3])); + strcpy(entry->data.ip.value, argv[3]); + break; + case MT_ARG_CHECKBOX: + if ((entry->data.checkbox.allow_gray) && + (strcasecmp(argv[3], "gray") == 0)) + entry->data.checkbox.value = 2; + else if (strcasecmp(argv[3], "on") == 0) + entry->data.checkbox.value = 1; + else + entry->data.checkbox.value = 0; + break; + default: + report(RPT_WARNING, "Illegal menu entry type for event"); + free(str2); + return -1; + } + } +#endif else { ; /* Ignore other menuevents */ } @@ -401,18 +472,64 @@ static int exec_command(MenuEntry *cmd) const char *argv[4]; pid_t pid; ProcInfo *p; +#if defined(LCDEXEC_PARAMS) + char *envp[cmd->numChildren+1]; + MenuEntry *arg; + int i; +#endif - report(RPT_NOTICE, "Executing: %s", command); - + /* set argument vector */ argv[0] = default_shell; argv[1] = "-c"; argv[2] = command; argv[3] = NULL; +#if defined(LCDEXEC_PARAMS) + /* set environment vector: allocate & fill contents */ + for (arg = cmd->children, i = 0; arg != NULL; arg = arg->next, i++) { + char buf[1025]; + + switch (arg->type) { + case MT_ARG_SLIDER: + snprintf(buf, 1024, "%s=%d", arg->name, arg->data.slider.value); + break; + case MT_ARG_RING: + snprintf(buf, 1024, "%s=%s", arg->name, arg->data.ring.strings[arg->data.ring.value]); + break; + case MT_ARG_NUMERIC: + snprintf(buf, 1024, "%s=%d", arg->name, arg->data.numeric.value); + break; + case MT_ARG_ALPHA: + snprintf(buf, 1024, "%s=%s", arg->name, arg->data.alpha.value); + break; + case MT_ARG_IP: + snprintf(buf, 1024, "%s=%s", arg->name, arg->data.ip.value); + break; + case MT_ARG_CHECKBOX: + snprintf(buf, 1024, "%s=%d", arg->name, arg->data.checkbox.value); + break; + default: + /* error ? */ + break; + } + buf[1024] ='\0'; + envp[i] = strdup(buf); + + debug(RPT_DEBUG, "Environment: %s", envp[i]); + } + envp[cmd->numChildren] = NULL; +#endif + + debug(RPT_DEBUG, "Executing '%s' via Shell %s", command, default_shell); + switch (pid = fork()) { case 0: /* We're the child: execute the command */ +#if defined(LCDEXEC_PARAMS) + execve(argv[0], (char **) argv, envp); +#else execv(argv[0], (char **) argv); +#endif exit(0); break; default: @@ -422,15 +539,23 @@ static int exec_command(MenuEntry *cmd) p->cmd = cmd; p->pid = pid; p->starttime = time(NULL); + p->feedback = cmd->data.exec.feedback; /* prepend it to existing queue atomically */ p->next = proc_queue; proc_queue = p; } - break; + break; case -1: report(RPT_ERR, "Could not fork"); return -1; } + +#if defined(LCDEXEC_PARAMS) + /* free envp's contents */ + for (i = 0; envp[i] != NULL; i++) + free(envp[i]); +#endif + return 0; } return -1; @@ -441,11 +566,15 @@ static int show_procinfo_msg(ProcInfo *p) { if ((p != NULL) && (lcd_wid > 0) && (lcd_hgt > 0)) { if (p->endtime > 0) { + /* nothing to do => the quick way out (successful) */ + if ((p->shown) || (!p->feedback)) + return 1; + sock_printf(sock, "screen_add [%u]\n", p->pid); sock_printf(sock, "screen_set [%u] -name {lcdexec [%u]}" " -priority alert -timeout %d" " -heartbeat off\n", - p->pid, p->pid, 8*8); + p->pid, p->pid, 6*8); if (lcd_hgt > 2) { sock_printf(sock, "widget_add [%u] t title\n", p->pid); @@ -472,8 +601,9 @@ static int show_procinfo_msg(ProcInfo *p) p->pid, WTERMSIG(p->status)); } - sock_printf(sock, "widget_set [%u] s3 1 4 {Exec time: %lds}\n", - p->pid, p->endtime - p->starttime); + if (lcd_hgt > 3) + sock_printf(sock, "widget_set [%u] s3 1 4 {Exec time: %lds}\n", + p->pid, p->endtime - p->starttime); } else { sock_printf(sock, "widget_add [%u] s1 string\n", p->pid); @@ -496,9 +626,10 @@ static int show_procinfo_msg(ProcInfo *p) } } + return 1; } } - return 1; + return 0; } @@ -560,3 +691,4 @@ static int main_loop(void) return 0; } +/* EOF */ diff --git a/clients/lcdexec/lcdexec.conf b/clients/lcdexec/lcdexec.conf index feef29e..0932e5c 100644 --- a/clients/lcdexec/lcdexec.conf +++ b/clients/lcdexec/lcdexec.conf @@ -38,6 +38,8 @@ Entry=MenuC DisplayName="You can say A" # the exec=... line tells that it is a command Exec="echo a" +# show a temporary feedback screen upon completion [default: no; legal: yes, no] +Feedback= yes [CmdB] DisplayName="Or you can say B" @@ -55,20 +57,77 @@ DisplayName=P Exec="echo P" [CmdQ] -DisplayName=Q -Exec="echo Q" +DisplayName="Show environment" +Exec="env" +# When lcdexec is compiled with compiler option -DLCDEXEC_PARAMS +# you can change parameters for commands within lcdexec. +# Parameters are handed to the program as environment variables; +# so they can be easily used in the Exec=... line as $XXX +# (e.g. "echo $SLIDER_ARG"). +# Please note a little issue with parameters: after changing the +# last parameter of a command the command gets executed automatically. +# This is IMHO noti ideal. I'm open for ideas to improve it. +# Here's how they are defined: +#Parameter=SLIDER_ARG +#Parameter=RING_ARG +#Parameter=NUMERIC_ARG +#Parameter=ALPHA_ARG +#Parameter=CHECKBOX_ARG +#Parameter=IP_ARG +# +#[SLIDER_ARG] +#DisplayName="Slider" +## Type of argument widget [legal: Slider, Checkbox, Ring, Numeric, Alpha, IP] +#Type=Slider +## inital value of the argument +#Value=5 +## options depending on the widget type +#MinValue=0 +#MaxValue=10 +# +#[CHECKBOX_ARG] +#DisplayName="Checkbox" +#Type=Checkbox +#Value=on +#AllowGray=no +# +#[RING_ARG] +#DisplayName="Ring" +#Type=Ring +#Value=0 +## list of alternative strings +#String=Eins +#String=Zwei +#String=Drei +# +#[NUMERIC_ARG] +#DisplayName="Numeric" +#Type=Numeric +#Value=5 +#MinValue=0 +#MaxValue=10 +# +#[ALPHA_ARG] +#DisplayName="Alpha" +#Type=Alpha +## range of characters allowed [default: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"] +#AllowedChars = "+-0123456789ABCDEF" +#Value=5 +#MinLength=0 +#MaxLength=10 +# +#[IP_ARG] +#DisplayName="IP" +#Type=IP +#Value=10.230.2.2 +#v6=no -# Further Extensions: +# Ideas for further extensions: # - shell selectable in command sections # - type definitions instead implicit depending on Exec/Entry # (the latter only as fallback) -# - use input for parameters e.g. IP-Adresses, Sliders -# e.g. Exec="ifconfig ${IF} ${IP} netmask ${MASK} broadcast ${BCAST}" -# where -# - ${IF} is the result of a selection input screen -# - ${IP}, ${MASK}n ${BCAST} are the results of IP input screens -# - display result if a command on the display +# - display configurable result of a command on the display # - jump to other menus depending on the output/result of a command # EOF diff --git a/clients/lcdexec/menu.c b/clients/lcdexec/menu.c index 9ccf6f0..2e7e90e 100644 --- a/clients/lcdexec/menu.c +++ b/clients/lcdexec/menu.c @@ -20,14 +20,21 @@ #include "menu.h" +/* names for boolean and tristate values */ +static char *boolValueName[] = { "false", "true" }; +#if defined(LCDEXEC_PARAMS) +static char *triGrayValueName[] = { "off", "on", "gray" }; +#endif + + /* recursively read the menu hierarchy */ MenuEntry *menu_read(MenuEntry *parent, const char *name) { static int id = 0; if (config_has_section(name)) { - MenuEntry *me = calloc(1, sizeof(MenuEntry)); - + MenuEntry *me = calloc(1, sizeof(MenuEntry)); // auto-NULL elements + if (me == NULL) return NULL; // set common entries @@ -36,55 +43,158 @@ MenuEntry *menu_read(MenuEntry *parent, const char *name) if (me->name == NULL) { //menu_free(me); return NULL; - } - + } + me->displayname = strdup(config_get_string(name, "DisplayName", 0, name)); - if (me->name == NULL) { + if (me->displayname == NULL) { //menu_free(me); return NULL; - } + } - me->entries = NULL; + me->parent = parent; me->next = NULL; - me->command = NULL; - + me->children = NULL; + me->numChildren = 0; + if (config_get_string(name, "Entry", 0, NULL) != NULL) { - MenuEntry **addr = &me->entries; + MenuEntry **addr = &me->children; const char *entryname; - int index = 0; - + // it is a sub-menu - me->type = menu; - - while ((entryname = config_get_string(name, "Entry", index++, NULL)) != NULL) { + me->type = MT_MENU; + + // read menu entries + while ((entryname = config_get_string(name, "Entry", me->numChildren, NULL)) != NULL) { MenuEntry *entry = menu_read(me, entryname); if (entry == NULL) { //menu_free(me); return NULL; - } + } + + me->numChildren++; *addr = entry; addr = &entry->next; - } - } + } + } else if (config_get_string(name, "Exec", 0, NULL) != NULL) { +#if defined(LCDEXEC_PARAMS) + MenuEntry **addr = &me->children; + const char *entryname; +#endif // it's a command to execute - me->type = exec; + me->type = MT_EXEC; - me->command = strdup(config_get_string(name, "Exec", 0, "")); - if (me->command == NULL) { + me->data.exec.command = strdup(config_get_string(name, "Exec", 0, "")); + if (me->data.exec.command == NULL) { //menu_free(me); return NULL; - } + } + me->data.exec.feedback = config_get_bool(name, "Feedback", 0, 0); + +#if defined(LCDEXEC_PARAMS) + // try to read parameters + while ((entryname = config_get_string(name, "Parameter", me->numChildren, NULL)) != NULL) { + MenuEntry *entry = menu_read(me, entryname); + + if (entry == NULL) { + //menu_free(me); + return NULL; + } + + me->numChildren++; + + *addr = entry; + addr = &entry->next; + } +#endif } +#if defined(LCDEXEC_PARAMS) + else if (config_get_string(name, "Type", 0, NULL) != NULL) { + // it's a command parameter + const char *type; + + type = config_get_string(name, "Type", 0, ""); + + if (strcasecmp(type, "slider") == 0) { + char buf[35]; + + me->type = MT_ARG_SLIDER; + + me->data.slider.value = config_get_int(name, "Value", 0, 0); + me->data.slider.minval = config_get_int(name, "MinValue", 0, 0); + me->data.slider.maxval = config_get_int(name, "MaxValue", 0, 1000); + + sprintf(buf, "%d", me->data.slider.minval); + me->data.slider.mintext = strdup(config_get_string(name, "MinText", 0, buf)); + sprintf(buf, "%d", me->data.slider.maxval); + me->data.slider.maxtext = strdup(config_get_string(name, "MaxText", 0, buf)); + + me->data.slider.stepsize = config_get_int(name, "StepSize", 0, 1); + } + else if (strcasecmp(type, "ring") == 0) { + const char *tmp; + int numStrings = 0; + int i = 0; + + me->type = MT_ARG_RING; + + me->data.ring.value = config_get_int(name, "Value", 0, 0); + numStrings = config_has_key(name, "String"); + me->data.ring.strings = calloc(sizeof(char *), numStrings+1); + me->data.ring.strings[numStrings] = NULL; + + while ((tmp = config_get_string(name, "String", i, NULL)) != NULL) { + me->data.ring.strings[i] = strdup(tmp); + i++; + } + me->data.ring.strings[i] = NULL; + } + else if (strcasecmp(type, "numeric") == 0) { + me->type = MT_ARG_NUMERIC; + + me->data.numeric.value = config_get_int(name, "Value", 0, 0); + me->data.numeric.minval = config_get_int(name, "MinValue", 0, 0); + me->data.numeric.maxval = config_get_int(name, "MaxValue", 0, 1000); + } + else if (strcasecmp(type, "alpha") == 0) { + me->type = MT_ARG_ALPHA; + + me->data.alpha.value = strdup(config_get_string(name, "Value", 0, "")); + me->data.alpha.minlen = config_get_int(name, "MinLength", 0, 0); + me->data.alpha.maxlen = config_get_int(name, "MaxLength", 0, 100); + me->data.alpha.allowed_chars = strdup(config_get_string(name, "AllowedChars", 0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + } + else if (strcasecmp(type, "ip") == 0) { + me->type = MT_ARG_IP; + + me->data.ip.value = strdup(config_get_string(name, "Value", 0, "")); + me->data.ip.v6 = config_get_bool(name, "Value", 0, 0); + } + else if (strcasecmp(type, "checkbox") == 0) { + me->type = MT_ARG_CHECKBOX; + + me->data.checkbox.allow_gray = config_get_bool(name, "AllowGray", 0, 0); + me->data.checkbox.value = (me->data.checkbox.allow_gray) + ? config_get_tristate(name, "Value", 0, "gray", 0) + : config_get_bool(name, "Value", 0, 0); + } + else { + report(RPT_DEBUG, "illegal parameter type"); + //menu_free(me); + return NULL; + } + } +#endif else { + report(RPT_DEBUG, "unknown menu entry type"); //menu_free(me); return NULL; - } + } return me; - } + } return NULL; } @@ -105,34 +215,169 @@ int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock) switch (me->type) { MenuEntry *entry; - case menu: + case MT_MENU: // don't create a separate entry for the main menu if ((parent != NULL) && (me->id != 0)) { if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" menu \"%s\"\n", parent_id, me->id, me->displayname) < 0) return -1; - } - + } + // recursively do it for the menu's sub-menus - for (entry = me->entries; entry != NULL; entry = entry->next) { + for (entry = me->children; entry != NULL; entry = entry->next) { if (menu_sock_send(entry, me, sock) < 0) return -1; - } + } break; - case exec: + case MT_EXEC: +#if defined(LCDEXEC_PARAMS) + if (me->children == NULL) { + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" action \"%s\"\n", + parent_id, me->id, me->displayname) < 0) + return -1; + + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -menu_result quit\n", + parent_id, me->id) < 0) + return -1; + } + else { + if ((parent != NULL) && (me->id != 0)) { + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" menu \"%s\"\n", + parent_id, me->id, me->displayname) < 0) + return -1; + } + + // (recursively) do it for the entry's parameters + for (entry = me->children; entry != NULL; entry = entry->next) { + if (menu_sock_send(entry, me, sock) < 0) + return -1; + } + } +#else if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" action \"%s\"\n", parent_id, me->id, me->displayname) < 0) return -1; + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -menu_result quit\n", parent_id, me->id) < 0) return -1; +#endif break; - case unknown: +#if defined(LCDEXEC_PARAMS) + case MT_ARG_SLIDER: + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" slider -text \"%s\"" + " -value %d -minvalue %d -maxvalue %d" + " -mintext \"%s\" -maxtext \"%s\" -stepsize %d\n", + parent_id, me->id, me->displayname, + me->data.slider.value, + me->data.slider.minval, + me->data.slider.maxval, + me->data.slider.mintext, + me->data.slider.maxtext, + me->data.slider.stepsize) <0) + return -1; + + if (me->next == NULL) { + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -next _quit_\n", + parent_id, me->id) < 0) + return -1; + } + break; + case MT_ARG_RING: + { + int i; + char *tmp = strdup(""); + + // join all strings with TAB as separator + for (i = 0; me->data.ring.strings[i] != NULL; i++) { + tmp = realloc(tmp, strlen(tmp) + 1 + + strlen(me->data.ring.strings[i]) + 1); + if (tmp[0] != '\0') + strcat(tmp, "\t"); + strcat(tmp, me->data.ring.strings[i]); + } + + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" ring -text \"%s\"" + " -value %d -strings \"%s\"\n", + parent_id, me->id, me->displayname, + me->data.ring.value, + tmp) < 0) + return -1; + } + + if (me->next == NULL) { + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -next _quit_\n", + parent_id, me->id) < 0) + return -1; + } + break; + case MT_ARG_NUMERIC: + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" numeric -text \"%s\"" + " -value %d -minvalue %d -maxvalue %d\n", + parent_id, me->id, me->displayname, + me->data.numeric.value, + me->data.numeric.minval, + me->data.numeric.maxval) < 0) + return -1; + + if (me->next == NULL) { + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -next _quit_\n", + parent_id, me->id) < 0) + return -1; + } + break; + case MT_ARG_ALPHA: + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" alpha -text \"%s\"" + " -value \"%s\" -minlength %d -maxlength %d" + " -allow_caps false -allow_noncaps false" + " -allow_numbers false -allowed_extra \"%s\"\n", + parent_id, me->id, me->displayname, + me->data.alpha.value, + me->data.alpha.minlen, + me->data.alpha.maxlen, + me->data.alpha.allowed_chars) <0) + return -1; + + if (me->next == NULL) { + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -next _quit_\n", + parent_id, me->id) < 0) + return -1; + } + break; + case MT_ARG_IP: + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" ip -text \"%s\"" + " -value \"%s\" -v6 %s\n", + parent_id, me->id, me->displayname, + me->data.ip.value, + boolValueName[me->data.ip.v6]) < 0) + return -1; + + if (me->next == NULL) { + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -next _quit_\n", + parent_id, me->id) < 0) + return -1; + } + break; + case MT_ARG_CHECKBOX: + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" checkbox -text \"%s\"" + " -value %s -allow_gray %s\n", + parent_id, me->id, me->displayname, + triGrayValueName[me->data.checkbox.value], + boolValueName[me->data.checkbox.allow_gray]) < 0) + return -1; + + if (me->next == NULL) { + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -next _quit_\n", + parent_id, me->id) < 0) + return -1; + } + break; +#endif default: return -1; } return 0; - } + } return -1; } @@ -144,16 +389,16 @@ MenuEntry *menu_find_by_id(MenuEntry *me, int id) if (me->id == id) return me; - if (me->type == menu) { + if ((me->type == MT_MENU) || (me->type == MT_EXEC)) { MenuEntry *entry; - - for (entry = me->entries; entry != NULL; entry = entry->next) { + + for (entry = me->children; entry != NULL; entry = entry->next) { MenuEntry *result = menu_find_by_id(entry, id); if (result != NULL) return result; } - } + } } return NULL; } @@ -162,8 +407,8 @@ MenuEntry *menu_find_by_id(MenuEntry *me, int id) /* return command of a menu entry */ const char *menu_command(MenuEntry *me) { - if ((me != NULL) && (me->type == exec)) - return me->command; + if ((me != NULL) && (me->type == MT_EXEC)) + return me->data.exec.command; return NULL; } @@ -176,22 +421,55 @@ void menu_free(MenuEntry *me) switch (me->type) { MenuEntry *entry; - case menu: - for (entry = me->entries; entry != NULL; ) { + case MT_EXEC: + if (me->data.exec.command != NULL) + free(me->data.exec.command); + me->data.exec.command = NULL; + /* fall through */ + case MT_MENU: + for (entry = me->children; entry != NULL; ) { MenuEntry *old = entry; entry = entry->next; old->next = NULL; menu_free(old); - } - me->entries = NULL; + } + me->children = NULL; break; - case exec: - if (me->command != NULL) - free(me->command); - me->command = NULL; +#if defined(LCDEXEC_PARAMS) + case MT_ARG_SLIDER: + if (me->data.slider.mintext != NULL) + free(me->data.slider.mintext); + me->data.slider.mintext = NULL; + if (me->data.slider.maxtext != NULL) + free(me->data.slider.maxtext); + me->data.slider.maxtext = NULL; break; - case unknown: + case MT_ARG_RING: + if (me->data.ring.strings != NULL) { + int i; + + for (i = 0; me->data.ring.strings[i] != NULL; i++) + free(me->data.ring.strings[i]); + + free(me->data.ring.strings); + me->data.ring.strings = NULL; + } + break; + case MT_ARG_ALPHA: + if (me->data.alpha.value != NULL) + free(me->data.alpha.value); + me->data.alpha.value = NULL; + if (me->data.alpha.allowed_chars != NULL) + free(me->data.alpha.allowed_chars); + me->data.alpha.allowed_chars = NULL; + break; + case MT_ARG_IP: + if (me->data.ip.value != NULL) + free(me->data.ip.value); + me->data.ip.value = NULL; + break; +#endif default: break; } @@ -199,12 +477,12 @@ void menu_free(MenuEntry *me) if (me->name != NULL) free(me->name); me->name = NULL; - + if (me->displayname != NULL) free(me->displayname); me->displayname = NULL; - me->type = unknown; + me->type = MT_UNKNOWN; free(me); } @@ -220,31 +498,90 @@ void menu_dump(MenuEntry *me) report(RPT_DEBUG, "[%s]", me->name); if (me->displayname != NULL) report(RPT_DEBUG, "DisplayName=\"%s\"", me->displayname); - + switch (me->type) { MenuEntry *entry; - case menu: - for (entry = me->entries; entry != NULL; entry = entry->next) + case MT_MENU: + // dump menu entry references + for (entry = me->children; entry != NULL; entry = entry->next) report(RPT_DEBUG, "Entry=%s", entry->name); + report(RPT_DEBUG, ""); + + // recursively walk through sub-menus + for (entry = me->children; entry != NULL; entry = entry->next) + menu_dump(entry); break; - case exec: - report(RPT_DEBUG, "Exec=\"%s\"", me->command); + + case MT_EXEC: + report(RPT_DEBUG, "Exec=\"%s\"", me->data.exec.command); + report(RPT_DEBUG, "Feedback=%s", boolValueName[me->data.exec.feedback]); +#if defined(LCDEXEC_PARAMS) + // dump entry's parameter referencess + for (entry = me->children; entry != NULL; entry = entry->next) + report(RPT_DEBUG, "Parameter=%s", entry->name); + report(RPT_DEBUG, ""); + + // dump entry's parameters + for (entry = me->children; entry != NULL; entry = entry->next) + menu_dump(entry); +#else + report(RPT_DEBUG, ""); +#endif break; - case unknown: +#if defined(LCDEXEC_PARAMS) + case MT_ARG_SLIDER: + report(RPT_DEBUG, "Type=slider"); + report(RPT_DEBUG, "Value=%d", me->data.slider.value); + report(RPT_DEBUG, "MinValue=%d", me->data.slider.minval); + report(RPT_DEBUG, "MaxValue=%d", me->data.slider.maxval); + report(RPT_DEBUG, "Stepsize=%d", me->data.slider.stepsize); + report(RPT_DEBUG, "MinText=%s", me->data.slider.mintext); + report(RPT_DEBUG, "MaxText=%s", me->data.slider.maxtext); + report(RPT_DEBUG, ""); + break; + case MT_ARG_RING: + report(RPT_DEBUG, "Type=ring"); + report(RPT_DEBUG, "Value: %d", me->data.ring.value); + { + int i; + + for (i = 0; me->data.ring.strings[i] != NULL; i++) + report(RPT_DEBUG, "String=\"%s\"", me->data.ring.strings[i]); + } + report(RPT_DEBUG, ""); + break; + case MT_ARG_NUMERIC: + report(RPT_DEBUG, "Type=numeric"); + report(RPT_DEBUG, "Value=%d", me->data.numeric.value); + report(RPT_DEBUG, "MinValue=%d", me->data.numeric.minval); + report(RPT_DEBUG, "MaxValue=%d", me->data.numeric.maxval); + report(RPT_DEBUG, ""); + break; + case MT_ARG_ALPHA: + report(RPT_DEBUG, "Type:=lpha"); + report(RPT_DEBUG, "Value=\"%s\"", me->data.alpha.value); + report(RPT_DEBUG, "AllowedChars=\"%s\"", me->data.alpha.allowed_chars); + report(RPT_DEBUG, ""); + break; + case MT_ARG_IP: + report(RPT_DEBUG, "Type=ip"); + report(RPT_DEBUG, "Value=\"%s\"", me->data.ip.value); + report(RPT_DEBUG, "V6=%s", boolValueName[me->data.ip.v6]); + report(RPT_DEBUG, ""); + break; + case MT_ARG_CHECKBOX: + report(RPT_DEBUG, "Type=ip"); + report(RPT_DEBUG, "Value=%s", triGrayValueName[me->data.checkbox.value]); + report(RPT_DEBUG, "AllowGray=%s", boolValueName[me->data.checkbox.allow_gray]); + report(RPT_DEBUG, ""); + break; +#endif default: report(RPT_DEBUG, "ERROR: unknown menu entry type"); break; } - report(RPT_DEBUG, ""); - // recursively walk through sub-menus - if (me->type == menu) { - MenuEntry *entry; - - for (entry = me->entries; entry != NULL; entry = entry->next) - menu_dump(entry); - } } } #endif diff --git a/clients/lcdexec/menu.h b/clients/lcdexec/menu.h index c5795d2..33609d5 100644 --- a/clients/lcdexec/menu.h +++ b/clients/lcdexec/menu.h @@ -24,9 +24,19 @@ /** Symbolic names for the types of a MenuEntry */ typedef enum { - unknown = 0, /**< Unknown MenuEntry type. */ - menu = 1, /**< MenuEntry representing a menu. */ - exec = 2, /**< MenuEntry representing an executable command. */ + MT_UNKNOWN = 0x00, /**< Unknown MenuEntry type. */ + MT_MENU = 0x01, /**< MenuEntry representing a menu. */ + MT_EXEC = 0x02, /**< MenuEntry representing an executable command. */ + + MT_ARG_ANY = 0x10, /**< Mask denoting a parameter of any type */ +#if defined(LCDEXEC_PARAMS) + MT_ARG_SLIDER = 0x11, /**< MenuEntry representing a slider parameter. */ + MT_ARG_RING = 0x12, /**< MenuEntry representing a ring parameter. */ + MT_ARG_NUMERIC = 0x13, /**< MenuEntry representing a numeric input parameter. */ + MT_ARG_ALPHA = 0x14, /**< MenuEntry representing a alpha input parameter. */ + MT_ARG_IP = 0x15, /**< MenuEntry representing a IP input parameter. */ + MT_ARG_CHECKBOX = 0x16, /**< MenuEntry representing a checkbox input parameter. */ +#endif } MenuType; @@ -35,13 +45,52 @@ typedef struct menu_entry { char *displayname; /**< isible name of the entry. */ int id; /**< Internal ID of the entry. */ MenuType type; /**< Type of the entry. */ + struct menu_entry *parent; /**< Parent menu entry */ + int numChildren; /**< # of child entries */ - // variables necessary for type menu - struct menu_entry *entries; /**< Subordinate menu entries (for MenuType \c menu). */ - struct menu_entry *next; /**< Next sibling menu entry (for MenuType \c menu). */ + // variables necessary for multiple types + struct menu_entry *children; /**< Subordinate menu entries (for MenuType \c MT_MENU & \c MT_EXEC). */ + struct menu_entry *next; /**< Next sibling menu entry (for MenuType \c MT_MENU). */ - // variables necessary for type exec - char *command; /**< Command to execute (for MenuType \c exec). */ + union { + struct { // elements necessary for type MT_EXEC + char *command; /**< Command to execute (for MenuType \c exec). */ + int feedback; /**< Feedback option (for MenuType \c exec). */ + } exec; +#if defined(LCDEXEC_PARAMS) + struct { // elements necessary for type MT_ARG_SLIDER + int value; + int minval; + int maxval; + int stepsize; + char *mintext; + char *maxtext; + } slider; + struct { // elements necessary for type MT_ARG_RING + int value; + char **strings; + } ring; + struct { // elements necessary for type MT_ARG_NUMERIC + int value; + int minval; + int maxval; + } numeric; + struct { // elements necessary for type MT_ARG_ALPHA + char *value; + int minlen; + int maxlen; + char *allowed_chars; + } alpha; + struct { // elements necessary for type MT_ARG_IP + char *value; + int v6; + } ip; + struct { // elements necessary for type MT_ARG_CHECKBOX + int value; + int allow_gray; + } checkbox; +#endif + } data; } MenuEntry; diff --git a/docs/lcdexec.1 b/docs/lcdexec.1 index f2b561f..61c5e57 100644 --- a/docs/lcdexec.1 +++ b/docs/lcdexec.1 @@ -94,7 +94,7 @@ is the hostname of the system \fIlcdexec\fP is running on. Set the shell to use when executing programs. If not given, \fBlcdexec\fP tries to read the environment variable \fISHELL\fP. If that fails, it defaults to \fB/bin/sh\fP. -Please note that the shell given here must understand the option \fB-c\fP +Please note that the shell given here must understand the option \fB\-c\fP followed by the command line to execute. .PP @@ -119,6 +119,11 @@ Sections containing an \fBExec=\fP option are command entries. .B DisplayName=\fIname\fP The name of the menu entry to be displayed on the LCD. If this option is not given, the the section header name is used (without the square brackets). +.TP 8 +.B Feedback=\fIbool\fP +In command entries, this option tells whether to inform the user of the completion of +commands using an alert screen on the display. +If not given, it defaults to \fBno\fB. .PP .SH FILES @@ -178,14 +183,17 @@ Entry=RebootNow [Shutdown5min] DisplayName="Shutdown in 5 minutes" Exec="shutdown -h +5" +Feedback=yes [Reboot5min] DisplayName="Reboot in 5 minutes" Exec="shutdown -r +5" +Feedback=yes [CanclShutdown] DisplayName="Cancel shutdown/reboot" Exec="shutdown -c" +Feedback=yes [ShutdownNow] DisplayName="Shutdown now"