From c4305125daf980f2946ab783a9e7b41f64db86a6 Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 23 Mar 2008 15:09:21 +0000 Subject: Replace the symlink configuration scheme with a simple yacc parser as found in other places of the tree. Remove sticky and font commandline options and add another one for alternative config locations. Split off cwmrc(5) from cwm(1), nuke #ifdef __OpenBSD__ while there. tested by various kind people, feedback from oga@ and okan@ - thanks! ok oga@, jasper@, okan@ --- Makefile | 9 +- calmwm.c | 25 +-- calmwm.h | 54 ++++-- conf.c | 293 +++++-------------------------- cwm.1 | 133 ++------------ cwmrc | 28 +++ cwmrc.5 | 158 +++++++++++++++++ group.c | 8 +- kbfunc.c | 10 +- parse.y | 583 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ xevents.c | 7 +- 11 files changed, 878 insertions(+), 430 deletions(-) create mode 100644 cwmrc create mode 100644 cwmrc.5 create mode 100644 parse.y diff --git a/Makefile b/Makefile index fa1c08e..4d5e39a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.4 2008/01/08 20:21:43 oga Exp $ +# $OpenBSD: Makefile,v 1.5 2008/03/23 15:09:21 simon Exp $ .include @@ -8,16 +8,17 @@ PROG= cwm SRCS= calmwm.c screen.c xmalloc.c client.c grab.c search.c \ util.c xutil.c conf.c input.c xevents.c group.c \ - kbfunc.c font.c + kbfunc.c font.c parse.y -CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 +CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 -I${.CURDIR} LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lXau -lXdmcp \ -lfontconfig -lexpat -lfreetype -lz -lX11 -lXau -lXdmcp -lXext MANDIR= ${X11BASE}/man/cat +MAN= cwm.1 cwmrc.5 -CLEANFILES= cwm.cat1 +CLEANFILES= cwm.cat1 cwmrc.cat5 obj: _xenocara_obj diff --git a/calmwm.c b/calmwm.c index 680acb4..123bdcc 100644 --- a/calmwm.c +++ b/calmwm.c @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: calmwm.c,v 1.12 2008/03/22 15:09:45 oga Exp $ + * $Id: calmwm.c,v 1.13 2008/03/23 15:09:21 simon Exp $ */ #include "headers.h" @@ -38,8 +38,7 @@ struct client_ctx_q Clientq; int Doshape, Shape_ev; int Starting; struct conf Conf; -struct fontdesc *DefaultFont; -char *DefaultFontName; +struct fontdesc *DefaultFont = NULL; /* From TWM */ #define gray_width 2 @@ -53,23 +52,18 @@ int main(int argc, char **argv) { int ch; - int conf_flags = 0; + const char *conffile = NULL; char *display_name = NULL; - DefaultFontName = "sans-serif:pixelsize=14:bold"; - - while ((ch = getopt(argc, argv, "d:sf:")) != -1) { + while ((ch = getopt(argc, argv, "c:d:")) != -1) { switch (ch) { + case 'c': + conffile = optarg; + break; case 'd': display_name = optarg; break; - case 's': - conf_flags |= CONF_STICKY_GROUPS; - break; - case 'f': - DefaultFontName = xstrdup(optarg); - break; default: usage(); } @@ -87,8 +81,7 @@ main(int argc, char **argv) group_init(); Starting = 1; - conf_setup(&Conf); - Conf.flags |= conf_flags; + conf_setup(&Conf, conffile); client_setup(); x_setup(display_name); Starting = 0; @@ -209,7 +202,7 @@ x_setupscreen(struct screen_ctx *sc, u_int which) GCLineWidth|GCSubwindowMode, &gv); font_init(sc); - DefaultFont = font_getx(sc, DefaultFontName); + DefaultFont = font_getx(sc, Conf.DefaultFontName); /* * XXX - this should *really* be in screen_init(). ordering diff --git a/calmwm.h b/calmwm.h index b960ed8..024cabe 100644 --- a/calmwm.h +++ b/calmwm.h @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: calmwm.h,v 1.24 2008/03/22 15:09:45 oga Exp $ + * $Id: calmwm.h,v 1.25 2008/03/23 15:09:21 simon Exp $ */ #ifndef _CALMWM_H_ @@ -30,6 +30,8 @@ #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define CONFFILE ".cwmrc" + enum conftype { CONF_BWIDTH, CONF_IGNORE, }; @@ -164,6 +166,12 @@ struct client_ctx { TAILQ_HEAD(client_ctx_q, client_ctx); +static char *shortcut_to_name[] = { + "XXX", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine" +}; + struct group_ctx { TAILQ_ENTRY(group_ctx) entry; struct client_ctx_q clients; @@ -204,6 +212,20 @@ enum directions { CWM_UP=0, CWM_DOWN, CWM_LEFT, CWM_RIGHT, }; +/* + * Match a window. + */ +#define CONF_MAX_WINTITLE 256 +#define CONF_IGNORECASE 0x01 +struct winmatch { + TAILQ_ENTRY(winmatch) entry; + + char title[CONF_MAX_WINTITLE]; + int opts; +}; + +TAILQ_HEAD(winmatch_q, winmatch); + /* for cwm_exec */ #define CWM_EXEC_PROGRAM 0x1 #define CWM_EXEC_WM 0x2 @@ -236,16 +258,20 @@ TAILQ_HEAD(cmd_q, cmd); /* Global configuration */ struct conf { - struct keybinding_q keybindingq; - struct autogroupwin_q autogroupq; - char menu_path[MAXPATHLEN]; - struct cmd_q cmdq; + struct keybinding_q keybindingq; + struct autogroupwin_q autogroupq; + struct winmatch_q ignoreq; + char conf_path[MAXPATHLEN]; + struct cmd_q cmdq; - int flags; + int flags; #define CONF_STICKY_GROUPS 0x0001 - char termpath[MAXPATHLEN]; - char lockpath[MAXPATHLEN]; + char termpath[MAXPATHLEN]; + char lockpath[MAXPATHLEN]; + +#define DEFAULTFONTNAME "sans-serif:pixelsize=14:bold" + char *DefaultFontName; }; /* Menu stuff */ @@ -397,21 +423,15 @@ struct screen_ctx *screen_current(void); void screen_updatestackingorder(void); void screen_infomsg(char *); -void conf_setup(struct conf *); +void conf_setup(struct conf *, const char *); int conf_get_int(struct client_ctx *, enum conftype); void conf_client(struct client_ctx *); void conf_bindkey(struct conf *, void (*)(struct client_ctx *, void *), int, int, int, void *); void conf_bindname(struct conf *, char *, char *); void conf_unbind(struct conf *, struct keybinding *); -void conf_parsekeys(struct conf *, char *); -void conf_parsesettings(struct conf *, char *); -void conf_parseignores(struct conf *, char *); -void conf_parseautogroups(struct conf *, char *); -void conf_cmd_clear(struct conf *); -int conf_cmd_changed(char *); -void conf_cmd_populate(struct conf *, char *); -void conf_cmd_refresh(struct conf *c); +int conf_changed(char *); +void conf_reload(struct conf *c); char *conf_get_str(struct client_ctx *, enum conftype); void kbfunc_client_lower(struct client_ctx *, void *); diff --git a/conf.c b/conf.c index f31abc4..d446f29 100644 --- a/conf.c +++ b/conf.c @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: conf.c,v 1.22 2008/03/22 15:09:45 oga Exp $ + * $Id: conf.c,v 1.23 2008/03/23 15:09:21 simon Exp $ */ #include "headers.h" @@ -28,58 +28,7 @@ ((tsp)->tv_sec cmp (usp)->tv_sec)) #endif -#define CONF_MAX_WINTITLE 256 -#define CONF_IGNORECASE 0x01 - - -/* - * Match a window. - */ -struct winmatch { - TAILQ_ENTRY(winmatch) entry; - - char title[CONF_MAX_WINTITLE]; - int opts; -}; - -TAILQ_HEAD(winmatch_q, winmatch); -struct winmatch_q ignoreq; - -/* XXX - until we get a real configuration parser. */ -#define WINMATCH_ADD(queue, str) do { \ - struct winmatch *wm; \ - XCALLOC(wm, struct winmatch); \ - strlcpy(wm->title, str, sizeof(wm->title)); \ - wm->opts |= CONF_IGNORECASE; \ - TAILQ_INSERT_TAIL(queue, wm, entry); \ -} while (0) - -/* Initializes the command menu */ - -void -conf_cmd_init(struct conf *c) -{ - TAILQ_INIT(&c->cmdq); -} - -/* Removes all static entries */ - -void -conf_cmd_clear(struct conf *c) -{ - struct cmd *cmd, *next; - - for (cmd = TAILQ_FIRST(&c->cmdq); cmd != NULL; cmd = next) { - next = TAILQ_NEXT(cmd, entry); - - /* Do not remove static entries */ - if (cmd->flags & CMD_STATIC) - continue; - - TAILQ_REMOVE(&c->cmdq, cmd, entry); - free(cmd); - } -} +extern struct screen_ctx *Curscreen; /* Add an command menu entry to the end of the menu */ void @@ -102,94 +51,45 @@ conf_cmd_add(struct conf *c, char *image, char *label, int flags) } int -conf_cmd_changed(char *path) +conf_changed(char *path) { -#ifdef __OpenBSD__ static struct timespec old_ts; -#else - static time_t old_time; -#endif struct stat sb; int changed; - /* If the directory does not exist we pretend that nothing changed */ - if (stat(path, &sb) == -1 || !(sb.st_mode & S_IFDIR)) + /* If the file does not exist we pretend that nothing changed */ + if (stat(path, &sb) == -1 || !(sb.st_mode & S_IFREG)) return (0); -#ifdef __OpenBSD__ changed = !timespeccmp(&sb.st_mtimespec, &old_ts, ==); old_ts = sb.st_mtimespec; -#else - changed = old_time != sb.st_mtime; - old_time = sb.st_mtime; -#endif return (changed); } void -conf_cmd_populate(struct conf *c, char *path) +conf_reload(struct conf *c) { - DIR *dir; - struct dirent *file; - char fullname[PATH_MAX]; - int off; - - if (strlen(path) >= sizeof (fullname) - 2) - errx(1, "directory name too long"); - - dir = opendir(path); - if (dir == NULL) - err(1, "opendir"); - - strlcpy(fullname, path, sizeof (fullname)); - off = strlen(fullname); - if (fullname[off - 1] != '/') { - strlcat(fullname, "/", sizeof(fullname)); - off++; - } - - while ((file = readdir(dir)) != NULL) { - char *filename = file->d_name; - if (filename[0] == '.') - continue; - - strlcpy(fullname + off, filename, sizeof(fullname) - off); - - /* Add a dynamic entry to the command menu */ - conf_cmd_add(c, fullname, filename, 0); - } - - closedir(dir); - -} + if (!conf_changed(c->conf_path)) + return; -void -conf_cmd_refresh(struct conf *c) -{ - if (!conf_cmd_changed(c->menu_path)) + if (parse_config(c->conf_path, c) == -1) { + warnx("config file %s has errors, not reloading", c->conf_path); return; + } - conf_cmd_clear(c); - conf_cmd_populate(c, c->menu_path); + DefaultFont = font_getx(Curscreen, c->DefaultFontName); } void -conf_setup(struct conf *c) +conf_init(struct conf *c) { - char dir_keydefs[MAXPATHLEN]; - char dir_settings[MAXPATHLEN]; - char dir_ignored[MAXPATHLEN]; - char dir_autogroup[MAXPATHLEN]; - char *home = getenv("HOME"); - - if (home == NULL) - errx(1, "No HOME directory."); - snprintf(c->menu_path, sizeof(c->menu_path), "%s/.calmwm", home); - - conf_cmd_init(c); + c->flags = 0; + TAILQ_INIT(&c->ignoreq); + TAILQ_INIT(&c->cmdq); TAILQ_INIT(&c->keybindingq); + TAILQ_INIT(&c->autogroupq); conf_bindname(c, "CM-Return", "terminal"); conf_bindname(c, "CM-Delete", "lock"); @@ -247,39 +147,31 @@ conf_setup(struct conf *c) conf_bindname(c, "CS-Up", "bigptrmoveup"); conf_bindname(c, "CS-Right", "bigptrmoveright"); - snprintf(dir_keydefs, sizeof(dir_keydefs), "%s/.calmwm/.keys", home); - if (dirent_isdir(dir_keydefs)) - conf_parsekeys(c, dir_keydefs); - - snprintf(dir_settings, sizeof(dir_settings), - "%s/.calmwm/.settings", home); - if (dirent_isdir(dir_settings)) - conf_parsesettings(c, dir_settings); - - TAILQ_INIT(&ignoreq); - - snprintf(dir_ignored, sizeof(dir_ignored), "%s/.calmwm/.ignore", home); - if (dirent_isdir(dir_ignored)) - conf_parseignores(c, dir_ignored); - else { - WINMATCH_ADD(&ignoreq, "XMMS"); - WINMATCH_ADD(&ignoreq, "xwi"); - WINMATCH_ADD(&ignoreq, "xapm"); - WINMATCH_ADD(&ignoreq, "xclock"); - } + /* Default term/lock */ + strlcpy(c->termpath, "xterm", sizeof(c->termpath)); + strlcpy(c->lockpath, "xlock", sizeof(c->lockpath)); - TAILQ_INIT(&c->autogroupq); + c->DefaultFontName = DEFAULTFONTNAME; +} + +void +conf_setup(struct conf *c, const char *conffile) +{ + if (conffile == NULL) { + char *home = getenv("HOME"); - snprintf(dir_autogroup, sizeof(dir_autogroup), - "%s/.calmwm/.autogroup", home); - if (dirent_isdir(dir_autogroup)) - conf_parseautogroups(c, dir_autogroup); + if (home == NULL) + errx(1, "No HOME directory."); - c->flags = 0; + snprintf(c->conf_path, sizeof(c->conf_path), "%s/%s", home, + CONFFILE); + } + else + snprintf(c->conf_path, sizeof(c->conf_path), "%s", conffile); - /* Default term/lock */ - strlcpy(Conf.termpath, "xterm", sizeof(Conf.termpath)); - strlcpy(Conf.lockpath, "xlock", sizeof(Conf.lockpath)); + conf_init(c); + + (void)parse_config(c->conf_path, c); } int @@ -294,7 +186,7 @@ conf_get_int(struct client_ctx *cc, enum conftype ctype) /* Can wname be NULL? */ if (wname != NULL) { - TAILQ_FOREACH(wm, &ignoreq, entry) { + TAILQ_FOREACH(wm, &Conf.ignoreq, entry) { int (*cmpfun)(const char *, const char *, size_t) = wm->opts & CONF_IGNORECASE ? strncasecmp : strncmp; if ((*cmpfun)(wm->title, wname, strlen(wm->title)) == 0) { @@ -392,37 +284,6 @@ struct { { NULL, NULL, 0, 0}, }; -void -conf_parsekeys(struct conf *c, char *filename) -{ - DIR *dir; - struct dirent *ent; - char buffer[MAXPATHLEN]; - char current_file[MAXPATHLEN]; - - dir = opendir(filename); - while ((ent = readdir(dir)) != NULL) { - if (ent->d_name[0] == '.') - continue; - - snprintf(current_file, sizeof(current_file), - "%s/%s", filename, ent->d_name); - if (strchr(ent->d_name, '-') == NULL && ent->d_name[0] != '[') - continue; - if (!dirent_islink(current_file)) - continue; - - - memset(buffer, 0, MAXPATHLEN); - if (readlink(current_file, buffer, MAXPATHLEN) < 0) - continue; - - conf_bindname(c, ent->d_name, buffer); - } - - closedir(dir); -} - void conf_bindname(struct conf *c, char *name, char *binding) { @@ -518,79 +379,3 @@ void conf_unbind(struct conf *c, struct keybinding *unbind) TAILQ_REMOVE(&c->keybindingq, key, entry); } } - -void -conf_parsesettings(struct conf *c, char *filename) -{ - DIR *dir; - struct dirent *ent; - - dir = opendir(filename); - while ((ent = readdir(dir)) != NULL) { - if (ent->d_name[0] == '.') - continue; - if (strncmp(ent->d_name, "sticky", 7)==0) - Conf.flags |= CONF_STICKY_GROUPS; - } - closedir(dir); -} - -void -conf_parseignores(struct conf *c, char *filename) -{ - DIR *dir; - struct dirent *ent; - - dir = opendir(filename); - while ((ent = readdir(dir)) != NULL) { - if (ent->d_name[0] == '.') - continue; - WINMATCH_ADD(&ignoreq, ent->d_name); - } - - closedir(dir); -} - -void -conf_parseautogroups(struct conf *c, char *filename) -{ - DIR *dir; - struct dirent *ent; - struct autogroupwin *aw; - char current_file[MAXPATHLEN], *p; - char group[CALMWM_MAXNAMELEN]; - int len; - - dir = opendir(filename); - while ((ent = readdir(dir)) != NULL) { - if (ent->d_name[0] == '.') - continue; - - snprintf(current_file, sizeof(current_file), - "%s/%s", filename, ent->d_name); - if (!dirent_islink(current_file)) - continue; - - if ((len = readlink(current_file, - group, sizeof(group) - 1)) < 0) - continue; - group[len] = '\0'; - - XCALLOC(aw, struct autogroupwin); - - if ((p = strchr(ent->d_name, ',')) == NULL) { - aw->name = NULL; - aw->class = xstrdup(ent->d_name); - } else { - *(p++) = '\0'; - aw->name = xstrdup(ent->d_name); - aw->class = xstrdup(p); - } - aw->group = xstrdup(group); - - TAILQ_INSERT_TAIL(&c->autogroupq, aw, entry); - } - - closedir(dir); - -} diff --git a/cwm.1 b/cwm.1 index 8869786..f2b735e 100644 --- a/cwm.1 +++ b/cwm.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: cwm.1,v 1.23 2008/03/22 15:09:45 oga Exp $ +.\" $OpenBSD: cwm.1,v 1.24 2008/03/23 15:09:21 simon Exp $ .\" .\" Copyright (c) 2004,2005 Marius Aamodt Eriksen .\" @@ -15,7 +15,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" The following requests are required for all man pages. -.Dd June 29, 2007 +.Dd $Mdocdate: March 23 2008 $ .Dt CWM 1 .Os .Sh NAME @@ -24,9 +24,8 @@ .Sh SYNOPSIS .\" For a program: program [-abc] file ... .Nm cwm -.Op Fl s .Op Fl d Ar display -.Op Fl f Ar fontname +.Op Fl c Ar file .Sh DESCRIPTION .Nm is a window manager for X11 which contains many features that @@ -132,17 +131,9 @@ are as follows: .Bl -tag -width Ds .It Fl d Ar display Specify the display to use. -.It Fl f Ar fontname -Makes the -.Xr Xft 3 -font string -.Ar fontname -the default font. -.It Fl s -Set sticky group mode on. -The default behavior for new windows is to not assign any group. -This changes the default behavior to assigning the currently selected -group to any newly created windows. +.It Fl c Ar file +Specify the config file to use. Defaults to +.Pa ~/.cwmrc . .El .Sh POINTER MOVEMENT The pointer can be moved with the use of the keyboard through bindings. @@ -205,7 +196,7 @@ perform operations on the entire group instead of just one window. Currently, the only operation that is supported is to hide and unhide the grouped windows. Together with the -.Fl s +.Pa sticky option, this can be used to emulate virtual desktops. .Pp To edit groups, use the group selection commands to toggle membership @@ -224,7 +215,7 @@ Show list of currently defined groups. Clicking on an item will hide/unhide that group. .It M3 Show list of applications as defined in -.Pa ~/.calmwm . +.Pa ~/.cwmrc . Clicking on an item will spawn that application. .El .Sh ENVIRONMENT @@ -237,111 +228,9 @@ option is given. .El .Sh FILES .Bl -tag -width Ds -.It Pa ~/.calmwm -Any directory entries here are shown in the application menu. -When it is selected, the image is executed with -.Xr execve 2 . -One use of this is to create symbolic links for your favorite -applications in this directory using -.Xr ln 1 . -.Pp -The entries -.Nm term -and -.Nm lock -have a special meaning. -When they exist they point to the terminal program and screen locking -programs used by the keybindings specified above. -The defaults for these are -.Xr xterm 1 -and -.Xr xlock 1 , -respectively. -.It Pa ~/.calmwm/.autogroup -Symlinks in this directory are read upon startup and control the -automatic grouping feature, which is based on the window name and class -properties. -To obtain the name and class of a window, use -.Ql xprop WM_CLASS , -then click on the window. -The first quoted string is the window name; the second one is the -window class. -.Pp -The name of a link can be the window class, or the window class and name -separated by a comma. -The link target is a group name (one, two, \&..., nine). -For example, to make all windows in the -.Xr xterm 1 -class go to the third group: -.Bd -literal -offset indent -$ ln -s three ~/.calmwm/.autogroup/XTerm -.Ed -.It Pa ~/.calmwm/.settings -Files in this directory cause various configuration options to be -set or unset. -Currently the only setting availiable is whether or not sticky groups -are activated. -To activate sticky groups create a file in this directory with the name -``sticky''. -.It Pa ~/.calmwm/.ignore -Any files in this directory cause -.Nm -to ignore programs by that name by not drawing borders around them. -For example the command -.Bd -literal -offset indent -$ ln -s three ~/.calmwm/.ignore/xclock -.Ed -will cause any instances of -.Xr xclock 1 -to not have borders. -.It Pa ~/.calmwm/.keys -Symlinks in this directory cause the creation of keyboard shortcuts. -The default shortcuts will always be created. In case of conflict, -user-defined shortcuts take precidence. -The name of a link here is first the modifier keys, followed by a ``-''. -The following modifiers are recognised: -.Bl -tag -width Ds -.It Pa C -The Control key. -.It Pa M -The Meta key. -.It Pa S -The Shift key. -.It Pa 2 -The Mod2 key. -.It Pa 3 -The Mod3 key. -.It Pa 4 -The Mod4 key (normally the windows key). -.El -The ``-'' should be followed by either a keysym name, taken from -.Pa /usr/X11R6/include/X11/keysymdef.h , -or a numerical keycode value enclosed in ``[]''. -The target of the link should be either the name of a task from the -``name_to_kbfunc'' -structure in -.Pa conf.c , -or, alternatively it should be the commandline that is wished to be executed. -A special case is the ``unmap'' keyword, which causes any bindings using the -named shortcut to be removed. This can be used to remove a binding which conflicts -with an application. -For example, to cause -.Ic C-M-r -to add a label to a window: -.Bd -literal -offset indent -$ ln -s "label" ~/.calmwm/.keys/CM-r -.Ed -Launch an xterm running -.Xr top 1 -with C-S-Enter: -.Bd -literal -offset indent -$ ln -s "/usr/X11R6/bin/xterm -e top" ~/.calmwm/.keys/CS-Return -.Ed -Remove a keybinding for Mod4-o -.Bd -literal -offset indent -$ ln -s "unmap" 4-o -.Ed -.El +.It Pa ~/.cwmrc +.Sh SEE ALSO +.Xr cwmrc 5 .Sh AUTHORS .An -nosplit .Pp diff --git a/cwmrc b/cwmrc new file mode 100644 index 0000000..24b6f9a --- /dev/null +++ b/cwmrc @@ -0,0 +1,28 @@ +# $OpenBSD: cwmrc,v 1.1 2008/03/23 15:09:21 simon Exp $ + +# Makes the Xft(3) font string fontname the default font +#fontname "sans-serif:pixelsize=14:bold" + +# Set sticky group mode on +#sticky no + +# Any entry here is shown in the application menu +#command firefox firefox +#command xmms xmms +#command top "xterm -e top" + +# Autogroup definition +#autogroup 3 "aterm,XTerm" +#autogroup 3 "xterm,XTerm" + +# Cause cwm to ignore programs by that name by not drawing borders around them. +#ignore XMMS +#ignore xwi +#ignore xapm +#ignore xclock + +# Keys +#bind CM-r "label" +#bind CS-Return "xterm -e top" +#bind 4-o "unmap" + diff --git a/cwmrc.5 b/cwmrc.5 new file mode 100644 index 0000000..fa22213 --- /dev/null +++ b/cwmrc.5 @@ -0,0 +1,158 @@ +.\" $OpenBSD: cwmrc.5,v 1.1 2008/03/23 15:09:21 simon Exp $ +.\" +.\" Copyright (c) 2004,2005 Marius Aamodt Eriksen +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" The following requests are required for all man pages. +.Dd $Mdocdate: March 23 2008 $ +.Dt CWMRC 1 +.Os +.Sh NAME +.Nm cwmrc +.Nd calm window manager configuration file +.Sh DESCRIPTION +The +.Xr cwm 1 +window manager configuration file format. +.Sh OPTIONS +There are quite a few settings that affect the operation of +.Xr cwm 1 . +.Pp +The following options are accepted in the configuration file: +.Pp +.Bl -tag -width Ds +.It Ic fontname Ar font +Makes the +.Xr Xft 3 +font string +.Ar font +the default font. +.It Ic sticky Ic yes Ns \&| Ns Ic no +Set sticky group mode on. +The default behavior for new windows is to not assign any group. +This changes the default behavior to assigning the currrently selected +group to any newly created windows. +.It Ic command Ar name Ar path +Every command entry is shown in the application menu. +When it is selected, the image is executed with +.Xr execve 2 . +.Pp +The entries +.Nm term +and +.Nm lock +have a special meaning. +When they exist they point to the terminal program and screen locking +programs used by the keybindings specified above. +The defaults for these are +.Xr xterm 1 +and +.Xr xlock 1 , +respectively. +.It Ic autogroup Ar group Dq windowclass +.It Ic autogroup Ar group Dq windowclass,windowname +Autogroups are read upon startup and control the +automatic grouping feature, which is based on the window name and class +properties. +The group is a number between 1 and 9. +.Pp +To obtain the name and class of a window, use +.Ql xprop WM_CLASS , +then click on the window. +The first quoted string is the window name; the second one is the +window class. +.Pp +For example, to make all windows in the +.Xr xterm 1 +class go to the third group: +.Bd -literal -offset indent +autogroup 3 XTerm +.Ed +.It Ic ignore Ar program +Ignore programs by that name by not drawing borders around them. +For example the command +.Bd -literal -offset indent +ignore xclock +.Ed +will cause any instances of +.Xr xclock 1 +to not have borders. +.It Ic bind Ar keys Ar command +Cause the creation of keyboard shortcuts. +The default shortcuts will always be created. In case of conflict, +user-defined shortcuts take precidence. +The modifier keys come first, followed by a ``-''. +The following modifiers are recognised: +.Bl -tag -width Ds +.It Pa C +The Control key. +.It Pa M +The Meta key. +.It Pa S +The Shift key. +.It Pa 2 +The Mod2 key. +.It Pa 3 +The Mod3 key. +.It Pa 4 +The Mod4 key (normally the windows key). +.El +The ``-'' should be followed by either a keysym name, taken from +.Pa /usr/X11R6/include/X11/keysymdef.h , +or a numerical keycode value enclosed in ``[]''. +The command should be either the name of a task from the +``name_to_kbfunc'' +structure in +.Pa conf.c , +or, alternatively it should be the commandline that is wished to be executed. +A special case is the ``unmap'' keyword, which causes any bindings using the +named shortcut to be removed. This can be used to remove a binding which conflicts +with an application. +.Pp +For example, to cause +.Ic C-M-r +to add a label to a window: +.Bd -literal -offset indent +bind CM-r "label" +.Ed +.Pp +Launch an xterm running +.Xr top 1 +with C-S-Enter: +.Bd -literal -offset indent +bind CS-Return "/usr/X11R6/bin/xterm -e top" +.Ed +.Pp +Remove a keybinding for Mod4-o +.Bd -literal -offset indent +bind 4-o "unmap" +.Ed +.El +.Sh SEE ALSO +.Xr cwm 1 +.Sh AUTHORS +.An -nosplit +.Pp +.Nm +was initially written by +.An Marius Aamodt Eriksen Aq marius@monkey.org +with contributions from +.An Andy Adamson Aq dros@monkey.org , +.An Niels Provos Aq provos@monkey.org , +and +.An Antti Nykänen Aq aon@iki.fi . +.Sh HISTORY +.Nm +first appeared in +.Ox 4.4 . diff --git a/group.c b/group.c index 26363ed..b8921e0 100644 --- a/group.c +++ b/group.c @@ -16,7 +16,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: group.c,v 1.7 2008/03/22 21:34:07 okan Exp $ + * $Id: group.c,v 1.8 2008/03/23 15:09:21 simon Exp $ */ #include "headers.h" @@ -31,12 +31,6 @@ char Group_name[256]; int Grouphideall = 0; struct group_ctx_q Groupq; -static char *shortcut_to_name[] = { - "XXX", "one", "two", "three", - "four", "five", "six", "seven", - "eight", "nine", -}; - static void _group_add(struct group_ctx *gc, struct client_ctx *cc) { diff --git a/kbfunc.c b/kbfunc.c index 0993fa6..7c2db84 100644 --- a/kbfunc.c +++ b/kbfunc.c @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: kbfunc.c,v 1.15 2008/03/22 15:09:45 oga Exp $ + * $Id: kbfunc.c,v 1.16 2008/03/23 15:09:21 simon Exp $ */ #include @@ -204,7 +204,7 @@ kbfunc_menu_search(struct client_ctx *scratch, void *arg) TAILQ_INIT(&menuq); - conf_cmd_refresh(&Conf); + conf_reload(&Conf); TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { XCALLOC(mi, struct menu); strlcpy(mi->text, cmd->label, sizeof(mi->text)); @@ -249,14 +249,14 @@ kbfunc_cmdexec(struct client_ctx *cc, void *arg) void kbfunc_term(struct client_ctx *cc, void *arg) { - conf_cmd_refresh(&Conf); + conf_reload(&Conf); u_spawn(Conf.termpath); } void kbfunc_lock(struct client_ctx *cc, void *arg) { - conf_cmd_refresh(&Conf); + conf_reload(&Conf); u_spawn(Conf.lockpath); } @@ -423,7 +423,7 @@ kbfunc_ssh(struct client_ctx *scratch, void *arg) if ((mi = search_start(&menuq, search_match_exec, NULL, "ssh", 1)) != NULL) { - conf_cmd_refresh(&Conf); + conf_reload(&Conf); l = snprintf(cmd, sizeof(cmd), "%s -e ssh %s", Conf.termpath, mi->text); if (l != -1 && l < sizeof(cmd)) diff --git a/parse.y b/parse.y new file mode 100644 index 0000000..c87d475 --- /dev/null +++ b/parse.y @@ -0,0 +1,583 @@ +/* $OpenBSD: parse.y,v 1.1 2008/03/23 15:09:21 simon Exp $ */ + +/* + * Copyright (c) 2002, 2003, 2004 Henning Brauer + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ + +#include +#include +#include +#include +#include +#include +#include + +#include "headers.h" +#include "calmwm.h" + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + int lineno; + int errors; +} *file; +struct file *pushfile(const char *); +int popfile(void); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(int); +int lungetc(int); +int findeol(void); + +static struct conf *conf; + +extern char *shortcut_to_name[]; + +typedef struct { + union { + int64_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token FONTNAME STICKY +%token AUTOGROUP BIND COMMAND IGNORE +%token YES NO +%token ERROR +%token STRING +%token NUMBER +%type yesno +%type string +%% + +grammar : /* empty */ + | grammar '\n' + | grammar main '\n' + | grammar error '\n' { file->errors++; } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +yesno : YES { $$ = 1; } + | NO { $$ = 0; } + ; + +main : FONTNAME STRING { + if (conf->DefaultFontName != NULL && + conf->DefaultFontName != DEFAULTFONTNAME) + free(conf->DefaultFontName); + if ((conf->DefaultFontName = xstrdup($2)) == NULL) { + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($2); + } + | STICKY yesno { + if ($2 == 0) + conf->flags &= ~CONF_STICKY_GROUPS; + else + conf->flags |= CONF_STICKY_GROUPS; + } + | COMMAND STRING string { + conf_cmd_add(conf, $3, $2, 0); + free($2); + free($3); + } + | AUTOGROUP NUMBER STRING { + struct autogroupwin *aw; + char *p; + + if ($2 < 1 || $2 > 9) { + free($3); + yyerror("autogroup number out of range: %d", $2); + YYERROR; + } + + XCALLOC(aw, struct autogroupwin); + + if ((p = strchr($3, ',')) == NULL) { + aw->name = NULL; + aw->class = xstrdup($3); + } else { + *(p++) = '\0'; + aw->name = xstrdup($3); + aw->class = xstrdup(p); + } + aw->group = xstrdup(shortcut_to_name[$2]); + + TAILQ_INSERT_TAIL(&conf->autogroupq, aw, entry); + + free($3); + } + | IGNORE STRING { + struct winmatch *wm; + + XCALLOC(wm, struct winmatch); + strlcpy(wm->title, $2, sizeof(wm->title)); + wm->opts |= CONF_IGNORECASE; + TAILQ_INSERT_TAIL(&conf->ignoreq, wm, entry); + + free($2); + } + | BIND STRING string { + conf_bindname(conf, $2, $3); + free($2); + free($3); + } + ; + +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + + file->errors++; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "autogroup", AUTOGROUP}, + { "bind", BIND}, + { "command", COMMAND}, + { "fontname", FONTNAME}, + { "ignore", IGNORE}, + { "no", NO}, + { "sticky", STICKY}, + { "yes", YES} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(int quotec) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + if (quotec) { + if ((c = getc(file->stream)) == EOF) { + yyerror("reached end of file while parsing quoted string"); + if (popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = getc(file->stream)) == '\\') { + next = getc(file->stream); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + + while (c == EOF) { + if (popfile() == EOF) + return (EOF); + c = getc(file->stream); + } + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + pushback_index = 0; + + /* skip to either EOF or the first real EOL */ + while (1) { + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p; + int quotec, next, c; + int token; + + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || c == ' ' || c == '\t') + c = next; + else if (next == '\n') + continue; + else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + err(1, "yylex: strdup"); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && x != '<' && x != '>' && \ + x != '!' && x != '=' && x != '/' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_' || c == '*') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +struct file * +pushfile(const char *name) +{ + struct file *nfile; + + if ((nfile = calloc(1, sizeof(struct file))) == NULL || + (nfile->name = strdup(name)) == NULL) { + warn("malloc"); + return (NULL); + } + if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + warn("%s", nfile->name); + free(nfile->name); + free(nfile); + return (NULL); + } + nfile->lineno = 1; + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { + prev->errors += file->errors; + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file); + file = prev; + return (0); + } + return (EOF); +} + +void +conf_clear(struct conf *c) +{ + struct autogroupwin *ag, *agnext; + struct keybinding *kb, *kbnext; + struct winmatch *wm, *wmnext; + struct cmd *cmd, *cmdnext; + + for (cmd = TAILQ_FIRST(&c->cmdq); cmd != NULL; cmd = cmdnext) { + cmdnext = TAILQ_NEXT(cmd, entry); + + TAILQ_REMOVE(&c->cmdq, cmd, entry); + free(cmd); + } + + for (kb = TAILQ_FIRST(&c->keybindingq); kb != NULL; kb = kbnext) { + kbnext = TAILQ_NEXT(kb, entry); + + TAILQ_REMOVE(&c->keybindingq, kb, entry); + free(kb); + } + + for (ag = TAILQ_FIRST(&c->autogroupq); ag != NULL; ag = agnext) { + agnext = TAILQ_NEXT(ag, entry); + + TAILQ_REMOVE(&c->autogroupq, ag, entry); + free(ag->class); + if (ag->name) + free(ag->name); + free(ag->group); + free(ag); + } + + for (wm = TAILQ_FIRST(&c->ignoreq); wm != NULL; wm = wmnext) { + wmnext = TAILQ_NEXT(wm, entry); + + TAILQ_REMOVE(&c->ignoreq, wm, entry); + free(wm); + } + + if (c->DefaultFontName != NULL && + c->DefaultFontName != DEFAULTFONTNAME) + free(c->DefaultFontName); +} + + +int +parse_config(const char *filename, struct conf *xconf) +{ + int errors = 0; + + if ((conf = malloc(sizeof(struct conf))) == NULL) + return (-1); + + if ((file = pushfile(filename)) == NULL) { + free(conf); + return (-1); + } + + strlcpy(conf->conf_path, filename, sizeof(conf->conf_path)); + + conf_init(conf); + + yyparse(); + errors = file->errors; + file->errors = 0; + popfile(); + + if (errors) { + conf_clear(conf); + } + else { + struct autogroupwin *ag, *agnext; + struct keybinding *kb, *kbnext; + struct winmatch *wm, *wmnext; + struct cmd *cmd, *cmdnext; + + conf_clear(xconf); + + xconf->flags = conf->flags; + + for (cmd = TAILQ_FIRST(&conf->cmdq); cmd != NULL; cmd = cmdnext) { + cmdnext = TAILQ_NEXT(cmd, entry); + + TAILQ_REMOVE(&conf->cmdq, cmd, entry); + TAILQ_INSERT_TAIL(&xconf->cmdq, cmd, entry); + } + + for (kb = TAILQ_FIRST(&conf->keybindingq); kb != NULL; kb = kbnext) { + kbnext = TAILQ_NEXT(kb, entry); + + TAILQ_REMOVE(&conf->keybindingq, kb, entry); + TAILQ_INSERT_TAIL(&xconf->keybindingq, kb, entry); + } + + for (ag = TAILQ_FIRST(&conf->autogroupq); ag != NULL; ag = agnext) { + agnext = TAILQ_NEXT(ag, entry); + + TAILQ_REMOVE(&conf->autogroupq, ag, entry); + TAILQ_INSERT_TAIL(&xconf->autogroupq, ag, entry); + } + + for (wm = TAILQ_FIRST(&conf->ignoreq); wm != NULL; wm = wmnext) { + wmnext = TAILQ_NEXT(wm, entry); + + TAILQ_REMOVE(&conf->ignoreq, wm, entry); + TAILQ_INSERT_TAIL(&xconf->ignoreq, wm, entry); + } + + strlcpy(xconf->termpath, conf->termpath, sizeof(xconf->termpath)); + strlcpy(xconf->lockpath, conf->lockpath, sizeof(xconf->lockpath)); + + xconf->DefaultFontName = conf->DefaultFontName; + } + + free(conf); + + return (errors ? -1 : 0); +} diff --git a/xevents.c b/xevents.c index 6189ac0..3504e0a 100644 --- a/xevents.c +++ b/xevents.c @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: xevents.c,v 1.7 2008/03/22 15:09:45 oga Exp $ + * $Id: xevents.c,v 1.8 2008/03/23 15:09:21 simon Exp $ */ /* @@ -275,10 +275,7 @@ xev_handle_buttonpress(struct xevent *xev, XEvent *ee) break; case Button3: { struct cmd *cmd; - if (conf_cmd_changed(Conf.menu_path)) { - conf_cmd_clear(&Conf); - conf_cmd_populate(&Conf, Conf.menu_path); - } + conf_reload(&Conf); TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { XCALLOC(mi, struct menu); strlcpy(mi->text, cmd->label, sizeof(mi->text)); -- cgit v1.2.3-2-gb3c3