JWM Source Documentation
parse.c
Go to the documentation of this file.
1 
10 #include "jwm.h"
11 #include "parse.h"
12 #include "lex.h"
13 #include "settings.h"
14 #include "menu.h"
15 #include "root.h"
16 #include "tray.h"
17 #include "group.h"
18 #include "misc.h"
19 #include "swallow.h"
20 #include "pager.h"
21 #include "error.h"
22 #include "key.h"
23 #include "main.h"
24 #include "font.h"
25 #include "icon.h"
26 #include "command.h"
27 #include "taskbar.h"
28 #include "traybutton.h"
29 #include "clock.h"
30 #include "dock.h"
31 #include "background.h"
32 #include "spacer.h"
33 #include "desktop.h"
34 #include "border.h"
35 
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/mman.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 
45 static const StringMappingType KEY_MAP[] = {
46  { "close", KEY_CLOSE },
47  { "ddesktop", KEY_DDESKTOP },
48  { "desktop#", KEY_DESKTOP },
49  { "down", KEY_DOWN },
50  { "escape", KEY_ESC },
51  { "exit", KEY_EXIT },
52  { "fullscreen", KEY_FULLSCREEN },
53  { "ldesktop", KEY_LDESKTOP },
54  { "left", KEY_LEFT },
55  { "maxbottom", KEY_MAXBOTTOM },
56  { "maxh", KEY_MAXH },
57  { "maximize", KEY_MAX },
58  { "maxleft", KEY_MAXLEFT },
59  { "maxright", KEY_MAXRIGHT },
60  { "maxtop", KEY_MAXTOP },
61  { "maxv", KEY_MAXV },
62  { "minimize", KEY_MIN },
63  { "move", KEY_MOVE },
64  { "next", KEY_NEXT },
65  { "nextstacked", KEY_NEXTSTACK },
66  { "prev", KEY_PREV },
67  { "prevstacked", KEY_PREVSTACK },
68  { "rdesktop", KEY_RDESKTOP },
69  { "resize", KEY_RESIZE },
70  { "restart", KEY_RESTART },
71  { "restore", KEY_RESTORE },
72  { "right", KEY_RIGHT },
73  { "select", KEY_ENTER },
74  { "sendd", KEY_SENDD },
75  { "sendl", KEY_SENDL },
76  { "sendr", KEY_SENDR },
77  { "sendu", KEY_SENDU },
78  { "shade", KEY_SHADE },
79  { "showdesktop", KEY_SHOWDESK },
80  { "showtray", KEY_SHOWTRAY },
81  { "stick", KEY_STICK },
82  { "udesktop", KEY_UDESKTOP },
83  { "up", KEY_UP },
84  { "window", KEY_WIN }
85 };
86 static const unsigned int KEY_MAP_COUNT = ARRAY_LENGTH(KEY_MAP);
87 
91 static const StringMappingType OPTION_MAP[] = {
92  { "aerosnap", OPTION_AEROSNAP },
93  { "border", OPTION_BORDER },
94  { "centered", OPTION_CENTERED },
95  { "constrain", OPTION_CONSTRAIN },
96  { "drag", OPTION_DRAG },
97  { "fixed", OPTION_FIXED },
98  { "fullscreen", OPTION_FULLSCREEN },
99  { "hmax", OPTION_MAX_H },
100  { "iignore", OPTION_IIGNORE },
101  { "ilist", OPTION_ILIST },
102  { "ipager", OPTION_IPAGER },
103  { "maximized", OPTION_MAXIMIZED },
104  { "minimized", OPTION_MINIMIZED },
105  { "noborder", OPTION_NOBORDER },
106  { "noclose", OPTION_NOCLOSE },
107  { "nodrag", OPTION_NODRAG },
108  { "nofocus", OPTION_NOFOCUS },
109  { "nofullscreen", OPTION_NOFULLSCREEN },
110  { "nolist", OPTION_NOLIST },
111  { "nomax", OPTION_NOMAX },
112  { "nomin", OPTION_NOMIN },
113  { "nomove", OPTION_NOMOVE },
114  { "nopager", OPTION_NOPAGER },
115  { "noresize", OPTION_NORESIZE },
116  { "noshade", OPTION_NOSHADE },
117  { "notitle", OPTION_NOTITLE },
118  { "noturgent", OPTION_NOTURGENT },
119  { "pignore", OPTION_PIGNORE },
120  { "sticky", OPTION_STICKY },
121  { "tiled", OPTION_TILED },
122  { "title", OPTION_TITLE },
123  { "vmax", OPTION_MAX_V }
124 };
125 static const unsigned int OPTION_MAP_COUNT = ARRAY_LENGTH(OPTION_MAP);
126 
127 static const char *DEFAULT_TITLE = "JWM";
128 static const char *LABEL_ATTRIBUTE = "label";
129 static const char *ICON_ATTRIBUTE = "icon";
130 static const char *TOOLTIP_ATTRIBUTE = "tooltip";
131 static const char *CONFIRM_ATTRIBUTE = "confirm";
132 static const char *LABELED_ATTRIBUTE = "labeled";
133 static const char *ONROOT_ATTRIBUTE = "onroot";
134 static const char *X_ATTRIBUTE = "x";
135 static const char *Y_ATTRIBUTE = "y";
136 static const char *WIDTH_ATTRIBUTE = "width";
137 static const char *HEIGHT_ATTRIBUTE = "height";
138 static const char *DYNAMIC_ATTRIBUTE = "dynamic";
139 
140 static const char *FALSE_VALUE = "false";
141 static const char *TRUE_VALUE = "true";
142 
143 static char ParseFile(const char *fileName, int depth);
144 static char *ReadFile(FILE *fd);
145 static TokenNode *TokenizeFile(const char *fileName);
146 static TokenNode *TokenizePipe(const char *command);
147 
148 /* Misc. */
149 static void Parse(const TokenNode *start, int depth);
150 static void ParseInclude(const TokenNode *tp, int depth);
151 static void ParseDesktops(const TokenNode *tp);
152 static void ParseDesktop(int desktop, const TokenNode *tp);
153 static void ParseDesktopBackground(int desktop, const TokenNode *tp);
154 
155 /* Menus. */
156 static Menu *ParseMenu(const TokenNode *start);
157 static void ParseRootMenu(const TokenNode *start);
158 static MenuItem *ParseMenuItem(const TokenNode *start, Menu *menu,
159  MenuItem *last);
160 static MenuItem *InsertMenuItem(MenuItem *last);
161 static MenuItem *ParseMenuInclude(const TokenNode *tp, Menu *menu,
162  MenuItem *last);
163 static TokenNode *ParseMenuIncludeHelper(const TokenNode *tp,
164  const char *command);
165 
166 /* Tray. */
167 typedef void (*AddTrayActionFunc)(TrayComponentType*, const char*, int);
168 static void ParseTray(const TokenNode *tp);
169 static void ParsePager(const TokenNode *tp, TrayType *tray);
170 static void ParseTaskList(const TokenNode *tp, TrayType *tray);
171 static void ParseSwallow(const TokenNode *tp, TrayType *tray);
172 static void ParseTrayButton(const TokenNode *tp, TrayType *tray);
173 static void ParseClock(const TokenNode *tp, TrayType *tray);
174 static void ParseTrayComponentActions(const TokenNode *tp,
175  TrayComponentType *cp,
176  AddTrayActionFunc func);
177 static void ParseDock(const TokenNode *tp, TrayType *tray);
178 static void ParseSpacer(const TokenNode *tp, TrayType *tray);
179 
180 /* Groups. */
181 static void ParseGroup(const TokenNode *tp);
182 static void ParseGroupOption(const TokenNode *tp,
183  struct GroupType *group,
184  const char *option);
185 
186 /* Style. */
187 static void ParseWindowStyle(const TokenNode *tp);
188 static void ParseActiveWindowStyle(const TokenNode *tp);
189 static void ParseTrayStyle(const TokenNode *tp,
190  FontType font, ColorType baseColor);
191 static void ParseActive(const TokenNode *tp, ColorType fg,
192  ColorType bg1, ColorType bg2,
194 static void ParsePagerStyle(const TokenNode *tp);
195 static void ParseClockStyle(const TokenNode *tp);
196 static void ParseMenuStyle(const TokenNode *tp);
197 static void ParsePopupStyle(const TokenNode *tp);
198 
199 /* Feel. */
200 static void ParseKey(const TokenNode *tp);
201 static void ParseSnapMode(const TokenNode *tp);
202 static void ParseMoveMode(const TokenNode *tp);
203 static void ParseResizeMode(const TokenNode *tp);
204 static void ParseFocusModel(const TokenNode *tp);
205 
206 static AlignmentType ParseTextAlignment(const TokenNode *tp);
207 static void ParseDecorations(const TokenNode *tp, DecorationsType *deco);
208 static void ParseGradient(const char *value, ColorType a, ColorType b);
209 static char *FindAttribute(AttributeNode *ap, const char *name);
210 static int ParseTokenValue(const StringMappingType *mapping, int count,
211  const TokenNode *tp, int def);
212 static int ParseAttribute(const StringMappingType *mapping, int count,
213  const TokenNode *tp, const char *attr,
214  int def);
215 static unsigned int ParseUnsigned(const TokenNode *tp, const char *str);
216 static unsigned int ParseOpacity(const TokenNode *tp, const char *str);
217 static WinLayerType ParseLayer(const TokenNode *tp, const char *str);
219 static void InvalidTag(const TokenNode *tp, TokenType parent);
220 static void ParseError(const TokenNode *tp, const char *str, ...);
221 
223 void ParseConfig(const char *fileName)
224 {
225  if(!ParseFile(fileName, 0)) {
226  if(JUNLIKELY(!ParseFile(SYSTEM_CONFIG, 0))) {
227  ParseError(NULL, _("could not open %s or %s"),
228  fileName, SYSTEM_CONFIG);
229  }
230  }
232  ValidateKeys();
233 }
234 
239 char ParseFile(const char *fileName, int depth)
240 {
241  TokenNode *tokens;
242 
243  depth += 1;
244  if(JUNLIKELY(depth > MAX_INCLUDE_DEPTH)) {
245  ParseError(NULL, _("include depth (%d) exceeded"),
247  return 0;
248  }
249 
250  tokens = TokenizeFile(fileName);
251  if(!tokens) {
252  return 0;
253  }
254 
255  Parse(tokens, depth);
256  ReleaseTokens(tokens);
257 
258  return 1;
259 }
260 
262 void Parse(const TokenNode *start, int depth)
263 {
264 
265  TokenNode *tp;
266 
267  if(!start) {
268  return;
269  }
270 
271  if(JLIKELY(start->type == TOK_JWM)) {
272  for(tp = start->subnodeHead; tp; tp = tp->next) {
273  if(shouldReload) {
274  switch(tp->type) {
275  case TOK_ROOTMENU:
276  ParseRootMenu(tp);
277  break;
278  case TOK_INCLUDE:
279  ParseInclude(tp, depth);
280  break;
281  default:
282  break;
283  }
284  } else {
285  switch(tp->type) {
286  case TOK_DESKTOPS:
287  ParseDesktops(tp);
288  break;
291  break;
294  break;
295  case TOK_FOCUSMODEL:
296  ParseFocusModel(tp);
297  break;
298  case TOK_GROUP:
299  ParseGroup(tp);
300  break;
301  case TOK_ICONPATH:
302  AddIconPath(tp->value);
303  break;
304  case TOK_INCLUDE:
305  ParseInclude(tp, depth);
306  break;
307  case TOK_KEY:
308  ParseKey(tp);
309  break;
310  case TOK_MENUSTYLE:
311  ParseMenuStyle(tp);
312  break;
313  case TOK_MOVEMODE:
314  ParseMoveMode(tp);
315  break;
316  case TOK_PAGERSTYLE:
317  ParsePagerStyle(tp);
318  break;
319  case TOK_POPUPSTYLE:
320  ParsePopupStyle(tp);
321  break;
322  case TOK_RESIZEMODE:
323  ParseResizeMode(tp);
324  break;
325  case TOK_RESTARTCOMMAND:
327  break;
328  case TOK_ROOTMENU:
329  ParseRootMenu(tp);
330  break;
331  case TOK_SHUTDOWNCOMMAND:
333  break;
334  case TOK_SNAPMODE:
335  ParseSnapMode(tp);
336  break;
337  case TOK_STARTUPCOMMAND:
339  break;
340  case TOK_TRAY:
341  ParseTray(tp);
342  break;
343  case TOK_TRAYSTYLE:
345  break;
346  case TOK_TASKLISTSTYLE:
348  break;
349  case TOK_TRAYBUTTONSTYLE:
351  break;
352  case TOK_CLOCKSTYLE:
353  ParseClockStyle(tp);
354  break;
355  case TOK_WINDOWSTYLE:
356  ParseWindowStyle(tp);
357  break;
358  case TOK_BUTTONCLOSE:
360  break;
361  case TOK_BUTTONMAX:
362  SetBorderIcon(BI_MAX, tp->value);
363  break;
364  case TOK_BUTTONMAXACTIVE:
366  break;
367  case TOK_BUTTONMIN:
368  SetBorderIcon(BI_MIN, tp->value);
369  break;
370  case TOK_BUTTONMENU:
372  break;
373  case TOK_DEFAULTICON:
374  SetDefaultIcon(tp->value);
375  break;
376  default:
377  InvalidTag(tp, TOK_JWM);
378  break;
379  }
380  }
381  }
382  } else {
383  ParseError(start, _("invalid start tag: %s"), GetTokenName(start));
384  }
385 
386 }
387 
389 void ParseFocusModel(const TokenNode *tp) {
390  static const StringMappingType mapping[] = {
391  { "click", FOCUS_CLICK },
392  { "sloppy", FOCUS_SLOPPY }
393  };
394  settings.focusModel = ParseTokenValue(mapping, ARRAY_LENGTH(mapping), tp,
396 }
397 
399 void ParseSnapMode(const TokenNode *tp)
400 {
401  const char *distance;
402  static const StringMappingType mapping[] = {
403  { "border", SNAP_BORDER },
404  { "none", SNAP_NONE },
405  { "screen", SNAP_SCREEN }
406  };
407 
408  distance = FindAttribute(tp->attributes, "distance");
409  if(distance) {
410  settings.snapDistance = ParseUnsigned(tp, distance);
411  }
412  settings.snapMode = ParseTokenValue(mapping, ARRAY_LENGTH(mapping), tp,
414 }
415 
417 void ParseMoveMode(const TokenNode *tp)
418 {
419  const char *str;
420  static const StringMappingType mapping[] = {
421  { "opaque", MOVE_OPAQUE },
422  { "outline", MOVE_OUTLINE }
423  };
424 
425  str = FindAttribute(tp->attributes, "delay");
426  if(str) {
428  }
429  str = FindAttribute(tp->attributes, "mask");
430  if(str && *str) {
432  }
433 
435  settings.moveMode = ParseTokenValue(mapping, ARRAY_LENGTH(mapping), tp,
437 }
438 
440 void ParseResizeMode(const TokenNode *tp)
441 {
442  static const StringMappingType mapping[] = {
443  { "opaque", RESIZE_OPAQUE },
444  { "outline", RESIZE_OUTLINE }
445  };
447  settings.resizeMode = ParseTokenValue(mapping, ARRAY_LENGTH(mapping), tp,
449 }
450 
452 Menu *ParseMenu(const TokenNode *start)
453 {
454  const char *value;
455  Menu *menu;
456 
457  menu = CreateMenu();
458 
459  value = FindAttribute(start->attributes, HEIGHT_ATTRIBUTE);
460  if(value) {
461  menu->itemHeight = ParseUnsigned(start, value);
462  } else {
463  menu->itemHeight = 0;
464  }
465 
466  value = FindAttribute(start->attributes, LABELED_ATTRIBUTE);
467  if(value && !strcmp(value, TRUE_VALUE)) {
468  value = FindAttribute(start->attributes, LABEL_ATTRIBUTE);
469  if(!value) {
470  value = DEFAULT_TITLE;
471  }
472  menu->label = CopyString(value);
473  } else {
474  menu->label = NULL;
475  }
476 
477  menu->items = NULL;
478  ParseMenuItem(start->subnodeHead, menu, NULL);
479 
480  return menu;
481 
482 }
483 
485 void ParseRootMenu(const TokenNode *start)
486 {
487  Menu *menu;
488  char *onroot;
489  const char *value;
490 
491  menu = ParseMenu(start);
492 
493  onroot = FindAttribute(start->attributes, ONROOT_ATTRIBUTE);
494  if(!onroot) {
495  onroot = "123";
496  }
497 
498  value = FindAttribute(start->attributes, DYNAMIC_ATTRIBUTE);
499  menu->dynamic = CopyString(value);
500 
501  SetRootMenu(onroot, menu);
502 
503 }
504 
507 {
509  if(last) {
510  last->next = item;
511  }
512  return item;
513 }
514 
516 MenuItem *ParseMenuItem(const TokenNode *start, Menu *menu, MenuItem *last)
517 {
518 
519  Menu *child;
520  const char *value;
521 
522  Assert(menu);
523 
524  menu->offsets = NULL;
525  while(start) {
526  switch(start->type) {
527  case TOK_DYNAMIC:
528 
529  last = InsertMenuItem(last);
530  if(!menu->items) {
531  menu->items = last;
532  }
533 
534  value = FindAttribute(start->attributes, LABEL_ATTRIBUTE);
535  last->name = CopyString(value);
536 
537  value = FindAttribute(start->attributes, ICON_ATTRIBUTE);
538  last->iconName = CopyString(value);
539 
540  value = FindAttribute(start->attributes, TOOLTIP_ATTRIBUTE);
541  last->tooltip = CopyString(value);
542 
543  last->action.type = MA_DYNAMIC;
544  last->action.str = CopyString(start->value);
545 
546  value = FindAttribute(start->attributes, HEIGHT_ATTRIBUTE);
547  if(value) {
548  last->action.value = ParseUnsigned(start, value);
549  } else {
550  last->action.value = menu->itemHeight;
551  }
552 
553  break;
554  case TOK_MENU:
555 
556  last = InsertMenuItem(last);
557  last->type = MENU_ITEM_SUBMENU;
558  if(!menu->items) {
559  menu->items = last;
560  }
561 
562  value = FindAttribute(start->attributes, LABEL_ATTRIBUTE);
563  last->name = CopyString(value);
564 
565  value = FindAttribute(start->attributes, ICON_ATTRIBUTE);
566  last->iconName = CopyString(value);
567 
568  value = FindAttribute(start->attributes, TOOLTIP_ATTRIBUTE);
569  last->tooltip = CopyString(value);
570 
571  last->submenu = CreateMenu();
572  child = last->submenu;
573 
574  value = FindAttribute(start->attributes, HEIGHT_ATTRIBUTE);
575  if(value) {
576  child->itemHeight = ParseUnsigned(start, value);
577  } else {
578  child->itemHeight = menu->itemHeight;
579  }
580 
581  value = FindAttribute(start->attributes, LABELED_ATTRIBUTE);
582  if(value && !strcmp(value, TRUE_VALUE)) {
583  if(last->name) {
584  child->label = CopyString(last->name);
585  } else {
586  child->label = CopyString(DEFAULT_TITLE);
587  }
588  } else {
589  child->label = NULL;
590  }
591 
592  last->submenu->items = NULL;
593  ParseMenuItem(start->subnodeHead, last->submenu, NULL);
594 
595  break;
596  case TOK_PROGRAM:
597 
598  last = InsertMenuItem(last);
599  if(!menu->items) {
600  menu->items = last;
601  }
602 
603  value = FindAttribute(start->attributes, LABEL_ATTRIBUTE);
604  if(value) {
605  last->name = CopyString(value);
606  } else if(start->value) {
607  last->name = CopyString(start->value);
608  }
609 
610  value = FindAttribute(start->attributes, TOOLTIP_ATTRIBUTE);
611  last->tooltip = CopyString(value);
612 
613  value = FindAttribute(start->attributes, ICON_ATTRIBUTE);
614  last->iconName = CopyString(value);
615 
616  last->action.type = MA_EXECUTE;
617  last->action.str = CopyString(start->value);
618 
619  break;
620  case TOK_SEPARATOR:
621 
622  last = InsertMenuItem(last);
623  last->type = MENU_ITEM_SEPARATOR;
624  if(!menu->items) {
625  menu->items = last;
626  }
627 
628  break;
629  case TOK_INCLUDE:
630  last = ParseMenuInclude(start, menu, last);
631  break;
632  case TOK_DESKTOPS:
633  case TOK_STICK:
634  case TOK_MAXIMIZE:
635  case TOK_MINIMIZE:
636  case TOK_SHADE:
637  case TOK_MOVE:
638  case TOK_RESIZE:
639  case TOK_KILL:
640  case TOK_CLOSE:
641  case TOK_SENDTO:
642 
643  last = InsertMenuItem(last);
644  if(!menu->items) {
645  menu->items = last;
646  }
647 
648  value = FindAttribute(start->attributes, LABEL_ATTRIBUTE);
649  if(!value) {
650  value = GetTokenName(start);
651  }
652  last->name = CopyString(value);
653 
654  value = FindAttribute(start->attributes, TOOLTIP_ATTRIBUTE);
655  last->tooltip = CopyString(value);
656 
657  value = FindAttribute(start->attributes, ICON_ATTRIBUTE);
658  last->iconName = CopyString(value);
659 
660  switch(start->type) {
661  case TOK_DESKTOPS:
662  last->action.type = MA_DESKTOP_MENU;
663  break;
664  case TOK_STICK:
665  last->action.type = MA_STICK;
666  break;
667  case TOK_MAXIMIZE:
668  last->action.type = MA_MAXIMIZE;
669  break;
670  case TOK_MINIMIZE:
671  last->action.type = MA_MINIMIZE;
672  break;
673  case TOK_SHADE:
674  last->action.type = MA_SHADE;
675  break;
676  case TOK_MOVE:
677  last->action.type = MA_MOVE;
678  break;
679  case TOK_RESIZE:
680  last->action.type = MA_RESIZE;
681  break;
682  case TOK_KILL:
683  last->action.type = MA_KILL;
684  break;
685  case TOK_CLOSE:
686  last->action.type = MA_CLOSE;
687  break;
688  case TOK_SENDTO:
689  last->action.type = MA_SENDTO_MENU;
690  break;
691  default:
692  break;
693  }
694 
695  break;
696  case TOK_EXIT:
697 
698  last = InsertMenuItem(last);
699  if(!menu->items) {
700  menu->items = last;
701  }
702 
703  value = FindAttribute(start->attributes, LABEL_ATTRIBUTE);
704  if(!value) {
705  value = GetTokenName(start);
706  }
707  last->name = CopyString(value);
708 
709  value = FindAttribute(start->attributes, TOOLTIP_ATTRIBUTE);
710  last->tooltip = CopyString(value);
711 
712  value = FindAttribute(start->attributes, ICON_ATTRIBUTE);
713  last->iconName = CopyString(value);
714 
715  last->action.type = MA_EXIT;
716  last->action.str = CopyString(start->value);
717  value = FindAttribute(start->attributes, CONFIRM_ATTRIBUTE);
718  if(value && !strcmp(value, FALSE_VALUE)) {
719  last->action.value = 0;
720  } else {
721  last->action.value = 1;
722  }
723 
724  break;
725  case TOK_RESTART:
726 
727  last = InsertMenuItem(last);
728  if(!menu->items) {
729  menu->items = last;
730  }
731 
732  value = FindAttribute(start->attributes, LABEL_ATTRIBUTE);
733  if(!value) {
734  value = GetTokenName(start);
735  }
736  last->name = CopyString(value);
737 
738  value = FindAttribute(start->attributes, TOOLTIP_ATTRIBUTE);
739  last->tooltip = CopyString(value);
740 
741  value = FindAttribute(start->attributes, ICON_ATTRIBUTE);
742  last->iconName = CopyString(value);
743 
744  last->action.type = MA_RESTART;
745 
746  break;
747  default:
748  InvalidTag(start, TOK_MENU);
749  break;
750  }
751  start = start->next;
752  }
753 
754  return last;
755 
756 }
757 
759 TokenNode *ParseMenuIncludeHelper(const TokenNode *tp, const char *command)
760 {
761  TokenNode *start;
762 
763  if(!strncmp(command, "exec:", 5)) {
764  start = TokenizePipe(&command[5]);
765  } else {
766  start = TokenizeFile(command);
767  }
768 
769  if(JUNLIKELY(!start || start->type != TOK_JWM))
770  {
771  ParseError(tp, _("invalid include: %s"), command);
772  ReleaseTokens(start);
773  return NULL;
774  }
775 
776  return start;
777 }
778 
781  MenuItem *last)
782 {
783  TokenNode *start = ParseMenuIncludeHelper(tp, tp->value);
784  if(JLIKELY(start)) {
785  last = ParseMenuItem(start->subnodeHead, menu, last);
786  ReleaseTokens(start);
787  }
788  return last;
789 }
790 
792 Menu *ParseDynamicMenu(const char *command)
793 {
794  Menu *menu = NULL;
795  TokenNode *start = ParseMenuIncludeHelper(NULL, command);
796  if(JLIKELY(start)) {
797  menu = ParseMenu(start);
798  ReleaseTokens(start);
799  }
800  return menu;
801 }
802 
804 void ParseKey(const TokenNode *tp) {
805 
806  const char *key;
807  const char *code;
808  const char *mask;
809  const char *action;
810  const char *command;
811  KeyType k;
812 
813  Assert(tp);
814 
815  mask = FindAttribute(tp->attributes, "mask");
816  key = FindAttribute(tp->attributes, "key");
817  code = FindAttribute(tp->attributes, "keycode");
818 
819  action = tp->value;
820  if(JUNLIKELY(action == NULL)) {
821  ParseError(tp, _("no action specified for Key"));
822  return;
823  }
824 
825  command = NULL;
826  k = KEY_NONE;
827  if(!strncmp(action, "exec:", 5)) {
828  k = KEY_EXEC;
829  command = action + 5;
830  } else if(!strncmp(action, "root:", 5)) {
831  k = KEY_ROOT;
832  command = action + 5;
833  } else {
834  /* Look up the option in the key map using binary search. */
835  const int x = FindValue(KEY_MAP, KEY_MAP_COUNT, action);
836  if(x >= 0) {
837  k = (KeyType)x;
838  }
839  }
840 
841  /* Insert the binding if it's valid. */
842  if(JUNLIKELY(k == KEY_NONE)) {
843 
844  ParseError(tp, _("invalid Key action: \"%s\""), action);
845 
846  } else {
847 
848  InsertBinding(k, mask, key, code, command);
849 
850  }
851 
852 }
853 
856 {
857  static const StringMappingType mapping[] = {
858  {"left", ALIGN_LEFT },
859  {"center", ALIGN_CENTER },
860  {"right", ALIGN_RIGHT }
861  };
862  const char *attr= FindAttribute(tp->attributes, "align");
863  if(attr) {
864  const int x = FindValue(mapping, ARRAY_LENGTH(mapping), attr);
865  if(JLIKELY(x >= 0)) {
866  return x;
867  } else {
868  Warning(_("invalid text alignment: \"%s\""), attr);
869  }
870  }
871 
872  return ALIGN_LEFT;
873 }
874 
877 {
878  const TokenNode *np;
879 
881  for(np = tp->subnodeHead; np; np = np->next) {
882  switch(np->type) {
883  case TOK_FONT:
884  SetFont(FONT_BORDER, np->value);
886  break;
887  case TOK_WIDTH:
889  break;
890  case TOK_HEIGHT:
892  break;
893  case TOK_CORNER:
895  break;
896  case TOK_ACTIVE:
898  break;
899  case TOK_FOREGROUND:
901  break;
902  case TOK_BACKGROUND:
904  break;
905  case TOK_OUTLINE:
907  break;
908  case TOK_OPACITY:
910  break;
911  default:
913  break;
914  }
915  }
916 }
917 
920 {
921 
922  const TokenNode *np;
923 
924  Assert(tp);
925 
926  for(np = tp->subnodeHead; np; np = np->next) {
927  switch(np->type) {
928  case TOK_FOREGROUND:
930  break;
931  case TOK_BACKGROUND:
932  ParseGradient(np->value,
934  break;
935  case TOK_OUTLINE:
938  break;
939  case TOK_OPACITY:
941  break;
942  default:
943  InvalidTag(np, TOK_ACTIVE);
944  break;
945  }
946  }
947 
948 }
949 
951 void ParseInclude(const TokenNode *tp, int depth)
952 {
953  if(JUNLIKELY(!tp->value)) {
954  ParseError(tp, _("no include file specified"));
955  return;
956  }
957 
958  if(!strncmp(tp->value, "exec:", 5)) {
959  TokenNode *tokens = TokenizePipe(&tp->value[5]);
960  if(JLIKELY(tokens)) {
961  Parse(tokens, 0);
962  ReleaseTokens(tokens);
963  } else {
964  ParseError(tp, _("could not process include: %s"), &tp->value[5]);
965  }
966  } else {
967  if(JUNLIKELY(!ParseFile(tp->value, depth))) {
968  ParseError(tp, _("could not open included file: %s"), tp->value);
969  }
970  }
971 
972 }
973 
975 void ParseDesktops(const TokenNode *tp) {
976 
977  TokenNode *np;
978  const char *width;
979  const char *height;
980  int desktop;
981 
982  Assert(tp);
983 
985  if(width != NULL) {
986  settings.desktopWidth = ParseUnsigned(tp, width);
987  }
989  if(height != NULL) {
990  settings.desktopHeight = ParseUnsigned(tp, height);
991  }
993 
994  desktop = 0;
995  for(np = tp->subnodeHead; np; np = np->next) {
996  if(desktop >= settings.desktopCount) {
997  break;
998  }
999  switch(np->type) {
1000  case TOK_BACKGROUND:
1001  ParseDesktopBackground(-1, np);
1002  break;
1003  case TOK_DESKTOP:
1004  ParseDesktop(desktop, np);
1005  desktop += 1;
1006  break;
1007  default:
1008  InvalidTag(np, TOK_DESKTOPS);
1009  break;
1010  }
1011  }
1012 
1013 }
1014 
1016 void ParseDesktop(int desktop, const TokenNode *tp) {
1017 
1018  TokenNode *np;
1019  const char *attr;
1020 
1021  attr = FindAttribute(tp->attributes, "name");
1022  if(attr) {
1023  SetDesktopName(desktop, attr);
1024  }
1025 
1026  for(np = tp->subnodeHead; np; np = np->next) {
1027  switch(np->type) {
1028  case TOK_BACKGROUND:
1029  ParseDesktopBackground(desktop, np);
1030  break;
1031  default:
1032  InvalidTag(np, TOK_DESKTOP);
1033  break;
1034  }
1035  }
1036 
1037 }
1038 
1040 void ParseDesktopBackground(int desktop, const TokenNode *tp)
1041 {
1042  const char *type = FindAttribute(tp->attributes, "type");
1043  SetBackground(desktop, type, tp->value);
1044 }
1045 
1047 void ParseTrayStyle(const TokenNode *tp, FontType font, ColorType fg)
1048 {
1049  const TokenNode *np;
1050  const ColorType bg1 = fg + COLOR_TRAY_BG1 - COLOR_TRAY_FG;
1051  const ColorType bg2 = fg + COLOR_TRAY_BG2 - COLOR_TRAY_FG;
1052  const ColorType activeFg = fg + COLOR_TRAY_ACTIVE_FG - COLOR_TRAY_FG;
1053  const ColorType activeBg1 = fg + COLOR_TRAY_ACTIVE_BG1 - COLOR_TRAY_FG;
1054  const ColorType activeBg2 = fg + COLOR_TRAY_ACTIVE_BG2 - COLOR_TRAY_FG;
1055  const ColorType up = fg + COLOR_TRAY_UP - COLOR_TRAY_FG;
1057  const ColorType activeUp = fg + COLOR_TRAY_ACTIVE_UP - COLOR_TRAY_FG;
1058  const ColorType activeDown = fg + COLOR_TRAY_ACTIVE_DOWN - COLOR_TRAY_FG;
1059 
1060  /* TrayStyle has extra attributes. */
1061  if(tp->type == TOK_TRAYSTYLE) {
1062  const char *temp;
1064  temp = FindAttribute(tp->attributes, "group");
1065  if(temp) {
1066  settings.groupTasks = !strcmp(temp, TRUE_VALUE);
1067  }
1068 
1069  temp = FindAttribute(tp->attributes, "list");
1070  if(temp) {
1071  settings.listAllTasks = !strcmp(temp, "all");
1072  }
1073  } else if(tp->type == TOK_TASKLISTSTYLE) {
1075  }
1076 
1077  for(np = tp->subnodeHead; np; np = np->next) {
1078  switch(np->type) {
1079  case TOK_FONT:
1080  SetFont(font, np->value);
1081  break;
1082  case TOK_ACTIVE:
1083  ParseActive(np, activeFg, activeBg1, activeBg2, activeUp, activeDown);
1084  break;
1085  case TOK_BACKGROUND:
1086  ParseGradient(np->value, bg1, bg2);
1087  break;
1088  case TOK_FOREGROUND:
1089  SetColor(fg, np->value);
1090  break;
1091  case TOK_OUTLINE:
1092  ParseGradient(np->value, down, up);
1093  break;
1094  case TOK_OPACITY:
1095  if(tp->type == TOK_TRAYSTYLE) {
1097  break;
1098  }
1099  /* fall through */
1100  default:
1101  InvalidTag(np, tp->type);
1102  break;
1103  }
1104  }
1105 
1106 }
1107 
1109 void ParseActive(const TokenNode *tp, ColorType fg,
1110  ColorType bg1, ColorType bg2,
1112 {
1113  const TokenNode *np;
1114 
1115  for(np = tp->subnodeHead; np; np = np->next) {
1116  switch(np->type) {
1117  case TOK_BACKGROUND:
1118  if(bg1 == bg2) {
1119  SetColor(bg1, np->value);
1120  } else {
1121  ParseGradient(np->value, bg1, bg2);
1122  }
1123  break;
1124  case TOK_FOREGROUND:
1125  SetColor(fg, np->value);
1126  break;
1127  case TOK_OUTLINE:
1128  if(up != COLOR_COUNT) {
1129  ParseGradient(np->value, down, up);
1130  break;
1131  }
1132  /* fall through */
1133  default:
1134  InvalidTag(np, TOK_ACTIVE);
1135  break;
1136  }
1137  }
1138 }
1139 
1141 void ParseTray(const TokenNode *tp)
1142 {
1143  static const StringMappingType mapping[] = {
1144  { "bottom", THIDE_BOTTOM },
1145  { "left", THIDE_LEFT },
1146  { "off", THIDE_OFF },
1147  { "right", THIDE_RIGHT },
1148  { "top", THIDE_TOP }
1149  };
1150  const TokenNode *np;
1151  const char *attr;
1152  TrayType *tray;
1153  TrayAutoHideType autohide;
1154 
1155  Assert(tp);
1156 
1157  tray = CreateTray();
1158 
1159  autohide = ParseAttribute(mapping, ARRAY_LENGTH(mapping), tp,
1160  "autohide", THIDE_OFF);
1161  SetAutoHideTray(tray, autohide);
1162 
1163  attr = FindAttribute(tp->attributes, X_ATTRIBUTE);
1164  if(attr) {
1165  SetTrayX(tray, attr);
1166  }
1167 
1168  attr = FindAttribute(tp->attributes, Y_ATTRIBUTE);
1169  if(attr) {
1170  SetTrayY(tray, attr);
1171  }
1172 
1174  if(attr) {
1175  SetTrayWidth(tray, attr);
1176  }
1177 
1179  if(attr) {
1180  SetTrayHeight(tray, attr);
1181  }
1182 
1183  attr = FindAttribute(tp->attributes, "valign");
1184  SetTrayVerticalAlignment(tray, attr);
1185 
1186  attr = FindAttribute(tp->attributes, "halign");
1187  SetTrayHorizontalAlignment(tray, attr);
1188 
1189  attr = FindAttribute(tp->attributes, "layout");
1190  SetTrayLayout(tray, attr);
1191 
1192  attr = FindAttribute(tp->attributes, "layer");
1193  if(attr) {
1194  SetTrayLayer(tray, ParseLayer(tp, attr));
1195  }
1196 
1197  for(np = tp->subnodeHead; np; np = np->next) {
1198  switch(np->type) {
1199  case TOK_PAGER:
1200  ParsePager(np, tray);
1201  break;
1202  case TOK_TASKLIST:
1203  ParseTaskList(np, tray);
1204  break;
1205  case TOK_SWALLOW:
1206  ParseSwallow(np, tray);
1207  break;
1208  case TOK_TRAYBUTTON:
1209  ParseTrayButton(np, tray);
1210  break;
1211  case TOK_CLOCK:
1212  ParseClock(np, tray);
1213  break;
1214  case TOK_DOCK:
1215  ParseDock(np, tray);
1216  break;
1217  case TOK_SPACER:
1218  ParseSpacer(np, tray);
1219  break;
1220  default:
1221  InvalidTag(np, TOK_TRAY);
1222  break;
1223  }
1224  }
1225 
1226 }
1227 
1230 {
1231 
1232  TrayComponentType *cp;
1233  const char *temp;
1234  int labeled;
1235 
1236  Assert(tp);
1237  Assert(tray);
1238 
1239  labeled = 0;
1241  if(temp && !strcmp(temp, TRUE_VALUE)) {
1242  labeled = 1;
1243  }
1244  cp = CreatePager(labeled);
1245  AddTrayComponent(tray, cp);
1246 
1247 }
1248 
1251 {
1252  TrayComponentType *cp;
1253  const char *temp;
1254 
1255  Assert(tp);
1256  Assert(tray);
1257 
1258  cp = CreateTaskBar();
1259  AddTrayComponent(tray, cp);
1260 
1261  temp = FindAttribute(tp->attributes, "maxwidth");
1262  if(temp) {
1263  SetMaxTaskBarItemWidth(cp, temp);
1264  }
1265 
1266  temp = FindAttribute(tp->attributes, "height");
1267  if(temp) {
1268  SetTaskBarHeight(cp, temp);
1269  }
1270 
1271  temp = FindAttribute(tp->attributes, "labeled");
1272  if(temp && !strcmp(temp, FALSE_VALUE)) {
1273  SetTaskBarLabeled(cp, 0);
1274  }
1275 }
1276 
1279 {
1280  const TokenNode *np;
1281  for(np = tp->subnodeHead; np; np = np->next) {
1282  switch(np->type) {
1283  case TOK_FONT:
1285  break;
1286  case TOK_ACTIVE:
1290  break;
1291  case TOK_BACKGROUND:
1293  break;
1294  case TOK_FOREGROUND:
1296  break;
1297  case TOK_OUTLINE:
1299  break;
1300  default:
1302  break;
1303  }
1304  }
1305 }
1306 
1309 {
1310 
1311  TrayComponentType *cp;
1312  const char *name;
1313  const char *temp;
1314  int width, height;
1315 
1316  Assert(tp);
1317  Assert(tray);
1318 
1319  name = FindAttribute(tp->attributes, "name");
1320  if(name == NULL) {
1321  name = tp->value;
1322  }
1323 
1325  if(temp) {
1326  width = ParseUnsigned(tp, temp);
1327  } else {
1328  width = 0;
1329  }
1330 
1332  if(temp) {
1333  height = ParseUnsigned(tp, temp);
1334  } else {
1335  height = 0;
1336  }
1337 
1338  cp = CreateSwallow(name, tp->value, width, height);
1339  if(cp) {
1340  AddTrayComponent(tray, cp);
1341  }
1342 
1343 }
1344 
1347 {
1348 
1349  TrayComponentType *cp;
1350  const char *icon;
1351  const char *label;
1352  const char *popup;
1353  const char *temp;
1354  unsigned int width, height;
1355 
1356  Assert(tp);
1357  Assert(tray);
1358 
1361  popup = FindAttribute(tp->attributes, "popup");
1362 
1364  if(temp) {
1365  width = ParseUnsigned(tp, temp);
1366  } else {
1367  width = 0;
1368  }
1369 
1371  if(temp) {
1372  height = ParseUnsigned(tp, temp);
1373  } else {
1374  height = 0;
1375  }
1376 
1377  cp = CreateTrayButton(icon, label, popup, width, height);
1378  if(JLIKELY(cp)) {
1379  AddTrayComponent(tray, cp);
1381  }
1382 
1383 }
1384 
1387 {
1388  TrayComponentType *cp;
1389  const char *format;
1390  const char *zone;
1391  const char *temp;
1392  int width, height;
1393 
1394  Assert(tp);
1395  Assert(tray);
1396 
1397  format = FindAttribute(tp->attributes, "format");
1398  zone = FindAttribute(tp->attributes, "zone");
1399 
1401  if(temp) {
1402  width = ParseUnsigned(tp, temp);
1403  } else {
1404  width = 0;
1405  }
1406 
1408  if(temp) {
1409  height = ParseUnsigned(tp, temp);
1410  } else {
1411  height = 0;
1412  }
1413 
1414  cp = CreateClock(format, zone, width, height);
1415  if(JLIKELY(cp)) {
1417  AddTrayComponent(tray, cp);
1418  }
1419 
1420 }
1421 
1424  AddTrayActionFunc func)
1425 {
1426  const TokenNode *np;
1427  const char *mask_str;
1428  const int default_mask = (1 << 1) | (1 << 2) | (1 << 3);
1429  int mask;
1430 
1431  if(tp->value) {
1432  (func)(cp, tp->value, default_mask);
1433  }
1434 
1435  for(np = tp->subnodeHead; np; np = np->next) {
1436  switch(np->type) {
1437  case TOK_BUTTON:
1438  mask_str = FindAttribute(np->attributes, "mask");
1439  if(mask_str) {
1440  int i;
1441  mask = 0;
1442  for(i = 0; mask_str[i]; i++) {
1443  mask |= 1 << (mask_str[i] - '0');
1444  }
1445  } else {
1446  mask = default_mask;
1447  }
1448  (func)(cp, np->value, mask);
1449  break;
1450  default:
1451  InvalidTag(np, tp->type);
1452  break;
1453  }
1454  }
1455 }
1456 
1457 
1459 void ParseDock(const TokenNode *tp, TrayType *tray) {
1460 
1461  TrayComponentType *cp;
1462  int width;
1463  char *str;
1464 
1465  Assert(tp);
1466  Assert(tray);
1467 
1469  if(str) {
1470  width = ParseUnsigned(tp, str);
1471  } else {
1472  width = 0;
1473  }
1474 
1475  cp = CreateDock(width);
1476  if(JLIKELY(cp)) {
1477  AddTrayComponent(tray, cp);
1478  }
1479 
1480 }
1481 
1483 void ParseSpacer(const TokenNode *tp, TrayType *tray) {
1484 
1485  TrayComponentType *cp;
1486  int width;
1487  int height;
1488  char *str;
1489 
1490  Assert(tp);
1491  Assert(tray);
1492 
1493  /* Get the width. */
1495  if(str) {
1496  width = ParseUnsigned(tp, str);
1497  } else {
1498  width = 0;
1499  }
1500 
1501  /* Get the height. */
1503  if(str) {
1504  height = ParseUnsigned(tp, str);
1505  } else {
1506  height = 0;
1507  }
1508 
1509  /* Create the spacer. */
1510  cp = CreateSpacer(width, height);
1511  if(JLIKELY(cp)) {
1512  AddTrayComponent(tray, cp);
1513  }
1514 
1515 }
1516 
1519 {
1520 
1521  const TokenNode *np;
1522 
1523  Assert(tp);
1524 
1525  for(np = tp->subnodeHead; np; np = np->next) {
1526  switch(np->type) {
1527  case TOK_OUTLINE:
1529  break;
1530  case TOK_FOREGROUND:
1532  break;
1533  case TOK_BACKGROUND:
1535  break;
1536  case TOK_ACTIVE:
1540  break;
1541  case TOK_FONT:
1542  SetFont(FONT_PAGER, np->value);
1543  break;
1544  case TOK_TEXT:
1546  break;
1547  default:
1549  break;
1550  }
1551  }
1552 
1553 }
1554 
1557 {
1558  const TokenNode *np;
1559  for(np = tp->subnodeHead; np; np = np->next) {
1560  switch(np->type) {
1561  case TOK_FOREGROUND:
1563  break;
1564  case TOK_BACKGROUND:
1566  break;
1567  case TOK_FONT:
1568  SetFont(FONT_CLOCK, np->value);
1569  break;
1570  default:
1572  break;
1573  }
1574  }
1575 }
1576 
1579 {
1580  static const StringMappingType enable_mapping[] = {
1581  { "button", POPUP_BUTTON },
1582  { "clock", POPUP_CLOCK },
1583  { "false", POPUP_NONE },
1584  { "menu", POPUP_MENU },
1585  { "pager", POPUP_PAGER },
1586  { "task", POPUP_TASK },
1587  { "true", POPUP_ALL }
1588  };
1589  const TokenNode *np;
1590  const char *str;
1591  char *tok;
1592 
1593  tok = FindAttribute(tp->attributes, "enabled");
1594  if(tok) {
1596  tok = strtok(tok, ",");
1597  while(tok) {
1598  const int x = FindValue(enable_mapping,
1599  ARRAY_LENGTH(enable_mapping), tok);
1600  if(JLIKELY(x >= 0)) {
1601  settings.popupMask |= x;
1602  } else {
1603  ParseError(tp, _("invalid value for 'enabled': \"%s\""), tok);
1604  }
1605  tok = strtok(NULL, ",");
1606  }
1607  }
1608 
1609  str = FindAttribute(tp->attributes, "delay");
1610  if(str) {
1611  settings.popupDelay = ParseUnsigned(tp, str);
1612  }
1613 
1614  for(np = tp->subnodeHead; np; np = np->next) {
1615  switch(np->type) {
1616  case TOK_FONT:
1617  SetFont(FONT_POPUP, np->value);
1618  break;
1619  case TOK_FOREGROUND:
1621  break;
1622  case TOK_BACKGROUND:
1624  break;
1625  case TOK_OUTLINE:
1627  break;
1628  default:
1630  break;
1631  }
1632  }
1633 
1634 }
1635 
1637 void ParseMenuStyle(const TokenNode *tp)
1638 {
1639  const TokenNode *np;
1640 
1642  for(np = tp->subnodeHead; np; np = np->next) {
1643  switch(np->type) {
1644  case TOK_FONT:
1645  SetFont(FONT_MENU, np->value);
1646  break;
1647  case TOK_FOREGROUND:
1649  break;
1650  case TOK_BACKGROUND:
1652  break;
1653  case TOK_ACTIVE:
1657  break;
1658  case TOK_OUTLINE:
1660  break;
1661  case TOK_OPACITY:
1663  break;
1664  default:
1666  break;
1667  }
1668  }
1669 
1670 }
1671 
1673 void ParseGroup(const TokenNode *tp)
1674 {
1675 
1676  const TokenNode *np;
1677  struct GroupType *group;
1678 
1679  Assert(tp);
1680 
1681  group = CreateGroup();
1682 
1683  for(np = tp->subnodeHead; np; np = np->next) {
1684  switch(np->type) {
1685  case TOK_CLASS:
1686  AddGroupClass(group, np->value);
1687  break;
1688  case TOK_NAME:
1689  AddGroupName(group, np->value);
1690  break;
1691  case TOK_OPTION:
1692  ParseGroupOption(np, group, np->value);
1693  break;
1694  default:
1695  InvalidTag(np, TOK_GROUP);
1696  break;
1697  }
1698  }
1699 
1700 }
1701 
1703 void ParseGroupOption(const TokenNode *tp, struct GroupType *group,
1704  const char *option)
1705 {
1706  int x;
1707 
1708  if(!option) {
1709  return;
1710  }
1711 
1712  /* Look up the option in the option map using binary search. */
1713  x = FindValue(OPTION_MAP, OPTION_MAP_COUNT, option);
1714  if(x >= 0) {
1715  AddGroupOption(group, (OptionType)x);
1716  return;
1717  }
1718 
1719  /* These options have arguments and so we handled them separately. */
1720  if(!strncmp(option, "layer:", 6)) {
1721  const WinLayerType layer = ParseLayer(tp, option + 6);
1722  AddGroupOptionUnsigned(group, OPTION_LAYER, layer);
1723  } else if(!strncmp(option, "desktop:", 8)) {
1724  const unsigned int desktop = (unsigned int)atoi(option + 8);
1725  AddGroupOptionUnsigned(group, OPTION_DESKTOP, desktop);
1726  } else if(!strncmp(option, "icon:", 5)) {
1727  AddGroupOptionString(group, OPTION_ICON, option + 5);
1728  } else if(!strncmp(option, "opacity:", 8)) {
1729  const unsigned int opacity = ParseOpacity(tp, option + 8);
1730  AddGroupOptionUnsigned(group, OPTION_OPACITY, opacity);
1731  } else {
1732  ParseError(tp, _("invalid Group Option: %s"), option);
1733  }
1734 
1735 }
1736 
1739 {
1740  const char *str = FindAttribute(tp->attributes, "decorations");
1741  if(str) {
1742  if(!strcmp(str, "motif")) {
1743  *deco = DECO_MOTIF;
1744  } else if(!strcmp(str, "flat")) {
1745  *deco = DECO_FLAT;
1746  } else {
1747  ParseError(tp, _("invalid decorations: %s"), str);
1748  }
1749  }
1750 }
1751 
1753 void ParseGradient(const char *value, ColorType a, ColorType b)
1754 {
1755 
1756  const char *sep;
1757  char *temp;
1758 
1759  /* Find the separator. */
1760  sep = strchr(value, ':');
1761 
1762  if(!sep) {
1763 
1764  /* Only one color given - use the same color for both. */
1765  SetColor(a, value);
1766  SetColor(b, value);
1767 
1768  } else {
1769 
1770  /* Two colors. */
1771 
1772  /* Get the first color. */
1773  int len = (int)(sep - value);
1774  temp = AllocateStack(len + 1);
1775  memcpy(temp, value, len);
1776  temp[len] = 0;
1777  SetColor(a, temp);
1778  ReleaseStack(temp);
1779 
1780  /* Get the second color. */
1781  len = strlen(sep + 1);
1782  temp = AllocateStack(len + 1);
1783  memcpy(temp, sep + 1, len);
1784  temp[len] = 0;
1785  SetColor(b, temp);
1786  ReleaseStack(temp);
1787 
1788  }
1789 
1790 }
1791 
1793 char *FindAttribute(AttributeNode *ap, const char *name)
1794 {
1795  while(ap) {
1796  if(!strcmp(name, ap->name)) {
1797  return ap->value;
1798  }
1799  ap = ap->next;
1800  }
1801  return NULL;
1802 }
1803 
1805 int ParseTokenValue(const StringMappingType *mapping, int count,
1806  const TokenNode *tp, int def)
1807 {
1808  if(JUNLIKELY(tp->value == NULL)) {
1809  ParseError(tp, _("%s is empty"), GetTokenName(tp));
1810  return def;
1811  } else {
1812  const int x = FindValue(mapping, count, tp->value);
1813  if(JLIKELY(x >= 0)) {
1814  return x;
1815  } else {
1816  ParseError(tp, _("invalid %s: \"%s\""), GetTokenName(tp), tp->value);
1817  return def;
1818  }
1819  }
1820 }
1821 
1823 int ParseAttribute(const StringMappingType *mapping, int count,
1824  const TokenNode *tp, const char *attr, int def)
1825 {
1826  const char *str = FindAttribute(tp->attributes, attr);
1827  if(str == NULL) {
1828  return def;
1829  } else {
1830  const int x = FindValue(mapping, count, str);
1831  if(JLIKELY(x >= 0)) {
1832  return x;
1833  } else {
1834  ParseError(tp, _("invalid value for %s: \"%s\""), attr, str);
1835  return def;
1836  }
1837  }
1838 }
1839 
1841 char *ReadFile(FILE *fd)
1842 {
1843  const int BLOCK_SIZE = 1024;
1844 
1845  char *buffer;
1846  int len, max;
1847 
1848  len = 0;
1849  max = BLOCK_SIZE;
1850  buffer = Allocate(max + 1);
1851 
1852  for(;;) {
1853  const size_t count = fread(&buffer[len], 1, max - len, fd);
1854  if(count == 0) {
1855  if(feof(fd)) {
1856  break;
1857  } else if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
1858  continue;
1859  } else {
1860  Warning(_("could not read file: %s"), strerror(errno));
1861  break;
1862  }
1863  }
1864  len += count;
1865  if(len == max) {
1866  max += BLOCK_SIZE;
1867  if(JUNLIKELY(max < 0)) {
1868  /* File is too big. */
1869  break;
1870  }
1871  buffer = Reallocate(buffer, max + 1);
1872  if(JUNLIKELY(buffer == NULL)) {
1873  FatalError(_("out of memory"));
1874  }
1875  }
1876  }
1877  buffer[len] = 0;
1878 
1879  return buffer;
1880 }
1881 
1883 TokenNode *TokenizeFile(const char *fileName)
1884 {
1885  struct stat sbuf;
1886  TokenNode *tokens;
1887  char *path;
1888  char *buffer;
1889 
1890  path = CopyString(fileName);
1891  ExpandPath(&path);
1892 
1893  int fd = open(path, O_RDONLY);
1894  Release(path);
1895 
1896  if(fd < 0) {
1897  return NULL;
1898  }
1899  if(JUNLIKELY(fstat(fd, &sbuf) == -1)) {
1900  close(fd);
1901  return NULL;
1902  }
1903  buffer = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
1904  if(JUNLIKELY(buffer == MAP_FAILED)) {
1905  close(fd);
1906  return NULL;
1907  }
1908  tokens = Tokenize(buffer, fileName);
1909  munmap(buffer, sbuf.st_size);
1910  close(fd);
1911  return tokens;
1912 }
1913 
1915 TokenNode *TokenizePipe(const char *command)
1916 {
1917  TokenNode *tokens;
1918  FILE *fp;
1919  char *path;
1920  char *buffer;
1921 
1922  path = CopyString(command);
1923  ExpandPath(&path);
1924 
1925  fp = popen(path, "r");
1926  Release(path);
1927 
1928  buffer = NULL;
1929  if(JLIKELY(fp)) {
1930  buffer = ReadFile(fp);
1931  pclose(fp);
1932  }
1933  if(JUNLIKELY(!buffer)) {
1934  return NULL;
1935  }
1936 
1937  tokens = Tokenize(buffer, command);
1938  Release(buffer);
1939  return tokens;
1940 }
1941 
1943 unsigned int ParseUnsigned(const TokenNode *tp, const char *str)
1944 {
1945  long value;
1946  if(JUNLIKELY(!str)) {
1947  ParseError(tp, _("no value specified"));
1948  return 0;
1949  }
1950  value = strtol(str, NULL, 0);
1951  if(JUNLIKELY(value < 0 || value > UINT_MAX)) {
1952  ParseError(tp, _("invalid setting: %s"), str);
1953  return 0;
1954  } else {
1955  return (unsigned int)value;
1956  }
1957 }
1958 
1960 unsigned int ParseOpacity(const TokenNode *tp, const char *str)
1961 {
1962  float value;
1963  if(JUNLIKELY(!str)) {
1964  ParseError(tp, _("no value specified"));
1965  return UINT_MAX;
1966  }
1967  value = ParseFloat(str);
1968  if(JUNLIKELY(value <= 0.0 || value > 1.0)) {
1969  ParseError(tp, _("invalid opacity: %s"), str);
1970  return UINT_MAX;
1971  } else if(value == 1.0) {
1972  return UINT_MAX;
1973  } else {
1974  return (unsigned int)(value * UINT_MAX);
1975  }
1976 }
1977 
1979 WinLayerType ParseLayer(const TokenNode *tp, const char *str)
1980 {
1981  static const StringMappingType mapping[] = {
1982  { "above", LAYER_ABOVE },
1983  { "below", LAYER_BELOW },
1984  { "normal", LAYER_NORMAL }
1985  };
1986  const int x = FindValue(mapping, ARRAY_LENGTH(mapping), str);
1987  if(JLIKELY(x >= 0)) {
1988  return x;
1989  } else {
1990  ParseError(tp, _("invalid layer: %s"), str);
1991  return LAYER_NORMAL;
1992  }
1993 }
1994 
1997 {
1998  static const StringMappingType mapping[] = {
1999  { "corner", SW_CORNER },
2000  { "off", SW_OFF },
2001  { "screen", SW_SCREEN },
2002  { "window", SW_WINDOW }
2003  };
2004  return ParseAttribute(mapping, ARRAY_LENGTH(mapping), tp,
2005  "coordinates", SW_SCREEN);
2006 }
2007 
2009 void InvalidTag(const TokenNode *tp, TokenType parent)
2010 {
2011  ParseError(tp, _("invalid tag in %s: %s"),
2012  GetTokenTypeName(parent), GetTokenName(tp));
2013 }
2014 
2016 void ParseError(const TokenNode *tp, const char *str, ...)
2017 {
2018 
2019  va_list ap;
2020 
2021  static const char FILE_MESSAGE[] = "%s[%u]";
2022 
2023  char *msg;
2024 
2025  va_start(ap, str);
2026 
2027  if(tp) {
2028  const size_t msg_len = sizeof(FILE_MESSAGE) + strlen(tp->fileName) + 1;
2029  msg = Allocate(msg_len);
2030  snprintf(msg, msg_len, FILE_MESSAGE, tp->fileName, tp->line);
2031  } else {
2032  msg = CopyString(_("configuration error"));
2033  }
2034 
2035  WarningVA(msg, str, ap);
2036 
2037  Release(msg);
2038 
2039  va_end(ap);
2040 
2041 }

joewing.net / Projects / JWM