Change to the linux kernel coding style
[wmaker-crm.git] / src / rootmenu.c
dissimilarity index 86%
index be1bbed..6a0b920 100644 (file)
-/* rootmenu.c- user defined menu
- *
- *  Window Maker window manager
- *
- *  Copyright (c) 1997-2003 Alfredo K. Kojima
- *  Copyright (c) 1998-2003 Dan Pascu
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- *  USA.
- */
-
-#include "wconfig.h"
-
-#ifndef LITE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#include <dirent.h>
-
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-
-#include "WindowMaker.h"
-#include "actions.h"
-#include "menu.h"
-#include "funcs.h"
-#include "dialog.h"
-#include "keybind.h"
-#include "stacking.h"
-#include "workspace.h"
-#include "defaults.h"
-#include "framewin.h"
-#include "session.h"
-#include "xmodifier.h"
-
-#include <WINGs/WUtil.h>
-
-#define MAX_SHORTCUT_LENGTH 32
-
-extern char *Locale;
-
-extern WDDomain *WDRootMenu;
-
-extern Cursor wCursor[WCUR_LAST];
-
-extern Time LastTimestamp;
-
-extern WPreferences wPreferences;
-
-extern int wScreenCount;
-
-static WMenu *readMenuPipe(WScreen *scr, char **file_name);
-static WMenu *readMenuFile(WScreen *scr, char *file_name);
-static WMenu *readMenuDirectory(WScreen *scr, char *title, char **file_name,
-                                char *command);
-
-
-typedef struct Shortcut {
-    struct Shortcut *next;
-
-    int modifier;
-    KeyCode keycode;
-    WMenuEntry *entry;
-    WMenu *menu;
-} Shortcut;
-
-
-
-static Shortcut *shortcutList = NULL;
-
-
-/*
- * Syntax:
- * # main menu
- * "Menu Name" MENU
- *     "Title" EXEC command_to_exec -params
- *     "Submenu" MENU
- *             "Title" EXEC command_to_exec -params
- *     "Submenu" END
- *     "Workspaces" WORKSPACE_MENU
- *     "Title" built_in_command
- *     "Quit" EXIT
- *     "Quick Quit" EXIT QUICK
- * "Menu Name" END
- *
- * Commands may be preceded by SHORTCUT key
- *
- * Built-in commands:
- *
- * INFO_PANEL - shows the Info Panel
- * LEGAL_PANEL - shows the Legal info panel
- * SHUTDOWN [QUICK] - closes the X server [without confirmation]
- * REFRESH - forces the desktop to be repainted
- * EXIT [QUICK] - exit the window manager [without confirmation]
- * EXEC <program> - execute an external program
- * SHEXEC <command> - execute a shell command
- * WORKSPACE_MENU - places the workspace submenu
- * ARRANGE_ICONS
- * RESTART [<window manager>] - restarts the window manager
- * SHOW_ALL - unhide all windows on workspace
- * HIDE_OTHERS - hides all windows excep the focused one
- * OPEN_MENU file - read menu data from file which must be a valid menu file.
- * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
- *              - read menu data from directory(ies) and
- *               eventually precede each with a command.
- * OPEN_MENU | command
- *              - opens command and uses its stdout to construct and insert
- *                the resulting menu in current position. The output of
- *                command must be a valid menu description.
- *                The space between '|' and command is optional.
- *                || will do the same, but will not cache the contents.
- * SAVE_SESSION - saves the current state of the desktop, which include
- *               all running applications, all their hints (geometry,
- *               position on screen, workspace they live on, the dock
- *               or clip from where they were launched, and
- *               if minimized, shaded or hidden. Also saves the current
- *               workspace the user is on. All will be restored on every
- *               start of windowmaker until another SAVE_SESSION or
- *               CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
- *               WindowMaker domain file, then saving is automatically
- *               done on every windowmaker exit, overwriting any
- *               SAVE_SESSION or CLEAR_SESSION (see below). Also save
- *               dock state now.
- * CLEAR_SESSION - clears any previous saved session. This will not have
- *               any effect if SaveSessionOnExit is True.
- *
- */
-
-#define M_QUICK                1
-
-/* menu commands */
-
-static void
-execCommand(WMenu *menu, WMenuEntry *entry)
-{
-    char *cmdline;
-
-    cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
-
-    XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
-                 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
-                 CurrentTime);
-    XSync(dpy, 0);
-
-    if (cmdline) {
-        ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
-        wfree(cmdline);
-    }
-    XUngrabPointer(dpy, CurrentTime);
-    XSync(dpy, 0);
-}
-
-
-static void
-exitCommand(WMenu *menu, WMenuEntry *entry)
-{
-    static int inside = 0;
-    int result;
-
-    /* prevent reentrant calls */
-    if (inside)
-        return;
-    inside = 1;
-
-#define R_CANCEL 0
-#define R_EXIT   1
-
-    result = R_CANCEL;
-
-    if ((long)entry->clientdata==M_QUICK) {
-        result = R_EXIT;
-    } else {
-        int r, oldSaveSessionFlag;
-
-        oldSaveSessionFlag = wPreferences.save_session_on_exit;
-        r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
-                        _("Exit window manager?"),
-                        _("Exit"), _("Cancel"), NULL);
-
-        if (r==WAPRDefault) {
-            result = R_EXIT;
-        } else if (r==WAPRAlternate) {
-            /* Don't modify the "save session on exit" flag if the
-             * user canceled the operation. */
-            wPreferences.save_session_on_exit = oldSaveSessionFlag;
-        }
-    }
-    if (result==R_EXIT) {
-#ifdef DEBUG
-        printf("Exiting WindowMaker.\n");
-#endif
-        Shutdown(WSExitMode);
-    }
-#undef R_EXIT
-#undef R_CANCEL
-    inside = 0;
-}
-
-
-static void
-shutdownCommand(WMenu *menu, WMenuEntry *entry)
-{
-    static int inside = 0;
-    int result;
-
-    /* prevent reentrant calls */
-    if (inside)
-        return;
-    inside = 1;
-
-#define R_CANCEL 0
-#define R_CLOSE 1
-#define R_KILL 2
-
-
-    result = R_CANCEL;
-    if ((long)entry->clientdata==M_QUICK)
-        result = R_CLOSE;
-    else {
-#ifdef XSMP_ENABLED
-        if (wSessionIsManaged()) {
-            int r;
-
-            r = wMessageDialog(menu->frame->screen_ptr,
-                               _("Close X session"),
-                               _("Close Window System session?\n"
-                                 "Kill might close applications with unsaved data."),
-                               _("Close"), _("Kill"), _("Cancel"));
-            if (r==WAPRDefault)
-                result = R_CLOSE;
-            else if (r==WAPRAlternate)
-                result = R_KILL;
-        } else
-#endif
-        {
-            int r, oldSaveSessionFlag;
-
-            oldSaveSessionFlag = wPreferences.save_session_on_exit;
-
-            r = wExitDialog(menu->frame->screen_ptr,
-                            _("Kill X session"),
-                            _("Kill Window System session?\n"
-                              "(all applications will be closed)"),
-                            _("Kill"), _("Cancel"), NULL);
-            if (r==WAPRDefault) {
-                result = R_KILL;
-            } else if (r==WAPRAlternate) {
-                /* Don't modify the "save session on exit" flag if the
-                 * user canceled the operation. */
-                wPreferences.save_session_on_exit = oldSaveSessionFlag;
-            }
-        }
-    }
-
-    if (result!=R_CANCEL) {
-#ifdef XSMP_ENABLED
-        if (result == R_CLOSE) {
-            Shutdown(WSLogoutMode);
-        } else
-#endif /* XSMP_ENABLED */
-        {
-            Shutdown(WSKillMode);
-        }
-    }
-#undef R_CLOSE
-#undef R_CANCEL
-#undef R_KILL
-    inside = 0;
-}
-
-
-static void
-restartCommand(WMenu *menu, WMenuEntry *entry)
-{
-    Shutdown(WSRestartPreparationMode);
-    Restart((char*)entry->clientdata, False);
-    Restart(NULL, True);
-}
-
-
-static void
-refreshCommand(WMenu *menu, WMenuEntry *entry)
-{
-    wRefreshDesktop(menu->frame->screen_ptr);
-}
-
-
-static void
-arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
-{
-    wArrangeIcons(menu->frame->screen_ptr, True);
-}
-
-static void
-showAllCommand(WMenu *menu, WMenuEntry *entry)
-{
-    wShowAllWindows(menu->frame->screen_ptr);
-}
-
-static void
-hideOthersCommand(WMenu *menu, WMenuEntry *entry)
-{
-    wHideOtherApplications(menu->frame->screen_ptr->focused_window);
-}
-
-
-static void
-saveSessionCommand(WMenu *menu, WMenuEntry *entry)
-{
-    if (!wPreferences.save_session_on_exit)
-        wSessionSaveState(menu->frame->screen_ptr);
-
-    wScreenSaveState(menu->frame->screen_ptr);
-}
-
-
-static void
-clearSessionCommand(WMenu *menu, WMenuEntry *entry)
-{
-    wSessionClearState(menu->frame->screen_ptr);
-    wScreenSaveState(menu->frame->screen_ptr);
-}
-
-
-static void
-infoPanelCommand(WMenu *menu, WMenuEntry *entry)
-{
-    wShowInfoPanel(menu->frame->screen_ptr);
-}
-
-
-static void
-legalPanelCommand(WMenu *menu, WMenuEntry *entry)
-{
-    wShowLegalPanel(menu->frame->screen_ptr);
-}
-
-
-/********************************************************************/
-
-
-static char*
-getLocalizedMenuFile(char *menu)
-{
-    char *buffer, *ptr, *locale;
-    int len;
-
-    if (!Locale)
-        return NULL;
-
-    len = strlen(menu)+strlen(Locale)+8;
-    buffer = wmalloc(len);
-
-    /* try menu.locale_name */
-    snprintf(buffer, len, "%s.%s", menu, Locale);
-    if (access(buffer, F_OK)==0) {
-        return buffer;
-    }
-
-    /* position of locale in our buffer */
-    locale = buffer + strlen(menu) + 1;
-
-    /* check if it is in the form aa_bb.encoding and check for aa_bb */
-    ptr = strchr(locale, '.');
-    if (ptr) {
-        *ptr = 0;
-        if (access(buffer, F_OK)==0) {
-            return buffer;
-        }
-    }
-    /* now check for aa */
-    ptr = strchr(locale, '_');
-    if (ptr) {
-        *ptr = 0;
-        if (access(buffer, F_OK)==0) {
-            return buffer;
-        }
-    }
-
-    wfree(buffer);
-
-    return NULL;
-}
-
-
-static void
-raiseMenus(WMenu *menu)
-{
-    int i;
-
-    if (menu->flags.mapped) {
-        wRaiseFrame(menu->frame->core);
-    }
-    for (i=0; i<menu->cascade_no; i++) {
-        if (menu->cascades[i])
-            raiseMenus(menu->cascades[i]);
-    }
-}
-
-
-
-Bool
-wRootMenuPerformShortcut(XEvent *event)
-{
-    WScreen *scr = wScreenForRootWindow(event->xkey.root);
-    Shortcut *ptr;
-    int modifiers;
-    int done = 0;
-
-    /* ignore CapsLock */
-    modifiers = event->xkey.state & ValidModMask;
-
-    for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
-        if (ptr->keycode==0 || ptr->menu->menu->screen_ptr!=scr)
-            continue;
-
-        if (ptr->keycode==event->xkey.keycode && ptr->modifier==modifiers) {
-            (*ptr->entry->callback)(ptr->menu, ptr->entry);
-            done = True;
-        }
-    }
-
-    return done;
-}
-
-
-void
-wRootMenuBindShortcuts(Window window)
-{
-    Shortcut *ptr;
-
-    ptr = shortcutList;
-    while (ptr) {
-        if (ptr->modifier!=AnyModifier) {
-            XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
-                     window, True, GrabModeAsync, GrabModeAsync);
-#ifdef NUMLOCK_HACK
-            wHackedGrabKey(ptr->keycode, ptr->modifier,
-                           window, True, GrabModeAsync, GrabModeAsync);
-#endif
-        }
-        XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
-                 GrabModeAsync, GrabModeAsync);
-        ptr = ptr->next;
-    }
-}
-
-
-static void
-rebindKeygrabs(WScreen *scr)
-{
-    WWindow *wwin;
-
-    wwin = scr->focused_window;
-
-    while (wwin!=NULL) {
-        XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
-
-        if (!WFLAGP(wwin, no_bind_keys)) {
-            wWindowSetKeyGrabs(wwin);
-        }
-        wwin = wwin->prev;
-    }
-}
-
-
-static void
-removeShortcutsForMenu(WMenu *menu)
-{
-    Shortcut *ptr, *tmp;
-    Shortcut *newList = NULL;
-
-    ptr = shortcutList;
-    while (ptr!=NULL) {
-        tmp = ptr->next;
-        if (ptr->menu == menu) {
-            wfree(ptr);
-        } else {
-            ptr->next = newList;
-            newList = ptr;
-        }
-        ptr = tmp;
-    }
-    shortcutList = newList;
-    menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
-}
-
-
-static Bool
-addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
-            WMenuEntry *entry)
-{
-    Shortcut *ptr;
-    KeySym ksym;
-    char *k;
-    char buf[MAX_SHORTCUT_LENGTH], *b;
-
-    ptr = wmalloc(sizeof(Shortcut));
-
-    strncpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
-    b = (char*)buf;
-
-    /* get modifiers */
-    ptr->modifier = 0;
-    while ((k = strchr(b, '+'))!=NULL) {
-        int mod;
-
-        *k = 0;
-        mod = wXModifierFromKey(b);
-        if (mod<0) {
-            wwarning(_("%s: invalid key modifier \"%s\""), file, b);
-            wfree(ptr);
-            return False;
-        }
-        ptr->modifier |= mod;
-
-        b = k+1;
-    }
-
-    /* get key */
-    ksym = XStringToKeysym(b);
-
-    if (ksym==NoSymbol) {
-        wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
-                 file, shortcutDefinition, entry->text);
-        wfree(ptr);
-        return False;
-    }
-
-    ptr->keycode = XKeysymToKeycode(dpy, ksym);
-    if (ptr->keycode==0) {
-        wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
-                 shortcutDefinition, entry->text);
-        wfree(ptr);
-        return False;
-    }
-
-    ptr->menu = menu;
-    ptr->entry = entry;
-
-    ptr->next = shortcutList;
-    shortcutList = ptr;
-
-    menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
-
-    return True;
-}
-
-
-/*******************************/
-
-static char*
-cropline(char *line)
-{
-    char *end;
-
-    if (strlen(line)==0)
-        return line;
-
-    end = &(line[strlen(line)])-1;
-    while (isspace(*line) && *line!=0) line++;
-    while (end>line && isspace(*end)) {
-        *end=0;
-        end--;
-    }
-    return line;
-}
-
-
-static  char*
-next_token(char *line, char **next)
-{
-    char *tmp, c;
-    char *ret;
-
-    *next = NULL;
-    while (*line==' ' || *line=='\t') line++;
-
-    tmp = line;
-
-    if (*tmp=='"') {
-        tmp++; line++;
-        while (*tmp!=0 && *tmp!='"') tmp++;
-        if (*tmp!='"') {
-            wwarning(_("%s: unmatched '\"' in menu file"), line);
-            return NULL;
-        }
-    } else {
-        do {
-            if (*tmp=='\\')
-                tmp++;
-
-            if (*tmp!=0)
-                tmp++;
-
-        } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
-    }
-
-    c = *tmp;
-    *tmp = 0;
-    ret = wstrdup(line);
-    *tmp = c;
-
-    if (c==0)
-        return ret;
-    else
-        tmp++;
-
-    /* skip blanks */
-    while (*tmp==' ' || *tmp=='\t') tmp++;
-
-    if (*tmp!=0)
-        *next = tmp;
-
-    return ret;
-}
-
-
-static void
-separateCommand(char *line, char ***file, char **command)
-{
-    char *token, *tmp = line;
-    WMArray *array = WMCreateArray(4);
-    int count, i;
-
-    *file = NULL;
-    *command = NULL;
-    do {
-        token = next_token(tmp, &tmp);
-        if (token) {
-            if (strcmp(token, "WITH")==0) {
-                if (tmp!=NULL && *tmp!=0)
-                    *command = wstrdup(tmp);
-                else
-                    wwarning(_("%s: missing command"), line);
-                break;
-            }
-            WMAddToArray(array, token);
-        }
-    } while (token!=NULL && tmp!=NULL);
-
-    count = WMGetArrayItemCount(array);
-    if (count>0) {
-        *file = wmalloc(sizeof(char*)*(count+1));
-        (*file)[count] = NULL;
-        for (i = 0; i < count; i++) {
-            (*file)[i] = WMGetFromArray(array, i);
-        }
-    }
-    WMFreeArray(array);
-}
-
-
-static void
-constructMenu(WMenu *menu, WMenuEntry *entry)
-{
-    WMenu *submenu;
-    struct stat stat_buf;
-    char **path;
-    char *cmd;
-    char *lpath = NULL;
-    int i, first=-1;
-    time_t last=0;
-
-    separateCommand((char*)entry->clientdata, &path, &cmd);
-    if (path == NULL || *path==NULL || **path==0) {
-        wwarning(_("invalid OPEN_MENU specification: %s"),
-                 (char*)entry->clientdata);
-        return;
-    }
-
-    if (path[0][0]=='|') {
-        /* pipe menu */
-
-        if (!menu->cascades[entry->cascade] ||
-            menu->cascades[entry->cascade]->timestamp == 0) {
-            /* parse pipe */
-
-            submenu = readMenuPipe(menu->frame->screen_ptr, path);
-
-            if(submenu != NULL) {
-                if (path[0][1] == '|')
-                    submenu->timestamp = 0;
-                else
-                    submenu->timestamp = 1; /* there's no automatic reloading */
-            }
-        } else {
-            submenu = NULL;
-        }
-
-    } else {
-        i=0;
-        while(path[i] != NULL) {
-            char *tmp;
-
-            if (strcmp(path[i], "-noext")==0) {
-                i++;
-                continue;
-            }
-
-            tmp = wexpandpath(path[i]);
-            wfree(path[i]);
-            lpath = getLocalizedMenuFile(tmp);
-            if (lpath) {
-                wfree(tmp);
-                path[i] = lpath;
-                lpath = NULL;
-            } else {
-                path[i] = tmp;
-            }
-
-            if (stat(path[i], &stat_buf)==0) {
-                if (last < stat_buf.st_mtime)
-                    last = stat_buf.st_mtime;
-                if (first<0)
-                    first=i;
-            } else {
-                wsyserror(_("%s:could not stat menu"), path[i]);
-                /*goto finish;*/
-            }
-
-            i++;
-        }
-
-        if (first < 0) {
-            wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
-                      (char*)entry->clientdata);
-            goto finish;
-        }
-        stat(path[first], &stat_buf);
-        if (!menu->cascades[entry->cascade]
-            || menu->cascades[entry->cascade]->timestamp < last) {
-
-            if (S_ISDIR(stat_buf.st_mode)) {
-                /* menu directory */
-                submenu = readMenuDirectory(menu->frame->screen_ptr,
-                                            entry->text, path, cmd);
-                if (submenu)
-                    submenu->timestamp = last;
-            } else if (S_ISREG(stat_buf.st_mode)) {
-                /* menu file */
-
-                if (cmd || path[1])
-                    wwarning(_("too many parameters in OPEN_MENU: %s"),
-                             (char*)entry->clientdata);
-
-                submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
-                if (submenu)
-                    submenu->timestamp = stat_buf.st_mtime;
-            } else {
-                submenu = NULL;
-            }
-        } else {
-            submenu = NULL;
-        }
-    }
-
-    if (submenu) {
-        wMenuEntryRemoveCascade(menu, entry);
-        wMenuEntrySetCascade(menu, entry, submenu);
-    }
-
-finish:
-    i = 0;
-    while (path[i]!=NULL)
-        wfree(path[i++]);
-    wfree(path);
-    if (cmd)
-        wfree(cmd);
-}
-
-
-static void
-cleanupWorkspaceMenu(WMenu *menu)
-{
-    if (menu->frame->screen_ptr->workspace_menu == menu)
-        menu->frame->screen_ptr->workspace_menu = NULL;
-}
-
-
-static WMenuEntry*
-addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
-{
-    WMenu *wsmenu;
-    WMenuEntry *entry;
-
-    if (scr->flags.added_workspace_menu) {
-        wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
-        return NULL;
-    } else {
-        scr->flags.added_workspace_menu = 1;
-
-        wsmenu = wWorkspaceMenuMake(scr, True);
-        wsmenu->on_destroy = cleanupWorkspaceMenu;
-
-        scr->workspace_menu = wsmenu;
-        entry = wMenuAddCallback(menu, title, NULL, NULL);
-        wMenuEntrySetCascade(menu, entry, wsmenu);
-
-        wWorkspaceMenuUpdate(scr, wsmenu);
-    }
-    return entry;
-}
-
-
-static void
-cleanupWindowsMenu(WMenu *menu)
-{
-    if (menu->frame->screen_ptr->switch_menu == menu)
-        menu->frame->screen_ptr->switch_menu = NULL;
-}
-
-
-static WMenuEntry*
-addWindowsMenu(WScreen *scr, WMenu *menu, char *title)
-{
-    WMenu *wwmenu;
-    WWindow *wwin;
-    WMenuEntry *entry;
-
-    if (scr->flags.added_windows_menu) {
-        wwarning(_("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
-        return NULL;
-    } else {
-        scr->flags.added_windows_menu = 1;
-
-        wwmenu = wMenuCreate(scr, _("Window List"), False);
-        wwmenu->on_destroy = cleanupWindowsMenu;
-        scr->switch_menu = wwmenu;
-        wwin = scr->focused_window;
-        while (wwin) {
-            UpdateSwitchMenu(scr, wwin, ACTION_ADD);
-
-            wwin = wwin->prev;
-        }
-        entry = wMenuAddCallback(menu, title, NULL, NULL);
-        wMenuEntrySetCascade(menu, entry, wwmenu);
-    }
-    return entry;
-}
-
-
-static WMenuEntry*
-addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
-             char *params, char *file_name)
-{
-    WScreen *scr;
-    WMenuEntry *entry = NULL;
-    Bool shortcutOk = False;
-
-    if (!menu)
-        return NULL;
-    scr = menu->frame->screen_ptr;
-    if (strcmp(command, "OPEN_MENU")==0) {
-        if (!params) {
-            wwarning(_("%s:missing parameter for menu command \"%s\""),
-                     file_name, command);
-        } else {
-            WMenu *dummy;
-            char *path;
-
-            path = wfindfile(DEF_CONFIG_PATHS, params);
-            if (!path) {
-                path = wstrdup(params);
-            }
-            dummy = wMenuCreate(scr, title, False);
-            dummy->on_destroy = removeShortcutsForMenu;
-            entry = wMenuAddCallback(menu, title, constructMenu, path);
-            entry->free_cdata = free;
-            wMenuEntrySetCascade(menu, entry, dummy);
-        }
-    } else if (strcmp(command, "EXEC")==0) {
-        if (!params)
-            wwarning(_("%s:missing parameter for menu command \"%s\""),
-                     file_name, command);
-        else {
-            entry = wMenuAddCallback(menu, title, execCommand,
-                                     wstrconcat("exec ", params));
-            entry->free_cdata = free;
-            shortcutOk = True;
-        }
-    } else if (strcmp(command, "SHEXEC")==0) {
-        if (!params)
-            wwarning(_("%s:missing parameter for menu command \"%s\""),
-                     file_name, command);
-        else {
-            entry = wMenuAddCallback(menu, title, execCommand,
-                                     wstrdup(params));
-            entry->free_cdata = free;
-            shortcutOk = True;
-        }
-    } else if (strcmp(command, "EXIT")==0) {
-
-        if (params && strcmp(params, "QUICK")==0)
-            entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
-        else
-            entry = wMenuAddCallback(menu, title, exitCommand, NULL);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "SHUTDOWN")==0) {
-
-        if (params && strcmp(params, "QUICK")==0)
-            entry = wMenuAddCallback(menu, title, shutdownCommand,
-                                     (void*)M_QUICK);
-        else
-            entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "REFRESH")==0) {
-        entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "WORKSPACE_MENU")==0) {
-        entry = addWorkspaceMenu(scr, menu, title);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "WINDOWS_MENU")==0) {
-        entry = addWindowsMenu(scr, menu, title);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "ARRANGE_ICONS")==0) {
-        entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "HIDE_OTHERS")==0) {
-        entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "SHOW_ALL")==0) {
-        entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "RESTART")==0) {
-        entry = wMenuAddCallback(menu, title, restartCommand,
-                                 params ? wstrdup(params) : NULL);
-        entry->free_cdata = free;
-        shortcutOk = True;
-    } else if (strcmp(command, "SAVE_SESSION")==0) {
-        entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
-
-        shortcutOk = True;
-    } else if (strcmp(command, "CLEAR_SESSION")==0) {
-        entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
-        shortcutOk = True;
-    } else if (strcmp(command, "INFO_PANEL")==0) {
-        entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
-        shortcutOk = True;
-    } else if (strcmp(command, "LEGAL_PANEL")==0) {
-        entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
-        shortcutOk = True;
-    } else {
-        wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
-                 command);
-
-        return NULL;
-    }
-
-    if (shortcut && entry) {
-        if (!shortcutOk) {
-            wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
-                     title);
-        } else {
-            if (addShortcut(file_name, shortcut, menu, entry)) {
-
-                entry->rtext = GetShortcutString(shortcut);
-                /*
-                 entry->rtext = wstrdup(shortcut);
-                 */
-            }
-        }
-    }
-
-    return entry;
-}
-
-
-
-/*******************   Menu Configuration From File   *******************/
-
-static void
-separateline(char *line, char *title, char *command, char *parameter,
-             char *shortcut)
-{
-    int l, i;
-
-    l = strlen(line);
-
-    *title = 0;
-    *command = 0;
-    *parameter = 0;
-    *shortcut = 0;
-    /* get the title */
-    while (isspace(*line) && (*line!=0)) line++;
-    if (*line=='"') {
-        line++;
-        i=0;
-        while (line[i]!='"' && (line[i]!=0)) i++;
-        if (line[i]!='"')
-            return;
-    } else {
-        i=0;
-        while (!isspace(line[i]) && (line[i]!=0)) i++;
-    }
-    strncpy(title, line, i);
-    title[i++]=0;
-    line+=i;
-
-    /* get the command or shortcut keyword */
-    while (isspace(*line) && (*line!=0)) line++;
-    if (*line==0)
-        return;
-    i=0;
-    while (!isspace(line[i]) && (line[i]!=0)) i++;
-    strncpy(command, line, i);
-    command[i++]=0;
-    line+=i;
-
-    if (strcmp(command, "SHORTCUT")==0) {
-        /* get the shortcut key */
-        while (isspace(*line) && (*line!=0)) line++;
-        if (*line=='"') {
-            line++;
-            i=0;
-            while (line[i]!='"' && (line[i]!=0)) i++;
-            if (line[i]!='"')
-                return;
-        } else {
-            i=0;
-            while (!isspace(line[i]) && (line[i]!=0)) i++;
-        }
-        strncpy(shortcut, line, i);
-        shortcut[i++]=0;
-        line+=i;
-
-        *command=0;
-
-        /* get the command */
-        while (isspace(*line) && (*line!=0)) line++;
-        if (*line==0)
-            return;
-        i=0;
-        while (!isspace(line[i]) && (line[i]!=0)) i++;
-        strncpy(command, line, i);
-        command[i++]=0;
-        line+=i;
-    }
-
-    /* get the parameters */
-    while (isspace(*line) && (*line!=0)) line++;
-    if (*line==0)
-        return;
-
-    if (*line=='"') {
-        line++;
-        l = 0;
-        while (line[l]!=0 && line[l]!='"') {
-            parameter[l] = line[l];
-            l++;
-        }
-        parameter[l] = 0;
-        return;
-    }
-
-    l = strlen(line);
-    while (isspace(line[l]) && (l>0)) l--;
-    strncpy(parameter, line, l);
-    parameter[l]=0;
-}
-
-
-static WMenu*
-parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
-{
-    char linebuf[MAXLINE];
-    char elinebuf[MAXLINE];
-    char title[MAXLINE];
-    char command[MAXLINE];
-    char shortcut[MAXLINE];
-    char params[MAXLINE];
-    char *line;
-
-    while (!feof(file)) {
-        int lsize, ok;
-
-        ok = 0;
-        fgets(linebuf, MAXLINE, file);
-        line = cropline(linebuf);
-        lsize = strlen(line);
-        do {
-            if (line[lsize-1]=='\\') {
-                char *line2;
-                int lsize2;
-                fgets(elinebuf, MAXLINE, file);
-                line2=cropline(elinebuf);
-                lsize2=strlen(line2);
-                if (lsize2+lsize>MAXLINE) {
-                    wwarning(_("%s:maximal line size exceeded in menu config: %s"),
-                             file_name, line);
-                    ok=2;
-                } else {
-                    line[lsize-1]=0;
-                    lsize+=lsize2-1;
-                    strcat(line, line2);
-                }
-            } else {
-                ok=1;
-            }
-        } while (!ok && !feof(file));
-        if (ok==2)
-            continue;
-
-        if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
-            continue;
-
-
-        separateline(line, title, command, params, shortcut);
-
-        if (!command[0]) {
-            wwarning(_("%s:missing command in menu config: %s"), file_name,
-                     line);
-            goto error;
-        }
-
-        if (strcasecmp(command, "MENU")==0) {
-            WMenu *cascade;
-
-            /* start submenu */
-
-            cascade = wMenuCreate(scr, title, False);
-            cascade->on_destroy = removeShortcutsForMenu;
-            if (parseCascade(scr, cascade, file, file_name)==NULL) {
-                wMenuDestroy(cascade, True);
-            } else {
-                wMenuEntrySetCascade(menu,
-                                     wMenuAddCallback(menu, title, NULL, NULL),
-                                     cascade);
-            }
-        } else if (strcasecmp(command, "END")==0) {
-            /* end of menu */
-            return menu;
-
-        } else {
-            /* normal items */
-            addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
-                         params[0] ? params : NULL, file_name);
-        }
-    }
-
-    wwarning(_("%s:syntax error in menu file:END declaration missing"),
-             file_name);
-    return menu;
-
-error:
-    return menu;
-}
-
-
-static WMenu*
-readMenuFile(WScreen *scr, char *file_name)
-{
-    WMenu *menu=NULL;
-    FILE *file = NULL;
-    char linebuf[MAXLINE];
-    char title[MAXLINE];
-    char shortcut[MAXLINE];
-    char command[MAXLINE];
-    char params[MAXLINE];
-    char *line;
-#ifdef USECPP
-    char *args;
-    int cpp = 0;
-#endif
-
-#ifdef USECPP
-    if (!wPreferences.flags.nocpp) {
-        args = MakeCPPArgs(file_name);
-        if (!args) {
-            wwarning(_("could not make arguments for menu file preprocessor"));
-        } else {
-            snprintf(command, sizeof(command), "%s %s %s",
-                     CPP_PATH, args, file_name);
-            wfree(args);
-            file = popen(command, "r");
-            if (!file) {
-                wsyserror(_("%s:could not open/preprocess menu file"),
-                          file_name);
-            } else {
-                cpp = 1;
-            }
-        }
-    }
-#endif /* USECPP */
-
-    if (!file) {
-        file = fopen(file_name, "rb");
-        if (!file) {
-            wsyserror(_("%s:could not open menu file"), file_name);
-            return NULL;
-        }
-    }
-
-    while (!feof(file)) {
-        if (!fgets(linebuf, MAXLINE, file))
-            break;
-        line = cropline(linebuf);
-        if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
-            continue;
-
-        separateline(line, title, command, params, shortcut);
-
-        if (!command[0]) {
-            wwarning(_("%s:missing command in menu config: %s"), file_name,
-                     line);
-            break;
-        }
-        if (strcasecmp(command, "MENU")==0) {
-            menu = wMenuCreate(scr, title, True);
-            menu->on_destroy = removeShortcutsForMenu;
-            if (!parseCascade(scr, menu, file, file_name)) {
-                wMenuDestroy(menu, True);
-            }
-            break;
-        } else {
-            wwarning(_("%s:invalid menu file. MENU command is missing"),
-                     file_name);
-            break;
-        }
-    }
-
-#ifdef CPP
-    if (cpp) {
-        if (pclose(file)==-1) {
-            wsyserror(_("error reading preprocessed menu data"));
-        }
-    } else {
-        fclose(file);
-    }
-#else
-    fclose(file);
-#endif
-
-    return menu;
-}
-
-
-/************    Menu Configuration From Pipe      *************/
-
-static WMenu*
-readMenuPipe(WScreen *scr, char **file_name)
-{
-    WMenu *menu=NULL;
-    FILE *file = NULL;
-    char linebuf[MAXLINE];
-    char title[MAXLINE];
-    char command[MAXLINE];
-    char params[MAXLINE];
-    char shortcut[MAXLINE];
-    char *line;
-    char *filename;
-    char flat_file[MAXLINE];
-    int i;
-#ifdef USECPP
-    char *args;
-    int cpp = 0;
-#endif
-
-    flat_file[0] = '\0';
-
-    for(i=0; file_name[i]!=NULL; i++) {
-        strcat(flat_file, file_name[i]);
-        strcat(flat_file, " ");
-    }
-    filename = flat_file + (flat_file[1]=='|'?2:1);
-
-
-#ifdef USECPP
-    if (!wPreferences.flags.nocpp) {
-        args = MakeCPPArgs(filename);
-        if (!args) {
-            wwarning(_("could not make arguments for menu file preprocessor"));
-        } else {
-            snprintf(command, sizeof(command), "%s | %s %s",
-                     filename, CPP_PATH, args);
-
-            wfree(args);
-            file = popen(command, "r");
-            if (!file) {
-                wsyserror(_("%s:could not open/preprocess menu file"), filename);
-            } else {
-                cpp = 1;
-            }
-        }
-    }
-
-#endif /* USECPP */
-
-    if (!file) {
-        file = popen(filename, "rb");
-
-        if (!file) {
-            wsyserror(_("%s:could not open menu file"), filename);
-            return NULL;
-        }
-    }
-
-    while (!feof(file)) {
-        if (!fgets(linebuf, MAXLINE, file))
-            break;
-        line = cropline(linebuf);
-        if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
-            continue;
-
-        separateline(line, title, command, params, shortcut);
-
-        if (!command[0]) {
-            wwarning(_("%s:missing command in menu config: %s"), file_name,
-                     line);
-            break;
-        }
-        if (strcasecmp(command, "MENU")==0) {
-            menu = wMenuCreate(scr, title, True);
-            menu->on_destroy = removeShortcutsForMenu;
-            if (!parseCascade(scr, menu, file, filename)) {
-                wMenuDestroy(menu, True);
-            }
-            break;
-        } else {
-            wwarning(_("%s:no title given for the root menu"), filename);
-            break;
-        }
-    }
-
-    pclose(file);
-
-    return menu;
-}
-
-
-
-typedef struct {
-    char *name;
-    int index;
-} dir_data;
-
-
-static int
-myCompare(const void *d1, const void *d2)
-{
-    dir_data *p1 = *(dir_data**)d1;
-    dir_data *p2 = *(dir_data**)d2;
-
-    return strcmp(p1->name, p2->name);
-}
-
-
-/************  Menu Configuration From Directory   *************/
-
-static Bool
-isFilePackage(char *file)
-{
-    int l;
-
-    /* check if the extension indicates this file is a
-     * file package. For now, only recognize .themed */
-
-    l = strlen(file);
-
-    if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
-        return True;
-    } else {
-        return False;
-    }
-}
-
-
-static WMenu*
-readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
-{
-    DIR *dir;
-    struct dirent *dentry;
-    struct stat stat_buf;
-    WMenu *menu=NULL;
-    char *buffer;
-    WMArray *dirs = NULL, *files = NULL;
-    WMArrayIterator iter;
-    int length, i, have_space=0;
-    dir_data *data;
-    int stripExtension = 0;
-
-
-    dirs = WMCreateArray(16);
-    files = WMCreateArray(16);
-
-    i=0;
-    while (path[i]!=NULL) {
-        if (strcmp(path[i], "-noext")==0) {
-            stripExtension = 1;
-            i++;
-            continue;
-        }
-
-        dir = opendir(path[i]);
-        if (!dir) {
-            i++;
-            continue;
-        }
-
-        while ((dentry = readdir(dir))) {
-
-            if (strcmp(dentry->d_name, ".")==0 ||
-                strcmp(dentry->d_name, "..")==0)
-                continue;
-
-            if (dentry->d_name[0] == '.')
-                continue;
-
-            buffer = malloc(strlen(path[i])+strlen(dentry->d_name)+4);
-            if (!buffer) {
-                wsyserror(_("out of memory while constructing directory menu %s"),
-                          path[i]);
-                break;
-            }
-
-            strcpy(buffer, path[i]);
-            strcat(buffer, "/");
-            strcat(buffer, dentry->d_name);
-
-            if (stat(buffer, &stat_buf)!=0) {
-                wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
-                          path[i], dentry->d_name);
-            } else {
-                Bool isFilePack = False;
-
-                data = NULL;
-                if (S_ISDIR(stat_buf.st_mode)
-                    && !(isFilePack = isFilePackage(dentry->d_name))) {
-
-                    /* access always returns success for user root */
-                    if (access(buffer, X_OK)==0) {
-                        /* Directory is accesible. Add to directory list */
-
-                        data = (dir_data*) wmalloc(sizeof(dir_data));
-                        data->name = wstrdup(dentry->d_name);
-                        data->index = i;
-
-                        WMAddToArray(dirs, data);
-                    }
-                } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
-                    /* Hack because access always returns X_OK success for user root */
-#define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
-                    if ((command!=NULL && access(buffer, R_OK)==0) ||
-                        (command==NULL && access(buffer, X_OK)==0 &&
-                         (stat_buf.st_mode & S_IXANY))) {
-
-                        data = (dir_data*) wmalloc(sizeof(dir_data));
-                        data->name = wstrdup(dentry->d_name);
-                        data->index = i;
-
-                        WMAddToArray(files, data);
-                    }
-                }
-            }
-            wfree(buffer);
-        }
-
-        closedir(dir);
-        i++;
-    }
-
-    if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
-        WMFreeArray(dirs);
-        WMFreeArray(files);
-        return NULL;
-    }
-
-    WMSortArray(dirs, myCompare);
-    WMSortArray(files, myCompare);
-
-    menu = wMenuCreate(scr, title, False);
-    menu->on_destroy = removeShortcutsForMenu;
-
-    WM_ITERATE_ARRAY(dirs, data, iter) {
-        /* New directory. Use same OPEN_MENU command that was used
-         * for the current directory. */
-        length = strlen(path[data->index])+strlen(data->name)+6;
-        if (stripExtension)
-            length += 7;
-        if (command)
-            length += strlen(command) + 6;
-        buffer = malloc(length);
-        if (!buffer) {
-            wsyserror(_("out of memory while constructing directory menu %s"),
-                      path[data->index]);
-            break;
-        }
-
-        buffer[0] = '\0';
-        if (stripExtension)
-            strcat(buffer, "-noext ");
-
-        have_space = strchr(path[data->index], ' ')!=NULL ||
-            strchr(data->name, ' ')!=NULL;
-
-        if (have_space)
-            strcat(buffer, "\"");
-        strcat(buffer, path[data->index]);
-
-        strcat(buffer, "/");
-        strcat(buffer, data->name);
-        if (have_space)
-            strcat(buffer, "\"");
-        if (command) {
-            strcat(buffer, " WITH ");
-            strcat(buffer, command);
-        }
-
-        addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]);
-
-        wfree(buffer);
-        if (data->name)
-            wfree(data->name);
-        wfree(data);
-    }
-
-    WM_ITERATE_ARRAY(files, data, iter) {
-        /* executable: add as entry */
-        length = strlen(path[data->index])+strlen(data->name)+6;
-        if (command)
-            length += strlen(command);
-
-        buffer = malloc(length);
-        if (!buffer) {
-            wsyserror(_("out of memory while constructing directory menu %s"),
-                      path[data->index]);
-            break;
-        }
-
-        have_space = strchr(path[data->index], ' ')!=NULL ||
-            strchr(data->name, ' ')!=NULL;
-        if (command!=NULL) {
-            strcpy(buffer, command);
-            strcat(buffer, " ");
-            if (have_space)
-                strcat(buffer, "\"");
-            strcat(buffer, path[data->index]);
-        } else {
-            if (have_space) {
-                buffer[0] = '"';
-                buffer[1] = 0;
-                strcat(buffer, path[data->index]);
-            } else {
-                strcpy(buffer, path[data->index]);
-            }
-        }
-        strcat(buffer, "/");
-        strcat(buffer, data->name);
-        if (have_space)
-            strcat(buffer, "\"");
-
-        if (stripExtension) {
-            char *ptr = strrchr(data->name, '.');
-            if (ptr && ptr!=data->name)
-                *ptr = 0;
-        }
-        addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]);
-
-        wfree(buffer);
-        if (data->name)
-            wfree(data->name);
-        wfree(data);
-    }
-
-    WMFreeArray(files);
-    WMFreeArray(dirs);
-
-    return menu;
-}
-
-
-/************  Menu Configuration From WMRootMenu   *************/
-
-static WMenu*
-makeDefaultMenu(WScreen *scr)
-{
-    WMenu *menu=NULL;
-
-    menu = wMenuCreate(scr, _("Commands"), True);
-    wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
-    wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
-    wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
-    wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
-    return menu;
-}
-
-
-
-
-
-/*
- *----------------------------------------------------------------------
- * configureMenu--
- *     Reads root menu configuration from defaults database.
- *
- *----------------------------------------------------------------------
- */
-static WMenu*
-configureMenu(WScreen *scr, WMPropList *definition)
-{
-    WMenu *menu = NULL;
-    WMPropList *elem;
-    int i, count;
-    WMPropList *title, *command, *params;
-    char *tmp, *mtitle;
-
-
-    if (WMIsPLString(definition)) {
-        struct stat stat_buf;
-        char *path = NULL;
-        Bool menu_is_default = False;
-
-        /* menu definition is a string. Probably a path, so parse the file */
-
-        tmp = wexpandpath(WMGetFromPLString(definition));
-
-        path = getLocalizedMenuFile(tmp);
-
-        if (!path)
-            path = wfindfile(DEF_CONFIG_PATHS, tmp);
-
-        if (!path) {
-            path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
-            menu_is_default = True;
-        }
-
-        if (!path) {
-            wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
-                      tmp);
-            wfree(tmp);
-            return NULL;
-        }
-
-        if (stat(path, &stat_buf)<0) {
-            wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
-            wfree(path);
-            wfree(tmp);
-            return NULL;
-        }
-
-        if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
-            /* if the pointer in WMRootMenu has changed */
-            || WDRootMenu->timestamp > scr->root_menu->timestamp) {
-
-            if (menu_is_default) {
-                wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
-                         path);
-            }
-
-            menu = readMenuFile(scr, path);
-            if (menu)
-                menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
-        } else {
-            menu = NULL;
-        }
-        wfree(path);
-        wfree(tmp);
-
-        return menu;
-    }
-
-    count = WMGetPropListItemCount(definition);
-    if (count==0)
-        return NULL;
-
-    elem = WMGetFromPLArray(definition, 0);
-    if (!WMIsPLString(elem)) {
-        tmp = WMGetPropListDescription(elem, False);
-        wwarning(_("%s:format error in root menu configuration \"%s\""),
-                 "WMRootMenu", tmp);
-        wfree(tmp);
-        return NULL;
-    }
-    mtitle = WMGetFromPLString(elem);
-
-    menu = wMenuCreate(scr, mtitle, False);
-    menu->on_destroy = removeShortcutsForMenu;
-
-#ifdef GLOBAL_SUBMENU_FILE
-    {
-        WMenu *submenu;
-        WMenuEntry *mentry;
-
-        submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
-
-        if (submenu) {
-            mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
-            wMenuEntrySetCascade(menu, mentry, submenu);
-        }
-    }
-#endif
-
-    for (i=1; i<count; i++) {
-        elem = WMGetFromPLArray(definition, i);
-#if 0
-        if (WMIsPLString(elem)) {
-            char *file;
-
-            file = WMGetFromPLString(elem);
-
-        }
-#endif
-        if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
-            goto error;
-
-        if (WMIsPLArray(WMGetFromPLArray(elem,1))) {
-            WMenu *submenu;
-            WMenuEntry *mentry;
-
-            /* submenu */
-            submenu = configureMenu(scr, elem);
-            if (submenu) {
-                mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
-                                          NULL);
-                wMenuEntrySetCascade(menu, mentry, submenu);
-            }
-        } else {
-            int idx = 0;
-            WMPropList *shortcut;
-            /* normal entry */
-
-            title = WMGetFromPLArray(elem, idx++);
-            shortcut = WMGetFromPLArray(elem, idx++);
-            if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT")==0) {
-                shortcut = WMGetFromPLArray(elem, idx++);
-                command = WMGetFromPLArray(elem, idx++);
-            } else {
-                command = shortcut;
-                shortcut = NULL;
-            }
-            params = WMGetFromPLArray(elem, idx++);
-
-            if (!title || !command)
-                goto error;
-
-            addMenuEntry(menu, WMGetFromPLString(title),
-                         shortcut ? WMGetFromPLString(shortcut) : NULL,
-                         WMGetFromPLString(command),
-                         params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
-        }
-        continue;
-
-    error:
-        tmp = WMGetPropListDescription(elem, False);
-        wwarning(_("%s:format error in root menu configuration \"%s\""),
-                 "WMRootMenu", tmp);
-        wfree(tmp);
-    }
-
-    return menu;
-}
-
-
-/*
- *----------------------------------------------------------------------
- * OpenRootMenu--
- *     Opens the root menu, parsing the menu configuration from the
- * defaults database.
- *     If the menu is already mapped and is not sticked to the
- * root window, it will be unmapped.
- *
- * Side effects:
- *     The menu may be remade.
- *
- * Notes:
- * Construction of OPEN_MENU entries are delayed to the moment the
- * user map's them.
- *----------------------------------------------------------------------
- */
-void
-OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
-{
-    WMenu *menu=NULL;
-    WMPropList *definition;
-    /*
-     static WMPropList *domain=NULL;
-
-     if (!domain) {
-     domain = WMCreatePLString("WMRootMenu");
-     }
-     */
-
-    scr->flags.root_menu_changed_shortcuts = 0;
-    scr->flags.added_workspace_menu = 0;
-    scr->flags.added_windows_menu = 0;
-
-    if (scr->root_menu && scr->root_menu->flags.mapped) {
-        menu = scr->root_menu;
-        if (!menu->flags.buttoned) {
-            wMenuUnmap(menu);
-        } else {
-            wRaiseFrame(menu->frame->core);
-
-            if (keyboard)
-                wMenuMapAt(menu, 0, 0, True);
-            else
-                wMenuMapCopyAt(menu, x-menu->frame->core->width/2, y);
-        }
-        return;
-    }
-
-
-    definition = WDRootMenu->dictionary;
-
-    /*
-     definition = PLGetDomain(domain);
-     */
-    if (definition) {
-        if (WMIsPLArray(definition)) {
-            if (!scr->root_menu
-                || WDRootMenu->timestamp > scr->root_menu->timestamp) {
-                menu = configureMenu(scr, definition);
-                if (menu)
-                    menu->timestamp = WDRootMenu->timestamp;
-
-            } else
-                menu = NULL;
-        } else {
-            menu = configureMenu(scr, definition);
-        }
-    }
-
-    if (!menu) {
-        /* menu hasn't changed or could not be read */
-        if (!scr->root_menu) {
-            wMessageDialog(scr, _("Error"),
-                           _("The applications menu could not be loaded. "
-                             "Look at the console output for a detailed "
-                             "description of the errors."),
-                           _("OK"), NULL, NULL);
-
-            menu = makeDefaultMenu(scr);
-            scr->root_menu = menu;
-        }
-        menu = scr->root_menu;
-    } else {
-        /* new root menu */
-        if (scr->root_menu) {
-            wMenuDestroy(scr->root_menu, True);
-        }
-        scr->root_menu = menu;
-    }
-    if (menu) {
-        int newx, newy;
-
-        if (keyboard && x==0 && y==0) {
-            newx = newy = 0;
-        } else if (keyboard && x==scr->scr_width/2 && y==scr->scr_height/2) {
-            newx = x - menu->frame->core->width/2;
-            newy = y - menu->frame->core->height/2;
-        } else {
-            newx = x - menu->frame->core->width/2;
-            newy = y;
-        }
-        wMenuMapAt(menu, newx, newy, keyboard);
-    }
-
-    if (scr->flags.root_menu_changed_shortcuts)
-        rebindKeygrabs(scr);
-}
-
-#endif /* !LITE */
-
+/* rootmenu.c- user defined menu
+ *
+ *  Window Maker window manager
+ *
+ *  Copyright (c) 1997-2003 Alfredo K. Kojima
+ *  Copyright (c) 1998-2003 Dan Pascu
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include "wconfig.h"
+
+#ifndef LITE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <dirent.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include "WindowMaker.h"
+#include "actions.h"
+#include "menu.h"
+#include "funcs.h"
+#include "dialog.h"
+#include "keybind.h"
+#include "stacking.h"
+#include "workspace.h"
+#include "defaults.h"
+#include "framewin.h"
+#include "session.h"
+#include "xmodifier.h"
+
+#include <WINGs/WUtil.h>
+
+#define MAX_SHORTCUT_LENGTH 32
+
+extern char *Locale;
+
+extern WDDomain *WDRootMenu;
+
+extern Cursor wCursor[WCUR_LAST];
+
+extern Time LastTimestamp;
+
+extern WPreferences wPreferences;
+
+extern int wScreenCount;
+
+static WMenu *readMenuPipe(WScreen * scr, char **file_name);
+static WMenu *readMenuFile(WScreen * scr, char *file_name);
+static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
+
+typedef struct Shortcut {
+       struct Shortcut *next;
+
+       int modifier;
+       KeyCode keycode;
+       WMenuEntry *entry;
+       WMenu *menu;
+} Shortcut;
+
+static Shortcut *shortcutList = NULL;
+
+/*
+ * Syntax:
+ * # main menu
+ * "Menu Name" MENU
+ *     "Title" EXEC command_to_exec -params
+ *     "Submenu" MENU
+ *             "Title" EXEC command_to_exec -params
+ *     "Submenu" END
+ *     "Workspaces" WORKSPACE_MENU
+ *     "Title" built_in_command
+ *     "Quit" EXIT
+ *     "Quick Quit" EXIT QUICK
+ * "Menu Name" END
+ *
+ * Commands may be preceded by SHORTCUT key
+ *
+ * Built-in commands:
+ *
+ * INFO_PANEL - shows the Info Panel
+ * LEGAL_PANEL - shows the Legal info panel
+ * SHUTDOWN [QUICK] - closes the X server [without confirmation]
+ * REFRESH - forces the desktop to be repainted
+ * EXIT [QUICK] - exit the window manager [without confirmation]
+ * EXEC <program> - execute an external program
+ * SHEXEC <command> - execute a shell command
+ * WORKSPACE_MENU - places the workspace submenu
+ * ARRANGE_ICONS
+ * RESTART [<window manager>] - restarts the window manager
+ * SHOW_ALL - unhide all windows on workspace
+ * HIDE_OTHERS - hides all windows excep the focused one
+ * OPEN_MENU file - read menu data from file which must be a valid menu file.
+ * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
+ *              - read menu data from directory(ies) and
+ *               eventually precede each with a command.
+ * OPEN_MENU | command
+ *              - opens command and uses its stdout to construct and insert
+ *                the resulting menu in current position. The output of
+ *                command must be a valid menu description.
+ *                The space between '|' and command is optional.
+ *                || will do the same, but will not cache the contents.
+ * SAVE_SESSION - saves the current state of the desktop, which include
+ *               all running applications, all their hints (geometry,
+ *               position on screen, workspace they live on, the dock
+ *               or clip from where they were launched, and
+ *               if minimized, shaded or hidden. Also saves the current
+ *               workspace the user is on. All will be restored on every
+ *               start of windowmaker until another SAVE_SESSION or
+ *               CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
+ *               WindowMaker domain file, then saving is automatically
+ *               done on every windowmaker exit, overwriting any
+ *               SAVE_SESSION or CLEAR_SESSION (see below). Also save
+ *               dock state now.
+ * CLEAR_SESSION - clears any previous saved session. This will not have
+ *               any effect if SaveSessionOnExit is True.
+ *
+ */
+
+#define M_QUICK                1
+
+/* menu commands */
+
+static void execCommand(WMenu * menu, WMenuEntry * entry)
+{
+       char *cmdline;
+
+       cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
+
+       XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
+                    GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
+       XSync(dpy, 0);
+
+       if (cmdline) {
+               ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
+               wfree(cmdline);
+       }
+       XUngrabPointer(dpy, CurrentTime);
+       XSync(dpy, 0);
+}
+
+static void exitCommand(WMenu * menu, WMenuEntry * entry)
+{
+       static int inside = 0;
+       int result;
+
+       /* prevent reentrant calls */
+       if (inside)
+               return;
+       inside = 1;
+
+#define R_CANCEL 0
+#define R_EXIT   1
+
+       result = R_CANCEL;
+
+       if ((long)entry->clientdata == M_QUICK) {
+               result = R_EXIT;
+       } else {
+               int r, oldSaveSessionFlag;
+
+               oldSaveSessionFlag = wPreferences.save_session_on_exit;
+               r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
+                               _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
+
+               if (r == WAPRDefault) {
+                       result = R_EXIT;
+               } else if (r == WAPRAlternate) {
+                       /* Don't modify the "save session on exit" flag if the
+                        * user canceled the operation. */
+                       wPreferences.save_session_on_exit = oldSaveSessionFlag;
+               }
+       }
+       if (result == R_EXIT) {
+#ifdef DEBUG
+               printf("Exiting WindowMaker.\n");
+#endif
+               Shutdown(WSExitMode);
+       }
+#undef R_EXIT
+#undef R_CANCEL
+       inside = 0;
+}
+
+static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
+{
+       static int inside = 0;
+       int result;
+
+       /* prevent reentrant calls */
+       if (inside)
+               return;
+       inside = 1;
+
+#define R_CANCEL 0
+#define R_CLOSE 1
+#define R_KILL 2
+
+       result = R_CANCEL;
+       if ((long)entry->clientdata == M_QUICK)
+               result = R_CLOSE;
+       else {
+#ifdef XSMP_ENABLED
+               if (wSessionIsManaged()) {
+                       int r;
+
+                       r = wMessageDialog(menu->frame->screen_ptr,
+                                          _("Close X session"),
+                                          _("Close Window System session?\n"
+                                            "Kill might close applications with unsaved data."),
+                                          _("Close"), _("Kill"), _("Cancel"));
+                       if (r == WAPRDefault)
+                               result = R_CLOSE;
+                       else if (r == WAPRAlternate)
+                               result = R_KILL;
+               } else
+#endif
+               {
+                       int r, oldSaveSessionFlag;
+
+                       oldSaveSessionFlag = wPreferences.save_session_on_exit;
+
+                       r = wExitDialog(menu->frame->screen_ptr,
+                                       _("Kill X session"),
+                                       _("Kill Window System session?\n"
+                                         "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
+                       if (r == WAPRDefault) {
+                               result = R_KILL;
+                       } else if (r == WAPRAlternate) {
+                               /* Don't modify the "save session on exit" flag if the
+                                * user canceled the operation. */
+                               wPreferences.save_session_on_exit = oldSaveSessionFlag;
+                       }
+               }
+       }
+
+       if (result != R_CANCEL) {
+#ifdef XSMP_ENABLED
+               if (result == R_CLOSE) {
+                       Shutdown(WSLogoutMode);
+               } else
+#endif                         /* XSMP_ENABLED */
+               {
+                       Shutdown(WSKillMode);
+               }
+       }
+#undef R_CLOSE
+#undef R_CANCEL
+#undef R_KILL
+       inside = 0;
+}
+
+static void restartCommand(WMenu * menu, WMenuEntry * entry)
+{
+       Shutdown(WSRestartPreparationMode);
+       Restart((char *)entry->clientdata, False);
+       Restart(NULL, True);
+}
+
+static void refreshCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wRefreshDesktop(menu->frame->screen_ptr);
+}
+
+static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wArrangeIcons(menu->frame->screen_ptr, True);
+}
+
+static void showAllCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wShowAllWindows(menu->frame->screen_ptr);
+}
+
+static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wHideOtherApplications(menu->frame->screen_ptr->focused_window);
+}
+
+static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
+{
+       if (!wPreferences.save_session_on_exit)
+               wSessionSaveState(menu->frame->screen_ptr);
+
+       wScreenSaveState(menu->frame->screen_ptr);
+}
+
+static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wSessionClearState(menu->frame->screen_ptr);
+       wScreenSaveState(menu->frame->screen_ptr);
+}
+
+static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wShowInfoPanel(menu->frame->screen_ptr);
+}
+
+static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wShowLegalPanel(menu->frame->screen_ptr);
+}
+
+/********************************************************************/
+
+static char *getLocalizedMenuFile(char *menu)
+{
+       char *buffer, *ptr, *locale;
+       int len;
+
+       if (!Locale)
+               return NULL;
+
+       len = strlen(menu) + strlen(Locale) + 8;
+       buffer = wmalloc(len);
+
+       /* try menu.locale_name */
+       snprintf(buffer, len, "%s.%s", menu, Locale);
+       if (access(buffer, F_OK) == 0) {
+               return buffer;
+       }
+
+       /* position of locale in our buffer */
+       locale = buffer + strlen(menu) + 1;
+
+       /* check if it is in the form aa_bb.encoding and check for aa_bb */
+       ptr = strchr(locale, '.');
+       if (ptr) {
+               *ptr = 0;
+               if (access(buffer, F_OK) == 0) {
+                       return buffer;
+               }
+       }
+       /* now check for aa */
+       ptr = strchr(locale, '_');
+       if (ptr) {
+               *ptr = 0;
+               if (access(buffer, F_OK) == 0) {
+                       return buffer;
+               }
+       }
+
+       wfree(buffer);
+
+       return NULL;
+}
+
+static void raiseMenus(WMenu * menu)
+{
+       int i;
+
+       if (menu->flags.mapped) {
+               wRaiseFrame(menu->frame->core);
+       }
+       for (i = 0; i < menu->cascade_no; i++) {
+               if (menu->cascades[i])
+                       raiseMenus(menu->cascades[i]);
+       }
+}
+
+Bool wRootMenuPerformShortcut(XEvent * event)
+{
+       WScreen *scr = wScreenForRootWindow(event->xkey.root);
+       Shortcut *ptr;
+       int modifiers;
+       int done = 0;
+
+       /* ignore CapsLock */
+       modifiers = event->xkey.state & ValidModMask;
+
+       for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
+               if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
+                       continue;
+
+               if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
+                       (*ptr->entry->callback) (ptr->menu, ptr->entry);
+                       done = True;
+               }
+       }
+
+       return done;
+}
+
+void wRootMenuBindShortcuts(Window window)
+{
+       Shortcut *ptr;
+
+       ptr = shortcutList;
+       while (ptr) {
+               if (ptr->modifier != AnyModifier) {
+                       XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
+                                window, True, GrabModeAsync, GrabModeAsync);
+#ifdef NUMLOCK_HACK
+                       wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
+#endif
+               }
+               XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
+               ptr = ptr->next;
+       }
+}
+
+static void rebindKeygrabs(WScreen * scr)
+{
+       WWindow *wwin;
+
+       wwin = scr->focused_window;
+
+       while (wwin != NULL) {
+               XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
+
+               if (!WFLAGP(wwin, no_bind_keys)) {
+                       wWindowSetKeyGrabs(wwin);
+               }
+               wwin = wwin->prev;
+       }
+}
+
+static void removeShortcutsForMenu(WMenu * menu)
+{
+       Shortcut *ptr, *tmp;
+       Shortcut *newList = NULL;
+
+       ptr = shortcutList;
+       while (ptr != NULL) {
+               tmp = ptr->next;
+               if (ptr->menu == menu) {
+                       wfree(ptr);
+               } else {
+                       ptr->next = newList;
+                       newList = ptr;
+               }
+               ptr = tmp;
+       }
+       shortcutList = newList;
+       menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
+}
+
+static Bool addShortcut(char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
+{
+       Shortcut *ptr;
+       KeySym ksym;
+       char *k;
+       char buf[MAX_SHORTCUT_LENGTH], *b;
+
+       ptr = wmalloc(sizeof(Shortcut));
+
+       strncpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
+       b = (char *)buf;
+
+       /* get modifiers */
+       ptr->modifier = 0;
+       while ((k = strchr(b, '+')) != NULL) {
+               int mod;
+
+               *k = 0;
+               mod = wXModifierFromKey(b);
+               if (mod < 0) {
+                       wwarning(_("%s: invalid key modifier \"%s\""), file, b);
+                       wfree(ptr);
+                       return False;
+               }
+               ptr->modifier |= mod;
+
+               b = k + 1;
+       }
+
+       /* get key */
+       ksym = XStringToKeysym(b);
+
+       if (ksym == NoSymbol) {
+               wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
+                        file, shortcutDefinition, entry->text);
+               wfree(ptr);
+               return False;
+       }
+
+       ptr->keycode = XKeysymToKeycode(dpy, ksym);
+       if (ptr->keycode == 0) {
+               wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
+                        shortcutDefinition, entry->text);
+               wfree(ptr);
+               return False;
+       }
+
+       ptr->menu = menu;
+       ptr->entry = entry;
+
+       ptr->next = shortcutList;
+       shortcutList = ptr;
+
+       menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
+
+       return True;
+}
+
+/*******************************/
+
+static char *cropline(char *line)
+{
+       char *end;
+
+       if (strlen(line) == 0)
+               return line;
+
+       end = &(line[strlen(line)]) - 1;
+       while (isspace(*line) && *line != 0)
+               line++;
+       while (end > line && isspace(*end)) {
+               *end = 0;
+               end--;
+       }
+       return line;
+}
+
+static char *next_token(char *line, char **next)
+{
+       char *tmp, c;
+       char *ret;
+
+       *next = NULL;
+       while (*line == ' ' || *line == '\t')
+               line++;
+
+       tmp = line;
+
+       if (*tmp == '"') {
+               tmp++;
+               line++;
+               while (*tmp != 0 && *tmp != '"')
+                       tmp++;
+               if (*tmp != '"') {
+                       wwarning(_("%s: unmatched '\"' in menu file"), line);
+                       return NULL;
+               }
+       } else {
+               do {
+                       if (*tmp == '\\')
+                               tmp++;
+
+                       if (*tmp != 0)
+                               tmp++;
+
+               } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
+       }
+
+       c = *tmp;
+       *tmp = 0;
+       ret = wstrdup(line);
+       *tmp = c;
+
+       if (c == 0)
+               return ret;
+       else
+               tmp++;
+
+       /* skip blanks */
+       while (*tmp == ' ' || *tmp == '\t')
+               tmp++;
+
+       if (*tmp != 0)
+               *next = tmp;
+
+       return ret;
+}
+
+static void separateCommand(char *line, char ***file, char **command)
+{
+       char *token, *tmp = line;
+       WMArray *array = WMCreateArray(4);
+       int count, i;
+
+       *file = NULL;
+       *command = NULL;
+       do {
+               token = next_token(tmp, &tmp);
+               if (token) {
+                       if (strcmp(token, "WITH") == 0) {
+                               if (tmp != NULL && *tmp != 0)
+                                       *command = wstrdup(tmp);
+                               else
+                                       wwarning(_("%s: missing command"), line);
+                               break;
+                       }
+                       WMAddToArray(array, token);
+               }
+       } while (token != NULL && tmp != NULL);
+
+       count = WMGetArrayItemCount(array);
+       if (count > 0) {
+               *file = wmalloc(sizeof(char *) * (count + 1));
+               (*file)[count] = NULL;
+               for (i = 0; i < count; i++) {
+                       (*file)[i] = WMGetFromArray(array, i);
+               }
+       }
+       WMFreeArray(array);
+}
+
+static void constructMenu(WMenu * menu, WMenuEntry * entry)
+{
+       WMenu *submenu;
+       struct stat stat_buf;
+       char **path;
+       char *cmd;
+       char *lpath = NULL;
+       int i, first = -1;
+       time_t last = 0;
+
+       separateCommand((char *)entry->clientdata, &path, &cmd);
+       if (path == NULL || *path == NULL || **path == 0) {
+               wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
+               return;
+       }
+
+       if (path[0][0] == '|') {
+               /* pipe menu */
+
+               if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
+                       /* parse pipe */
+
+                       submenu = readMenuPipe(menu->frame->screen_ptr, path);
+
+                       if (submenu != NULL) {
+                               if (path[0][1] == '|')
+                                       submenu->timestamp = 0;
+                               else
+                                       submenu->timestamp = 1; /* there's no automatic reloading */
+                       }
+               } else {
+                       submenu = NULL;
+               }
+
+       } else {
+               i = 0;
+               while (path[i] != NULL) {
+                       char *tmp;
+
+                       if (strcmp(path[i], "-noext") == 0) {
+                               i++;
+                               continue;
+                       }
+
+                       tmp = wexpandpath(path[i]);
+                       wfree(path[i]);
+                       lpath = getLocalizedMenuFile(tmp);
+                       if (lpath) {
+                               wfree(tmp);
+                               path[i] = lpath;
+                               lpath = NULL;
+                       } else {
+                               path[i] = tmp;
+                       }
+
+                       if (stat(path[i], &stat_buf) == 0) {
+                               if (last < stat_buf.st_mtime)
+                                       last = stat_buf.st_mtime;
+                               if (first < 0)
+                                       first = i;
+                       } else {
+                               wsyserror(_("%s:could not stat menu"), path[i]);
+                               /*goto finish; */
+                       }
+
+                       i++;
+               }
+
+               if (first < 0) {
+                       wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
+                       goto finish;
+               }
+               stat(path[first], &stat_buf);
+               if (!menu->cascades[entry->cascade]
+                   || menu->cascades[entry->cascade]->timestamp < last) {
+
+                       if (S_ISDIR(stat_buf.st_mode)) {
+                               /* menu directory */
+                               submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
+                               if (submenu)
+                                       submenu->timestamp = last;
+                       } else if (S_ISREG(stat_buf.st_mode)) {
+                               /* menu file */
+
+                               if (cmd || path[1])
+                                       wwarning(_("too many parameters in OPEN_MENU: %s"),
+                                                (char *)entry->clientdata);
+
+                               submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
+                               if (submenu)
+                                       submenu->timestamp = stat_buf.st_mtime;
+                       } else {
+                               submenu = NULL;
+                       }
+               } else {
+                       submenu = NULL;
+               }
+       }
+
+       if (submenu) {
+               wMenuEntryRemoveCascade(menu, entry);
+               wMenuEntrySetCascade(menu, entry, submenu);
+       }
+
+ finish:
+       i = 0;
+       while (path[i] != NULL)
+               wfree(path[i++]);
+       wfree(path);
+       if (cmd)
+               wfree(cmd);
+}
+
+static void cleanupWorkspaceMenu(WMenu * menu)
+{
+       if (menu->frame->screen_ptr->workspace_menu == menu)
+               menu->frame->screen_ptr->workspace_menu = NULL;
+}
+
+static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
+{
+       WMenu *wsmenu;
+       WMenuEntry *entry;
+
+       if (scr->flags.added_workspace_menu) {
+               wwarning(_
+                        ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
+               return NULL;
+       } else {
+               scr->flags.added_workspace_menu = 1;
+
+               wsmenu = wWorkspaceMenuMake(scr, True);
+               wsmenu->on_destroy = cleanupWorkspaceMenu;
+
+               scr->workspace_menu = wsmenu;
+               entry = wMenuAddCallback(menu, title, NULL, NULL);
+               wMenuEntrySetCascade(menu, entry, wsmenu);
+
+               wWorkspaceMenuUpdate(scr, wsmenu);
+       }
+       return entry;
+}
+
+static void cleanupWindowsMenu(WMenu * menu)
+{
+       if (menu->frame->screen_ptr->switch_menu == menu)
+               menu->frame->screen_ptr->switch_menu = NULL;
+}
+
+static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
+{
+       WMenu *wwmenu;
+       WWindow *wwin;
+       WMenuEntry *entry;
+
+       if (scr->flags.added_windows_menu) {
+               wwarning(_
+                        ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
+               return NULL;
+       } else {
+               scr->flags.added_windows_menu = 1;
+
+               wwmenu = wMenuCreate(scr, _("Window List"), False);
+               wwmenu->on_destroy = cleanupWindowsMenu;
+               scr->switch_menu = wwmenu;
+               wwin = scr->focused_window;
+               while (wwin) {
+                       UpdateSwitchMenu(scr, wwin, ACTION_ADD);
+
+                       wwin = wwin->prev;
+               }
+               entry = wMenuAddCallback(menu, title, NULL, NULL);
+               wMenuEntrySetCascade(menu, entry, wwmenu);
+       }
+       return entry;
+}
+
+static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
+                               char *params, char *file_name)
+{
+       WScreen *scr;
+       WMenuEntry *entry = NULL;
+       Bool shortcutOk = False;
+
+       if (!menu)
+               return NULL;
+       scr = menu->frame->screen_ptr;
+       if (strcmp(command, "OPEN_MENU") == 0) {
+               if (!params) {
+                       wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
+               } else {
+                       WMenu *dummy;
+                       char *path;
+
+                       path = wfindfile(DEF_CONFIG_PATHS, params);
+                       if (!path) {
+                               path = wstrdup(params);
+                       }
+                       dummy = wMenuCreate(scr, title, False);
+                       dummy->on_destroy = removeShortcutsForMenu;
+                       entry = wMenuAddCallback(menu, title, constructMenu, path);
+                       entry->free_cdata = free;
+                       wMenuEntrySetCascade(menu, entry, dummy);
+               }
+       } else if (strcmp(command, "EXEC") == 0) {
+               if (!params)
+                       wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
+               else {
+                       entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
+                       entry->free_cdata = free;
+                       shortcutOk = True;
+               }
+       } else if (strcmp(command, "SHEXEC") == 0) {
+               if (!params)
+                       wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
+               else {
+                       entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
+                       entry->free_cdata = free;
+                       shortcutOk = True;
+               }
+       } else if (strcmp(command, "EXIT") == 0) {
+
+               if (params && strcmp(params, "QUICK") == 0)
+                       entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
+               else
+                       entry = wMenuAddCallback(menu, title, exitCommand, NULL);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "SHUTDOWN") == 0) {
+
+               if (params && strcmp(params, "QUICK") == 0)
+                       entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
+               else
+                       entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "REFRESH") == 0) {
+               entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
+               entry = addWorkspaceMenu(scr, menu, title);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "WINDOWS_MENU") == 0) {
+               entry = addWindowsMenu(scr, menu, title);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
+               entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "HIDE_OTHERS") == 0) {
+               entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "SHOW_ALL") == 0) {
+               entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "RESTART") == 0) {
+               entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
+               entry->free_cdata = free;
+               shortcutOk = True;
+       } else if (strcmp(command, "SAVE_SESSION") == 0) {
+               entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
+
+               shortcutOk = True;
+       } else if (strcmp(command, "CLEAR_SESSION") == 0) {
+               entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
+               shortcutOk = True;
+       } else if (strcmp(command, "INFO_PANEL") == 0) {
+               entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
+               shortcutOk = True;
+       } else if (strcmp(command, "LEGAL_PANEL") == 0) {
+               entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
+               shortcutOk = True;
+       } else {
+               wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
+
+               return NULL;
+       }
+
+       if (shortcut && entry) {
+               if (!shortcutOk) {
+                       wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
+               } else {
+                       if (addShortcut(file_name, shortcut, menu, entry)) {
+
+                               entry->rtext = GetShortcutString(shortcut);
+                               /*
+                                  entry->rtext = wstrdup(shortcut);
+                                */
+                       }
+               }
+       }
+
+       return entry;
+}
+
+/*******************   Menu Configuration From File   *******************/
+
+static void separateline(char *line, char *title, char *command, char *parameter, char *shortcut)
+{
+       int l, i;
+
+       l = strlen(line);
+
+       *title = 0;
+       *command = 0;
+       *parameter = 0;
+       *shortcut = 0;
+       /* get the title */
+       while (isspace(*line) && (*line != 0))
+               line++;
+       if (*line == '"') {
+               line++;
+               i = 0;
+               while (line[i] != '"' && (line[i] != 0))
+                       i++;
+               if (line[i] != '"')
+                       return;
+       } else {
+               i = 0;
+               while (!isspace(line[i]) && (line[i] != 0))
+                       i++;
+       }
+       strncpy(title, line, i);
+       title[i++] = 0;
+       line += i;
+
+       /* get the command or shortcut keyword */
+       while (isspace(*line) && (*line != 0))
+               line++;
+       if (*line == 0)
+               return;
+       i = 0;
+       while (!isspace(line[i]) && (line[i] != 0))
+               i++;
+       strncpy(command, line, i);
+       command[i++] = 0;
+       line += i;
+
+       if (strcmp(command, "SHORTCUT") == 0) {
+               /* get the shortcut key */
+               while (isspace(*line) && (*line != 0))
+                       line++;
+               if (*line == '"') {
+                       line++;
+                       i = 0;
+                       while (line[i] != '"' && (line[i] != 0))
+                               i++;
+                       if (line[i] != '"')
+                               return;
+               } else {
+                       i = 0;
+                       while (!isspace(line[i]) && (line[i] != 0))
+                               i++;
+               }
+               strncpy(shortcut, line, i);
+               shortcut[i++] = 0;
+               line += i;
+
+               *command = 0;
+
+               /* get the command */
+               while (isspace(*line) && (*line != 0))
+                       line++;
+               if (*line == 0)
+                       return;
+               i = 0;
+               while (!isspace(line[i]) && (line[i] != 0))
+                       i++;
+               strncpy(command, line, i);
+               command[i++] = 0;
+               line += i;
+       }
+
+       /* get the parameters */
+       while (isspace(*line) && (*line != 0))
+               line++;
+       if (*line == 0)
+               return;
+
+       if (*line == '"') {
+               line++;
+               l = 0;
+               while (line[l] != 0 && line[l] != '"') {
+                       parameter[l] = line[l];
+                       l++;
+               }
+               parameter[l] = 0;
+               return;
+       }
+
+       l = strlen(line);
+       while (isspace(line[l]) && (l > 0))
+               l--;
+       strncpy(parameter, line, l);
+       parameter[l] = 0;
+}
+
+static WMenu *parseCascade(WScreen * scr, WMenu * menu, FILE * file, char *file_name)
+{
+       char linebuf[MAXLINE];
+       char elinebuf[MAXLINE];
+       char title[MAXLINE];
+       char command[MAXLINE];
+       char shortcut[MAXLINE];
+       char params[MAXLINE];
+       char *line;
+
+       while (!feof(file)) {
+               int lsize, ok;
+
+               ok = 0;
+               fgets(linebuf, MAXLINE, file);
+               line = cropline(linebuf);
+               lsize = strlen(line);
+               do {
+                       if (line[lsize - 1] == '\\') {
+                               char *line2;
+                               int lsize2;
+                               fgets(elinebuf, MAXLINE, file);
+                               line2 = cropline(elinebuf);
+                               lsize2 = strlen(line2);
+                               if (lsize2 + lsize > MAXLINE) {
+                                       wwarning(_("%s:maximal line size exceeded in menu config: %s"),
+                                                file_name, line);
+                                       ok = 2;
+                               } else {
+                                       line[lsize - 1] = 0;
+                                       lsize += lsize2 - 1;
+                                       strcat(line, line2);
+                               }
+                       } else {
+                               ok = 1;
+                       }
+               } while (!ok && !feof(file));
+               if (ok == 2)
+                       continue;
+
+               if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
+                       continue;
+
+               separateline(line, title, command, params, shortcut);
+
+               if (!command[0]) {
+                       wwarning(_("%s:missing command in menu config: %s"), file_name, line);
+                       goto error;
+               }
+
+               if (strcasecmp(command, "MENU") == 0) {
+                       WMenu *cascade;
+
+                       /* start submenu */
+
+                       cascade = wMenuCreate(scr, title, False);
+                       cascade->on_destroy = removeShortcutsForMenu;
+                       if (parseCascade(scr, cascade, file, file_name) == NULL) {
+                               wMenuDestroy(cascade, True);
+                       } else {
+                               wMenuEntrySetCascade(menu, wMenuAddCallback(menu, title, NULL, NULL), cascade);
+                       }
+               } else if (strcasecmp(command, "END") == 0) {
+                       /* end of menu */
+                       return menu;
+
+               } else {
+                       /* normal items */
+                       addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
+                                    params[0] ? params : NULL, file_name);
+               }
+       }
+
+       wwarning(_("%s:syntax error in menu file:END declaration missing"), file_name);
+       return menu;
+
+ error:
+       return menu;
+}
+
+static WMenu *readMenuFile(WScreen * scr, char *file_name)
+{
+       WMenu *menu = NULL;
+       FILE *file = NULL;
+       char linebuf[MAXLINE];
+       char title[MAXLINE];
+       char shortcut[MAXLINE];
+       char command[MAXLINE];
+       char params[MAXLINE];
+       char *line;
+#ifdef USECPP
+       char *args;
+       int cpp = 0;
+#endif
+
+#ifdef USECPP
+       if (!wPreferences.flags.nocpp) {
+               args = MakeCPPArgs(file_name);
+               if (!args) {
+                       wwarning(_("could not make arguments for menu file preprocessor"));
+               } else {
+                       snprintf(command, sizeof(command), "%s %s %s", CPP_PATH, args, file_name);
+                       wfree(args);
+                       file = popen(command, "r");
+                       if (!file) {
+                               wsyserror(_("%s:could not open/preprocess menu file"), file_name);
+                       } else {
+                               cpp = 1;
+                       }
+               }
+       }
+#endif                         /* USECPP */
+
+       if (!file) {
+               file = fopen(file_name, "rb");
+               if (!file) {
+                       wsyserror(_("%s:could not open menu file"), file_name);
+                       return NULL;
+               }
+       }
+
+       while (!feof(file)) {
+               if (!fgets(linebuf, MAXLINE, file))
+                       break;
+               line = cropline(linebuf);
+               if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
+                       continue;
+
+               separateline(line, title, command, params, shortcut);
+
+               if (!command[0]) {
+                       wwarning(_("%s:missing command in menu config: %s"), file_name, line);
+                       break;
+               }
+               if (strcasecmp(command, "MENU") == 0) {
+                       menu = wMenuCreate(scr, title, True);
+                       menu->on_destroy = removeShortcutsForMenu;
+                       if (!parseCascade(scr, menu, file, file_name)) {
+                               wMenuDestroy(menu, True);
+                       }
+                       break;
+               } else {
+                       wwarning(_("%s:invalid menu file. MENU command is missing"), file_name);
+                       break;
+               }
+       }
+
+#ifdef CPP
+       if (cpp) {
+               if (pclose(file) == -1) {
+                       wsyserror(_("error reading preprocessed menu data"));
+               }
+       } else {
+               fclose(file);
+       }
+#else
+       fclose(file);
+#endif
+
+       return menu;
+}
+
+/************    Menu Configuration From Pipe      *************/
+
+static WMenu *readMenuPipe(WScreen * scr, char **file_name)
+{
+       WMenu *menu = NULL;
+       FILE *file = NULL;
+       char linebuf[MAXLINE];
+       char title[MAXLINE];
+       char command[MAXLINE];
+       char params[MAXLINE];
+       char shortcut[MAXLINE];
+       char *line;
+       char *filename;
+       char flat_file[MAXLINE];
+       int i;
+#ifdef USECPP
+       char *args;
+       int cpp = 0;
+#endif
+
+       flat_file[0] = '\0';
+
+       for (i = 0; file_name[i] != NULL; i++) {
+               strcat(flat_file, file_name[i]);
+               strcat(flat_file, " ");
+       }
+       filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
+
+#ifdef USECPP
+       if (!wPreferences.flags.nocpp) {
+               args = MakeCPPArgs(filename);
+               if (!args) {
+                       wwarning(_("could not make arguments for menu file preprocessor"));
+               } else {
+                       snprintf(command, sizeof(command), "%s | %s %s", filename, CPP_PATH, args);
+
+                       wfree(args);
+                       file = popen(command, "r");
+                       if (!file) {
+                               wsyserror(_("%s:could not open/preprocess menu file"), filename);
+                       } else {
+                               cpp = 1;
+                       }
+               }
+       }
+#endif                         /* USECPP */
+
+       if (!file) {
+               file = popen(filename, "rb");
+
+               if (!file) {
+                       wsyserror(_("%s:could not open menu file"), filename);
+                       return NULL;
+               }
+       }
+
+       while (!feof(file)) {
+               if (!fgets(linebuf, MAXLINE, file))
+                       break;
+               line = cropline(linebuf);
+               if (line[0] == 0 || line[0] == '#' || (line[0] == '/' && line[1] == '/'))
+                       continue;
+
+               separateline(line, title, command, params, shortcut);
+
+               if (!command[0]) {
+                       wwarning(_("%s:missing command in menu config: %s"), file_name, line);
+                       break;
+               }
+               if (strcasecmp(command, "MENU") == 0) {
+                       menu = wMenuCreate(scr, title, True);
+                       menu->on_destroy = removeShortcutsForMenu;
+                       if (!parseCascade(scr, menu, file, filename)) {
+                               wMenuDestroy(menu, True);
+                       }
+                       break;
+               } else {
+                       wwarning(_("%s:no title given for the root menu"), filename);
+                       break;
+               }
+       }
+
+       pclose(file);
+
+       return menu;
+}
+
+typedef struct {
+       char *name;
+       int index;
+} dir_data;
+
+static int myCompare(const void *d1, const void *d2)
+{
+       dir_data *p1 = *(dir_data **) d1;
+       dir_data *p2 = *(dir_data **) d2;
+
+       return strcmp(p1->name, p2->name);
+}
+
+/************  Menu Configuration From Directory   *************/
+
+static Bool isFilePackage(char *file)
+{
+       int l;
+
+       /* check if the extension indicates this file is a
+        * file package. For now, only recognize .themed */
+
+       l = strlen(file);
+
+       if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
+               return True;
+       } else {
+               return False;
+       }
+}
+
+static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
+{
+       DIR *dir;
+       struct dirent *dentry;
+       struct stat stat_buf;
+       WMenu *menu = NULL;
+       char *buffer;
+       WMArray *dirs = NULL, *files = NULL;
+       WMArrayIterator iter;
+       int length, i, have_space = 0;
+       dir_data *data;
+       int stripExtension = 0;
+
+       dirs = WMCreateArray(16);
+       files = WMCreateArray(16);
+
+       i = 0;
+       while (path[i] != NULL) {
+               if (strcmp(path[i], "-noext") == 0) {
+                       stripExtension = 1;
+                       i++;
+                       continue;
+               }
+
+               dir = opendir(path[i]);
+               if (!dir) {
+                       i++;
+                       continue;
+               }
+
+               while ((dentry = readdir(dir))) {
+
+                       if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
+                               continue;
+
+                       if (dentry->d_name[0] == '.')
+                               continue;
+
+                       buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
+                       if (!buffer) {
+                               wsyserror(_("out of memory while constructing directory menu %s"), path[i]);
+                               break;
+                       }
+
+                       strcpy(buffer, path[i]);
+                       strcat(buffer, "/");
+                       strcat(buffer, dentry->d_name);
+
+                       if (stat(buffer, &stat_buf) != 0) {
+                               wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
+                                         path[i], dentry->d_name);
+                       } else {
+                               Bool isFilePack = False;
+
+                               data = NULL;
+                               if (S_ISDIR(stat_buf.st_mode)
+                                   && !(isFilePack = isFilePackage(dentry->d_name))) {
+
+                                       /* access always returns success for user root */
+                                       if (access(buffer, X_OK) == 0) {
+                                               /* Directory is accesible. Add to directory list */
+
+                                               data = (dir_data *) wmalloc(sizeof(dir_data));
+                                               data->name = wstrdup(dentry->d_name);
+                                               data->index = i;
+
+                                               WMAddToArray(dirs, data);
+                                       }
+                               } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
+                                       /* Hack because access always returns X_OK success for user root */
+#define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
+                                       if ((command != NULL && access(buffer, R_OK) == 0) ||
+                                           (command == NULL && access(buffer, X_OK) == 0 &&
+                                            (stat_buf.st_mode & S_IXANY))) {
+
+                                               data = (dir_data *) wmalloc(sizeof(dir_data));
+                                               data->name = wstrdup(dentry->d_name);
+                                               data->index = i;
+
+                                               WMAddToArray(files, data);
+                                       }
+                               }
+                       }
+                       wfree(buffer);
+               }
+
+               closedir(dir);
+               i++;
+       }
+
+       if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
+               WMFreeArray(dirs);
+               WMFreeArray(files);
+               return NULL;
+       }
+
+       WMSortArray(dirs, myCompare);
+       WMSortArray(files, myCompare);
+
+       menu = wMenuCreate(scr, title, False);
+       menu->on_destroy = removeShortcutsForMenu;
+
+       WM_ITERATE_ARRAY(dirs, data, iter) {
+               /* New directory. Use same OPEN_MENU command that was used
+                * for the current directory. */
+               length = strlen(path[data->index]) + strlen(data->name) + 6;
+               if (stripExtension)
+                       length += 7;
+               if (command)
+                       length += strlen(command) + 6;
+               buffer = malloc(length);
+               if (!buffer) {
+                       wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]);
+                       break;
+               }
+
+               buffer[0] = '\0';
+               if (stripExtension)
+                       strcat(buffer, "-noext ");
+
+               have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
+
+               if (have_space)
+                       strcat(buffer, "\"");
+               strcat(buffer, path[data->index]);
+
+               strcat(buffer, "/");
+               strcat(buffer, data->name);
+               if (have_space)
+                       strcat(buffer, "\"");
+               if (command) {
+                       strcat(buffer, " WITH ");
+                       strcat(buffer, command);
+               }
+
+               addMenuEntry(menu, data->name, NULL, "OPEN_MENU", buffer, path[data->index]);
+
+               wfree(buffer);
+               if (data->name)
+                       wfree(data->name);
+               wfree(data);
+       }
+
+       WM_ITERATE_ARRAY(files, data, iter) {
+               /* executable: add as entry */
+               length = strlen(path[data->index]) + strlen(data->name) + 6;
+               if (command)
+                       length += strlen(command);
+
+               buffer = malloc(length);
+               if (!buffer) {
+                       wsyserror(_("out of memory while constructing directory menu %s"), path[data->index]);
+                       break;
+               }
+
+               have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
+               if (command != NULL) {
+                       strcpy(buffer, command);
+                       strcat(buffer, " ");
+                       if (have_space)
+                               strcat(buffer, "\"");
+                       strcat(buffer, path[data->index]);
+               } else {
+                       if (have_space) {
+                               buffer[0] = '"';
+                               buffer[1] = 0;
+                               strcat(buffer, path[data->index]);
+                       } else {
+                               strcpy(buffer, path[data->index]);
+                       }
+               }
+               strcat(buffer, "/");
+               strcat(buffer, data->name);
+               if (have_space)
+                       strcat(buffer, "\"");
+
+               if (stripExtension) {
+                       char *ptr = strrchr(data->name, '.');
+                       if (ptr && ptr != data->name)
+                               *ptr = 0;
+               }
+               addMenuEntry(menu, data->name, NULL, "SHEXEC", buffer, path[data->index]);
+
+               wfree(buffer);
+               if (data->name)
+                       wfree(data->name);
+               wfree(data);
+       }
+
+       WMFreeArray(files);
+       WMFreeArray(dirs);
+
+       return menu;
+}
+
+/************  Menu Configuration From WMRootMenu   *************/
+
+static WMenu *makeDefaultMenu(WScreen * scr)
+{
+       WMenu *menu = NULL;
+
+       menu = wMenuCreate(scr, _("Commands"), True);
+       wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
+       wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
+       wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
+       wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
+       return menu;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * configureMenu--
+ *     Reads root menu configuration from defaults database.
+ *
+ *----------------------------------------------------------------------
+ */
+static WMenu *configureMenu(WScreen * scr, WMPropList * definition)
+{
+       WMenu *menu = NULL;
+       WMPropList *elem;
+       int i, count;
+       WMPropList *title, *command, *params;
+       char *tmp, *mtitle;
+
+       if (WMIsPLString(definition)) {
+               struct stat stat_buf;
+               char *path = NULL;
+               Bool menu_is_default = False;
+
+               /* menu definition is a string. Probably a path, so parse the file */
+
+               tmp = wexpandpath(WMGetFromPLString(definition));
+
+               path = getLocalizedMenuFile(tmp);
+
+               if (!path)
+                       path = wfindfile(DEF_CONFIG_PATHS, tmp);
+
+               if (!path) {
+                       path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
+                       menu_is_default = True;
+               }
+
+               if (!path) {
+                       wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
+                       wfree(tmp);
+                       return NULL;
+               }
+
+               if (stat(path, &stat_buf) < 0) {
+                       wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
+                       wfree(path);
+                       wfree(tmp);
+                       return NULL;
+               }
+
+               if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
+                   /* if the pointer in WMRootMenu has changed */
+                   || WDRootMenu->timestamp > scr->root_menu->timestamp) {
+
+                       if (menu_is_default) {
+                               wwarning(_
+                                        ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
+                                        path);
+                       }
+
+                       menu = readMenuFile(scr, path);
+                       if (menu)
+                               menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
+               } else {
+                       menu = NULL;
+               }
+               wfree(path);
+               wfree(tmp);
+
+               return menu;
+       }
+
+       count = WMGetPropListItemCount(definition);
+       if (count == 0)
+               return NULL;
+
+       elem = WMGetFromPLArray(definition, 0);
+       if (!WMIsPLString(elem)) {
+               tmp = WMGetPropListDescription(elem, False);
+               wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
+               wfree(tmp);
+               return NULL;
+       }
+       mtitle = WMGetFromPLString(elem);
+
+       menu = wMenuCreate(scr, mtitle, False);
+       menu->on_destroy = removeShortcutsForMenu;
+
+#ifdef GLOBAL_SUBMENU_FILE
+       {
+               WMenu *submenu;
+               WMenuEntry *mentry;
+
+               submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
+
+               if (submenu) {
+                       mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
+                       wMenuEntrySetCascade(menu, mentry, submenu);
+               }
+       }
+#endif
+
+       for (i = 1; i < count; i++) {
+               elem = WMGetFromPLArray(definition, i);
+#if 0
+               if (WMIsPLString(elem)) {
+                       char *file;
+
+                       file = WMGetFromPLString(elem);
+
+               }
+#endif
+               if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
+                       goto error;
+
+               if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
+                       WMenu *submenu;
+                       WMenuEntry *mentry;
+
+                       /* submenu */
+                       submenu = configureMenu(scr, elem);
+                       if (submenu) {
+                               mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
+                               wMenuEntrySetCascade(menu, mentry, submenu);
+                       }
+               } else {
+                       int idx = 0;
+                       WMPropList *shortcut;
+                       /* normal entry */
+
+                       title = WMGetFromPLArray(elem, idx++);
+                       shortcut = WMGetFromPLArray(elem, idx++);
+                       if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
+                               shortcut = WMGetFromPLArray(elem, idx++);
+                               command = WMGetFromPLArray(elem, idx++);
+                       } else {
+                               command = shortcut;
+                               shortcut = NULL;
+                       }
+                       params = WMGetFromPLArray(elem, idx++);
+
+                       if (!title || !command)
+                               goto error;
+
+                       addMenuEntry(menu, WMGetFromPLString(title),
+                                    shortcut ? WMGetFromPLString(shortcut) : NULL,
+                                    WMGetFromPLString(command),
+                                    params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
+               }
+               continue;
+
+ error:
+               tmp = WMGetPropListDescription(elem, False);
+               wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
+               wfree(tmp);
+       }
+
+       return menu;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * OpenRootMenu--
+ *     Opens the root menu, parsing the menu configuration from the
+ * defaults database.
+ *     If the menu is already mapped and is not sticked to the
+ * root window, it will be unmapped.
+ *
+ * Side effects:
+ *     The menu may be remade.
+ *
+ * Notes:
+ * Construction of OPEN_MENU entries are delayed to the moment the
+ * user map's them.
+ *----------------------------------------------------------------------
+ */
+void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
+{
+       WMenu *menu = NULL;
+       WMPropList *definition;
+       /*
+          static WMPropList *domain=NULL;
+
+          if (!domain) {
+          domain = WMCreatePLString("WMRootMenu");
+          }
+        */
+
+       scr->flags.root_menu_changed_shortcuts = 0;
+       scr->flags.added_workspace_menu = 0;
+       scr->flags.added_windows_menu = 0;
+
+       if (scr->root_menu && scr->root_menu->flags.mapped) {
+               menu = scr->root_menu;
+               if (!menu->flags.buttoned) {
+                       wMenuUnmap(menu);
+               } else {
+                       wRaiseFrame(menu->frame->core);
+
+                       if (keyboard)
+                               wMenuMapAt(menu, 0, 0, True);
+                       else
+                               wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
+               }
+               return;
+       }
+
+       definition = WDRootMenu->dictionary;
+
+       /*
+          definition = PLGetDomain(domain);
+        */
+       if (definition) {
+               if (WMIsPLArray(definition)) {
+                       if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
+                               menu = configureMenu(scr, definition);
+                               if (menu)
+                                       menu->timestamp = WDRootMenu->timestamp;
+
+                       } else
+                               menu = NULL;
+               } else {
+                       menu = configureMenu(scr, definition);
+               }
+       }
+
+       if (!menu) {
+               /* menu hasn't changed or could not be read */
+               if (!scr->root_menu) {
+                       wMessageDialog(scr, _("Error"),
+                                      _("The applications menu could not be loaded. "
+                                        "Look at the console output for a detailed "
+                                        "description of the errors."), _("OK"), NULL, NULL);
+
+                       menu = makeDefaultMenu(scr);
+                       scr->root_menu = menu;
+               }
+               menu = scr->root_menu;
+       } else {
+               /* new root menu */
+               if (scr->root_menu) {
+                       wMenuDestroy(scr->root_menu, True);
+               }
+               scr->root_menu = menu;
+       }
+       if (menu) {
+               int newx, newy;
+
+               if (keyboard && x == 0 && y == 0) {
+                       newx = newy = 0;
+               } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
+                       newx = x - menu->frame->core->width / 2;
+                       newy = y - menu->frame->core->height / 2;
+               } else {
+                       newx = x - menu->frame->core->width / 2;
+                       newy = y;
+               }
+               wMenuMapAt(menu, newx, newy, keyboard);
+       }
+
+       if (scr->flags.root_menu_changed_shortcuts)
+               rebindKeygrabs(scr);
+}
+
+#endif                         /* !LITE */