X11workbench Toolkit  1.0
conf_help.c
Go to the documentation of this file.
1 // __ _ _ //
3 // ___ ___ _ __ / _| | |__ ___ | | _ __ ___ //
4 // / __|/ _ \ | '_ \ | |_ | '_ \ / _ \| || '_ \ / __| //
5 // | (__| (_) || | | || _| | | | || __/| || |_) |_| (__ //
6 // \___|\___/ |_| |_||_|_____|_| |_| \___||_|| .__/(_)\___| //
7 // |_____| |_| //
8 // //
9 // helper API for 'conf' files (global and per-user) //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2019 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17 
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  MIT-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  MIT-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 #include <memory.h>
60 #include <string.h>
61 #include <strings.h>
62 #include <errno.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <sys/param.h> // for MAXPATHLEN and PATH_MAX (also includes limits.h in some cases)
66 #include <fcntl.h>
67 #include <netinet/in.h> // for htonl, htons, etc.
68 #include <X11/Xlib.h>
69 #include "conf_help.h"
70 #include "file_help.h"
71 
72 #include "window_helper.h" // for debug stuff
73 #include "draw_text.h" // string-related stuff
74 
75 
76 // STRUCTURES
77 
78 typedef struct _CONF_FILE_
79 {
80  int iGlobal, iLocal; // handles for global and local (typically read-only on global)
81  int iOffsGlobal, iOffsLocal; // offset in struct to global and local conf file names
82 
83  char *pBuf; // a buffer for storing file names and other variable length data
84 
85  int cbBuf, cbBufMax; // current size and max size of 'pBuf'
86 
87  file_help_buf_t *pfhbL, *pfhbG; // local and global file help buffers
88 
89 } CONF_FILE;
90 
91 
92 // GLOBAL VARIABLES
93 
94 static const char * const *argv_copy = NULL;
95 static int argc_copy = 0;
96 
97 static CHXSettings *pXSettings = NULL;
98 
99 
100 // PROTOTYPES
101 
102 static void __settings_cleanup(void);
103 
104 
105 
106 // INLINE UTILITIES
107 
108 static __inline__ void trim_ends(const char **ppLeft, const char **ppRight)
109 {
110  const char *p1=*ppLeft, *p2 = *ppRight;
111  while(p1 < p2 && *p1 <= ' ')
112  {
113  p1++;
114  }
115  while(p2 > p1 && *(p2 - 1) <= ' ')
116  {
117  p2--;
118  }
119 
120  *ppLeft = p1;
121  *ppRight = p2;
122 }
123 
124 static __inline__ char * DoMakePath(char *pSrc, const char *szPath, const char *szName, const char *szExt)
125 {
126  const char *p1;
127  char *pRval = pSrc;
128 
129  for(p1=szPath; *p1; p1++)
130  {
131  *(pRval++) = *p1;
132  }
133 
134  if(*szPath && *(p1 - 1) != '/')
135  {
136  *(pRval++) = '/'; // add a slash to the end of the path
137  }
138 
139  for(p1=szName; *p1; p1++)
140  {
141  *(pRval++) = *p1;
142  }
143 
144  for(p1=szExt; *p1; p1++)
145  {
146  *(pRval++) = *p1;
147  }
148 
149  *(pRval++) = 0;
150  *pRval = 0;
151 
152  return pRval;
153 }
154 
155 
156 // initialization and termination
157 
158 void CHRegisterArgs(int argc, char **argv)
159 {
160  argc_copy = argc;
161  argv_copy = (const char * const *)argv;
162 
163 // CHSettingsRefresh(); // let window_helper do this
164 }
165 
166 void CHOnExit(void)
167 {
168  // TODO: uninitialization
169 
170  __settings_cleanup();
171 }
172 
173 
174 
175 // atoms for querying settings manager
176 // for description on usage and format, see
177 // http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html
178 // selection atom: _XSETTINGS_S# where '#' is the screen ID formatted as %d
179 // xsettings atom: _XSETTINGS_SETTINGS
180 // manager atom: MANAGER
181 // use 'XInternAtom(display, szNAME, False)' to create an atom
182 
183 // default GTK style colors:
184 //#define GTK_GRAY 0xdcdc, 0xdada, 0xd5d5
185 //#define GTK_DARK_GRAY 0xc4c4, 0xc2c2, 0xbdbd
186 //#define GTK_LIGHT_GRAY 0xeeee, 0xebeb, 0xe7e7
187 //#define GTK_WHITE 0xffff, 0xffff, 0xffff
188 //#define GTK_BLUE 0x4b4b, 0x6969, 0x8383
189 //#define GTK_VERY_DARK_GRAY 0x9c9c, 0x9a9a, 0x9494
190 //#define GTK_BLACK 0x0000, 0x0000, 0x0000
191 //#define GTK_WEAK_GRAY 0x7530, 0x7530, 0x7530
192 
193 // NOTE: use 'xrdb -query -all' as a hint of what the resource database has in it
194 // use XScreenResourceString and XResourceManagerString to get the data
195 // (it will need parsing)
196 // ALSO see docs on 'listres' for 'widget-related' resource information
197 
198 
199 
200 // OLD version - uses XResourceManagerSring and XScreenResourceString (deprecated)
201 // this version typically won't return anything useful any more.
202 
203 int CHGetResourceString_Old(Display *pDisplay, const char *szIdentifier, char *szData, int cbData)
204 {
205 char *pData;
206 Screen *pScreen;
207 const char *p1, *p2, *p3;
208 int iRval = -1, iLen;
209 
210  iLen = strlen(szIdentifier);
211 
212  if(!iLen)
213  {
214  return -1;
215  }
216 
217  pScreen = XDefaultScreenOfDisplay((Display *)pDisplay);
218  // was (Screen *)XDefaultScreen((Display *)pDisplay);
219 
220  if(pScreen)
221  {
222  pData = XScreenResourceString(pScreen);
223  }
224  else
225  {
226  pData = NULL;
227  }
228 
229  if(!pData)
230  {
231  pData = XResourceManagerString((Display *)pDisplay);
232  }
233 
234  if(!pData)
235  {
236  return -1;
237  }
238 
239  p1 = pData;
240  while(*p1)
241  {
242  p2 = p1;
243  while(*p1 && *p1 != '\n')
244  {
245  p1++;
246  }
247 
248  p3 = p2;
249  while(p3 < p1 && *p3 != ':')
250  {
251  p3++;
252  }
253 
254  if(*p3 == ':')
255  {
256  if(iLen == (p3 - p2) &&
257  !strncasecmp(p2, szIdentifier, iLen))
258  {
259  p3++;
260  while(p3 < p1 && *p3 <= ' ')
261  {
262  p3++;
263  }
264  iRval = cbData - 1;
265  if((p1 - p3) < (cbData - 1))
266  {
267  iRval = p1 - p3;
268  }
269  memcpy(szData, p3, iRval);
270  szData[iRval] = 0;
271  return iRval;
272  }
273  }
274 
275  while(*p1 && *p1 <= ' ')
276  {
277  p1++;
278  }
279  }
280 
281  // pass 2 - inexact matches
282 
283  if(szIdentifier[0] != '*' || !szIdentifier[1])
284  {
285  return -1;
286  }
287 
288  p1 = pData;
289  while(*p1)
290  {
291  p2 = p1;
292  while(*p1 && *p1 != '\n')
293  {
294  p1++;
295  }
296 
297  p3 = p2;
298  while(p3 < p1 && *p3 != ':')
299  {
300  p3++;
301  }
302 
303  if(*p3 == ':')
304  {
305  if((p2 + iLen - 1) <= p3)
306  {
307  if(!strncasecmp(p3 - iLen + 1, szIdentifier + 1, iLen - 1) &&
308  (p2 == (p3 - iLen + 1) || *(p3 - iLen) == '.'))
309  {
310  p3++;
311  while(p3 < p1 && *p3 <= ' ')
312  {
313  p3++;
314  }
315  iRval = cbData - 1;
316  if((p1 - p3) < (cbData - 1))
317  {
318  iRval = p1 - p3;
319  }
320  memcpy(szData, p3, iRval);
321  szData[iRval] = 0;
322  break;
323  }
324  }
325  }
326 
327  while(*p1 && *p1 <= ' ')
328  p1++;
329  }
330 
331  return iRval;
332 }
333 
334 
335 static const char * TranslateColorRequest(const char *szIdentifier)
336 {
337 int iLen;
338 
339  if(szIdentifier[0] != '*')
340  {
341  // is it one of the color names I might return?
342 
343  if(!strcmp(szIdentifier, "fg_color") ||
344  !strcmp(szIdentifier, "bg_color") ||
345  !strcmp(szIdentifier, "text_color") ||
346  !strcmp(szIdentifier, "base_color") ||
347  !strcmp(szIdentifier, "selected_fg_color") ||
348  !strcmp(szIdentifier, "selected_bg_color") ||
349  !strcmp(szIdentifier, "tooltip_fg_color") ||
350  !strcmp(szIdentifier, "tooltip_bg_color"))
351  {
352 // WB_ERROR_PRINT("TEMPORARY: %s - returning %s 'pass through'\n", __FUNCTION__, szIdentifier);
353  return szIdentifier;
354  }
355 
356  // TODO: any OTHER pre-qualifiers? or is returning 'NULL' correct?
357  // perhaps having the suffix '_color' ?
358 
359  return NULL;
360  }
361 
362  // SPECIAL entries come first
363 
364  // menu colors slightly different
365 
366  if(!strcmp(szIdentifier, "*Menu.activeForeground"))
367  {
368  return "selected_fg_color";
369  }
370 
371  if(!strcmp(szIdentifier, "*Menu.activeBackground"))
372  {
373  return "selected_bg_color";
374  }
375 
376  // and text box and list box use 'input' colors
377 
378  if(!strcmp(szIdentifier, "*Text.foreground") ||
379  !strcmp(szIdentifier, "*List.foreground") ||
380  !strcmp(szIdentifier, "*Combo.foreground"))
381  {
382  return "text_color";
383  }
384 
385  if(!strcmp(szIdentifier, "*Text.background") ||
386  !strcmp(szIdentifier, "*List.background") ||
387  !strcmp(szIdentifier, "*Combo.background"))
388  {
389  return "base_color";
390  }
391 
392 
393  // and then the (other) generic ones
394 
395  iLen = strlen(szIdentifier);
396 
397  if(iLen >= 11 &&
398  (iLen == 11 || szIdentifier[iLen - 11] == '.') &&
399  !strcmp(szIdentifier + iLen - 10, "foreground"))
400  {
401  return "fg_color";
402  }
403 
404  if(iLen >= 11 &&
405  (iLen == 11 || szIdentifier[iLen - 11] == '.') &&
406  !strcmp(szIdentifier + iLen - 10, "background"))
407  {
408  return "bg_color";
409  }
410 
411  if(iLen >= 7 &&
412  (iLen == 7 || szIdentifier[iLen - 7] == '.') &&
413  !strcmp(szIdentifier + iLen - 6, "border"))
414  {
415  return "fg_color"; // TODO: return NULL instead?
416  }
417 
418  if(iLen >= 12 &&
419  (iLen == 12 || szIdentifier[iLen - 12] == '.') &&
420  !strcmp(szIdentifier + iLen - 11, "borderColor"))
421  {
422  return "fg_color"; // TODO: return NULL instead? or actual color?
423  }
424 
425  if(iLen >= 17 &&
426  (iLen == 17 || szIdentifier[iLen - 17] == '.') &&
427  !strcmp(szIdentifier + iLen - 16, "activeForeground"))
428  {
429  return "text_color"; // instead of 'fg_color'
430  }
431 
432  if(iLen >= 17 &&
433  (iLen == 17 || szIdentifier[iLen - 17] == '.') &&
434  !strcmp(szIdentifier + iLen - 16, "activeBackground"))
435  {
436  return "base_color"; // "bg_color"; - TODO: derive this maybe?
437  }
438 
439  if(iLen >= 17 &&
440  (iLen == 17 || szIdentifier[iLen - 17] == '.') &&
441  !strcmp(szIdentifier + iLen - 16, "selectForeground"))
442  {
443  return "selected_fg_color";
444  }
445 
446  if(iLen >= 17 &&
447  (iLen == 17 || szIdentifier[iLen - 17] == '.') &&
448  !strcmp(szIdentifier + iLen - 16, "selectBackground"))
449  {
450  return "selected_bg_color";
451  }
452 
453  if(iLen >= 20 &&
454  (iLen == 20 || szIdentifier[iLen - 20] == '.') &&
455  !strcmp(szIdentifier + iLen - 19, "highlightForeground"))
456  {
457  return "selected_fg_color";
458  }
459 
460  if(iLen >= 20 &&
461  (iLen == 20 || szIdentifier[iLen - 20] == '.') &&
462  !strcmp(szIdentifier + iLen - 19, "highlightBackground"))
463  {
464  return "selected_bg_color";
465  }
466 
467  // TODO: others (like tooltips) - might return text_color, base_color, tooltip_fg_color, tooltip_bg_color
468 
469 
470  return NULL; // not recognized
471 }
472 
473 
474 int CHGetResourceString(Display *pDisplay, const char *szIdentifier, char *szData, int cbData)
475 {
476 int iRval = -1;//, iFormat;
477 
478  // TODO: use XSETTINGS first, then resource manager
479  // TODO: see listres and appres and the 'X TOOLKIT' resource management system
480 
481 // gnome config may have more useful information, see
482 // http://developer.gnome.org/integration-guide/stable/thumbnailer.html.en (thumbnailers)
483 //
484 // main menu integration - /usr/[local/]share/applications and ~/.local/share/applications
485 // http://developer.gnome.org/integration-guide/stable/desktop-files.html.en
486 //
487 // and startup notification
488 // http://developer.gnome.org/integration-guide/stable/startup-notification.html.en
489 // http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
490 
491  if(WB_LIKELY(pXSettings))
492  {
493  const CHXSetting *pXS;
494 
495  // mapping the string accordingly
496 
497  pXS = CHGetXSetting(pXSettings->pDisplay, szIdentifier); // exact match.
498 
499  if(!pXS)
500  {
501  // color schemes are stored hierarchically - look in Gtk/ColorScheme for now
502  // TODO: this is apparently deprecated; find how to do this properly.
503  // https://wiki.gnome.org/Attic/GnomeArt/Tutorials/GtkThemes/SymbolicColors
504  // (some info here) https://unix.stackexchange.com/questions/365607/overriding-gtk3-theme-colors
505  // https://developer.gnome.org/gtk3/stable/GtkSettings.html
506  // (some info here) https://forum.xfce.org/viewtopic.php?id=11947
507 
508 
509  const char *pTemp = TranslateColorRequest(szIdentifier);
510 
511  if(pTemp && *pTemp)
512  {
513 // WB_ERROR_PRINT("TEMPORARY: %s - found color %s from %s\n", __FUNCTION__, pTemp, szIdentifier);
514 
515  pXS = CHGetXSetting(pXSettings->pDisplay, "Gtk/ColorScheme"); // get color scheme info
516 
517  if(pXS && pXS->iType == XSettingsTypeString)
518  {
519  int iLen0 = strlen(pTemp);
520  const char *p1 = pXS->uData.szData;
521  // format is "colorname=#colordef\n"
522 
523  while(*p1)
524  {
525  const char *p2 = p1;
526 
527  p1 = p2;
528 
529  while(*p1 && *p1 != '\n')
530  {
531  p1++;
532  }
533 
534 // WB_ERROR_PRINT("TEMPORARY: %s - %-.*s\n", __FUNCTION__, p1 - p2, p2);
535 
536  if(!memcmp(p2, pTemp, iLen0) && p2[iLen0] == ':')
537  {
538  p2 = p2 + iLen0 + 1;
539  iLen0 = p1 - p2;
540 
541  if(iLen0 >= cbData)
542  {
543  iLen0 = cbData - 1;
544  }
545  if(iLen0 > 0)
546  {
547  memcpy(szData, p2, iLen0);
548  }
549 
550  szData[iLen0] = 0;
551  return iLen0;
552  }
553 
554  if(*p1 == '\n')
555  {
556  p1++;
557  }
558  }
559  }
560 
561  pXS = NULL; // not found if it gets here
562  }
563  }
564 
565  if(pXS)
566  {
567  if(pXS->iType == XSettingsTypeInteger)
568  {
569  iRval = snprintf(szData, cbData, "%d", pXS->uData.iData);
570  }
571  else if(pXS->iType == XSettingsTypeString)
572  {
573  iRval = strlen(pXS->uData.szData);
574 
575  if(iRval >= cbData)
576  {
577  iRval = cbData - 1;
578  }
579 
580  if(iRval > 0)
581  {
582  memcpy(szData, pXS->uData.szData, iRval);
583  }
584 
585  szData[iRval] = 0;
586  }
587  else
588  {
589  iRval = -1; // for now... (later try and fix this)
590  }
591  }
592 
593 // iRval = something
594  }
595 
596  if(iRval < 0) // i.e. "not found"
597  {
598  return CHGetResourceString_Old(pDisplay, szIdentifier, szData, cbData); // fallback
599  }
600 
601  return iRval;
602 }
603 
604 int CHGetResourceInt(Display *pDisplay, const char *szIdentifier)
605 {
606  int iLen;
607  char tbuf[64];
608 
609  if((iLen = CHGetResourceString(pDisplay, szIdentifier, tbuf, sizeof(tbuf) - 1)) > 0)
610  {
611  tbuf[iLen] = 0;
612  while(tbuf[0] && tbuf[0] <= ' ')
613  {
614  strcpy(tbuf, tbuf + 1);
615  }
616 
617  if(tbuf[0])
618  {
619  return atoi(tbuf);
620  }
621  }
622 
623  return 0;
624 }
625 
626 
627 
628 // argc/argv utilities (assigned via initialization)
629 
631 {
632  return argc_copy;
633 }
634 
635 const char * const *CHGetArgV()
636 {
637  return argv_copy;
638 }
639 
640 
641 // configuration file utilities
642 
643 void * CHOpenConfFile(const char *szAppName, int iFlags)
644 {
645  CONF_FILE *pRval;
646  char *p1, *p2, *p3, *p4, /* *p5, */ *p6;
647 // int i1;
648  struct stat st;
649 // file_help_buf_t *pFHB = NULL;
650 
651  static const char szGlobalPath[] = GLOBAL_PATH;
652  static const char szGlobalXPath[] = GLOBAL_XPATH;
653  static const char szLocalPath[] = LOCAL_PATH;
654  static const char szConf[] = ".conf";
655  char szLocalPath0[PATH_MAX];
656 
657 
658  // create struct
659 
660  pRval = (CONF_FILE *)WBAlloc(sizeof(*pRval) + strlen(szAppName) * 4 + sizeof(szGlobalPath)
661  + sizeof(szGlobalXPath) + sizeof(szLocalPath) + 16 + PATH_MAX * 2);
662  if(!pRval)
663  {
664  return NULL;
665  }
666 
667  bzero(pRval, sizeof(*pRval)); // make sure it's zero'd out
668 
669  // construct file names
670 
671  p1 = (char *)pRval + sizeof(*pRval); // this is the start of string buffers
672  // it is also the global path, by default either /etc or /usr/local/etc
673 
674  p2 = DoMakePath(p1, szGlobalPath, szAppName, szConf);
675  p3 = DoMakePath(p2, szGlobalXPath, szAppName, szConf);
676 
677  if(!(iFlags & CH_FLAGS_GLOBAL)) // not "global only"
678  {
679  // NOTE: the new standard for config files is:
680  //
681  // ~/.config/application/whatever
682  // ~/.local/share/application/whatever
683  //
684  // the old standard was ~/.application/whatever
685  //
686  // TODO: a utility to move old files to new location? yeah probably not...
687 
688 
689  strcpy(szLocalPath0, szLocalPath); // ~/.local/share/
690  if(!szLocalPath0[0] || szLocalPath0[strlen(szLocalPath0)-1] != '/') // unlikely
691  {
692  strcat(szLocalPath0, "/");
693  }
694 // strcat(szLocalPath0, "."); don't prepend a dot any more
695  strncat(szLocalPath0, szAppName, sizeof(szLocalPath0) - strlen(szLocalPath0) - 1); // now contains ~/.local/share/appname
696 
697  // make sure the directory 'szLocalPath0' exists
698  p4 = WBGetCanonicalPath(szLocalPath0);
699  if(p4)
700  {
701  if(!WBIsDirectory(p4)) // see if it exists first...
702  {
703  WBMkDir(p4, 0755); // TODO: check user's UMASK ???
704  }
705  strncpy(szLocalPath0, p4, sizeof(szLocalPath0)); // the canonical path
706  WBFree(p4);
707  }
708 
709  if(szLocalPath0[strlen(szLocalPath0) - 1] != '/')
710  {
711  strcat(szLocalPath0, "/");
712  }
713 
714  p4 = DoMakePath(p3, szLocalPath0, LOCAL_CONF_NAME, szConf); // first THIS one
715 
716  p6 = WBGetCanonicalPath(szLocalPath);
717  if(p6)
718  {
719 // NOTE: p5 not being used; commented out because of linux gcc warnings
720 // p5 = DoMakePath(p4, p6, szAppName, szConf); // then THIS one
721  WBFree(p6);
722  }
723  else
724  {
725 // NOTE: p5 not being used; commented out because of linux gcc warnings
726 // p5 = DoMakePath(p4, szLocalPath, szAppName, szConf); // alternate (uncanonical) name
727  }
728  }
729  else
730  {
731  p4 = NULL; // warning avoidance (uninitialized variable, actually won't matter)
732  }
733 
734  if(!stat(p1, &st) && S_ISREG(st.st_mode))
735  {
736  if(iFlags & CH_FLAGS_WRITE)
737  {
738  pRval->iGlobal = open(p1,O_RDWR);
739  }
740  else
741  {
742  pRval->iGlobal = -1;
743  }
744 
745  if(pRval->iGlobal == -1)
746  {
747  pRval->iGlobal = open(p1,O_RDONLY);
748  }
749  }
750 
751  if(pRval->iGlobal != -1)
752  {
753  pRval->iOffsGlobal = (int)(p1 - (char *)pRval);
754  }
755  else if(!stat(p2, &st) && S_ISREG(st.st_mode))
756  {
757  if(iFlags & CH_FLAGS_WRITE)
758  {
759  pRval->iGlobal = open(p2,O_RDWR);
760  }
761  else
762  {
763  pRval->iGlobal = -1;
764  }
765  if(pRval->iGlobal == -1)
766  {
767  pRval->iGlobal = open(p2,O_RDONLY);
768  }
769  if(pRval->iGlobal != -1)
770  {
771  pRval->iOffsGlobal = (int)(p2 - (char *)pRval);
772  }
773  }
774 
775  if(!(iFlags & CH_FLAGS_GLOBAL)) // not "global only"
776  {
777  // if local file does not exist, create it (always open read/write)
778 
779  if(!stat(p3, &st) && S_ISREG(st.st_mode))
780  {
781  pRval->iLocal = open(p3,O_RDWR);
782  }
783  else
784  {
785  pRval->iLocal = open(p3,O_CREAT|O_RDWR,0644); // use 0644 for now - TODO: check umask
786  }
787 
788  if(pRval->iLocal != -1)
789  {
790  pRval->iOffsLocal = (int)(p3 - (char *)pRval);
791  }
792  else // try the OTHER local
793  {
794  // if local file does not exist, create it (always open read/write)
795 
796  if(!stat(p4, &st) && S_ISREG(st.st_mode))
797  {
798  pRval->iLocal = open(p4,O_RDWR);
799  }
800  else
801  {
802  pRval->iLocal = open(p4,O_CREAT|O_RDWR,0644); // use 0644 for now - TODO: check umask
803  }
804 
805  if(pRval->iLocal != -1)
806  {
807  pRval->iOffsLocal = (int)(p4 - (char *)pRval);
808  }
809  }
810  }
811  else // GLOBAL ONLY
812  {
813  pRval->iLocal = -1;
814  pRval->iOffsLocal = 0;
815  }
816 
817  // next read and parse the files
818 
819  if(pRval->iLocal != -1)
820  {
821  pRval->pfhbL = FBGetFileBufViaHandle(pRval->iLocal);
822  if(pRval->pfhbL)
823  {
824  FBParseFileBuf(pRval->pfhbL);
825  }
826  else
827  {
828  CHDestroyConfFile(pRval);
829  return NULL;
830  }
831  }
832 
833  if(pRval->iGlobal != -1)
834  {
835  pRval->pfhbG = FBGetFileBufViaHandle(pRval->iGlobal);
836  if(pRval->pfhbG)
837  {
838  FBParseFileBuf(pRval->pfhbG);
839  }
840  else
841  {
842  CHDestroyConfFile(pRval);
843  return NULL;
844  }
845  }
846 
847  return pRval;
848 }
849 
850 void CHCloseConfFile(void * pFile)
851 {
852  CONF_FILE *pTemp = (CONF_FILE *)pFile;
853 
854  if(!pTemp)
855  {
856  return;
857  }
858 
859  if(pTemp->iLocal != -1)
860  {
861  if(pTemp->pfhbL && FBIsFileBufDirty(pTemp->pfhbL))
862  {
863  FBWriteFileBufHandle(pTemp->iLocal, pTemp->pfhbL);
864  }
865 
866  close(pTemp->iLocal);
867  pTemp->iLocal = -1;
868  }
869 
870  if(pTemp->iGlobal != -1)
871  {
872  if(pTemp->pfhbG && FBIsFileBufDirty(pTemp->pfhbG))
873  {
874  FBWriteFileBufHandle(pTemp->iGlobal, pTemp->pfhbG);
875  }
876 
877  close(pTemp->iGlobal);
878  pTemp->iGlobal = -1;
879  }
880 }
881 
882 void CHDestroyConfFile(void * pFile)
883 {
884  CONF_FILE *pTemp = (CONF_FILE *)pFile;
885 
886  if(!pTemp)
887  {
888  return;
889  }
890 
891  CHCloseConfFile(pFile);
892 
893  if(pTemp->pfhbL)
894  {
895  FBDestroyFileBuf(pTemp->pfhbL);
896  }
897 
898  if(pTemp->pfhbG)
899  {
900  FBDestroyFileBuf(pTemp->pfhbG);
901  }
902 }
903 
904 
905 // Utilities to find stuff within a config file
906 // on entry ppStart points to the beginning of the line, and ppEnd points to the end of it
907 // on return, ppStart points to the start of the data, ppEnd points to the end of it (excluding comments)
908 
909 static void __get_line_strip_comments__(const char **ppStart, const char **ppEnd)
910 {
911  const char *p1 = *ppStart, *p2 = *ppEnd;
912 
913  // skip leading white space
914  while(p1 < p2 && *p1 <= ' ')
915  {
916  p1++;
917  }
918  if(p1 >= p2)
919  {
920  *ppStart = *ppEnd = p2;
921 
922  return;
923  }
924 
925  *ppStart = p1;
926 
927  while(p1 < p2)
928  {
929  if(*p1 == '=') // everything to the right of '=' isn't a comment
930  {
931  break;
932  }
933 
934  if(*p1 == ';') // comment?
935  {
936  p2 = p1;
937  break;
938  }
939 
940  p1++;
941  }
942 
943  // now trim off any trailing white space
944  p1 = *ppStart;
945  while(p2 > p1 && *(p2 - 1) <= ' ')
946  {
947  p2--;
948  }
949 
950  *ppEnd = p2;
951 }
952 
953 static void __find_section__(void *hFile, const char *szSection,
954  const char **ppSection, const char **ppEndSection)
955 {
956  int i1, /* i2,*/ iSectionLen;
957  const char *p1, *p2, *pSection, *pEndSection;
958  CONF_FILE *pTemp = (CONF_FILE *)hFile;
959 
960  *ppSection = pSection = NULL;
961  *ppEndSection = pEndSection = NULL;
962 
963  if(!pTemp ||
964  ((!pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf) &&
965  (!pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)))
966  {
967  return;
968  }
969 
970  iSectionLen = strlen(szSection);
971 
972  if(pTemp->pfhbL)
973  {
974  for(i1=0; i1 < pTemp->pfhbL->lLineCount; i1++)
975  {
976  // search for the section header
977  p1 = pTemp->pfhbL->ppLineBuf[i1];
978  p2 = pTemp->pfhbL->ppLineBuf[i1 + 1];
979  if(!p2)
980  {
981  p2 = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
982  }
983 
984  __get_line_strip_comments__(&p1, &p2);
985 
986  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
987  {
988  p1++;
989  p2--;
990  trim_ends(&p1, &p2);
991  if((p2 - p1) == iSectionLen && !strncasecmp(p1, szSection, iSectionLen))
992  {
993  pSection = pTemp->pfhbL->ppLineBuf[i1 + 1];
994  break;
995  }
996  }
997  }
998 
999  if(pSection)
1000  {
1001  // starting with the current position, keep going until I find another section
1002 
1003  for(i1++; i1 < pTemp->pfhbL->lLineCount; i1++)
1004  {
1005  // search for the section header
1006  p1 = pTemp->pfhbL->ppLineBuf[i1];
1007  p2 = pTemp->pfhbL->ppLineBuf[i1 + 1];
1008  if(!p2)
1009  {
1010  p2 = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
1011  }
1012 
1013  __get_line_strip_comments__(&p1, &p2);
1014 
1015  if((p2 - p1) > 2 && *p1 == '[' && *(p2 - 1) == ']')
1016  {
1017  pEndSection = pTemp->pfhbL->ppLineBuf[i1];
1018  break;
1019  }
1020  }
1021 
1022  if(!pEndSection)
1023  {
1024  pEndSection = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
1025  }
1026 
1027  // return values
1028  *ppSection = pSection;
1029  *ppEndSection = pEndSection;
1030  }
1031  }
1032 
1033  if(pTemp->pfhbG && !*ppSection) // not found yet
1034  {
1035  *ppSection = pSection = NULL; // make sure
1036  *ppEndSection = pEndSection = NULL;
1037 
1038  for(i1=0; i1 < pTemp->pfhbG->lLineCount; i1++)
1039  {
1040  // search for the section header
1041  p1 = pTemp->pfhbG->ppLineBuf[i1];
1042  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1043  if(!p2)
1044  {
1045  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1046  }
1047 
1048  __get_line_strip_comments__(&p1, &p2);
1049 
1050  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
1051  {
1052  p1++;
1053  p2--;
1054  trim_ends(&p1, &p2);
1055  if((p2 - p1) == iSectionLen && !strncasecmp(p1, szSection, iSectionLen))
1056  {
1057  pSection = pTemp->pfhbG->ppLineBuf[i1 + 1];
1058  break;
1059  }
1060  }
1061  }
1062 
1063  if(!pSection)
1064  {
1065 // fprintf(stderr, "pSection is NULL\n");
1066  return;
1067  }
1068 
1069  // starting with the current position, keep going until I find another section
1070 
1071  for(i1++; i1 < pTemp->pfhbG->lLineCount; i1++)
1072  {
1073  // search for the section header
1074  p1 = pTemp->pfhbG->ppLineBuf[i1];
1075  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1076  if(!p2)
1077  {
1078  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1079  }
1080 
1081  __get_line_strip_comments__(&p1, &p2);
1082 
1083  if((p2 - p1) > 2 && *p1 == '[' && *(p2 - 1) == ']')
1084  {
1085  pEndSection = pTemp->pfhbG->ppLineBuf[i1];
1086  break;
1087  }
1088  }
1089 
1090  if(!pEndSection)
1091  {
1092  pEndSection = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1093  }
1094 
1095  // return values
1096  *ppSection = pSection;
1097  *ppEndSection = pEndSection;
1098  }
1099 
1100 // WB_ERROR_PRINT("TEMPORARY: %s \"%s\" section %d:\n", __FUNCTION__, szSection, (int)(pEndSection - pSection));
1101 // WB_ERROR_PRINT("%-.*s\n------------------------\n", (int)(pEndSection - pSection), pSection);
1102 
1103  fflush(stderr);
1104 }
1105 
1106 #if 0 // RESERVED (not currently used)
1107 
1108 static void __find_global_section__(void *hFile, const char *szSection,
1109  const char **ppSection, const char **ppEndSection)
1110 {
1111  int i1, /* i2,*/ iSectionLen;
1112  const char *p1, *p2, *pSection, *pEndSection;
1113  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1114 
1115  *ppSection = pSection = NULL;
1116  *ppEndSection = pEndSection = NULL;
1117 
1118  if(!pTemp || !pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)
1119  {
1120  return;
1121  }
1122 
1123  iSectionLen = strlen(szSection);
1124 
1125  for(i1=0; i1 < pTemp->pfhbG->lLineCount; i1++)
1126  {
1127  // search for the section header
1128  p1 = pTemp->pfhbG->ppLineBuf[i1];
1129  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1130  if(!p2)
1131  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1132 
1133  __get_line_strip_comments__(&p1, &p2);
1134 
1135  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
1136  {
1137  p1++;
1138  p2--;
1139  trim_ends(&p1, &p2);
1140  if((p2 - p1) == iSectionLen && !strncasecmp(p1, szSection, iSectionLen))
1141  {
1142  pSection = pTemp->pfhbG->ppLineBuf[i1 + 1];
1143  break;
1144  }
1145  }
1146  }
1147 
1148  if(!pSection)
1149  return;
1150 
1151  // starting with the current position, keep going until I find another section
1152 
1153  for(; i1 < pTemp->pfhbG->lLineCount; i1++)
1154  {
1155  // search for the section header
1156  p1 = pTemp->pfhbG->ppLineBuf[i1];
1157  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1158  if(!p2)
1159  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1160 
1161  __get_line_strip_comments__(&p1, &p2);
1162 
1163  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
1164  {
1165  pEndSection = pTemp->pfhbG->ppLineBuf[i1];
1166  break;
1167  }
1168  }
1169 
1170  if(!pEndSection)
1171  pEndSection = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1172 
1173  // return values
1174  *ppSection = pSection;
1175  *ppEndSection = pEndSection;
1176 }
1177 
1178 #endif // 0
1179 
1180 
1181 static int __enum_conf_file_sections__(void *hFile, char *szData, int cbData)
1182 {
1183  int i1, i2;
1184  const char *p1, *p2;
1185  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1186 
1187  if(!pTemp || !pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf)
1188  {
1189  // TODO: check globals also
1190  return -1;
1191  }
1192 
1193  for(i1=0, i2=0; i1 < pTemp->pfhbL->lLineCount; i1++)
1194  {
1195  // search for the section header
1196  p1 = pTemp->pfhbL->ppLineBuf[i1];
1197  p2 = pTemp->pfhbL->ppLineBuf[i1 + 1];
1198  if(!p2)
1199  {
1200  p2 = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
1201  }
1202 
1203  __get_line_strip_comments__(&p1, &p2);
1204 
1205 // WB_ERROR_PRINT("TEMPORARY: %s \"%-.*s\"\n", __FUNCTION__, (int)(p2 - p1), p1);
1206 
1207  if((p2 - p1) >= 2 && *p1 == '[' && *(p2 - 1) == ']')
1208  {
1209  p1++;
1210  p2--;
1211  trim_ends(&p1, &p2);
1212  if(p2 > p1)
1213  {
1214  i2 += (p2 - p1) + 1; // calculate additional space needed (always)
1215  if(cbData >= (p2 - p1) + 2)
1216  {
1217  // add the text for the section header to 'szData' if there's room for it
1218  memcpy(szData, p1, p2 - p1);
1219  szData[p2 - p1] = 0;
1220  szData[p2 - p1 + 1] = 0;
1221 
1222  szData += (p2 - p1) + 1;
1223  cbData -= (p2 - p1) + 1;
1224  }
1225  else
1226  {
1227  cbData = 0; // because there's no more room
1228  }
1229  }
1230  }
1231  }
1232 
1233  // TODO: enumerate global sections also
1234 
1235  i2++; // always need room for 1 more
1236  return i2;
1237 }
1238 
1239 
1240 // Utilities to query and assign values within a config file
1241 
1242 int CHGetConfFileString(void * hFile, const char *szSection,
1243  const char *szIdentifier, char *szData, int cbData)
1244 {
1245  int i1, i2, iIdentifierLen;
1246  const char *p1, *p2, *pSection, *pEndSection;
1247  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1248  file_help_buf_t *pFHB = NULL;
1249 
1250 
1251  if(!pTemp || ((!pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf)
1252  && (!pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)))
1253  {
1254  if(!pTemp)
1255  {
1256  WB_ERROR_PRINT("%s - hFile/pTemp is NULL\n", __FUNCTION__);
1257  }
1258  else
1259  {
1260  WB_ERROR_PRINT("%s - 'linebuf' problem - %p %p %p %p\n", __FUNCTION__,
1261  pTemp->pfhbL, pTemp->pfhbL->ppLineBuf,
1262  pTemp->pfhbG, pTemp->pfhbG->ppLineBuf);
1263  }
1264 
1265  return -1;
1266  }
1267 
1268  if(!szSection || !*szSection) // empty section == get a list of all of them (ignore szIdentifier)
1269  {
1270  return __enum_conf_file_sections__(hFile, szData, cbData);
1271  }
1272 
1273  iIdentifierLen = strlen(szIdentifier);
1274 
1275  __find_section__(hFile, szSection, &pSection, &pEndSection);
1276  if(!pSection)
1277  {
1278  WB_ERROR_PRINT("TEMPORARY: %s - did not find section \"%s\"\n", __FUNCTION__, szSection);
1279  return -1;
1280  }
1281 
1282  // search for 'szIdentifier string' followed by '='
1283 
1284  if(pTemp->pfhbL &&
1285  pTemp->pfhbL->ppLineBuf &&
1286  pTemp->pfhbL->lLineCount > 0 &&
1287  pTemp->pfhbL->ppLineBuf[0] < pSection &&
1288  pTemp->pfhbL->ppLineBuf[pTemp->pfhbL->lLineCount - 1] >= pSection)
1289  {
1290  pFHB = pTemp->pfhbL;
1291  }
1292  else if(pTemp->pfhbG &&
1293  pTemp->pfhbG->ppLineBuf &&
1294  pTemp->pfhbG->lLineCount > 0 &&
1295  pTemp->pfhbG->ppLineBuf[0] < pSection &&
1296  pTemp->pfhbG->ppLineBuf[pTemp->pfhbG->lLineCount - 1] >= pSection)
1297  {
1298  pFHB = pTemp->pfhbG;
1299  }
1300  else
1301  {
1302 // WB_ERROR_PRINT("INTERNAL ERROR: %s\n", __FUNCTION__);
1303 // fprintf(stderr, "INTERNAL ERROR %s:%d\n", __FUNCTION__, __LINE__);
1304  return -1;
1305  }
1306 
1307 
1308  for(i1=0; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pSection; i1++)
1309  ;
1310 
1311  for(; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pEndSection; i1++)
1312  {
1313  p1 = pFHB->ppLineBuf[i1];
1314  p2 = pFHB->ppLineBuf[i1 + 1];
1315 
1316  if(!p2)
1317  {
1318  p2 = pFHB->cData + pFHB->lBufferCount;
1319  }
1320 
1321  while(p1 < p2 && *p1 <= ' ')
1322  {
1323  p1++;
1324  }
1325 
1326  if(*p1 == ';') // comment
1327  {
1328  continue;
1329  }
1330 
1331  if(p2 - p1 > iIdentifierLen &&
1332  !strncasecmp(szIdentifier, p1, iIdentifierLen) &&
1333  p1[iIdentifierLen] == '=')
1334  {
1335  // FOUND! eliminate trailing newline but keep other white space (for now)
1336  p1 += iIdentifierLen + 1;
1337  if(p2 > p1 && *(p2 - 1) == '\n')
1338  {
1339  if(p2 > (p1 + 1) && *(p2 - 1) == '\n' && *(p2 - 2) == '\r')
1340  {
1341  p2--;
1342  }
1343 
1344  p2--;
1345  }
1346  // copy string into destination buffer and return the length of the actual data
1347  i2 = cbData;
1348  if(i2 > (p2 - p1))
1349  {
1350  i2 = p2 - p1;
1351  }
1352  if(i2 > 0)
1353  {
1354  memcpy(szData, p1, i2);
1355  }
1356 
1357  if(i2 < cbData)
1358  {
1359  szData[i2] = 0; // as a matter of course
1360  }
1361 
1362  return p2 - p1;
1363  }
1364  }
1365 
1366  // not found locally - try global
1367 
1368  // TODO: global
1369 
1370 
1371 // WB_ERROR_PRINT("TEMPORARY: %s - did not find section \"%s\" item \"%s\"\n", __FUNCTION__, szSection, szIdentifier);
1372 
1373  return -1; // not found
1374 }
1375 
1376 int CHWriteConfFileString(void * hFile, const char *szSection,
1377  const char *szIdentifier, const char *szData)
1378 {
1379  int i1, i2, iSectionLine, iSectionLen, iIdentifierLen, iDataLen;
1380  char *pBuf;
1381  const char *p1, *p2, *pSection, *pEndSection;
1382  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1383  file_help_buf_t *pFHB;
1384 
1385 
1386  if(!pTemp ||
1387  ((!pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf) &&
1388  (!pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)))
1389  {
1390  return -1;
1391  }
1392  if(!szSection || !*szSection)
1393  {
1394  return -1; // don't allow this on write
1395  }
1396 
1397  iIdentifierLen = strlen(szIdentifier);
1398  iDataLen = szData ? strlen(szData) : -1;
1399 
1400  __find_section__(hFile, szSection, &pSection, &pEndSection);
1401  if(pSection)
1402  {
1403  if(pTemp->pfhbL &&
1404  pTemp->pfhbL->ppLineBuf &&
1405  pTemp->pfhbL->lLineCount > 0 &&
1406  pTemp->pfhbL->ppLineBuf[0] < pSection &&
1407  pTemp->pfhbL->ppLineBuf[pTemp->pfhbL->lLineCount - 1] >= pSection)
1408  {
1409  pFHB = pTemp->pfhbL;
1410  }
1411  else if(pTemp->pfhbG &&
1412  pTemp->pfhbG->ppLineBuf &&
1413  pTemp->pfhbG->lLineCount > 0 &&
1414  pTemp->pfhbG->ppLineBuf[0] < pSection &&
1415  pTemp->pfhbG->ppLineBuf[pTemp->pfhbG->lLineCount - 1] >= pSection)
1416  {
1417  // TODO: verify global CAN be written. if not, assume 'local'
1418  pFHB = pTemp->pfhbG;
1419  }
1420  else
1421  {
1422  pSection = NULL; // will be added to 'local' or 'global' as needed
1423  pFHB = NULL;
1424  }
1425  }
1426  else
1427  {
1428  pFHB = NULL;
1429  }
1430 
1431 
1432  if(pFHB)
1433  {
1434  // search for 'szIdentifier string' followed by '='
1435 
1436  for(i1=0; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pSection; i1++)
1437  ;
1438 
1439  iSectionLine = i1;
1440 
1441  for(; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pEndSection; i1++)
1442  {
1443  p1 = pFHB->ppLineBuf[i1];
1444  p2 = pFHB->ppLineBuf[i1 + 1];
1445 
1446  if(!p2)
1447  {
1448  p2 = pFHB->cData + pFHB->lBufferCount;
1449  }
1450 
1451  while(p1 < p2 && *p1 <= ' ')
1452  {
1453  p1++;
1454  }
1455 
1456  if(*p1 == ';') // comment
1457  {
1458  continue;
1459  }
1460 
1461  if(p2 - p1 > iIdentifierLen &&
1462  !strncasecmp(szIdentifier, p1, iIdentifierLen) &&
1463  p1[iIdentifierLen] == '=')
1464  {
1465  // FOUND! replace or delete contents (NULL szData --> delete)
1466  if(!szData)
1467  {
1468  FBDeleteLineFromFileBuf(pFHB, i1);
1469  }
1470  else
1471  {
1472  pBuf = (char *)WBAlloc(iDataLen + iIdentifierLen + 4);
1473 
1474  if(!pBuf)
1475  {
1476  WB_ERROR_PRINT("%s - 'pBuf' NULL (c)\n", __FUNCTION__);
1477 
1478  return -1; // error
1479  }
1480 
1481  // todo: search backwards from 'i1' for blanks & comments
1482  sprintf(pBuf, "%s=%s", szIdentifier, szData);
1483 
1484  if(pFHB == pTemp->pfhbG)
1485  {
1486  FBReplaceLineInFileBuf(&(pTemp->pfhbG), i1, pBuf);
1487  }
1488  else if(pFHB == pTemp->pfhbL)
1489  {
1490  FBReplaceLineInFileBuf(&(pTemp->pfhbL), i1, pBuf);
1491  }
1492  else
1493  {
1494  WBFree(pBuf);
1495  return -1; // error
1496  }
1497 
1498  WBFree(pBuf);
1499  }
1500 
1501  return 0; // success
1502  }
1503  }
1504 
1505  if(!szData)
1506  {
1507  return 0; // success [deleting something that's not there is 'OK']
1508  }
1509 
1510 
1511  // since the section doesn't contain this entry, create it after the
1512  // last entry, searching back from 'pEndSection' skipping blank lines and comments
1513  // until I get to 'pSection' or an entry, whichever happens first.
1514 
1515  pBuf = (char *)WBAlloc(iDataLen + iIdentifierLen + 4);
1516 
1517  if(!pBuf)
1518  {
1519  WB_ERROR_PRINT("%s - 'pBuf' NULL (a)\n", __FUNCTION__);
1520 
1521  return -1; // error
1522  }
1523 
1524  // search backwards from 'i1' for blank lines. insert right after
1525  // the last non-blank line ABOVE where I am.
1526 
1527  while(i1 > iSectionLine)
1528  {
1529  p1 = pFHB->ppLineBuf[i1 - 1];
1530  if(p1 && *p1 != '\r' && *p1 != '\n')
1531  {
1532  break;
1533  }
1534 
1535  i1--;
1536  }
1537 
1538  sprintf(pBuf, "%s=%s", szIdentifier, szData);
1539 
1540  if(pTemp->pfhbL /*&&
1541  (!(pTemp->pfhbG) || !(pTemp->iFlags & CH_FLAGS_GLOBAL))*/) // local first, unless the 'global' flag is set
1542  {
1543  FBInsertLineIntoFileBuf(&(pTemp->pfhbL), i1, pBuf);
1544  }
1545  else if(pTemp->pfhbG)
1546  {
1547  FBInsertLineIntoFileBuf(&(pTemp->pfhbG), i1, pBuf);
1548  }
1549 
1550  WBFree(pBuf);
1551 
1552  return 0; // ok!
1553  }
1554 
1555  if(!szData)
1556  {
1557  return 0; // already deleted, no need for further effort
1558  }
1559 
1560  iSectionLen = strlen(szSection);
1561 
1562  // allocate memory for the section and the entry, and add each in its turn
1563  i1 = iSectionLen + 4;
1564  if(i1 < (iDataLen + iIdentifierLen + 2))
1565  {
1566  i1 = iDataLen + iIdentifierLen + 2;
1567  }
1568 
1569  pBuf = (char *)WBAlloc(i1 + 2);
1570 
1571  if(!pBuf)
1572  {
1573  WB_ERROR_PRINT("%s - 'pBuf' NULL (b)\n", __FUNCTION__);
1574 
1575  return -1; // error
1576  }
1577 
1578  sprintf(pBuf, "[%s]\n", szSection);
1579 
1580  if(pTemp->pfhbL /*&& // always do local FIRST if I can
1581  (!(pTemp->pfhbG) || !(pTemp->iFlags & CH_FLAGS_GLOBAL))*/)
1582  {
1583  i2 = pTemp->pfhbL->lLineCount;
1584  FBInsertLineIntoFileBuf(&(pTemp->pfhbL), pTemp->pfhbL->lLineCount, pBuf);
1585  if(i2 >= pTemp->pfhbL->lLineCount)
1586  {
1587  WBFree(pBuf);
1588 
1589  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (a)\n", __FUNCTION__);
1590  return -1;
1591  }
1592 
1593  // now do it again, this time for the data
1594  sprintf(pBuf, "%s=%s\n", szIdentifier, szData);
1595 
1596  i2 = pTemp->pfhbL->lLineCount;
1597  FBInsertLineIntoFileBuf(&(pTemp->pfhbL), pTemp->pfhbL->lLineCount, pBuf);
1598  WBFree(pBuf);
1599 
1600  if(i2 >= pTemp->pfhbL->lLineCount)
1601  {
1602  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (b)\n", __FUNCTION__);
1603  return -1;
1604  }
1605  }
1606  else if(pTemp->pfhbG) // global [TODO: verify global can be written]
1607  {
1608  i2 = pTemp->pfhbG->lLineCount;
1609  FBInsertLineIntoFileBuf(&(pTemp->pfhbG), pTemp->pfhbG->lLineCount, pBuf);
1610  if(i2 >= pTemp->pfhbG->lLineCount)
1611  {
1612  WBFree(pBuf);
1613  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (c)\n", __FUNCTION__);
1614  return -1;
1615  }
1616  // now do it again, this time for the data
1617  sprintf(pBuf, "%s=%s\n", szIdentifier, szData);
1618 
1619  i2 = pTemp->pfhbG->lLineCount;
1620  FBInsertLineIntoFileBuf(&(pTemp->pfhbG), pTemp->pfhbG->lLineCount, pBuf);
1621  WBFree(pBuf);
1622 
1623  if(i2 >= pTemp->pfhbG->lLineCount)
1624  {
1625  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (d)\n", __FUNCTION__);
1626  return -1;
1627  }
1628  }
1629  else
1630  {
1631  WBFree(pBuf);
1632  WB_ERROR_PRINT("%s - pfhbG and pfhbL are both NULL\n", __FUNCTION__);
1633 
1634  return -1; // error
1635  }
1636 
1637  return 0; // success!
1638 }
1639 
1640 int CHGetConfFileInt(void * hFile, const char *szSection, const char *szIdentifier)
1641 {
1642  int iLen;
1643  char tbuf[64];
1644 
1645  if((iLen = CHGetConfFileString(hFile, szSection, szIdentifier, tbuf, sizeof(tbuf) - 1)) > 0)
1646  {
1647  tbuf[iLen] = 0;
1648  while(tbuf[0] && tbuf[0] <= ' ')
1649  {
1650  strcpy(tbuf, tbuf + 1);
1651  }
1652 
1653  if(tbuf[0])
1654  {
1655  return atoi(tbuf);
1656  }
1657  }
1658 
1659  return 0;
1660 }
1661 
1662 int CHWriteConfFileInt(void * hFile, const char *szSection, const char *szIdentifier, int iData)
1663 {
1664  int iFlag;
1665  char tbuf[64];
1666  char *p1;
1667 
1668  if(iData < 0)
1669  {
1670  iFlag = -1;
1671  iData = -iData;
1672  }
1673  else
1674  {
1675  iFlag = 0; // a sign flag, temporarily
1676  }
1677 
1678  p1 = tbuf + sizeof(tbuf) - 1;
1679 
1680  *p1 = 0;
1681  do
1682  {
1683  *(--p1) = (char)('0' + iData % 10);
1684 
1685  iData /= 10;
1686 
1687  } while(p1 > (tbuf + 1) && iData);
1688 
1689  if(iFlag < 0)
1690  {
1691  *(--p1) = '-';
1692  }
1693 
1694  return CHWriteConfFileString(hFile, szSection, szIdentifier, p1);
1695 }
1696 
1697 
1698 
1699 
1700 
1702 // __ __ ____ _____ _____ _____ ___ _ _ ____ ____ //
1703 // \ \/ // ___| | ____||_ _||_ _||_ _|| \ | | / ___|/ ___| //
1704 // \ / \___ \ | _| | | | | | | | \| || | _ \___ \ //
1705 // / \ ___) || |___ | | | | | | | |\ || |_| | ___) | //
1706 // /_/\_\|____/ |_____| |_| |_| |___||_| \_| \____||____/ //
1707 // //
1709 
1710 // INTERNAL STRUCTURES
1711 // header, followed by 'nSettings' XSETTINGS_DATA structs (each of variable length)
1712 typedef struct __XSETTINGS_HEADER__
1713 {
1714  char cByteOrder, cUnused[3];
1715  unsigned int uiSerial; // may be high or low endian, see 'cByteOrder'
1716  unsigned int nSettings; // may be high or low endian, see 'cByteOrder'
1717 } __attribute__((__packed__)) XSETTINGS_HEADER;
1718 
1719 typedef struct __XSETTINGS_DATAHDR__
1720 {
1721  char cSettingType, cUnused;
1722  unsigned short wNameLen;
1723  char szName[4]; // actually 'wNameLen' bytes long, including any padding
1724 // unsigned int dwLastChangeSerial; immediately follows 'szName'
1725 } __attribute__((__packed__)) XSETTINGS_DATAHDR;
1726 
1727 // data immediately follows XSETTINGS_DATAHDR, one of these three structs
1728 
1729 typedef struct __XSETTINGS_DATA_INT__
1730 {
1731  unsigned int uiValue; // endian matches that of header
1732 } XSETTINGS_DATA_INT;
1733 
1734 typedef struct __XSETTINGS_DATA_STRING__
1735 {
1736  unsigned int cbLength;
1737  char szData[4]; // followed immediately by character data (may be zero length)
1738 } __attribute__((__packed__)) XSETTINGS_DATA_STRING;
1739 
1740 
1741 const CHXSettings * CHGetXSettings(Display *pDisplay)
1742 {
1743  return pXSettings; // for now, just do this (if NULL do I call CHSettingsRefresh ?)
1744 }
1745 
1746 const CHXSetting * CHGetXSetting(Display *pDisplay, const char *szSettingName)
1747 {
1748 int i1, nSettings;
1749 // if it's Gtk/ColorScheme and the scheme is empty, return a dummied-up one
1750 // This spec is a MOVING TARGET, and it's a REAL PAIN to keep up with it.
1751 // currently has been marked as 'deprecated'
1752 // TODO: find out what replaced this
1753 static char the_colors[]="fg_color:#000000000000\nbg_color:#ededececebeb\ntext_color:#1a1a1a1a1a1a\nbase_color:#ffffffffffff\n"
1754  "selected_fg_color:#ffffffffffff\nselected_bg_color:#2020abab2020\ntooltip_fg_color:#000000000000\n"
1755  "tooltip_bg_color:#f5f5f5f5b5b5";
1756 static CHXSetting defcolorscheme =
1757 {
1758  "Gtk/ColorScheme",
1760  sizeof(the_colors),
1761  0,
1762  {
1763  .szData = (char *)the_colors // as a char * actually
1764  }
1765 };
1766 
1767 
1768  if(!pXSettings || pXSettings->pDisplay != pDisplay)
1769  {
1770  return NULL;
1771  }
1772 
1773  nSettings = pXSettings->nSettings;
1774 
1775  for(i1=0; i1 < nSettings; i1++)
1776  {
1777  if(!strcasecmp(szSettingName, pXSettings->aData[i1].szName))
1778  {
1779  if(!strcasecmp(szSettingName, "Gtk/ColorScheme") &&
1780  (pXSettings->aData[i1].iLen==0 || !pXSettings->aData[i1].uData.szData ||
1781  !*(pXSettings->aData[i1].uData.szData)))
1782  {
1783  return &defcolorscheme;
1784  }
1785 
1786  return pXSettings->aData + i1;
1787  }
1788  }
1789 
1790  // TODO: if there is no Gtk/ColorScheme do I want to return this anyway?
1791 
1792  if(!strcasecmp(szSettingName, "Gtk/ColorScheme"))
1793  return &defcolorscheme; // for now, yes
1794 
1795  return NULL;
1796 }
1797 
1798 void CHSettingsRefresh(Display *pDisplay)
1799 {
1800 // TODO: implement an XSETTINGS (gnome-settings-manager) 'collection' object and query it
1801 // NOTE: so far gnome-settings-manager doesn't provide anything really useful except the theme name
1802 // and everything else is either cloned from or implemented as the old-style resource manager
1803 
1804 int i1, iLen, nLen, iFormat, nItems, cbSize, cbNameLen, cbStrLen;
1805 //unsigned long cbData0;
1806 void *pData;
1807 unsigned long cbLeft, nI;
1808 char *pCur, *pDataEnd, *pXSCur, *pXSEnd;
1809 Atom a_XSETTINGS_Sn, a_XSETTINGS_SETTINGS, aType;
1810 Window wOwn;
1811 XSETTINGS_HEADER *pHdr;
1812 XSETTINGS_DATAHDR *pDHdr;
1813 char tbuf[256];
1814 
1815 
1816 // aTARGET = XInternAtom(pDisplay, "TARGET", False);
1817 // a_MANAGER = XInternAtom(pDisplay, "MANAGER", False);
1818 // XA_CLIPBOARD=XInternAtom(pDisplay, "CLIPBOARD", False);
1819 // XA_CLIPBOARD_MANAGER=XInternAtom(pDisplay, "CLIPBOARD_MANAGER", False);
1820  a_XSETTINGS_Sn = XInternAtom(pDisplay, "_XSETTINGS_S0", False);
1821  a_XSETTINGS_SETTINGS = XInternAtom(pDisplay, "_XSETTINGS_SETTINGS", False);
1822 
1823  // see https://specifications.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html
1824 
1825  XGrabServer(pDisplay); // required by above documentation
1826  wOwn = XGetSelectionOwner(pDisplay, a_XSETTINGS_Sn);
1827 
1828  if(wOwn == None)
1829  {
1830  XUngrabServer(pDisplay);
1831  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (no owner)\n",
1832  __FILE__, __LINE__, __FUNCTION__);
1833  return;
1834  }
1835 
1836  // read the property now
1837 
1838  pData = NULL;
1839  aType = None; //a_MANAGER;
1840  iFormat = 32;
1841  nI = 0;
1842  cbLeft = 0;
1843 
1844  if(XGetWindowProperty(pDisplay, wOwn, a_XSETTINGS_SETTINGS, 0, 0, False,
1845  AnyPropertyType, &aType, &iFormat, &nI, &cbLeft, (unsigned char **)&pData))
1846  {
1847  XUngrabServer(pDisplay);
1848  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (XSETTINGS_SETTINGS)(a)\n",
1849  __FILE__, __LINE__, __FUNCTION__);
1850  return;
1851  }
1852 
1853  if(pData)
1854  {
1855  XFree(pData);
1856  pData = NULL;
1857  }
1858 
1859  // is this the actual data, or a return that says "do it incrementally" ?
1860 
1861  if(aType == aINCR) // incremental
1862  {
1863  XUngrabServer(pDisplay);
1864  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (INCR)\n",
1865  __FILE__, __LINE__, __FUNCTION__);
1866  return;
1867  }
1868 
1869  nLen = iLen = cbLeft; // the RAW length (in bytes)
1870 
1871  if(iFormat == 16)
1872  {
1873  nLen /= 2;
1874  }
1875  else if(iFormat == 32)
1876  {
1877  nLen /= 4;
1878  }
1879 
1880  // now get it for reals
1881 
1882  if(XGetWindowProperty(pDisplay, wOwn, a_XSETTINGS_SETTINGS, 0, nLen, False,
1883  AnyPropertyType, &aType, &iFormat, &nI, &cbLeft, (unsigned char **)&pData)
1884  || !pData)
1885  {
1886  XUngrabServer(pDisplay);
1887  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (XSETTINGS_SETTINGS)(b)\n",
1888  __FILE__, __LINE__, __FUNCTION__);
1889  return;
1890  }
1891 
1892  XUngrabServer(pDisplay); // MUST do this or else bad things happen
1893 
1894 
1895  if(pData)
1896  {
1897  if(aType != a_XSETTINGS_SETTINGS)
1898  {
1899 #ifndef NO_DEBUG
1900  char *p1 = WBGetAtomName(pDisplay, aType);
1901  WB_ERROR_PRINT("TEMPORARY: %s:%d - %s returned type %d (%s)\n",
1902  __FILE__, __LINE__, __FUNCTION__, (int)aType, p1);
1903  if(p1)
1904  {
1905  WBFree(p1);
1906  }
1907 #endif // NO_DEBUG
1908 
1909  // TODO: is this an error??
1910  }
1911  }
1912  else
1913  {
1914  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data\n",
1915  __FILE__, __LINE__, __FUNCTION__);
1916  return;
1917  }
1918 
1919 
1920  // first part is __XSETTINGS_HEADER__ header
1921  pHdr = (XSETTINGS_HEADER *)pData;
1922  pCur = (char *)(pHdr + 1);
1923  pDataEnd = (char *)pData + iLen;//cbData0;
1924 
1925  if(pCur >= pDataEnd)
1926  {
1927  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small)\n",
1928  __FILE__, __LINE__, __FUNCTION__);
1929  XFree(pData);
1930  return;
1931  }
1932 
1933 // WB_ERROR_PRINT("TEMPORARY: pHdr %d %d %d\n",
1934 // pHdr->cByteOrder, pHdr->uiSerial, pHdr->nSettings);
1935 
1936  cbSize = 0;
1937  if(pHdr->cByteOrder) // assume HIGH endian
1938  {
1939  nItems = htonl(pHdr->nSettings);
1940  }
1941  else
1942  {
1943  nItems = pHdr->nSettings;
1944  }
1945 
1946  for(i1=0; i1 < nItems; i1++)
1947  {
1948  pDHdr = (XSETTINGS_DATAHDR *)pCur;
1949 
1950  // re-arranged this so it doesn't throw the following warning
1951  // warning: taking address of packed member 'wNameLen' of class or structure '__XSETTINGS_DATAHDR__'
1952  // may result in an unaligned pointer value [-Waddress-of-packed-member]
1953  // was: if((char *)(&(pDHdr->wNameLen) + 1) > pDataEnd)
1954 
1955  if(((char *)&(pDHdr->wNameLen) + sizeof(pDHdr->wNameLen)) > pDataEnd) // make sure I can read wNameLen safely
1956  {
1957  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small) %p %p %d of %d\n",
1958  __FILE__, __LINE__, __FUNCTION__, pCur, pDataEnd, i1, nItems);
1959  XFree(pData);
1960  return;
1961  }
1962 
1963  // NOTE: this COULD become unaligned access right here, but I had no warning...
1964  cbNameLen = pHdr->cByteOrder ? htons(pDHdr->wNameLen) : pDHdr->wNameLen;
1965 
1966  if(pDHdr->szName + cbNameLen + 1 >= pDataEnd)
1967  {
1968  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small)\n",
1969  __FILE__, __LINE__, __FUNCTION__);
1970  XFree(pData);
1971  return;
1972  }
1973 
1974  cbSize += sizeof(CHXSetting) + cbNameLen + 1;
1975  bzero(tbuf, sizeof(tbuf));
1976  if(cbNameLen < sizeof(tbuf))
1977  {
1978  memcpy(tbuf, pDHdr->szName, cbNameLen);
1979  }
1980  else
1981  {
1982  memcpy(tbuf, pDHdr->szName, sizeof(tbuf) - 1);
1983  }
1984 
1985  pCur = (char *)&(pDHdr->szName) + ((cbNameLen + 7) & (~3));
1986 
1987  switch(pDHdr->cSettingType)
1988  {
1989  case XSettingsTypeInteger:
1990 // WB_ERROR_PRINT("TEMPORARY: integer %s %d\n", tbuf, ((XSETTINGS_DATA_INT *)pCur)->uiValue);
1991 
1992  cbSize += sizeof(XSETTINGS_DATA_INT);
1993  pCur += sizeof(XSETTINGS_DATA_INT);
1994 
1995  break;
1996 
1997  case XSettingsTypeColor:
1998  cbSize += sizeof(XSETTINGS_DATA_COLOR);
1999  pCur += sizeof(XSETTINGS_DATA_COLOR);
2000 
2001  break;
2002 
2003  case XSettingsTypeString:
2004 
2005  if(pCur + sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData)
2006  >= pDataEnd) // make sure string length is readable
2007  {
2008  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small)\n",
2009  __FILE__, __LINE__, __FUNCTION__);
2010  XFree(pData);
2011  return;
2012  }
2013 
2014  cbStrLen = pHdr->cByteOrder ? htons(((XSETTINGS_DATA_STRING *)pCur)->cbLength) : ((XSETTINGS_DATA_STRING *)pCur)->cbLength;
2015 
2016 // WB_ERROR_PRINT("TEMPORARY: string %s length %d\n", tbuf, cbStrLen);
2017 // WB_ERROR_PRINT("TEMPORARY: string %s length %d \"%-.*s\"\n",
2018 // tbuf, cbStrLen, cbStrLen, ((XSETTINGS_DATA_STRING *)pCur)->szData);
2019 
2020  if(cbStrLen > 0)
2021  {
2022  cbSize += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData) + cbStrLen + 1;
2023  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData)
2024  + ((cbStrLen + 3) & (~3));
2025  }
2026  else
2027  {
2028  cbSize += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData);
2029  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData);
2030  }
2031 
2032  break;
2033  }
2034 
2035  // must check this at end of loop
2036 
2037  if(pCur > pDataEnd) // last can have pCurDataEnd == pCur
2038  {
2039  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small) %p %p %d of %d\n",
2040  __FILE__, __LINE__, __FUNCTION__, pCur, pDataEnd, i1, nItems);
2041  XFree(pData);
2042  return;
2043  }
2044  }
2045 
2046 // WB_ERROR_PRINT("TEMPORARY: total length needed %d bytes\n", cbSize);
2047 
2048  pCur = (char *)(pHdr + 1); // back to the beginning
2049 
2050  if(pXSettings)
2051  {
2052  WBFree(pXSettings);
2053  pXSettings = NULL;
2054  }
2055 
2056  pXSettings = (CHXSettings *)WBAlloc(cbSize + sizeof(CHXSettings));
2057 
2058  if(!pXSettings)
2059  {
2060  WB_ERROR_PRINT("%s:%d - %s not enough memory for XSettings\n",
2061  __FILE__, __LINE__, __FUNCTION__);
2062  XFree(pData);
2063  return;
2064  }
2065 
2066  pXSettings->pDisplay = pDisplay;
2067  pXSettings->nSettings = nItems;
2068  pXSettings->uiSerial = pHdr->cByteOrder ? htonl(pHdr->uiSerial) : pHdr->uiSerial;
2069 
2070  pXSEnd = (char *)pXSettings + cbSize + sizeof(CHXSettings); // to duplicate 'malloc' size calc
2071  pXSCur = (char *)(pXSettings->aData + nItems); // this is where I put string data
2072 
2073  for(i1=0; i1 < nItems; i1++)
2074  {
2075  XSETTINGS_DATAHDR *pDHdr = (XSETTINGS_DATAHDR *)pCur;
2076  int cbNameLen = pHdr->cByteOrder ? htons(pDHdr->wNameLen) : pDHdr->wNameLen;
2077  CHXSetting *pXS = pXSettings->aData + i1;
2078 
2079  if(pXSCur + cbNameLen + 1 > pXSEnd)
2080  {
2081  WB_ERROR_PRINT("%s:%d - %s CHXSettings data corrupt (data buffer too small)\n",
2082  __FILE__, __LINE__, __FUNCTION__);
2083 
2084  WBFree(pXSettings);
2085  pXSettings = NULL;
2086 
2087  XFree(pData);
2088  return;
2089  }
2090 
2091  pXS->szName = pXSCur;
2092 
2093  memcpy(pXSCur, pDHdr->szName, cbNameLen);
2094  pXSCur += cbNameLen;
2095  *(pXSCur++) = 0;
2096 
2097  pCur = (char *)&(pDHdr->szName) + ((cbNameLen + 7) & (~3));
2098 
2099  pXS->iType = pDHdr->cSettingType;
2100 
2101  switch(pDHdr->cSettingType)
2102  {
2103  case XSettingsTypeInteger:
2104 
2105  pXS->iLen = 0;
2106  pXS->uData.iData = ((XSETTINGS_DATA_INT *)pCur)->uiValue;
2107 
2108  pCur += sizeof(XSETTINGS_DATA_INT);
2109 
2110  break;
2111 
2112  case XSettingsTypeColor:
2113 
2114  pXS->iLen = 0;
2115 
2116  pXS->uData.clrData.sRed = ((XSETTINGS_DATA_COLOR *)pCur)->sRed;
2117  pXS->uData.clrData.sBlue = ((XSETTINGS_DATA_COLOR *)pCur)->sBlue;
2118  pXS->uData.clrData.sGreen = ((XSETTINGS_DATA_COLOR *)pCur)->sGreen;
2119  pXS->uData.clrData.sAlpha = ((XSETTINGS_DATA_COLOR *)pCur)->sAlpha;
2120 
2121  cbSize += sizeof(XSETTINGS_DATA_COLOR);
2122  pCur += sizeof(XSETTINGS_DATA_COLOR);
2123 
2124  break;
2125 
2126  case XSettingsTypeString:
2127 
2128  cbStrLen = pHdr->cByteOrder ? htons(((XSETTINGS_DATA_STRING *)pCur)->cbLength) : ((XSETTINGS_DATA_STRING *)pCur)->cbLength;
2129  pXS->iLen = cbStrLen;
2130 
2131  if(cbStrLen > 0)
2132  {
2133  if(pXSCur + cbStrLen + 1 > pXSEnd)
2134  {
2135  WB_ERROR_PRINT("%s:%d - %s CHXSettings data corrupt (data buffer too small)\n",
2136  __FILE__, __LINE__, __FUNCTION__);
2137 
2138  WBFree(pXSettings);
2139  pXSettings = NULL;
2140 
2141  XFree(pData);
2142  return;
2143  }
2144 
2145  pXS->uData.szData = pXSCur;
2146  memcpy(pXSCur, ((XSETTINGS_DATA_STRING *)pCur)->szData, cbStrLen);
2147 
2148  pXSCur += cbStrLen;
2149  *(pXSCur++) = 0;
2150 
2151  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData)
2152  + ((cbStrLen + 3) & (~3));
2153  }
2154  else
2155  {
2156  pXS->uData.szData = ""; // so that it's not NULL
2157 
2158  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData);
2159  }
2160 
2161  break;
2162  }
2163  }
2164 
2165  // if I'm debuggin I'll want a copy of what's there
2166 
2167 #ifndef NO_DEBUG
2168  WB_IF_DEBUG_LEVEL(DebugLevel_WARN | DebugSubSystem_Settings)
2169  {
2170  CHDumpConfig();
2171  }
2172 #endif // !NO_DEBUG
2173 }
2174 
2175 static void __settings_cleanup(void)
2176 {
2177  if(pXSettings)
2178  {
2179  WBFree(pXSettings);
2180  pXSettings = NULL;
2181  }
2182 }
2183 
2184 
2185 
2187 // QUERY SYSTEM PARAMETERS
2189 
2190 
2191 // basic 'window_helper' helpers
2192 // NOTE: see page on Specifications/XSettingsRegistry at freedesktop.org
2193 // http://www.freedesktop.org/wiki/Specifications/XSettingsRegistry
2194 
2195 #define DEFAULT_DOUBLE_CLICK_TIME 250 /* msec */
2196 #define DEFAULT_DOUBLE_CLICK_DISTANCE 5 /* pixels */
2197 #define DEFAULT_DRAG_THRESHOLD 9 /* pixels */
2198 #define DEFAULT_CURSOR_BLINK (!0) /* boolean */
2199 #define DEFAULT_CURSOR_BLINK_TIME 1200 /* msec */
2200 
2201 
2202 int CHGetDoubleClickTime(Display *pDisplay)
2203 {
2204 static int iFirstTime = 1, iRval = DEFAULT_DOUBLE_CLICK_TIME;
2205 static unsigned int uiSerial;
2206 const CHXSetting *pXS;
2207 
2208  // These first checks should be optimized for speed. It is likely that
2209  // they will be performed OFTEN, as often as 'every message'
2210 
2211  if(WB_UNLIKELY(!pXSettings))
2212  {
2213  return iRval; // last known value
2214  }
2215 
2216  if(WB_LIKELY(!iFirstTime) &&
2217  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2218  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2219  {
2220  return iRval; // no need to check again
2221  }
2222 
2223  if(pDisplay && pDisplay != pXSettings->pDisplay)
2224  {
2225  return DEFAULT_DOUBLE_CLICK_TIME; // return the default value (for now)
2226  }
2227 
2228  iFirstTime = 0; // make sure
2229  uiSerial = pXSettings->uiSerial; // keep track of serial #
2230 
2231  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/DoubleClickTime");
2232 
2233  if(pXS && pXS->iType == XSettingsTypeInteger)
2234  {
2235  iRval = pXS->uData.iData;
2236  }
2237 
2238  return iRval;
2239 }
2240 
2241 int CHGetDoubleClickDistance(Display *pDisplay)
2242 {
2243 static int iFirstTime = 1, iRval = DEFAULT_DOUBLE_CLICK_DISTANCE;
2244 static unsigned int uiSerial;
2245 const CHXSetting *pXS;
2246 
2247  // These first checks should be optimized for speed. It is likely that
2248  // they will be performed OFTEN, as often as 'every message'
2249 
2250  if(WB_UNLIKELY(!pXSettings))
2251  {
2252  return iRval; // last known value
2253  }
2254 
2255  if(WB_LIKELY(!iFirstTime) &&
2256  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2257  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2258  {
2259  return iRval; // no need to check again
2260  }
2261 
2262  if(pDisplay && pDisplay != pXSettings->pDisplay)
2263  {
2264  return DEFAULT_DOUBLE_CLICK_DISTANCE; // return the default value (for now)
2265  }
2266 
2267  iFirstTime = 0; // make sure
2268  uiSerial = pXSettings->uiSerial; // keep track of serial #
2269 
2270  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/DoubleClickDistance");
2271 
2272  if(pXS && pXS->iType == XSettingsTypeInteger)
2273  {
2274  iRval = pXS->uData.iData;
2275  }
2276 
2277  return iRval;
2278 }
2279 
2280 int CHGetDragThreshold(Display *pDisplay)
2281 {
2282 static int iFirstTime = 1, iRval = DEFAULT_DRAG_THRESHOLD;
2283 static unsigned int uiSerial;
2284 const CHXSetting *pXS;
2285 
2286  // These first checks should be optimized for speed. It is likely that
2287  // they will be performed OFTEN, as often as 'every message'
2288 
2289  if(WB_UNLIKELY(!pXSettings))
2290  {
2291  return iRval; // last known value
2292  }
2293 
2294  if(WB_LIKELY(!iFirstTime) &&
2295  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2296  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2297  {
2298  return iRval; // no need to check again
2299  }
2300 
2301  if(pDisplay && pDisplay != pXSettings->pDisplay)
2302  {
2303  return DEFAULT_DRAG_THRESHOLD; // return the default value (for now)
2304  }
2305 
2306  iFirstTime = 0; // make sure
2307  uiSerial = pXSettings->uiSerial; // keep track of serial #
2308 
2309  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/DndDragThreshold");
2310 
2311  if(pXS && pXS->iType == XSettingsTypeInteger)
2312  {
2313  iRval = pXS->uData.iData;
2314  }
2315 
2316  return iRval;
2317 }
2318 
2319 int CHGetCursorBlink(Display *pDisplay)
2320 {
2321 static int iFirstTime = 1, iRval = DEFAULT_CURSOR_BLINK;
2322 static unsigned int uiSerial;
2323 const CHXSetting *pXS;
2324 
2325  // These first checks should be optimized for speed. It is likely that
2326  // they will be performed OFTEN, as often as 'every message'
2327 
2328  if(WB_UNLIKELY(!pXSettings))
2329  {
2330  return iRval; // last known value
2331  }
2332 
2333  if(WB_LIKELY(!iFirstTime) &&
2334  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2335  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2336  {
2337  return iRval; // no need to check again
2338  }
2339 
2340  if(pDisplay && pDisplay != pXSettings->pDisplay)
2341  {
2342  return DEFAULT_CURSOR_BLINK; // return the default value (for now)
2343  }
2344 
2345  iFirstTime = 0; // make sure
2346  uiSerial = pXSettings->uiSerial; // keep track of serial #
2347 
2348  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/CursorBlink");
2349 
2350  if(pXS && pXS->iType == XSettingsTypeInteger)
2351  {
2352  iRval = pXS->uData.iData ? !0 : 0; // convert to "true boolean"
2353  }
2354 
2355  return iRval;
2356 }
2357 
2358 int CHGetCursorBlinkTime(Display *pDisplay)
2359 {
2360 static int iFirstTime = 1, iRval = DEFAULT_CURSOR_BLINK_TIME;
2361 static unsigned int uiSerial;
2362 const CHXSetting *pXS;
2363 
2364  // These first checks should be optimized for speed. It is likely that
2365  // they will be performed OFTEN, as often as 'every message'
2366 
2367  if(WB_UNLIKELY(!pXSettings))
2368  {
2369  return iRval; // last known value
2370  }
2371 
2372  if(WB_LIKELY(!iFirstTime) &&
2373  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2374  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2375  {
2376  return iRval; // no need to check again
2377  }
2378 
2379  if(pDisplay && pDisplay != pXSettings->pDisplay)
2380  {
2381  return DEFAULT_CURSOR_BLINK_TIME; // return the default value (for now)
2382  }
2383 
2384  iFirstTime = 0; // make sure
2385  uiSerial = pXSettings->uiSerial; // keep track of serial #
2386 
2387  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/CursorBlinkTime");
2388 
2389  if(pXS && pXS->iType == XSettingsTypeInteger)
2390  {
2391  iRval = pXS->uData.iData;
2392  }
2393 
2394  return iRval;
2395 }
2396 
2397 
2398 
2400 // X M L P A R S I N G //
2402 
2411 static const char *InternalParseXML(CHXMLEntry **ppOrigin, int *pcbOrigin, CHXMLEntry **ppCur,
2412  char **ppData, int *pcbData, char **ppCurData,
2413  const char *ppXMLData, const char *pXMLDataEnd)
2414 {
2415  // parse a section of XML, adding contents to the end of 'ppOrigin', and returning
2416  // a pointer to the next element on success (or NULL otherwise). This function will
2417  // re-allocate '*ppOrigin' as needed, storing the max size in '*pcbOrigin'. It can
2418  // also recurse to embedded sections, and then process them as needed to get the XML
2419  // hierarchy correct in the CHXMLEntry pointed to by 'ppOrigin'.
2420  //
2421  // A recursive call can affect *ppOrigin. It should be explicitly re-loaded on return.
2422 
2423  // this isn't needed per se, but having it here can't hurt..
2424  if(!ppOrigin || !*ppOrigin || !pcbOrigin || !ppCur || !*ppCur ||
2425  !ppXMLData || !*ppXMLData || !pXMLDataEnd ||
2426  (((WB_UINTPTR)pXMLDataEnd) < ((WB_UINTPTR)*ppXMLData))) // warning abatement, use type cast for WB_UINTPTR
2427  {
2428  return NULL; // just reject these possibilities outright and return "error"
2429  }
2430 
2431  // this function will return on error or if it finds and parses the ending tag
2432 
2433  // OK what kind of tag do we have now?
2434 
2435 
2436 // pCur = CHFindNextXMLTag(pCur, cbLength, 0);
2437 //
2438 // if(!pCur) // no more tags
2439 // {
2440 // break;
2441 // }
2442 //
2443 // pCur++; // points past the tag
2444 
2445 
2446  // returned pointer is the next point at which to parse a 'same level' tag
2447 
2448  return pXMLDataEnd; // for now
2449 }
2450 
2451 
2452 // typedef struct _CHXMLEntry_
2453 // {
2454 // int iNextIndex; // 0-based index for next item at this level; <= 0 for none. 0 marks "end of list" for top level
2455 // int iContainer; // 0-based index for container; <= 0 for none.
2456 // int iContentsIndex; // 0-based first array index for 'contents' for this entry; <= 0 for none
2457 //
2458 // int nLabelOffset; // BYTE offset to label (zero-byte-terminated) string (from beginning of array)
2459 // // for this entry; <= 0 for 'no label'
2460 // int nDataOffset; // BYTE offset to data (zero-byte-terminated) string (from beginning of array)
2461 // // for the entry data; <= 0 for 'no data'
2462 //
2463 // } CHXMLEntry;
2464 
2465 CHXMLEntry *CHParseXML(const char *pXMLData, int cbLength)
2466 {
2467 CHXMLEntry *pRval = NULL;
2468 CHXMLEntry *pXE, *pXCur;
2469 int cbRval, cbData, cbNeed, cbOffs;
2470 const char *pEnd = pXMLData + cbLength;
2471 const char *pCur;
2472 char *pData, *pCurData;
2473 
2474 
2475  if(!pXMLData || !cbLength || !*pXMLData)
2476  {
2477  return NULL;
2478  }
2479 
2480  cbRval = 0x1000 * sizeof(CHXMLEntry); // 64k entries
2481  cbData = 0x100000; // 256k, to start with
2482 
2483  pRval = (CHXMLEntry *)malloc(cbRval);
2484  pData = malloc(cbData);
2485  if(!pRval || !pData)
2486  {
2487  if(pRval)
2488  {
2489  free(pRval);
2490  }
2491 
2492  if(pData)
2493  {
2494  free(pData);
2495  }
2496  return NULL; // not enough memory (oops)
2497  }
2498 
2499 
2500 
2501  pCur = pXMLData;
2502  pXCur = pRval;
2503  pCurData = pData;
2504 
2505  *pData = 0; // ending zero byte - must be present at pData[length]
2506 
2507  pXCur->iNextIndex = 0; // marks "end of list"
2508  pXCur->iContainer = 0; // marks it as "top level"
2509  pXCur->iContentsIndex = 0;
2510  pXCur->nLabelOffset = 0;
2511  pXCur->nDataOffset = 0;
2512 
2513 
2514  while(pCur < pEnd)
2515  {
2516  // call recursive function that does "one level" of XML and all of its contents
2517 
2518  pCur = InternalParseXML(&pRval, &cbRval, &pXCur, &pData, &cbData, &pCurData, pCur, pEnd);
2519 
2520  if(!pCur) // error
2521  {
2522  goto error_exit;
2523  }
2524 
2525  // TODO: look at the beginning for things like <?xml version="xx"?> and <!DOCTYPE xxx>
2526 
2527  }
2528 
2529  // at this point 'pXCur' is the pointer to the final entry, and there's room in the array for it.
2530 
2531  pXCur->iNextIndex = 0; // marks "end of list"
2532  pXCur->iContainer = 0; // assign the other zeros by convention
2533  pXCur->iContentsIndex = 0;
2534  pXCur->nLabelOffset = 0;
2535  pXCur->nDataOffset = 0;
2536 
2537  cbOffs = ((char *)(pXCur + 1) - (char *)pRval); // offset to where the data is, for fixups
2538  // I use 'pXCur + 1' here because I'll increment it later. but I also need it
2539  // for a limit pointer in the fixup loop, so I don't increment it YET...
2540  cbNeed = cbOffs + 2 * sizeof(*pXCur)
2541  + (pCurData - pData); // the actual size of the data
2542 
2543  if(cbNeed > cbRval) // need to re-allocate
2544  {
2545  void *pTemp = realloc(pRval, cbNeed);
2546 
2547  if(!pTemp)
2548  {
2549  goto error_exit;
2550  }
2551 
2552  pXCur = (pXCur - pRval) + ((CHXMLEntry *)pTemp); // new 'pXCur'
2553  pRval = (CHXMLEntry *)pTemp; // new 'pRval'
2554  }
2555 
2556  // fix up all of the data indices
2557  for(pXE=pRval; pXE < pXCur; pXE++)
2558  {
2559  if(pXCur->nLabelOffset >= 0) // allow '0' for this part
2560  {
2561  pXCur->nLabelOffset += cbOffs; // fix up the data offset
2562  }
2563  else
2564  {
2565  pXCur->nLabelOffset = 0; // make it zero to mark it 'unused'
2566  }
2567 
2568  if(pXCur->nDataOffset >= 0) // allow '0' for this part
2569  {
2570  pXCur->nDataOffset += cbOffs; // fix up the data offset
2571  }
2572  else
2573  {
2574  pXCur->nDataOffset = 0; // make it zero to mark it 'unused'
2575  }
2576  }
2577 
2578  pXCur++; // this is where the data will start, now
2579 
2580  // copy the data where it needs to be
2581 
2582  memcpy(pXCur, pData, (pCurData - pData) + 1); // copy data, including the final 0-byte at 'pData[length]'
2583 
2584  // and now I'm done!
2585 
2586  goto the_end;
2587 
2588 error_exit:
2589 
2590  free(pRval);
2591  pRval = NULL;
2592 
2593 
2594 the_end:
2595  free(pData); // not needed any more
2596 
2597  return pRval; // the self-contained structure, or NULL on error
2598 }
2599 
2600 char *CHParseXMLTagContents(const char *pTagContents, int cbLength)
2601 {
2602 const char *pCur = pTagContents, *pEnd = pTagContents + cbLength;
2603 const char *p1, *p2, *p3;
2604 char *pRval, *pC, *pE, *p4, *p5;
2605 int i1, cbRval = 4096;
2606 
2607  if(!pCur)
2608  {
2609  return NULL;
2610  }
2611 
2612  if(cbLength < 0)
2613  {
2614  cbLength = strlen(pTagContents);
2615  pEnd = pTagContents + cbLength;
2616  }
2617 
2618  pC = pRval = WBAlloc(cbRval);
2619  if(!pRval)
2620  {
2621  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2622  return NULL;
2623  }
2624 
2625  pE = pRval + cbRval;
2626 
2627  pC[0] = pC[1] = 0;
2628 
2629 
2630  // NOTE: XML spec requires that '>' and '&' be treated special, and quotes ignored for these.
2631  // The strings "&amp;" "&lt;" "&gt;" must also be honored inside or outside of quotes.
2632  // when I find '>' inside of quotes, I could optionally ignore it, but the spec says "NO"
2633  // so the result should be some kind of XML syntax error...
2634 
2635  while(pCur < pEnd && *pCur)
2636  {
2637  // find value name
2638  while(pCur < pEnd && *pCur && *pCur <= ' ')
2639  {
2640  pCur++; // skip white space
2641  }
2642 
2643  p1 = pCur;
2644  while(pCur < pEnd && *pCur > ' ' && *pCur != '=' && *pCur != '>' && *pCur != '[' && *pCur != '(')
2645  {
2646  pCur++; // find end of string
2647  }
2648 
2649  if(pCur >= pEnd)
2650  {
2651  break; // went past the end of the buffer
2652  }
2653 
2654  if(*pCur == '>') // end of tag?
2655  {
2656  if(pCur == p1) // empty value
2657  {
2658  break; // I am done
2659  }
2660  else if(pCur >= p1 + 2 && *(pCur - 1) == '-' && *(pCur - 2) == '-')
2661  {
2662  pCur -= 2; // prior to '-->'
2663  }
2664  else if(*(pCur - 1) == '/')
2665  {
2666  pCur --; // prior to '/>'
2667  }
2668  }
2669 #if 0
2670  else if(*pCur == '(' || *pCur == '[') // an embedded section ??? [this only applies to CDATA]
2671  {
2672  // in this case it's an embedded entity and I want to preserve it in its entirety
2673  char cTemp = *pCur;
2674 
2675  if(cTemp == '(')
2676  {
2677  cTemp = ')';
2678  }
2679  else
2680  {
2681  cTemp = ']';
2682  }
2683 
2684  pCur++;
2685  if(pCur >= pEnd)
2686  {
2687  break; // I'm outa here - past end of buffer
2688  }
2689 
2690  p2 = pCur;
2691  pCur = CHFindEndOfXMLSection(pCur, pEnd - pCur, cTemp, 0); // find end of section
2692 
2693  if(pCur < pEnd)
2694  {
2695  pCur++; // point to next char past the end of this section
2696  }
2697 
2698  p5 = WBCopyStringN(p2, pCur - p2); // make a copy of the section, allocated as p5
2699 
2700  goto value_is_now_p5; // this will check for NULL 'p5' also
2701  }
2702 #endif // 0
2703 
2704  if(pCur == p1)
2705  {
2706  break; // I am done
2707  }
2708 
2709 
2710  p2 = pCur;
2711 
2712  while(pCur < pEnd && *pCur && *pCur <= ' ')
2713  {
2714  pCur++; // skip white space
2715  }
2716 
2717  // NOTE: this function does not handle '&amp;' or '&gt;' etc. outside of quotes
2718 
2719  if(pCur < pEnd && *pCur == '=') // value follows
2720  {
2721  pCur++;
2722 
2723  while(pCur < pEnd && *pCur && *pCur <= ' ')
2724  {
2725  pCur++; // skip white space
2726  }
2727 
2728  p3 = pCur;
2729 
2730  if(*pCur == '"' || *pCur == '\'') // quoted string
2731  {
2732  char c1 = *pCur;
2733  pCur++;
2734  while(pCur < pEnd && *pCur &&
2735  (*pCur != c1 || (pCur < pEnd - 1 && pCur[1] == c1)))
2736  {
2737  if(*pCur == c1) // will be < pEnd - 1
2738  {
2739  pCur += 2;
2740  }
2741  else
2742  {
2743  pCur++;
2744  }
2745  }
2746 
2747  if(*pCur == '"')
2748  {
2749  pCur++; // now past the quote
2750  }
2751 
2752  p5 = WBCopyStringN(p3, pCur - p3); // copy all including start/end quotes
2753 
2754  // make de-quoted normalized version
2755  if(p5)
2756  {
2757  WBNormalizeXMLString(p5); // remove quotes and sub '&gt;' '&amp;' etc.
2758  }
2759  }
2760  else
2761  {
2762  while(pCur < pEnd && *pCur > ' ' && *pCur != '>')
2763  {
2764  pCur++; // find end of string
2765  }
2766 
2767  if(*pCur == '>') // end of tag?
2768  {
2769  if(pCur == p3) // empty value
2770  {
2771  goto no_value;
2772  }
2773  else if(pCur >= p3 + 2 && *(pCur - 1) == '-' && *(pCur - 2) == '-')
2774  {
2775  pCur -= 2; // prior to '-->'
2776  }
2777  else if(*(pCur - 1) == '/')
2778  {
2779  pCur --; // prior to '/>'
2780  }
2781  }
2782 
2783  if(pCur == p3) // 'value= />' yeah, I accept it
2784  {
2785  goto no_value;
2786  }
2787 
2788  p5 = WBCopyStringN(p3, pCur - p3);
2789 
2790  // make normalized version
2791  if(p5)
2792  {
2793  WBNormalizeXMLString(p5); // remove quotes and sub '&gt;' '&amp;' etc.
2794  }
2795  }
2796 
2797 //value_is_now_p5:
2798 
2799  if(!p5)
2800  {
2801  WBFree(pRval);
2802  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2803  return NULL;
2804  }
2805 
2806  // value is now p5
2807 
2808  if(pC + (p2 - p1) + strlen(p5) + 4 >= pE)
2809  {
2810  i1 = 4096;
2811  while((p2 - p1) + strlen(p5) + 4 >= i1)
2812  {
2813  i1 += 4096; // to make sure it's big enough in 4k increments
2814  }
2815 
2816  cbRval += i1;
2817  p4 = WBReAlloc(pRval, cbRval);
2818 
2819  if(!p4)
2820  {
2821  WBFree(p5);
2822  WBFree(pRval);
2823  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2824  return NULL;
2825  }
2826 
2827  if(p4 != pRval)
2828  {
2829  pC = p4 + (pC - pRval);
2830  pRval = p4;
2831  }
2832 
2833  pE = pRval + cbRval;
2834  }
2835 
2836  // now do value=the value\0\0 and point 'pC' to the 2nd '\0'
2837  memcpy(pC, p1, p2 - p1);
2838  pC += p2 - p1;
2839  *(pC++) = '=';
2840  strcpy(pC, p5);
2841  pC += strlen(pC) + 1;
2842  *pC = 0;
2843 
2844  WBFree(p5); // done with it
2845  }
2846  else // no value
2847  {
2848 no_value:
2849  if(pC + (p2 - p1) + 3 >= pE)
2850  {
2851  i1 = 4096;
2852  while((p2 - p1) + 3 >= i1)
2853  {
2854  i1 += 4096; // to make sure it's big enough in 4k increments
2855  }
2856 
2857  cbRval += i1;
2858  p4 = WBReAlloc(pRval, cbRval);
2859 
2860  if(!p4)
2861  {
2862  WBFree(pRval);
2863  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2864  return NULL;
2865  }
2866 
2867  if(p4 != pRval)
2868  {
2869  pC = p4 + (pC - pRval);
2870  pRval = p4;
2871  }
2872 
2873  pE = pRval + cbRval;
2874  }
2875 
2876  // now do value=\0\0 and point 'pC' to the 2nd '\0'
2877  memcpy(pC, p1, p2 - p1);
2878  pC += p2 - p1;
2879  *(pC++) = '=';
2880  *(pC++) = 0;
2881  *pC = 0; // by convention
2882  }
2883  }
2884 
2885 
2886  return pRval;
2887 }
2888 
2889 const char *CHFindNextXMLTag(const char *pTagContents, int cbLength, int nNestingFlags)
2890 {
2891 const char *p1, *pEnd = pTagContents + cbLength;
2892 
2893 
2894  if(!pTagContents || cbLength == 0 || !*pTagContents)
2895  {
2896  return pTagContents;
2897  }
2898 
2899  if(cbLength < 0)
2900  {
2901  cbLength = strlen(pTagContents);
2902  }
2903 
2904  p1 = pTagContents;
2905 
2906  // outside of a tag, we don't check quote marks. however, I do check for parens
2907  // when the bit flags in 'nNestingFlags' tell me to.
2908  // TODO: add a 'quote mark' check to 'nNestingFlags' ?
2909 
2910  // NOTE: XML spec requires that '>' and '&' be treated special, and quotes ignored for these.
2911  // The strings "&amp;" "&lt;" "&gt;" must also be honored inside or outside of quotes.
2912  // when I find '>' inside of quotes, I could optionally ignore it, but the spec says "NO"
2913  // so the result should be some kind of XML syntax error...
2914 
2915  while(p1 < pEnd && *p1)
2916  {
2917  if(*p1 == '<') // next tag (includes comment tags, etc.)
2918  {
2919  break;
2920  }
2921 
2922  // TODO: exit if I find ending tag?
2923 
2924  if((nNestingFlags & CHPARSEXML_PAREN) && (*p1 == '(' || *p1 == ')'))
2925  {
2926  break;
2927  }
2928 
2929  if((nNestingFlags & CHPARSEXML_BRACKET) && (*p1 == '[' || *p1 == ']'))
2930  {
2931  break;
2932  }
2933 
2934  p1++;
2935  }
2936 
2937  return p1;
2938 }
2939 
2940 
2941 const char *CHFindEndOfXMLSection(const char *pTagContents, int cbLength, char cEndChar, int bUseQuotes)
2942 {
2943 register const char *p1 = pTagContents;
2944 const char *pE;
2945 
2946 
2947  if(!p1 || !cbLength)
2948  {
2949  return NULL;
2950  }
2951 
2952  if(cbLength < 0)
2953  {
2954  cbLength = strlen(p1);
2955  }
2956 
2957  pE = p1 + cbLength;
2958 
2959  // in cases of <!CDATA ... > and other tags that might have nesting within them, this
2960  // function needs to pay attention to '[' ']' '(' and ')' that are OUTSIDE of quoted strings
2961 
2962  // NOTE: XML spec requires that '>' and '&' be treated special, and quotes ignored for these.
2963  // The strings "&amp;" "&lt;" "&gt;" must also be honored inside or outside of quotes.
2964  // when I find '>' inside of quotes, I could optionally ignore it, but the spec says "NO"
2965  // so the result should be some kind of XML syntax error...
2966 
2967  while(p1 < pE && *p1)
2968  {
2969  // need to parse out this tag.
2970  if(bUseQuotes && (*p1 == '"' || *p1 == '\'')) //handle quotes
2971  {
2972  char c1 = *p1;
2973  p1++;
2974  if(p1 >= pE)
2975  {
2976  break;
2977  }
2978 
2979  while(p1 < pE && *p1 &&
2980  (*p1 != c1 || ((p1 + 1) < pE && p1[1] == c1)))
2981  {
2982  if(*p1 == c1) // doubled quote?
2983  {
2984  p1 += 2;
2985  }
2986  else
2987  {
2988  p1++;
2989  }
2990  }
2991 
2992  if(p1 >= pE)
2993  {
2994  break;
2995  }
2996 
2997  if(*p1 == c1)
2998  {
2999  p1++; // now past the quote
3000  }
3001  }
3002  else if(*p1 == cEndChar) // end of the tag/section
3003  {
3004  break;
3005  }
3006 
3007  // Assuming we are already "within a tag" it's possible, for some tags, to have
3008  // a bunch of stuff embedded within them using '( )' '[ ]' etc.. This function
3009  // will recurse and allow for nested things like that.
3010 
3011  else if(*p1 == '(') // now we look for embedded things
3012  {
3013  p1++;
3014  if(p1 >= pE)
3015  {
3016  break;
3017  }
3018 
3019  p1 = CHFindEndOfXMLSection(p1, pE - p1, ')', 0);
3020 
3021  if(!p1 || p1 >= pE || !*p1 )
3022  {
3023  break;
3024  }
3025 
3026  p1++; // point it past the ')' I just found
3027  }
3028  else if(*p1 == '[')
3029  {
3030  p1++;
3031  if(p1 >= pE)
3032  {
3033  break;
3034  }
3035 
3036  p1 = CHFindEndOfXMLSection(p1, pE - p1, ']', 0);
3037 
3038  if(!p1 || p1 >= pE || !*p1 )
3039  {
3040  break;
3041  }
3042 
3043  p1++; // point it past the ']' I just found
3044  }
3045  else if(!bUseQuotes && *p1 == '<' && (cEndChar == ']' || cEndChar == ')'))
3046  {
3047  // special case within an embedded section surrounded by '[]' or '()'
3048  // IF I'm searching for an end bracket/paren, and
3049  // IF I've just run across the beginning of a tag, and
3050  // IF I'm currently ignoring quote marks
3051  // THEN, I want to parse the XML tag with respect to quote marks until the end of the tag
3052 
3053  p1++;
3054  if(p1 >= pE)
3055  {
3056  break;
3057  }
3058 
3059  p1 = CHFindEndOfXMLSection(p1, pE - p1, '>', 1);
3060 
3061  if(!p1 || p1 >= pE || !*p1 )
3062  {
3063  break;
3064  }
3065 
3066  p1++; // point it past the '>' I just found (it's embedded)
3067  }
3068  else
3069  {
3070  p1++;
3071  }
3072  }
3073 
3074  return p1;
3075 }
3076 
3077 const char *CHFindEndOfXMLTag(const char *pTagContents, int cbLength)
3078 {
3079  return CHFindEndOfXMLSection(pTagContents, cbLength, '>', 1);
3080 }
3081 
3082 
3083 
3085 // MIME HELPERS
3087 
3088 char *CHGetFileMimeType(const char *szFileName)
3089 {
3090 char *pRval, *p2;
3091 
3092  // for NOW just do this... later check if it's just a file extension
3093  // and get the mime type from just that
3094 
3095  pRval = WBRunResult("xdg-mime","query","filetype",szFileName,NULL);
3096 
3097  // right-trim the result (always)
3098  if(pRval)
3099  {
3100  p2 = pRval + strlen(pRval);
3101  while(p2 > pRval && *(p2 - 1) <= ' ')
3102  {
3103  *(--p2) = 0;
3104  }
3105  }
3106 
3107  return pRval;
3108 }
3109 
3110 char *CHGetMimeDefaultApp(const char *szMimeType)
3111 {
3112 char *pRval, *p2;
3113 
3114  // this function uses xdg-mime to obtain the correct default application
3115  // for handling whatever mime type has been passed to the function
3116 
3117  pRval = WBRunResult("xdg-mime","query","default",szMimeType,NULL);
3118 
3119  // right-trim the result (always)
3120 
3121  if(pRval)
3122  {
3123  p2 = pRval + strlen(pRval);
3124  while(p2 > pRval && *(p2 - 1) <= ' ')
3125  {
3126  *(--p2) = 0;
3127  }
3128  }
3129 
3130 // if(pRval && *pRval) // make sure the thing exists
3131 // {
3132 // if(WBStat(pRval, NULL));
3133 // {
3134 // WB_ERROR_PRINT("%s - unable to stat \"%s\"\n", __FUNCTION__, pRval);
3135 //
3136 // // if the file does not exist or does not resolve, then I'm
3137 // // more or less "b0ned" and must return NULL to indicate the error...
3138 //
3139 // WBFree(pRval);
3140 // pRval = NULL;
3141 // }
3142 // }
3143 
3144  return pRval;
3145 }
3146 
3147 // NOTE: according to http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
3148 // the 'Exec' key in a '.desktop' file may have the following parameters:
3149 // %f - a single file name
3150 // %F - one or more file names
3151 // %u - a single URL
3152 // %U - one or more URLs
3153 // %i - the icon key expanded as 2 arguments (see web page)
3154 // %c - the translated application name (see web page)
3155 // %k - the desktop file's name/location/URI/whatever (see web page)
3156 // (all others are deprecated)
3157 
3158 char *CHGetDesktopFileInfo(const char *szDesktopFile, const char *szInfo)
3159 {
3160 char *pRval, *p1, *p2, *pTemp;
3161 int i1;
3162 
3163  p1 = WBSearchPath(szDesktopFile);
3164  if(!p1)
3165  {
3166  // desktop files can be in a couple of OTHER places...
3167 
3168  if(*szDesktopFile != '/')
3169  {
3170  p1 = WBAlloc(PATH_MAX * 2 + strlen(szDesktopFile));
3171  if(p1)
3172  {
3173  // NOTE: there may be some system config var that tells me whether to use "/usr/share" or "/usr/local/share"
3174  // may ALSO want to look in '/usr/local/share/applications/*' (xdg-open does)
3175  // additionally, '~/.local/share/applications
3176  static const char * const aszPaths[]=
3177  { "/usr/local/share/applications/", "/usr/share/applications/",
3178  "/usr/local/share/app-install/desktop/", "/usr/share/app-install/desktop/" };
3179 
3180  char *p2 = WBGetCanonicalPath("~/.local/share/applications/");
3181  if(p2)
3182  {
3183  strcpy(p1, p2);
3184  WBFree(p2); // keep non-NULL as a flag for later
3185 
3186  strcat(p1, szDesktopFile);
3187  if(WBStat(p1, NULL))
3188  {
3189  p2 = NULL; // as a flag, i.e. NOT found
3190  }
3191  }
3192 
3193  if(!p2) // did not find file in '~/.local/share/applications'
3194  {
3195  for(i1=0; i1 < sizeof(aszPaths)/sizeof(aszPaths[0]); i1++)
3196  {
3197  strcpy(p1, aszPaths[i1]);
3198  strcat(p1, szDesktopFile);
3199  if(!WBStat(p1, NULL)) // file exists?
3200  {
3201  break;
3202  }
3203  }
3204 
3205  if(i1 >= sizeof(aszPaths)/sizeof(aszPaths[0]))
3206  {
3207  WBFree(p1);
3208  p1 = NULL;
3209  }
3210  }
3211  }
3212  }
3213  }
3214 
3215  if(!p1)
3216  {
3217  WB_ERROR_PRINT("%s - File \"%s\" does not exist or cannot be located\n", __FUNCTION__, szDesktopFile);
3218  return NULL;
3219  }
3220 
3221  pTemp = WBCopyString("^");
3222 
3223  if(pTemp)
3224  {
3225  WBCatString(&pTemp, szInfo);
3226  }
3227  if(pTemp)
3228  {
3229  WBCatString(&pTemp, "="); // '^whatever=' for szInfo -> "whatever"
3230  }
3231 
3232  if(!pTemp)
3233  {
3234  WBFree(p1);
3235  return NULL;
3236  }
3237 
3238  pRval = WBRunResult("grep",pTemp,p1,NULL);
3239 
3240  WBFree(p1);
3241  p1 = NULL;
3242 
3243  WB_ERROR_PRINT("TEMPORARY: %s - result \"%s\"\n", __FUNCTION__, pRval);
3244 
3245 
3246  if(pRval && strlen(pRval) >= strlen(pTemp))
3247  {
3248  strcpy(pRval, pRval + strlen(pTemp) - 1); // note '^' at beginning won't be in the result
3249  // TODO: find the '=' and use THAT instead?
3250  }
3251 
3252  WBFree(pTemp);
3253 
3254  // right-trim the result (always)
3255  if(pRval)
3256  {
3257  p2 = pRval + strlen(pRval);
3258  while(p2 > pRval && *(p2 - 1) <= ' ')
3259  {
3260  *(--p2) = 0;
3261  }
3262 
3263  if(!*pRval) // if the resulting string is BLANK
3264  {
3265  WBFree(pRval);
3266  pRval = NULL;
3267  }
3268  }
3269 
3270  return pRval;
3271 }
3272 
3273 
3274 
3275 // this was what I was doing in dialog_control.c and other places...
3276 #define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
3277 #define LOAD_COLOR(X,Y,Z) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) <= 0){ WB_WARN_PRINT("%s - WARNING: can't find color %s, using default value %s\n", __FUNCTION__, X, Z); strcpy(Y,Z); }
3278 
3279 const char *CHGetBorderColor(Display *pDisplay)
3280 {
3281 static char szRval[256];
3282 
3283  LOAD_COLOR("border_color", szRval, "#000000");
3284 
3285  return szRval;
3286 }
3287 
3288 const char *CHGetStaticBackgroundColor(Display *pDisplay)
3289 {
3290 static char szRval[256];
3291 
3292  LOAD_COLOR0("bg_color",szRval) else
3293  LOAD_COLOR0("*Form.background", szRval) else
3294  LOAD_COLOR("*WmForm.background", szRval, "#dcdad5"); // default for gnome is dcdad5
3295 
3296  return szRval;
3297 }
3298 
3299 const char *CHGetToolTipBackgroundColor(Display *pDisplay)
3300 {
3301 static char szRval[256];
3302 
3303  LOAD_COLOR("tooltip_bg_color",szRval, "#f8f880");
3304 
3305  return szRval;
3306 }
3307 
3308 const char *CHGetDialogBackgroundColor(Display *pDisplay)
3309 {
3310 static char szRval[256];
3311 
3312  LOAD_COLOR0("bg_color",szRval) else
3313  LOAD_COLOR0("*Dialog.background",szRval) else
3314  LOAD_COLOR0("*Form.background", szRval) else
3315  LOAD_COLOR0("*WmDialogShell.background",szRval) else
3316  LOAD_COLOR0("*WmForm.background", szRval) else
3317  LOAD_COLOR0("*Window.background",szRval) else
3318  LOAD_COLOR("*background", szRval, "#dcdad5"); // default for gnome is dcdad5
3319 
3320  return szRval;
3321 }
3322 
3323 const char *CHGetActiveBackgroundColor(Display *pDisplay)
3324 {
3325 static char szRval[256];
3326 
3327  // for now, same as normal (non-static) background color
3328 
3329  LOAD_COLOR0("base_color",szRval) else // 'base_color' is the normal background color for non-static windows
3330  LOAD_COLOR0("*Window.background",szRval) else
3331  LOAD_COLOR("*background", szRval, "#edeceb");
3332 
3333  return szRval;
3334 }
3335 
3336 const char *CHGetDisabledBackgroundColor(Display *pDisplay)
3337 {
3338  return CHGetStaticBackgroundColor(pDisplay); // for now
3339 }
3340 
3341 const char *CHGetBackgroundColor(Display *pDisplay)
3342 {
3343 static char szRval[256];
3344 
3345  LOAD_COLOR0("base_color",szRval) else // 'base_color' is the normal background color for non-static windows
3346  LOAD_COLOR0("*Window.background",szRval) else
3347  LOAD_COLOR("*background", szRval, "#ffffff");
3348 
3349  return szRval;
3350 }
3351 
3352 const char *CHGetDialogTextColor(Display *pDisplay)
3353 {
3354 static char szRval[256];
3355 
3356  LOAD_COLOR0("fg_color",szRval) else
3357  LOAD_COLOR0("*Dialog.foreground",szRval) else
3358  LOAD_COLOR0("*Form.foreground", szRval) else
3359  LOAD_COLOR0("*WmDialogShell.foreground",szRval) else
3360  LOAD_COLOR0("*WmForm.foreground", szRval) else
3361  LOAD_COLOR("*foreground", szRval, "#000000");
3362 
3363  return szRval;
3364 }
3365 
3366 const char *CHGetActiveTextColor(Display *pDisplay)
3367 {
3368 static char szRval[256];
3369 
3370  // for now, same as normal (non-static) foreground/text color
3371 
3372  LOAD_COLOR0("fg_color",szRval) else
3373  LOAD_COLOR("*foreground", szRval, "#000000");
3374 
3375  return szRval;
3376 }
3377 
3378 const char *CHGetDisabledTextColor(Display *pDisplay)
3379 {
3380 //static char szRval[256];
3381 //
3382 // LOAD_COLOR0("fg_color",szRval) else
3383 // LOAD_COLOR("*foreground", szRval, "#000000");
3384 //
3385 // return szRval;
3386 
3387  return "#808080"; // for now
3388 }
3389 
3390 const char *CHGetToolTipTextColor(Display *pDisplay)
3391 {
3392 static char szRval[256];
3393 
3394  LOAD_COLOR("tooltip_fg_color",szRval, "#202020");
3395 
3396  return szRval;
3397 }
3398 
3399 const char *CHGetTextColor(Display *pDisplay)
3400 {
3401 static char szRval[256];
3402 
3403  LOAD_COLOR0("fg_color",szRval) else
3404  LOAD_COLOR("*foreground", szRval, "#000000");
3405 
3406  return szRval;
3407 }
3408 
3409 const char *CHGetHighlightForegroundColor(Display *pDisplay)
3410 {
3411 static char szRval[256];
3412 
3413  LOAD_COLOR0("selected_fg_color",szRval) else
3414  LOAD_COLOR0("*selectForeground",szRval) else
3415  LOAD_COLOR0("*highlightColor",szRval) else
3416  LOAD_COLOR("*highlightForeground", szRval, "#ffffff");
3417 
3418 // "*Text.selectForeground", "*Text.selectBackground",
3419 // "*Label.highlightColor", "*Label.highlightBackground",
3420 // "*Button.highlightForeground", "*Button.highlightBackground",
3421 
3422  return szRval;
3423 }
3424 
3425 const char *CHGetHighlightBackgroundColor(Display *pDisplay)
3426 {
3427 static char szRval[256];
3428 
3429  LOAD_COLOR0("selected_bg_color",szRval) else
3430  LOAD_COLOR0("*selectForeground",szRval) else
3431  LOAD_COLOR("*highlightBackground", szRval, "#20ab20");
3432 
3433  return szRval;
3434 }
3435 
3436 
3437 
3438 
3440 // DEBUG FUNCTIONS
3442 
3443 #ifndef NO_DEBUG
3445 {
3446 int i1, nSettings;
3447 
3448  if(!pXSettings)
3449  {
3450  WBDebugPrint("WARNING: no CHXSettings data in CHDumpConfig\n");
3451  return;
3452  }
3453 
3454  WBDebugPrint("========================================================================\n");
3455  WBDebugPrint("CHXSettings dump - DISP: %p # %d SER: %u (%08xH)\n",
3456  pXSettings->pDisplay, pXSettings->nSettings, pXSettings->uiSerial, pXSettings->uiSerial);
3457 
3458  nSettings = pXSettings->nSettings;
3459 
3460  for(i1=0; i1 < nSettings; i1++)
3461  {
3462  WBDebugPrint("%3d %-32.32s ", i1, pXSettings->aData[i1].szName);
3463 
3464  switch(pXSettings->aData[i1].iType)
3465  {
3466  case XSettingsTypeInteger:
3467  WBDebugPrint(" integer %d (%08xH)\n",
3468  pXSettings->aData[i1].uData.iData,
3469  pXSettings->aData[i1].uData.iData);
3470  break;
3471 
3472  case XSettingsTypeColor:
3473  WBDebugPrint(" color R:%d B:%d G:%d A:%d\n",
3474  pXSettings->aData[i1].uData.clrData.sRed,
3475  pXSettings->aData[i1].uData.clrData.sBlue,
3476  pXSettings->aData[i1].uData.clrData.sGreen,
3477  pXSettings->aData[i1].uData.clrData.sAlpha);
3478  break;
3479 
3480  case XSettingsTypeString:
3481 // if(pXSettings->aData[i1].iLen > 32)
3482 // {
3483 // WBDebugPrint(" \"%-.32s ...\n",
3484 // pXSettings->aData[i1].uData.szData);
3485 // }
3486 // else
3487  {
3488  WBDebugPrint(" \"%s\"\n",
3489  pXSettings->aData[i1].uData.szData);
3490  }
3491  break;
3492  }
3493  }
3494 
3495  WBDebugPrint("========================================================================\n");
3496 
3497 }
3498 
3499 #endif // !NO_DEBUG
3500 
const char * CHGetHighlightForegroundColor(Display *pDisplay)
returns highlight foreground color
Definition: conf_help.c:3409
char * CHGetMimeDefaultApp(const char *szMimeType)
Get the default application for a particular MIME type.
Definition: conf_help.c:3110
#define WB_LIKELY(x)
optimization for code branching when condition is 'likely'. use within conditionals
char * szData
'char' typed pointer to data within string area
Definition: conf_help.h:435
char ** ppLineBuf
array of pointers to beginning of each line (WBAlloc'd TODO: make it part of 'cData'?...
Definition: file_help.h:171
const CHXSettings * CHGetXSettings(Display *pDisplay)
returns a pointer to the cached X settings
int CHGetDoubleClickDistance(Display *pDisplay)
returns default double click distance (from X settings)
Definition: conf_help.c:2241
int iContentsIndex
0-based first array index for 'contents' for this entry; <= 0 for none
Definition: conf_help.h:798
unsigned short sBlue
16-bit RGB value for Blue
Definition: conf_help.h:385
'window helper' main header file for the X11workbench Toolkit API
#define CHPARSEXML_PAREN
Definition: conf_help.h:131
int FBWriteFileBufHandle(int iFile, const file_help_buf_t *pBuf)
Write the file_help_buf_t object's text data to a file using an open file handle.
Definition: file_help.c:303
long lBufferCount
number of bytes of valid data
Definition: file_help.h:167
int nDataOffset
BYTE offset to data (zero-byte-terminated) string (from beginning of array) for the entry data; <= 0 ...
Definition: conf_help.h:801
Utilities for copying and drawing text, determining text extents, and so on.
int iContainer
0-based index for container; <= 0 for none.
Definition: conf_help.h:797
const CHXSetting * CHGetXSetting(Display *pDisplay, const char *szSettingName)
returns a pointer to a specific X setting entry by name
Definition: conf_help.c:1746
char * WBRunResult(const char *szAppName,...)
Run an application synchronously, returning 'stdout' output in a character buffer.
XSETTINGS_DATA_COLOR clrData
The data as a color value.
Definition: conf_help.h:433
void FBInsertLineIntoFileBuf(file_help_buf_t **ppBuf, long lLineNum, const char *szLine)
Insert a line of text into a file_help_buf_t object at a specific line index.
Definition: file_help.c:461
void CHOnExit(void)
frees resources used by Configuration 'helper' functions
Definition: conf_help.c:166
const char * CHFindEndOfXMLTag(const char *pTagContents, int cbLength)
Parses contents of an XML tag to find the end of it.
Definition: conf_help.c:3077
Atom aINCR
INCR Atom for the clipboard.
const char * CHGetActiveBackgroundColor(Display *pDisplay)
returns background color for active elements
Definition: conf_help.c:3323
const char * CHGetStaticBackgroundColor(Display *pDisplay)
returns background color for static elements
Definition: conf_help.c:3288
void FBDestroyFileBuf(file_help_buf_t *pBuf)
Destroy a file_help_buf_t object.
Definition: file_help.c:186
char cData[sizeof(char *)]
the data itself (aligned to size of a pointer)
Definition: file_help.h:172
Array wrapper for CHXSetting cache.
Definition: conf_help.h:465
CHXSetting aData[1]
An array of 'nSettings' CHXSetting structures. Binary and string data directly follows the array.
Definition: conf_help.h:470
struct tagXSETTINGS_DATA_COLOR XSETTINGS_DATA_COLOR
Structure for storing configuration color information, XSettingsTypeColor.
void * CHOpenConfFile(const char *szAppName, int iFlags)
open configuration file for read/write, optionally creating it, based on application name
Definition: conf_help.c:643
static const char * InternalParseXML(CHXMLEntry **ppOrigin, int *pcbOrigin, CHXMLEntry **ppCur, char **ppData, int *pcbData, char **ppCurData, const char *ppXMLData, const char *pXMLDataEnd)
Parses contents of an XML tag, returning as WBAlloc'd string list similar to environment strings.
Definition: conf_help.c:2411
const char * CHFindEndOfXMLSection(const char *pTagContents, int cbLength, char cEndChar, int bUseQuotes)
Parses XML text for the end of a 'section', typically ending in '>' ')' or ']'.
Definition: conf_help.c:2941
int CHGetConfFileString(void *hFile, const char *szSection, const char *szIdentifier, char *szData, int cbData)
obtain a string from a configuration file
Definition: conf_help.c:1242
unsigned int uiSerial
serial number from last XSETTINGS query
Definition: conf_help.h:469
'configuration helper' main header file for the X11 Work Bench Toolkit API
int WBMkDir(const char *szFileName, int flags)
a wrapper for 'mkdir' that makes directories recursively (as needed)
const char * CHGetActiveTextColor(Display *pDisplay)
returns 'active' text color
Definition: conf_help.c:3366
void FBDeleteLineFromFileBuf(file_help_buf_t *pBuf, long lLineNum)
Delete a line of text from a file_help_buf_t object at a specific line index.
Definition: file_help.c:525
void * WBReAlloc(void *pBuf, int nNewSize)
High performance memory sub-allocator 're-allocate'.
int CHGetCursorBlinkTime(Display *pDisplay)
returns default cursor blink time (from X settings)
Definition: conf_help.c:2358
int iType
The setting type (XSettingsType enumeration)
Definition: conf_help.h:426
const char * CHFindNextXMLTag(const char *pTagContents, int cbLength, int nNestingFlags)
Parses contents of a XML to find the next tag, skipping comments along the way.
Definition: conf_help.c:2889
#define CH_FLAGS_WRITE
Definition: conf_help.h:128
const char * CHGetDisabledTextColor(Display *pDisplay)
returns 'disabled' (greyed) text color
Definition: conf_help.c:3378
int CHGetArgC()
retrieves the argument count stored by CHRegisterArgs()
Definition: conf_help.c:630
int WBStat(const char *szLinkName, unsigned long *pdwModeAttrReturn)
Obtain the 'stat' flags for a file name, resolving links as needed.
Definition: file_help.c:1574
int CHGetResourceString(Display *pDisplay, const char *szIdentifier, char *szData, int cbData)
Queries desktop resource strings (may become deprecated)
Definition: conf_help.c:474
int CHWriteConfFileString(void *hFile, const char *szSection, const char *szIdentifier, const char *szData)
write a string to a configuration file
Definition: conf_help.c:1376
int nLabelOffset
BYTE offset to label (zero-byte-terminated) string (from beginning of array) for this entry; <= 0 for...
Definition: conf_help.h:800
void CHCloseConfFile(void *pFile)
close configuration file opened by CHOpenConfFile(), but does NOT free memory resources
Definition: conf_help.c:850
const char * CHGetTextColor(Display *pDisplay)
returns text color
Definition: conf_help.c:3399
long lLineCount
number of lines in 'cData' when ppLineBuf not NULL
Definition: file_help.h:168
const char * CHGetDialogTextColor(Display *pDisplay)
returns text color for dialog frame elements
Definition: conf_help.c:3352
void * WBAlloc(int nSize)
High performance memory sub-allocator 'allocate'.
int CHGetResourceInt(Display *pDisplay, const char *szIdentifier)
Queryies desktop resource integer values (from strings) (may become deprecated)
Definition: conf_help.c:604
CHXMLEntry * CHParseXML(const char *pXMLData, int cbLength)
Parses contents of an XML tag, returning as WBAlloc'd string list similar to environment strings.
Definition: conf_help.c:2465
void CHRegisterArgs(int argc, char **argv)
registers arguments from the command line for later use
Definition: conf_help.c:158
void CHDumpConfig()
dump config information using debug output functions
Definition: conf_help.c:3444
#define CH_FLAGS_GLOBAL
Definition: conf_help.h:127
int iData
The data as an integer value.
Definition: conf_help.h:431
const char * CHGetHighlightBackgroundColor(Display *pDisplay)
returns highlight background color
Definition: conf_help.c:3425
#define WB_ERROR_PRINT(...)
Preferred method of implementing an 'error level' debug message for all subsystems.
Definition: debug_helper.h:474
char * WBSearchPath(const char *szFileName)
search for a file using the PATH environment variable
int WBIsDirectory(const char *szFileName)
Return whether a file is a directory or a symlink to a directory.
Definition: file_help.c:776
void WBFree(void *pBuf)
High performance memory sub-allocator 'free'.
int FBParseFileBuf(file_help_buf_t *pBuf)
Parse or Re-Parse the data for a file_help_buf_t object.
Definition: file_help.c:209
const char * CHGetDialogBackgroundColor(Display *pDisplay)
returns background color for dialog frame elements
Definition: conf_help.c:3308
const char * szName
pointer to the NAME string within memory that follows CHXSettings::aData
Definition: conf_help.h:425
int CHGetDoubleClickTime(Display *pDisplay)
returns default double click time (from X settings)
Definition: conf_help.c:2202
The setting is an RGBA Color stored as XSETTINGS_DATA_COLOR.
Definition: conf_help.h:354
int CHGetCursorBlink(Display *pDisplay)
returns default cursor blink 'enable' flag (from X settings)
Definition: conf_help.c:2319
int iLen
length (where applicable)
Definition: conf_help.h:427
unsigned long long WB_UINTPTR
Platform abstract unsigned integer that matches pointer size.
int CHWriteConfFileInt(void *hFile, const char *szSection, const char *szIdentifier, int iData)
write an integer value to a configuration file
Definition: conf_help.c:1662
unsigned short sAlpha
16-bit ALPHA value. If not used, it will be 0xffff
Definition: conf_help.h:387
Display * pDisplay
identifies which display it belongs to (reserved)
Definition: conf_help.h:467
Descriptor for parsed XML entry.
Definition: conf_help.h:794
void CHSettingsRefresh(Display *pDisplay)
refresh the internally cached X settings
Definition: conf_help.c:1798
int CHGetConfFileInt(void *hFile, const char *szSection, const char *szIdentifier)
obtain an integer value from a configuration file
Definition: conf_help.c:1640
void FBReplaceLineInFileBuf(file_help_buf_t **ppBuf, long lLineNum, const char *szLine)
Insert a line of text into a file_help_buf_t object at a specific line index.
Definition: file_help.c:561
static __inline__ int FBIsFileBufDirty(const file_help_buf_t *pBuf)
Inline function, returns TRUE if file_help_buf_t is 'dirty'.
Definition: file_help.h:210
basic 'buffered I/O' object structure for 'FileBuf' APIs
Definition: file_help.h:162
char * CHGetFileMimeType(const char *szFileName)
Get the MIME type for a particular file name or extension.
Definition: conf_help.c:3088
The setting is an integer.
Definition: conf_help.h:352
char * WBCopyStringN(const char *pSrc, unsigned int nMaxChars)
A simple utility that returns a WBAlloc() copy of a string up to a maximum length (can also be 0-byte...
unsigned short sGreen
16-bit RGB value for Green
Definition: conf_help.h:386
const char * CHGetDisabledBackgroundColor(Display *pDisplay)
returns background color for disabled (greyed) elements
Definition: conf_help.c:3336
const char * CHGetToolTipTextColor(Display *pDisplay)
returns text color for 'tool tips'
Definition: conf_help.c:3390
char * WBGetCanonicalPath(const char *szFileName)
Return the canonical path for a file name (similar to POSIX 'realpath()' funtion)
Definition: file_help.c:803
const char * CHGetBackgroundColor(Display *pDisplay)
returns background color for non-static elements
Definition: conf_help.c:3341
#define CHPARSEXML_BRACKET
Definition: conf_help.h:132
int CHGetDragThreshold(Display *pDisplay)
returns default drag threshold (from X settings)
Definition: conf_help.c:2280
int iNextIndex
0-based index for next item at this level; <= 0 for none. 0 marks "end of list" for top level
Definition: conf_help.h:796
const char *const * CHGetArgV()
retrieves the array of arguments stored by CHRegisterArgs()
Definition: conf_help.c:635
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
union tagCHXSetting::@7 uData
union of the data alias'
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
void CHDestroyConfFile(void *pFile)
destroy configuration file opened by CHOpenConfFile(), freeing memory resources (but not the files)
Definition: conf_help.c:882
The setting is a 0-byte terminated ASCII string.
Definition: conf_help.h:353
int nSettings
the total number of settings in aData
Definition: conf_help.h:468
file_help_buf_t * FBGetFileBufViaHandle(int iFile)
Construct a file_help_buf_t from a file handle.
Definition: file_help.c:123
#define WB_IF_DEBUG_LEVEL(L)
Preferred method of implementing conditional debug 'if block' code.
Definition: debug_helper.h:404
struct tagCHXMLEntry CHXMLEntry
Descriptor for parsed XML entry.
char * CHGetDesktopFileInfo(const char *szDesktopFile, const char *szInfo)
Get the default application for a particular MIME type.
Definition: conf_help.c:3158
const char * CHGetBorderColor(Display *pDisplay)
returns border color
Definition: conf_help.c:3279
Structure for storing configuration color information, XSettingsTypeColor.
Definition: conf_help.h:382
unsigned short sRed
16-bit RGB value for RED
Definition: conf_help.h:384
void WBNormalizeXMLString(char *pString)
De-Quote and 'normalize' an XML string 'in place', that is modifying the original string by removing ...
#define WB_UNLIKELY(x)
optimization for code branching when condition is 'unlikely'. use within conditionals
struct tagCHXSetting CHXSetting
Structure for storing settings information internally.
char * CHParseXMLTagContents(const char *pTagContents, int cbLength)
Parses contents of a single XML tag, returning as WBAlloc'd string list similar to environment string...
Definition: conf_help.c:2600
void WBCatString(char **ppDest, const char *pSrc)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string.
Structure for storing settings information internally.
Definition: conf_help.h:423
const char * CHGetToolTipBackgroundColor(Display *pDisplay)
returns background color for 'tool tips'
Definition: conf_help.c:3299