/* * menu.c * 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 */ #include #include #include #include "shared/str.h" #include "shared/report.h" #include "shared/configfile.h" #include "shared/sockets.h" #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)); // auto-NULL elements if (me == NULL) return NULL; // set common entries me->id = id++; me->name = strdup(name); if (me->name == NULL) { //menu_free(me); return NULL; } me->displayname = strdup(config_get_string(name, "DisplayName", 0, name)); if (me->displayname == NULL) { //menu_free(me); return NULL; } me->parent = parent; me->next = NULL; me->children = NULL; me->numChildren = 0; if (config_get_string(name, "Entry", 0, NULL) != NULL) { MenuEntry **addr = &me->children; const char *entryname; // it is a sub-menu 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 = MT_EXEC; 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; } /* create LCDproc commands for the menu entry hierarchy and send it to the server */ int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock) { if ((me != NULL) && (sock > 0)) { char parent_id[12]; // set parent_id depending on the parent given if ((parent != NULL) && (parent->id != 0)) sprintf(parent_id, "%d", parent->id); else strcpy(parent_id, ""); switch (me->type) { MenuEntry *entry; 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->children; entry != NULL; entry = entry->next) { if (menu_sock_send(entry, me, sock) < 0) return -1; } 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) 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; #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; } /* find menu entry by its id */ MenuEntry *menu_find_by_id(MenuEntry *me, int id) { if (me != NULL) { if (me->id == id) return me; if ((me->type == MT_MENU) || (me->type == MT_EXEC)) { MenuEntry *entry; for (entry = me->children; entry != NULL; entry = entry->next) { MenuEntry *result = menu_find_by_id(entry, id); if (result != NULL) return result; } } } return NULL; } /* return command of a menu entry */ const char *menu_command(MenuEntry *me) { if ((me != NULL) && (me->type == MT_EXEC)) return me->data.exec.command; return NULL; } /* free menu entry hierarchy */ void menu_free(MenuEntry *me) { if (me != NULL) { switch (me->type) { MenuEntry *entry; 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->children = NULL; break; #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 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; } if (me->name != NULL) free(me->name); me->name = NULL; if (me->displayname != NULL) free(me->displayname); me->displayname = NULL; me->type = MT_UNKNOWN; free(me); } } #if defined(DEBUG) /* dump menu entry hierarchy to screen */ void menu_dump(MenuEntry *me) { if (me != NULL) { report(RPT_DEBUG, "# menu ID: %d", me->id); report(RPT_DEBUG, "[%s]", me->name); if (me->displayname != NULL) report(RPT_DEBUG, "DisplayName=\"%s\"", me->displayname); switch (me->type) { MenuEntry *entry; 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 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; #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; } } } #endif /* EOF */