diff --git a/ChangeLog b/ChangeLog index 365dfc6..8910f62 100644 --- a/ChangeLog +++ b/ChangeLog @@ -33,7 +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 + + lcdexec: parameter support using widgets * server core: display values in menu for all input MenuEntry types v.0.5.2 diff --git a/clients/lcdexec/lcdexec.c b/clients/lcdexec/lcdexec.c index b840ee5..c877bfb 100644 --- a/clients/lcdexec/lcdexec.c +++ b/clients/lcdexec/lcdexec.c @@ -1,12 +1,14 @@ -/* - * lcdexec.c - * This file is part of lcdexec, an LCDproc client. +/* \file clients/lcdexec/lcdexec.c + * Main file for \lcdexec, the program starter in the LCDproc suite. + */ + +/* This file is part of lcdexec, an LCDproc client. * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. * * Copyright (c) 2002, Joris Robijn - * Copyright (c) 2006, Peter Marschall + * Copyright (c) 2006-7, Peter Marschall */ #include @@ -26,12 +28,14 @@ #include "menu.h" + #if !defined(SYSCONFDIR) # define SYSCONFDIR "/etc" #endif #define DEFAULT_CONFIGFILE SYSCONFDIR "/lcdexec.conf" + /** information about a process started by lcdexec */ typedef struct ProcInfo { struct ProcInfo *next; /**< pointer to the next ProcInfo entry */ @@ -95,6 +99,7 @@ static int exec_command(MenuEntry *cmd); static int show_procinfo_msg(ProcInfo *p); static int main_loop(void); + #define CHAIN(e,f) { if (e>=0) { e=(f); }} #define CHAIN_END(e) { if (e<0) { report(RPT_CRIT,"Critical error, abort"); exit(e); }} @@ -376,7 +381,6 @@ static int process_response(char *str) exec_command(entry); } } -#if defined(LCDEXEC_PARAMS) else if ((strcmp(argv[1], "plus") == 0) || (strcmp(argv[1], "minus") == 0) || (strcmp(argv[1], "update") == 0)) { @@ -431,7 +435,6 @@ static int process_response(char *str) return -1; } } -#endif else { ; /* Ignore other menuevents */ } @@ -472,11 +475,9 @@ 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 /* set argument vector */ argv[0] = default_shell; @@ -484,52 +485,56 @@ static int exec_command(MenuEntry *cmd) 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); + snprintf(buf, sizeof(buf)-1, "%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]); + snprintf(buf, sizeof(buf)-1, "%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); + snprintf(buf, sizeof(buf)-1, "%s=%d", + arg->name, arg->data.numeric.value); break; case MT_ARG_ALPHA: - snprintf(buf, 1024, "%s=%s", arg->name, arg->data.alpha.value); + snprintf(buf, sizeof(buf)-1, "%s=%s", + arg->name, arg->data.alpha.value); break; case MT_ARG_IP: - snprintf(buf, 1024, "%s=%s", arg->name, arg->data.ip.value); + snprintf(buf, sizeof(buf)-1, "%s=%s", + arg->name, arg->data.ip.value); break; case MT_ARG_CHECKBOX: - snprintf(buf, 1024, "%s=%d", arg->name, arg->data.checkbox.value); + if (arg->data.checkbox.map[arg->data.checkbox.value] != NULL) + strncpy(buf, arg->data.checkbox.map[arg->data.checkbox.value], + sizeof(buf)-1); + else + snprintf(buf, sizeof(buf)-1, "%s=%d", + arg->name, arg->data.checkbox.value); break; default: /* error ? */ break; } - buf[1024] ='\0'; + buf[sizeof(buf)-1] ='\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: @@ -550,11 +555,9 @@ static int exec_command(MenuEntry *cmd) return -1; } -#if defined(LCDEXEC_PARAMS) /* free envp's contents */ for (i = 0; envp[i] != NULL; i++) free(envp[i]); -#endif return 0; } diff --git a/clients/lcdexec/lcdexec.conf b/clients/lcdexec/lcdexec.conf index 17fcb5c..565b26e 100644 --- a/clients/lcdexec/lcdexec.conf +++ b/clients/lcdexec/lcdexec.conf @@ -59,76 +59,77 @@ Exec="echo P" [CmdQ] DisplayName="Show environment" Exec="env" -# When lcdexec is compiled with compiler option -DLCDEXEC_PARAMS -# you can change parameters for commands within lcdexec. +# Now 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: command with parameters get an action entry -# added to the parameter menu automatically. -# Activating this entry will execute the program. -# This is IMHO not ideal, but the best idea I could come up with +# Please note: commands with parameters get an action entry +# added to end of the parameter menu automatically. +# Activating this action named "Apply!" will execute the program. +# This is IMHO not ideal, but the best idea I could come up with. # 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 +# Here's how paramters get 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=yes +# replacement texts for the possible values +OffText="off" +OnText="on" +GrayText="gray" + +[RING_ARG] +DisplayName="Ring" +Type=Ring +Value=0 +# list of alternative strings to choose from +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 -# Ideas for further extensions: +# Ideas for further extensions (patches welcome): # - shell selectable in command sections -# - type definitions instead implicit depending on Exec/Entry -# (the latter only as fallback) # - display configurable result of a command on the display # - jump to other menus depending on the output/result of a command diff --git a/clients/lcdexec/menu.c b/clients/lcdexec/menu.c index 77957cf..6e48434 100644 --- a/clients/lcdexec/menu.c +++ b/clients/lcdexec/menu.c @@ -1,12 +1,14 @@ -/* - * menu.c - * This file is part of lcdexec, an LCDproc client. +/** \file clients/lcdexec/menu.c + * Menu parsing and building functions for the \c lcdexec client + */ + +/* This file is part of lcdexec, an LCDproc client. * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. * * Copyright (c) 2002, Joris Robijn - * Copyright (c) 2006, Peter Marschall + * Copyright (c) 2006-7, Peter Marschall */ #include @@ -20,11 +22,10 @@ #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 */ @@ -79,10 +80,9 @@ MenuEntry *menu_read(MenuEntry *parent, const char *name) } } 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 = MT_EXEC; @@ -93,7 +93,6 @@ MenuEntry *menu_read(MenuEntry *parent, const char *name) } 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); @@ -112,9 +111,7 @@ MenuEntry *menu_read(MenuEntry *parent, const char *name) // automagically add an "Apply ?" action if ((me->numChildren > 0) && (addr != NULL)) *addr = menu_read(me, NULL); -#endif } -#if defined(LCDEXEC_PARAMS) else if (config_get_string(name, "Type", 0, NULL) != NULL) { // it's a command parameter const char *type; @@ -168,7 +165,8 @@ MenuEntry *menu_read(MenuEntry *parent, const char *name) 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")); + me->data.alpha.allowed = strdup(config_get_string(name, "AllowedChars", 0, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ")); } else if (strcasecmp(type, "ip") == 0) { me->type = MT_ARG_IP; @@ -177,12 +175,21 @@ MenuEntry *menu_read(MenuEntry *parent, const char *name) me->data.ip.v6 = config_get_bool(name, "Value", 0, 0); } else if (strcasecmp(type, "checkbox") == 0) { + const char *tmp; + 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); + // get replacement strings for different values + tmp = config_get_string(name, "OffText", 0, NULL); + me->data.checkbox.map[0] = (tmp != NULL) ? strdup(tmp) : NULL; + tmp = config_get_string(name, "OnText", 0, NULL); + me->data.checkbox.map[1] = (tmp != NULL) ? strdup(tmp) : NULL; + tmp = config_get_string(name, "GrayText", 0, NULL); + me->data.checkbox.map[2] = (tmp != NULL) ? strdup(tmp) : NULL; } else { report(RPT_DEBUG, "illegal parameter type"); @@ -190,7 +197,6 @@ MenuEntry *menu_read(MenuEntry *parent, const char *name) return NULL; } } -#endif else { report(RPT_DEBUG, "unknown menu entry type"); //menu_free(me); @@ -267,7 +273,6 @@ int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock) } break; 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) @@ -290,17 +295,7 @@ int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock) 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; -#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" @@ -372,7 +367,7 @@ int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock) me->data.alpha.value, me->data.alpha.minlen, me->data.alpha.maxlen, - me->data.alpha.allowed_chars) <0) + me->data.alpha.allowed) <0) return -1; if (me->next == NULL) { @@ -418,7 +413,6 @@ int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock) parent_id, me->id) < 0) return -1; break; -#endif default: return -1; } @@ -464,9 +458,10 @@ const char *menu_command(MenuEntry *me) void menu_free(MenuEntry *me) { if (me != NULL) { - switch (me->type) { - MenuEntry *entry; + MenuEntry *entry; + int i; + switch (me->type) { case MT_EXEC: if (me->data.exec.command != NULL) free(me->data.exec.command); @@ -482,7 +477,6 @@ void menu_free(MenuEntry *me) } me->children = NULL; break; -#if defined(LCDEXEC_PARAMS) case MT_ARG_SLIDER: if (me->data.slider.mintext != NULL) free(me->data.slider.mintext); @@ -506,16 +500,22 @@ void menu_free(MenuEntry *me) 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; + if (me->data.alpha.allowed != NULL) + free(me->data.alpha.allowed); + me->data.alpha.allowed = NULL; break; case MT_ARG_IP: if (me->data.ip.value != NULL) free(me->data.ip.value); me->data.ip.value = NULL; break; -#endif + case MT_ARG_CHECKBOX: + for (i = 0; i < sizeof(me->data.checkbox.map)/sizeof(me->data.checkbox.map[0]); i++) { + if (me->data.checkbox.map[i] != NULL) { + free(me->data.checkbox.map[i]); + me->data.checkbox.map[i] = NULL; + } + } default: break; } @@ -562,11 +562,10 @@ void menu_dump(MenuEntry *me) for (entry = me->children; entry != NULL; entry = entry->next) menu_dump(entry); break; - 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); @@ -575,11 +574,7 @@ void menu_dump(MenuEntry *me) // dump entry's parameters for (entry = me->children; entry != NULL; entry = entry->next) menu_dump(entry); -#else - report(RPT_DEBUG, ""); -#endif break; -#if defined(LCDEXEC_PARAMS) case MT_ARG_SLIDER: report(RPT_DEBUG, "Type=slider"); report(RPT_DEBUG, "Value=%d", me->data.slider.value); @@ -611,7 +606,7 @@ void menu_dump(MenuEntry *me) 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, "AllowedChars=\"%s\"", me->data.alpha.allowed); report(RPT_DEBUG, ""); break; case MT_ARG_IP: @@ -624,9 +619,14 @@ void menu_dump(MenuEntry *me) 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]); + if (me->data.checkbox.map[0] != NULL) + report(RPT_DEBUG, "OffText=%s", me->data.checkbox.map[0]); + if (me->data.checkbox.map[1] != NULL) + report(RPT_DEBUG, "OnText=%s", me->data.checkbox.map[1]); + if (me->data.checkbox.map[2] != NULL) + report(RPT_DEBUG, "GrayText=%s", me->data.checkbox.map[2]); report(RPT_DEBUG, ""); break; -#endif default: report(RPT_DEBUG, "ERROR: unknown menu entry type"); break; diff --git a/clients/lcdexec/menu.h b/clients/lcdexec/menu.h index fd698f9..d045eb2 100644 --- a/clients/lcdexec/menu.h +++ b/clients/lcdexec/menu.h @@ -8,13 +8,13 @@ * COPYING file distributed with this package. * * Copyright (c) 2002, Joris Robijn - * Copyright (c) 2006, Peter Marschall - * + * Copyright (c) 2006-7, Peter Marschall */ #ifndef LCDEXEC_MENU_H #define LCDEXEC_MENU_H +/* boolean values */ #ifndef TRUE # define TRUE 1 #endif @@ -22,6 +22,7 @@ # define FALSE 0 #endif + /** Symbolic names for the types of a MenuEntry */ typedef enum { MT_UNKNOWN = 0x00, /**< Unknown MenuEntry type. */ @@ -29,7 +30,7 @@ typedef enum { MT_EXEC = 0x20, /**< MenuEntry representing an executable command. */ MT_ARGUMENT = 0x40, /**< Mask denoting a parameter of any type */ MT_AUTOMATIC = 0x80, /**< BitFlag denoting automatically generated entries */ -#if defined(LCDEXEC_PARAMS) + MT_ARG_SLIDER = 0x41, /**< MenuEntry representing a slider parameter. */ MT_ARG_RING = 0x42, /**< MenuEntry representing a ring parameter. */ MT_ARG_NUMERIC = 0x43, /**< MenuEntry representing a numeric input parameter. */ @@ -37,60 +38,59 @@ typedef enum { MT_ARG_IP = 0x45, /**< MenuEntry representing a IP input parameter. */ MT_ARG_CHECKBOX = 0x46, /**< MenuEntry representing a checkbox input parameter. */ MT_ARG_ACTION = 0x47, /**< MenuEntry representing a checkbox input parameter. */ -#endif } MenuType; +/** Data structure to hold a menu entry in \c lcdexec */ typedef struct menu_entry { + // Variables necessary for multiple/all types char *name; /**< Name of the menu entry (from section name). */ - char *displayname; /**< isible name of the entry. */ + char *displayname; /**< Visible 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 */ + struct menu_entry *parent; /**< Parent menu entry. */ + int numChildren; /**< # of child entries. */ + struct menu_entry *children; /**< Subordinate menu entries (for type \c MT_MENU & \c MT_EXEC). */ + struct menu_entry *next; /**< Next sibling menu entry (for type \c MT_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). */ - - 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). */ + // Variables specific to one special type + union data { + struct exec{ // elements necessary for type MT_EXEC + char *command; /**< Command to execute. */ + int feedback; /**< Feedback flag. */ } 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; + struct slider { // elements necessary for type MT_ARG_SLIDER + int value; /**< Numeric value of slider. */ + int minval; /**< Minimal allowed value. */ + int maxval; /**< Maximal allowed value. */ + int stepsize; /**< Increments/decrement for value. */ + char *mintext; /**< Label for the min. value. */ + char *maxtext; /**< Label for the max. value. */ } slider; - struct { // elements necessary for type MT_ARG_RING - int value; - char **strings; + struct ring { // elements necessary for type MT_ARG_RING + int value; /**< Index into list of alternatives. */ + char **strings; /**< List of alternatives. */ } ring; - struct { // elements necessary for type MT_ARG_NUMERIC - int value; - int minval; - int maxval; + struct numeric { // elements necessary for type MT_ARG_NUMERIC + int value; /**< Numeric input value. */ + int minval; /**< Minimal allowed value. */ + int maxval; /**< Maximal allowed value. */ } numeric; - struct { // elements necessary for type MT_ARG_ALPHA - char *value; - int minlen; - int maxlen; - char *allowed_chars; + struct alpha { // elements necessary for type MT_ARG_ALPHA + char *value; /**< Text value. */ + int minlen; /**< Maximal allowed length. */ + int maxlen; /**< Minimal required length. */ + char *allowed; /**< Characters allowed in input. */ } alpha; - struct { // elements necessary for type MT_ARG_IP - char *value; - int v6; + struct ip { // elements necessary for type MT_ARG_IP + char *value; /**< IP address value. */ + int v6; /**< Flag: use IPv6 editing logic. */ } ip; - struct { // elements necessary for type MT_ARG_CHECKBOX - int value; - int allow_gray; + struct checkbox { // elements necessary for type MT_ARG_CHECKBOX + int value; /**< Checkbox value. */ + int allow_gray; /**< Flag for tristate chechboxes. */ + char *map[3]; /**< Replacement strings for each value. */ } checkbox; -#endif } data; } MenuEntry;