X11 Work Bench 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-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17  all rights reserved
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  BSD-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  BSD-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 
503  const char *pTemp = TranslateColorRequest(szIdentifier);
504 
505  if(pTemp && *pTemp)
506  {
507 // WB_ERROR_PRINT("TEMPORARY: %s - found color %s from %s\n", __FUNCTION__, pTemp, szIdentifier);
508 
509  pXS = CHGetXSetting(pXSettings->pDisplay, "Gtk/ColorScheme"); // get color scheme info
510 
511  if(pXS && pXS->iType == XSettingsTypeString)
512  {
513  int iLen0 = strlen(pTemp);
514  const char *p1 = pXS->uData.szData;
515  // format is "colorname=#colordef\n"
516 
517  while(*p1)
518  {
519  const char *p2 = p1;
520 
521  p1 = p2;
522 
523  while(*p1 && *p1 != '\n')
524  {
525  p1++;
526  }
527 
528 // WB_ERROR_PRINT("TEMPORARY: %s - %-.*s\n", __FUNCTION__, p1 - p2, p2);
529 
530  if(!memcmp(p2, pTemp, iLen0) && p2[iLen0] == ':')
531  {
532  p2 = p2 + iLen0 + 1;
533  iLen0 = p1 - p2;
534 
535  if(iLen0 >= cbData)
536  {
537  iLen0 = cbData - 1;
538  }
539  if(iLen0 > 0)
540  {
541  memcpy(szData, p2, iLen0);
542  }
543 
544  szData[iLen0] = 0;
545  return iLen0;
546  }
547 
548  if(*p1 == '\n')
549  {
550  p1++;
551  }
552  }
553  }
554 
555  pXS = NULL; // not found if it gets here
556  }
557  }
558 
559  if(pXS)
560  {
561  if(pXS->iType == XSettingsTypeInteger)
562  {
563  iRval = snprintf(szData, cbData, "%d", pXS->uData.iData);
564  }
565  else if(pXS->iType == XSettingsTypeString)
566  {
567  iRval = strlen(pXS->uData.szData);
568 
569  if(iRval >= cbData)
570  {
571  iRval = cbData - 1;
572  }
573 
574  if(iRval > 0)
575  {
576  memcpy(szData, pXS->uData.szData, iRval);
577  }
578 
579  szData[iRval] = 0;
580  }
581  else
582  {
583  iRval = -1; // for now... (later try and fix this)
584  }
585  }
586 
587 // iRval = something
588  }
589 
590  if(iRval < 0) // i.e. "not found"
591  {
592  return CHGetResourceString_Old(pDisplay, szIdentifier, szData, cbData); // fallback
593  }
594 
595  return iRval;
596 }
597 
598 int CHGetResourceInt(Display *pDisplay, const char *szIdentifier)
599 {
600  int iLen;
601  char tbuf[64];
602 
603  if((iLen = CHGetResourceString(pDisplay, szIdentifier, tbuf, sizeof(tbuf) - 1)) > 0)
604  {
605  tbuf[iLen] = 0;
606  while(tbuf[0] && tbuf[0] <= ' ')
607  {
608  strcpy(tbuf, tbuf + 1);
609  }
610 
611  if(tbuf[0])
612  {
613  return atoi(tbuf);
614  }
615  }
616 
617  return 0;
618 }
619 
620 
621 
622 // argc/argv utilities (assigned via initialization)
623 
625 {
626  return argc_copy;
627 }
628 
629 const char * const *CHGetArgV()
630 {
631  return argv_copy;
632 }
633 
634 
635 // configuration file utilities
636 
637 void * CHOpenConfFile(const char *szAppName, int iFlags)
638 {
639  CONF_FILE *pRval;
640  char *p1, *p2, *p3, *p4, /* *p5, */ *p6;
641 // int i1;
642  struct stat st;
643 // file_help_buf_t *pFHB = NULL;
644 
645  static const char szGlobalPath[] = GLOBAL_PATH;
646  static const char szGlobalXPath[] = GLOBAL_XPATH;
647  static const char szLocalPath[] = LOCAL_PATH;
648  static const char szConf[] = ".conf";
649  char szLocalPath0[PATH_MAX];
650 
651 
652  // create struct
653 
654  pRval = (CONF_FILE *)WBAlloc(sizeof(*pRval) + strlen(szAppName) * 4 + sizeof(szGlobalPath)
655  + sizeof(szGlobalXPath) + sizeof(szLocalPath) + 16 + PATH_MAX * 2);
656  if(!pRval)
657  {
658  return NULL;
659  }
660 
661  bzero(pRval, sizeof(*pRval)); // make sure it's zero'd out
662 
663  // construct file names
664 
665  p1 = (char *)pRval + sizeof(*pRval); // this is the start of string buffers
666  // it is also the global path, by default either /etc or /usr/local/etc
667 
668  p2 = DoMakePath(p1, szGlobalPath, szAppName, szConf);
669  p3 = DoMakePath(p2, szGlobalXPath, szAppName, szConf);
670 
671  if(!(iFlags & CH_FLAGS_GLOBAL)) // not "global only"
672  {
673  strcpy(szLocalPath0, szLocalPath);
674  if(!szLocalPath0[0] || szLocalPath0[strlen(szLocalPath0)-1] != '/')
675  {
676  strcat(szLocalPath0, "/");
677  }
678  strcat(szLocalPath0, ".");
679  strncat(szLocalPath0, szAppName, sizeof(szLocalPath0) - strlen(szLocalPath0) - 1);
680 
681  // make sure the directory 'szLocalPath0' exists
682  p4 = WBGetCanonicalPath(szLocalPath0);
683  if(p4)
684  {
685  mkdir(p4, 0755); // TODO: check user's UMASK, 'stat' first to see if I need to create it, etc.
686  strncpy(szLocalPath0, p4, sizeof(szLocalPath0));
687  WBFree(p4);
688  }
689 
690  if(szLocalPath0[strlen(szLocalPath0) - 1] != '/')
691  {
692  strcat(szLocalPath0, "/");
693  }
694 
695  p4 = DoMakePath(p3, szLocalPath0, LOCAL_CONF_NAME, szConf); // first THIS one
696 
697  p6 = WBGetCanonicalPath(szLocalPath);
698  if(p6)
699  {
700 // NOTE: p5 not being used; commented out because of linux gcc warnings
701 // p5 = DoMakePath(p4, p6, szAppName, szConf); // then THIS one
702  WBFree(p6);
703  }
704  else
705  {
706 // NOTE: p5 not being used; commented out because of linux gcc warnings
707 // p5 = DoMakePath(p4, szLocalPath, szAppName, szConf); // alternate (uncanonical) name
708  }
709  }
710  else
711  {
712  p4 = NULL; // warning avoidance (uninitialized variable, actually won't matter)
713  }
714 
715  if(!stat(p1, &st) && S_ISREG(st.st_mode))
716  {
717  if(iFlags & CH_FLAGS_WRITE)
718  {
719  pRval->iGlobal = open(p1,O_RDWR);
720  }
721  else
722  {
723  pRval->iGlobal = -1;
724  }
725 
726  if(pRval->iGlobal == -1)
727  {
728  pRval->iGlobal = open(p1,O_RDONLY);
729  }
730  }
731 
732  if(pRval->iGlobal != -1)
733  {
734  pRval->iOffsGlobal = (int)(p1 - (char *)pRval);
735  }
736  else if(!stat(p2, &st) && S_ISREG(st.st_mode))
737  {
738  if(iFlags & CH_FLAGS_WRITE)
739  {
740  pRval->iGlobal = open(p2,O_RDWR);
741  }
742  else
743  {
744  pRval->iGlobal = -1;
745  }
746  if(pRval->iGlobal == -1)
747  {
748  pRval->iGlobal = open(p2,O_RDONLY);
749  }
750  if(pRval->iGlobal != -1)
751  {
752  pRval->iOffsGlobal = (int)(p2 - (char *)pRval);
753  }
754  }
755 
756  if(!(iFlags & CH_FLAGS_GLOBAL)) // not "global only"
757  {
758  // if local file does not exist, create it (always open read/write)
759 
760  if(!stat(p3, &st) && S_ISREG(st.st_mode))
761  {
762  pRval->iLocal = open(p3,O_RDWR);
763  }
764  else
765  {
766  pRval->iLocal = open(p3,O_CREAT|O_RDWR,0644); // use 0644 for now - TODO: check umask
767  }
768 
769  if(pRval->iLocal != -1)
770  {
771  pRval->iOffsLocal = (int)(p3 - (char *)pRval);
772  }
773  else // try the OTHER local
774  {
775  // if local file does not exist, create it (always open read/write)
776 
777  if(!stat(p4, &st) && S_ISREG(st.st_mode))
778  {
779  pRval->iLocal = open(p4,O_RDWR);
780  }
781  else
782  {
783  pRval->iLocal = open(p4,O_CREAT|O_RDWR,0644); // use 0644 for now - TODO: check umask
784  }
785 
786  if(pRval->iLocal != -1)
787  {
788  pRval->iOffsLocal = (int)(p4 - (char *)pRval);
789  }
790  }
791  }
792  else // GLOBAL ONLY
793  {
794  pRval->iLocal = -1;
795  pRval->iOffsLocal = 0;
796  }
797 
798  // next read and parse the files
799 
800  if(pRval->iLocal != -1)
801  {
802  pRval->pfhbL = FBGetFileBufViaHandle(pRval->iLocal);
803  if(pRval->pfhbL)
804  {
805  FBParseFileBuf(pRval->pfhbL);
806  }
807  else
808  {
809  CHDestroyConfFile(pRval);
810  return NULL;
811  }
812  }
813 
814  if(pRval->iGlobal != -1)
815  {
816  pRval->pfhbG = FBGetFileBufViaHandle(pRval->iGlobal);
817  if(pRval->pfhbG)
818  {
819  FBParseFileBuf(pRval->pfhbG);
820  }
821  else
822  {
823  CHDestroyConfFile(pRval);
824  return NULL;
825  }
826  }
827 
828  return pRval;
829 }
830 
831 void CHCloseConfFile(void * pFile)
832 {
833  CONF_FILE *pTemp = (CONF_FILE *)pFile;
834 
835  if(!pTemp)
836  {
837  return;
838  }
839 
840  if(pTemp->iLocal != -1)
841  {
842  if(pTemp->pfhbL && FBIsFileBufDirty(pTemp->pfhbL))
843  {
844  FBWriteFileBufHandle(pTemp->iLocal, pTemp->pfhbL);
845  }
846 
847  close(pTemp->iLocal);
848  pTemp->iLocal = -1;
849  }
850 
851  if(pTemp->iGlobal != -1)
852  {
853  if(pTemp->pfhbG && FBIsFileBufDirty(pTemp->pfhbG))
854  {
855  FBWriteFileBufHandle(pTemp->iGlobal, pTemp->pfhbG);
856  }
857 
858  close(pTemp->iGlobal);
859  pTemp->iGlobal = -1;
860  }
861 }
862 
863 void CHDestroyConfFile(void * pFile)
864 {
865  CONF_FILE *pTemp = (CONF_FILE *)pFile;
866 
867  if(!pTemp)
868  {
869  return;
870  }
871 
872  CHCloseConfFile(pFile);
873 
874  if(pTemp->pfhbL)
875  {
876  FBDestroyFileBuf(pTemp->pfhbL);
877  }
878 
879  if(pTemp->pfhbG)
880  {
881  FBDestroyFileBuf(pTemp->pfhbG);
882  }
883 }
884 
885 
886 // Utilities to find stuff within a config file
887 // on entry ppStart points to the beginning of the line, and ppEnd points to the end of it
888 // on return, ppStart points to the start of the data, ppEnd points to the end of it (excluding comments)
889 
890 static void __get_line_strip_comments__(const char **ppStart, const char **ppEnd)
891 {
892  const char *p1 = *ppStart, *p2 = *ppEnd;
893 
894  // skip leading white space
895  while(p1 < p2 && *p1 <= ' ')
896  {
897  p1++;
898  }
899  if(p1 >= p2)
900  {
901  *ppStart = *ppEnd = p2;
902 
903  return;
904  }
905 
906  *ppStart = p1;
907 
908  while(p1 < p2)
909  {
910  if(*p1 == '=') // everything to the right of '=' isn't a comment
911  {
912  break;
913  }
914 
915  if(*p1 == ';') // comment?
916  {
917  p2 = p1;
918  break;
919  }
920 
921  p1++;
922  }
923 
924  // now trim off any trailing white space
925  p1 = *ppStart;
926  while(p2 > p1 && *(p2 - 1) <= ' ')
927  {
928  p2--;
929  }
930 
931  *ppEnd = p2;
932 }
933 
934 static void __find_section__(void *hFile, const char *szSection,
935  const char **ppSection, const char **ppEndSection)
936 {
937  int i1, /* i2,*/ iSectionLen;
938  const char *p1, *p2, *pSection, *pEndSection;
939  CONF_FILE *pTemp = (CONF_FILE *)hFile;
940 
941  *ppSection = pSection = NULL;
942  *ppEndSection = pEndSection = NULL;
943 
944  if(!pTemp ||
945  ((!pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf) &&
946  (!pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)))
947  {
948  return;
949  }
950 
951  iSectionLen = strlen(szSection);
952 
953  if(pTemp->pfhbL)
954  {
955  for(i1=0; i1 < pTemp->pfhbL->lLineCount; i1++)
956  {
957  // search for the section header
958  p1 = pTemp->pfhbL->ppLineBuf[i1];
959  p2 = pTemp->pfhbL->ppLineBuf[i1 + 1];
960  if(!p2)
961  {
962  p2 = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
963  }
964 
965  __get_line_strip_comments__(&p1, &p2);
966 
967  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
968  {
969  p1++;
970  p2--;
971  trim_ends(&p1, &p2);
972  if((p2 - p1) == iSectionLen && !strncasecmp(p1, szSection, iSectionLen))
973  {
974  pSection = pTemp->pfhbL->ppLineBuf[i1 + 1];
975  break;
976  }
977  }
978  }
979 
980  if(pSection)
981  {
982  // starting with the current position, keep going until I find another section
983 
984  for(i1++; i1 < pTemp->pfhbL->lLineCount; i1++)
985  {
986  // search for the section header
987  p1 = pTemp->pfhbL->ppLineBuf[i1];
988  p2 = pTemp->pfhbL->ppLineBuf[i1 + 1];
989  if(!p2)
990  {
991  p2 = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
992  }
993 
994  __get_line_strip_comments__(&p1, &p2);
995 
996  if((p2 - p1) > 2 && *p1 == '[' && *(p2 - 1) == ']')
997  {
998  pEndSection = pTemp->pfhbL->ppLineBuf[i1];
999  break;
1000  }
1001  }
1002 
1003  if(!pEndSection)
1004  {
1005  pEndSection = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
1006  }
1007 
1008  // return values
1009  *ppSection = pSection;
1010  *ppEndSection = pEndSection;
1011  }
1012  }
1013 
1014  if(pTemp->pfhbG && !*ppSection) // not found yet
1015  {
1016  *ppSection = pSection = NULL; // make sure
1017  *ppEndSection = pEndSection = NULL;
1018 
1019  for(i1=0; i1 < pTemp->pfhbG->lLineCount; i1++)
1020  {
1021  // search for the section header
1022  p1 = pTemp->pfhbG->ppLineBuf[i1];
1023  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1024  if(!p2)
1025  {
1026  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1027  }
1028 
1029  __get_line_strip_comments__(&p1, &p2);
1030 
1031  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
1032  {
1033  p1++;
1034  p2--;
1035  trim_ends(&p1, &p2);
1036  if((p2 - p1) == iSectionLen && !strncasecmp(p1, szSection, iSectionLen))
1037  {
1038  pSection = pTemp->pfhbG->ppLineBuf[i1 + 1];
1039  break;
1040  }
1041  }
1042  }
1043 
1044  if(!pSection)
1045  {
1046 // fprintf(stderr, "pSection is NULL\n");
1047  return;
1048  }
1049 
1050  // starting with the current position, keep going until I find another section
1051 
1052  for(i1++; i1 < pTemp->pfhbG->lLineCount; i1++)
1053  {
1054  // search for the section header
1055  p1 = pTemp->pfhbG->ppLineBuf[i1];
1056  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1057  if(!p2)
1058  {
1059  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1060  }
1061 
1062  __get_line_strip_comments__(&p1, &p2);
1063 
1064  if((p2 - p1) > 2 && *p1 == '[' && *(p2 - 1) == ']')
1065  {
1066  pEndSection = pTemp->pfhbG->ppLineBuf[i1];
1067  break;
1068  }
1069  }
1070 
1071  if(!pEndSection)
1072  {
1073  pEndSection = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1074  }
1075 
1076  // return values
1077  *ppSection = pSection;
1078  *ppEndSection = pEndSection;
1079  }
1080 
1081 // WB_ERROR_PRINT("TEMPORARY: %s \"%s\" section %d:\n", __FUNCTION__, szSection, (int)(pEndSection - pSection));
1082 // WB_ERROR_PRINT("%-.*s\n------------------------\n", (int)(pEndSection - pSection), pSection);
1083 
1084  fflush(stderr);
1085 }
1086 
1087 #if 0 // RESERVED (not currently used)
1088 
1089 static void __find_global_section__(void *hFile, const char *szSection,
1090  const char **ppSection, const char **ppEndSection)
1091 {
1092  int i1, /* i2,*/ iSectionLen;
1093  const char *p1, *p2, *pSection, *pEndSection;
1094  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1095 
1096  *ppSection = pSection = NULL;
1097  *ppEndSection = pEndSection = NULL;
1098 
1099  if(!pTemp || !pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)
1100  {
1101  return;
1102  }
1103 
1104  iSectionLen = strlen(szSection);
1105 
1106  for(i1=0; i1 < pTemp->pfhbG->lLineCount; i1++)
1107  {
1108  // search for the section header
1109  p1 = pTemp->pfhbG->ppLineBuf[i1];
1110  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1111  if(!p2)
1112  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1113 
1114  __get_line_strip_comments__(&p1, &p2);
1115 
1116  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
1117  {
1118  p1++;
1119  p2--;
1120  trim_ends(&p1, &p2);
1121  if((p2 - p1) == iSectionLen && !strncasecmp(p1, szSection, iSectionLen))
1122  {
1123  pSection = pTemp->pfhbG->ppLineBuf[i1 + 1];
1124  break;
1125  }
1126  }
1127  }
1128 
1129  if(!pSection)
1130  return;
1131 
1132  // starting with the current position, keep going until I find another section
1133 
1134  for(; i1 < pTemp->pfhbG->lLineCount; i1++)
1135  {
1136  // search for the section header
1137  p1 = pTemp->pfhbG->ppLineBuf[i1];
1138  p2 = pTemp->pfhbG->ppLineBuf[i1 + 1];
1139  if(!p2)
1140  p2 = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1141 
1142  __get_line_strip_comments__(&p1, &p2);
1143 
1144  if((p2 - p1) >= iSectionLen + 2 && *p1 == '[' && *(p2 - 1) == ']')
1145  {
1146  pEndSection = pTemp->pfhbG->ppLineBuf[i1];
1147  break;
1148  }
1149  }
1150 
1151  if(!pEndSection)
1152  pEndSection = pTemp->pfhbG->cData + pTemp->pfhbG->lBufferCount;
1153 
1154  // return values
1155  *ppSection = pSection;
1156  *ppEndSection = pEndSection;
1157 }
1158 
1159 #endif // 0
1160 
1161 
1162 static int __enum_conf_file_sections__(void *hFile, char *szData, int cbData)
1163 {
1164  int i1, i2;
1165  const char *p1, *p2;
1166  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1167 
1168  if(!pTemp || !pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf)
1169  {
1170  // TODO: check globals also
1171  return -1;
1172  }
1173 
1174  for(i1=0, i2=0; i1 < pTemp->pfhbL->lLineCount; i1++)
1175  {
1176  // search for the section header
1177  p1 = pTemp->pfhbL->ppLineBuf[i1];
1178  p2 = pTemp->pfhbL->ppLineBuf[i1 + 1];
1179  if(!p2)
1180  {
1181  p2 = pTemp->pfhbL->cData + pTemp->pfhbL->lBufferCount;
1182  }
1183 
1184  __get_line_strip_comments__(&p1, &p2);
1185 
1186 // WB_ERROR_PRINT("TEMPORARY: %s \"%-.*s\"\n", __FUNCTION__, (int)(p2 - p1), p1);
1187 
1188  if((p2 - p1) >= 2 && *p1 == '[' && *(p2 - 1) == ']')
1189  {
1190  p1++;
1191  p2--;
1192  trim_ends(&p1, &p2);
1193  if(p2 > p1)
1194  {
1195  i2 += (p2 - p1) + 1; // calculate additional space needed (always)
1196  if(cbData >= (p2 - p1) + 2)
1197  {
1198  // add the text for the section header to 'szData' if there's room for it
1199  memcpy(szData, p1, p2 - p1);
1200  szData[p2 - p1] = 0;
1201  szData[p2 - p1 + 1] = 0;
1202 
1203  szData += (p2 - p1) + 1;
1204  cbData -= (p2 - p1) + 1;
1205  }
1206  else
1207  {
1208  cbData = 0; // because there's no more room
1209  }
1210  }
1211  }
1212  }
1213 
1214  // TODO: enumerate global sections also
1215 
1216  i2++; // always need room for 1 more
1217  return i2;
1218 }
1219 
1220 
1221 // Utilities to query and assign values within a config file
1222 
1223 int CHGetConfFileString(void * hFile, const char *szSection,
1224  const char *szIdentifier, char *szData, int cbData)
1225 {
1226  int i1, i2, iIdentifierLen;
1227  const char *p1, *p2, *pSection, *pEndSection;
1228  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1229  file_help_buf_t *pFHB = NULL;
1230 
1231 
1232  if(!pTemp || ((!pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf)
1233  && (!pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)))
1234  {
1235  if(!pTemp)
1236  {
1237  WB_ERROR_PRINT("%s - hFile/pTemp is NULL\n", __FUNCTION__);
1238  }
1239  else
1240  {
1241  WB_ERROR_PRINT("%s - 'linebuf' problem - %p %p %p %p\n", __FUNCTION__,
1242  pTemp->pfhbL, pTemp->pfhbL->ppLineBuf,
1243  pTemp->pfhbG, pTemp->pfhbG->ppLineBuf);
1244  }
1245 
1246  return -1;
1247  }
1248 
1249  if(!szSection || !*szSection) // empty section == get a list of all of them (ignore szIdentifier)
1250  {
1251  return __enum_conf_file_sections__(hFile, szData, cbData);
1252  }
1253 
1254  iIdentifierLen = strlen(szIdentifier);
1255 
1256  __find_section__(hFile, szSection, &pSection, &pEndSection);
1257  if(!pSection)
1258  {
1259  WB_ERROR_PRINT("TEMPORARY: %s - did not find section \"%s\"\n", __FUNCTION__, szSection);
1260  return -1;
1261  }
1262 
1263  // search for 'szIdentifier string' followed by '='
1264 
1265  if(pTemp->pfhbL &&
1266  pTemp->pfhbL->ppLineBuf &&
1267  pTemp->pfhbL->lLineCount > 0 &&
1268  pTemp->pfhbL->ppLineBuf[0] < pSection &&
1269  pTemp->pfhbL->ppLineBuf[pTemp->pfhbL->lLineCount - 1] >= pSection)
1270  {
1271  pFHB = pTemp->pfhbL;
1272  }
1273  else if(pTemp->pfhbG &&
1274  pTemp->pfhbG->ppLineBuf &&
1275  pTemp->pfhbG->lLineCount > 0 &&
1276  pTemp->pfhbG->ppLineBuf[0] < pSection &&
1277  pTemp->pfhbG->ppLineBuf[pTemp->pfhbG->lLineCount - 1] >= pSection)
1278  {
1279  pFHB = pTemp->pfhbG;
1280  }
1281  else
1282  {
1283 // WB_ERROR_PRINT("INTERNAL ERROR: %s\n", __FUNCTION__);
1284 // fprintf(stderr, "INTERNAL ERROR %s:%d\n", __FUNCTION__, __LINE__);
1285  return -1;
1286  }
1287 
1288 
1289  for(i1=0; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pSection; i1++)
1290  ;
1291 
1292  for(; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pEndSection; i1++)
1293  {
1294  p1 = pFHB->ppLineBuf[i1];
1295  p2 = pFHB->ppLineBuf[i1 + 1];
1296 
1297  if(!p2)
1298  {
1299  p2 = pFHB->cData + pFHB->lBufferCount;
1300  }
1301 
1302  while(p1 < p2 && *p1 <= ' ')
1303  {
1304  p1++;
1305  }
1306 
1307  if(*p1 == ';') // comment
1308  {
1309  continue;
1310  }
1311 
1312  if(p2 - p1 > iIdentifierLen &&
1313  !strncasecmp(szIdentifier, p1, iIdentifierLen) &&
1314  p1[iIdentifierLen] == '=')
1315  {
1316  // FOUND! eliminate trailing newline but keep other white space (for now)
1317  p1 += iIdentifierLen + 1;
1318  if(p2 > p1 && *(p2 - 1) == '\n')
1319  {
1320  if(p2 > (p1 + 1) && *(p2 - 1) == '\n' && *(p2 - 2) == '\r')
1321  {
1322  p2--;
1323  }
1324 
1325  p2--;
1326  }
1327  // copy string into destination buffer and return the length of the actual data
1328  i2 = cbData;
1329  if(i2 > (p2 - p1))
1330  {
1331  i2 = p2 - p1;
1332  }
1333  if(i2 > 0)
1334  {
1335  memcpy(szData, p1, i2);
1336  }
1337 
1338  if(i2 < cbData)
1339  {
1340  szData[i2] = 0; // as a matter of course
1341  }
1342 
1343  return p2 - p1;
1344  }
1345  }
1346 
1347  // not found locally - try global
1348 
1349  // TODO: global
1350 
1351 
1352 // WB_ERROR_PRINT("TEMPORARY: %s - did not find section \"%s\" item \"%s\"\n", __FUNCTION__, szSection, szIdentifier);
1353 
1354  return -1; // not found
1355 }
1356 
1357 int CHWriteConfFileString(void * hFile, const char *szSection,
1358  const char *szIdentifier, const char *szData)
1359 {
1360  int i1, i2, iSectionLine, iSectionLen, iIdentifierLen, iDataLen;
1361  char *pBuf;
1362  const char *p1, *p2, *pSection, *pEndSection;
1363  CONF_FILE *pTemp = (CONF_FILE *)hFile;
1364  file_help_buf_t *pFHB;
1365 
1366 
1367  if(!pTemp ||
1368  ((!pTemp->pfhbL || !pTemp->pfhbL->ppLineBuf) &&
1369  (!pTemp->pfhbG || !pTemp->pfhbG->ppLineBuf)))
1370  {
1371  return -1;
1372  }
1373  if(!szSection || !*szSection)
1374  {
1375  return -1; // don't allow this on write
1376  }
1377 
1378  iIdentifierLen = strlen(szIdentifier);
1379  iDataLen = szData ? strlen(szData) : -1;
1380 
1381  __find_section__(hFile, szSection, &pSection, &pEndSection);
1382  if(pSection)
1383  {
1384  if(pTemp->pfhbL &&
1385  pTemp->pfhbL->ppLineBuf &&
1386  pTemp->pfhbL->lLineCount > 0 &&
1387  pTemp->pfhbL->ppLineBuf[0] < pSection &&
1388  pTemp->pfhbL->ppLineBuf[pTemp->pfhbL->lLineCount - 1] >= pSection)
1389  {
1390  pFHB = pTemp->pfhbL;
1391  }
1392  else if(pTemp->pfhbG &&
1393  pTemp->pfhbG->ppLineBuf &&
1394  pTemp->pfhbG->lLineCount > 0 &&
1395  pTemp->pfhbG->ppLineBuf[0] < pSection &&
1396  pTemp->pfhbG->ppLineBuf[pTemp->pfhbG->lLineCount - 1] >= pSection)
1397  {
1398  // TODO: verify global CAN be written. if not, assume 'local'
1399  pFHB = pTemp->pfhbG;
1400  }
1401  else
1402  {
1403  pSection = NULL; // will be added to 'local' or 'global' as needed
1404  pFHB = NULL;
1405  }
1406  }
1407  else
1408  {
1409  pFHB = NULL;
1410  }
1411 
1412 
1413  if(pFHB)
1414  {
1415  // search for 'szIdentifier string' followed by '='
1416 
1417  for(i1=0; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pSection; i1++)
1418  ;
1419 
1420  iSectionLine = i1;
1421 
1422  for(; i1 < pFHB->lLineCount && pFHB->ppLineBuf[i1] < pEndSection; i1++)
1423  {
1424  p1 = pFHB->ppLineBuf[i1];
1425  p2 = pFHB->ppLineBuf[i1 + 1];
1426 
1427  if(!p2)
1428  {
1429  p2 = pFHB->cData + pFHB->lBufferCount;
1430  }
1431 
1432  while(p1 < p2 && *p1 <= ' ')
1433  {
1434  p1++;
1435  }
1436 
1437  if(*p1 == ';') // comment
1438  {
1439  continue;
1440  }
1441 
1442  if(p2 - p1 > iIdentifierLen &&
1443  !strncasecmp(szIdentifier, p1, iIdentifierLen) &&
1444  p1[iIdentifierLen] == '=')
1445  {
1446  // FOUND! replace or delete contents (NULL szData --> delete)
1447  if(!szData)
1448  {
1449  FBDeleteLineFromFileBuf(pFHB, i1);
1450  }
1451  else
1452  {
1453  pBuf = (char *)WBAlloc(iDataLen + iIdentifierLen + 4);
1454 
1455  if(!pBuf)
1456  {
1457  WB_ERROR_PRINT("%s - 'pBuf' NULL (c)\n", __FUNCTION__);
1458 
1459  return -1; // error
1460  }
1461 
1462  // todo: search backwards from 'i1' for blanks & comments
1463  sprintf(pBuf, "%s=%s", szIdentifier, szData);
1464 
1465  if(pFHB == pTemp->pfhbG)
1466  {
1467  FBReplaceLineInFileBuf(&(pTemp->pfhbG), i1, pBuf);
1468  }
1469  else if(pFHB == pTemp->pfhbL)
1470  {
1471  FBReplaceLineInFileBuf(&(pTemp->pfhbL), i1, pBuf);
1472  }
1473  else
1474  {
1475  WBFree(pBuf);
1476  return -1; // error
1477  }
1478 
1479  WBFree(pBuf);
1480  }
1481 
1482  return 0; // success
1483  }
1484  }
1485 
1486  if(!szData)
1487  {
1488  return 0; // success [deleting something that's not there is 'OK']
1489  }
1490 
1491 
1492  // since the section doesn't contain this entry, create it after the
1493  // last entry, searching back from 'pEndSection' skipping blank lines and comments
1494  // until I get to 'pSection' or an entry, whichever happens first.
1495 
1496  pBuf = (char *)WBAlloc(iDataLen + iIdentifierLen + 4);
1497 
1498  if(!pBuf)
1499  {
1500  WB_ERROR_PRINT("%s - 'pBuf' NULL (a)\n", __FUNCTION__);
1501 
1502  return -1; // error
1503  }
1504 
1505  // search backwards from 'i1' for blank lines. insert right after
1506  // the last non-blank line ABOVE where I am.
1507 
1508  while(i1 > iSectionLine)
1509  {
1510  p1 = pFHB->ppLineBuf[i1 - 1];
1511  if(p1 && *p1 != '\r' && *p1 != '\n')
1512  {
1513  break;
1514  }
1515 
1516  i1--;
1517  }
1518 
1519  sprintf(pBuf, "%s=%s", szIdentifier, szData);
1520 
1521  if(pTemp->pfhbL /*&&
1522  (!(pTemp->pfhbG) || !(pTemp->iFlags & CH_FLAGS_GLOBAL))*/) // local first, unless the 'global' flag is set
1523  {
1524  FBInsertLineIntoFileBuf(&(pTemp->pfhbL), i1, pBuf);
1525  }
1526  else if(pTemp->pfhbG)
1527  {
1528  FBInsertLineIntoFileBuf(&(pTemp->pfhbG), i1, pBuf);
1529  }
1530 
1531  WBFree(pBuf);
1532 
1533  return 0; // ok!
1534  }
1535 
1536  if(!szData)
1537  {
1538  return 0; // already deleted, no need for further effort
1539  }
1540 
1541  iSectionLen = strlen(szSection);
1542 
1543  // allocate memory for the section and the entry, and add each in its turn
1544  i1 = iSectionLen + 4;
1545  if(i1 < (iDataLen + iIdentifierLen + 2))
1546  {
1547  i1 = iDataLen + iIdentifierLen + 2;
1548  }
1549 
1550  pBuf = (char *)WBAlloc(i1 + 2);
1551 
1552  if(!pBuf)
1553  {
1554  WB_ERROR_PRINT("%s - 'pBuf' NULL (b)\n", __FUNCTION__);
1555 
1556  return -1; // error
1557  }
1558 
1559  sprintf(pBuf, "[%s]\n", szSection);
1560 
1561  if(pTemp->pfhbL /*&& // always do local FIRST if I can
1562  (!(pTemp->pfhbG) || !(pTemp->iFlags & CH_FLAGS_GLOBAL))*/)
1563  {
1564  i2 = pTemp->pfhbL->lLineCount;
1565  FBInsertLineIntoFileBuf(&(pTemp->pfhbL), pTemp->pfhbL->lLineCount, pBuf);
1566  if(i2 >= pTemp->pfhbL->lLineCount)
1567  {
1568  WBFree(pBuf);
1569 
1570  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (a)\n", __FUNCTION__);
1571  return -1;
1572  }
1573 
1574  // now do it again, this time for the data
1575  sprintf(pBuf, "%s=%s\n", szIdentifier, szData);
1576 
1577  i2 = pTemp->pfhbL->lLineCount;
1578  FBInsertLineIntoFileBuf(&(pTemp->pfhbL), pTemp->pfhbL->lLineCount, pBuf);
1579  WBFree(pBuf);
1580 
1581  if(i2 >= pTemp->pfhbL->lLineCount)
1582  {
1583  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (b)\n", __FUNCTION__);
1584  return -1;
1585  }
1586  }
1587  else if(pTemp->pfhbG) // global [TODO: verify global can be written]
1588  {
1589  i2 = pTemp->pfhbG->lLineCount;
1590  FBInsertLineIntoFileBuf(&(pTemp->pfhbG), pTemp->pfhbG->lLineCount, pBuf);
1591  if(i2 >= pTemp->pfhbG->lLineCount)
1592  {
1593  WBFree(pBuf);
1594  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (c)\n", __FUNCTION__);
1595  return -1;
1596  }
1597  // now do it again, this time for the data
1598  sprintf(pBuf, "%s=%s\n", szIdentifier, szData);
1599 
1600  i2 = pTemp->pfhbG->lLineCount;
1601  FBInsertLineIntoFileBuf(&(pTemp->pfhbG), pTemp->pfhbG->lLineCount, pBuf);
1602  WBFree(pBuf);
1603 
1604  if(i2 >= pTemp->pfhbG->lLineCount)
1605  {
1606  WB_ERROR_PRINT("%s - FBInsertLineIntoFileBuf failed (d)\n", __FUNCTION__);
1607  return -1;
1608  }
1609  }
1610  else
1611  {
1612  WBFree(pBuf);
1613  WB_ERROR_PRINT("%s - pfhbG and pfhbL are both NULL\n", __FUNCTION__);
1614 
1615  return -1; // error
1616  }
1617 
1618  return 0; // success!
1619 }
1620 
1621 int CHGetConfFileInt(void * hFile, const char *szSection, const char *szIdentifier)
1622 {
1623  int iLen;
1624  char tbuf[64];
1625 
1626  if((iLen = CHGetConfFileString(hFile, szSection, szIdentifier, tbuf, sizeof(tbuf) - 1)) > 0)
1627  {
1628  tbuf[iLen] = 0;
1629  while(tbuf[0] && tbuf[0] <= ' ')
1630  {
1631  strcpy(tbuf, tbuf + 1);
1632  }
1633 
1634  if(tbuf[0])
1635  {
1636  return atoi(tbuf);
1637  }
1638  }
1639 
1640  return 0;
1641 }
1642 
1643 int CHWriteConfFileInt(void * hFile, const char *szSection, const char *szIdentifier, int iData)
1644 {
1645  int iFlag;
1646  char tbuf[64];
1647  char *p1;
1648 
1649  if(iData < 0)
1650  {
1651  iFlag = -1;
1652  iData = -iData;
1653  }
1654  else
1655  {
1656  iFlag = 0; // a sign flag, temporarily
1657  }
1658 
1659  p1 = tbuf + sizeof(tbuf) - 1;
1660 
1661  *p1 = 0;
1662  do
1663  {
1664  *(--p1) = (char)('0' + iData % 10);
1665 
1666  iData /= 10;
1667 
1668  } while(p1 > (tbuf + 1) && iData);
1669 
1670  if(iFlag < 0)
1671  {
1672  *(--p1) = '-';
1673  }
1674 
1675  return CHWriteConfFileString(hFile, szSection, szIdentifier, p1);
1676 }
1677 
1678 
1679 
1680 
1681 
1683 // __ __ ____ _____ _____ _____ ___ _ _ ____ ____ //
1684 // \ \/ // ___| | ____||_ _||_ _||_ _|| \ | | / ___|/ ___| //
1685 // \ / \___ \ | _| | | | | | | | \| || | _ \___ \ //
1686 // / \ ___) || |___ | | | | | | | |\ || |_| | ___) | //
1687 // /_/\_\|____/ |_____| |_| |_| |___||_| \_| \____||____/ //
1688 // //
1690 
1691 // INTERNAL STRUCTURES
1692 // header, followed by 'nSettings' XSETTINGS_DATA structs (each of variable length)
1693 typedef struct __XSETTINGS_HEADER__
1694 {
1695  char cByteOrder, cUnused[3];
1696  unsigned int uiSerial; // may be high or low endian, see 'cByteOrder'
1697  unsigned int nSettings; // may be high or low endian, see 'cByteOrder'
1698 } __attribute__((__packed__)) XSETTINGS_HEADER;
1699 
1700 typedef struct __XSETTINGS_DATAHDR__
1701 {
1702  char cSettingType, cUnused;
1703  unsigned short wNameLen;
1704  char szName[4]; // actually 'wNameLen' bytes long, including any padding
1705 // unsigned int dwLastChangeSerial; immediately follows 'szName'
1706 } __attribute__((__packed__)) XSETTINGS_DATAHDR;
1707 
1708 // data immediately follows XSETTINGS_DATAHDR, one of these three structs
1709 
1710 typedef struct __XSETTINGS_DATA_INT__
1711 {
1712  unsigned int uiValue; // endian matches that of header
1713 } XSETTINGS_DATA_INT;
1714 
1715 typedef struct __XSETTINGS_DATA_STRING__
1716 {
1717  unsigned int cbLength;
1718  char szData[4]; // followed immediately by character data (may be zero length)
1719 } __attribute__((__packed__)) XSETTINGS_DATA_STRING;
1720 
1721 
1722 const CHXSettings * CHGetXSettings(Display *pDisplay)
1723 {
1724  return pXSettings; // for now, just do this (if NULL do I call CHSettingsRefresh ?)
1725 }
1726 
1727 const CHXSetting * CHGetXSetting(Display *pDisplay, const char *szSettingName)
1728 {
1729 int i1, nSettings;
1730 
1731  if(!pXSettings || pXSettings->pDisplay != pDisplay)
1732  {
1733  return NULL;
1734  }
1735 
1736  nSettings = pXSettings->nSettings;
1737 
1738  for(i1=0; i1 < nSettings; i1++)
1739  {
1740  if(!strcasecmp(szSettingName, pXSettings->aData[i1].szName))
1741  {
1742  return pXSettings->aData + i1;
1743  }
1744  }
1745 
1746  return NULL;
1747 }
1748 
1749 void CHSettingsRefresh(Display *pDisplay)
1750 {
1751 // TODO: implement an XSETTINGS (gnome-settings-manager) 'collection' object and query it
1752 // NOTE: so far gnome-settings-manager doesn't provide anything really useful except the theme name
1753 // and everything else is either cloned from or implemented as the old-style resource manager
1754 
1755 int i1, iLen, nLen, iFormat, nItems, cbSize, cbNameLen, cbStrLen;
1756 //unsigned long cbData0;
1757 void *pData;
1758 unsigned long cbLeft, nI;
1759 char *pCur, *pDataEnd, *pXSCur, *pXSEnd;
1760 Atom a_XSETTINGS_Sn, a_XSETTINGS_SETTINGS, aType;
1761 Window wOwn;
1762 XSETTINGS_HEADER *pHdr;
1763 XSETTINGS_DATAHDR *pDHdr;
1764 char tbuf[256];
1765 
1766 
1767 // aTARGET = XInternAtom(pDisplay, "TARGET", False);
1768 // a_MANAGER = XInternAtom(pDisplay, "MANAGER", False);
1769 // XA_CLIPBOARD=XInternAtom(pDisplay, "CLIPBOARD", False);
1770 // XA_CLIPBOARD_MANAGER=XInternAtom(pDisplay, "CLIPBOARD_MANAGER", False);
1771  a_XSETTINGS_Sn = XInternAtom(pDisplay, "_XSETTINGS_S0", False);
1772  a_XSETTINGS_SETTINGS = XInternAtom(pDisplay, "_XSETTINGS_SETTINGS", False);
1773 
1774  // see https://specifications.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html
1775 
1776  XGrabServer(pDisplay); // required by above documentation
1777  wOwn = XGetSelectionOwner(pDisplay, a_XSETTINGS_Sn);
1778 
1779  if(wOwn == None)
1780  {
1781  XUngrabServer(pDisplay);
1782  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (no owner)\n",
1783  __FILE__, __LINE__, __FUNCTION__);
1784  return;
1785  }
1786 
1787  // read the property now
1788 
1789  pData = NULL;
1790  aType = None; //a_MANAGER;
1791  iFormat = 32;
1792  nI = 0;
1793  cbLeft = 0;
1794 
1795  if(XGetWindowProperty(pDisplay, wOwn, a_XSETTINGS_SETTINGS, 0, 0, False,
1796  AnyPropertyType, &aType, &iFormat, &nI, &cbLeft, (unsigned char **)&pData))
1797  {
1798  XUngrabServer(pDisplay);
1799  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (XSETTINGS_SETTINGS)(a)\n",
1800  __FILE__, __LINE__, __FUNCTION__);
1801  return;
1802  }
1803 
1804  if(pData)
1805  {
1806  XFree(pData);
1807  pData = NULL;
1808  }
1809 
1810  // is this the actual data, or a return that says "do it incrementally" ?
1811 
1812  if(aType == aINCR) // incremental
1813  {
1814  XUngrabServer(pDisplay);
1815  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (INCR)\n",
1816  __FILE__, __LINE__, __FUNCTION__);
1817  return;
1818  }
1819 
1820  nLen = iLen = cbLeft; // the RAW length (in bytes)
1821 
1822  if(iFormat == 16)
1823  {
1824  nLen /= 2;
1825  }
1826  else if(iFormat == 32)
1827  {
1828  nLen /= 4;
1829  }
1830 
1831  // now get it for reals
1832 
1833  if(XGetWindowProperty(pDisplay, wOwn, a_XSETTINGS_SETTINGS, 0, nLen, False,
1834  AnyPropertyType, &aType, &iFormat, &nI, &cbLeft, (unsigned char **)&pData)
1835  || !pData)
1836  {
1837  XUngrabServer(pDisplay);
1838  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data (XSETTINGS_SETTINGS)(b)\n",
1839  __FILE__, __LINE__, __FUNCTION__);
1840  return;
1841  }
1842 
1843  XUngrabServer(pDisplay); // MUST do this or else bad things happen
1844 
1845 
1846  if(pData)
1847  {
1848  if(aType != a_XSETTINGS_SETTINGS)
1849  {
1850 #ifndef NO_DEBUG
1851  char *p1 = WBGetAtomName(pDisplay, aType);
1852  WB_ERROR_PRINT("TEMPORARY: %s:%d - %s returned type %d (%s)\n",
1853  __FILE__, __LINE__, __FUNCTION__, (int)aType, p1);
1854  if(p1)
1855  {
1856  WBFree(p1);
1857  }
1858 #endif // NO_DEBUG
1859 
1860  // TODO: is this an error??
1861  }
1862  }
1863  else
1864  {
1865  WB_ERROR_PRINT("%s:%d - %s unable to retrieve XSETTINGS data\n",
1866  __FILE__, __LINE__, __FUNCTION__);
1867  return;
1868  }
1869 
1870 
1871  // first part is __XSETTINGS_HEADER__ header
1872  pHdr = (XSETTINGS_HEADER *)pData;
1873  pCur = (char *)(pHdr + 1);
1874  pDataEnd = (char *)pData + iLen;//cbData0;
1875 
1876  if(pCur >= pDataEnd)
1877  {
1878  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small)\n",
1879  __FILE__, __LINE__, __FUNCTION__);
1880  XFree(pData);
1881  return;
1882  }
1883 
1884 // WB_ERROR_PRINT("TEMPORARY: pHdr %d %d %d\n",
1885 // pHdr->cByteOrder, pHdr->uiSerial, pHdr->nSettings);
1886 
1887  cbSize = 0;
1888  if(pHdr->cByteOrder) // assume HIGH endian
1889  {
1890  nItems = htonl(pHdr->nSettings);
1891  }
1892  else
1893  {
1894  nItems = pHdr->nSettings;
1895  }
1896 
1897  for(i1=0; i1 < nItems; i1++)
1898  {
1899  pDHdr = (XSETTINGS_DATAHDR *)pCur;
1900 
1901  if((char *)(&(pDHdr->wNameLen) + 1) > pDataEnd) // make sure I can read wNameLen safely
1902  {
1903  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small) %p %p %d of %d\n",
1904  __FILE__, __LINE__, __FUNCTION__, pCur, pDataEnd, i1, nItems);
1905  XFree(pData);
1906  return;
1907  }
1908 
1909  cbNameLen = pHdr->cByteOrder ? htons(pDHdr->wNameLen) : pDHdr->wNameLen;
1910 
1911  if(pDHdr->szName + cbNameLen + 1 >= pDataEnd)
1912  {
1913  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small)\n",
1914  __FILE__, __LINE__, __FUNCTION__);
1915  XFree(pData);
1916  return;
1917  }
1918 
1919  cbSize += sizeof(CHXSetting) + cbNameLen + 1;
1920  bzero(tbuf, sizeof(tbuf));
1921  if(cbNameLen < sizeof(tbuf))
1922  {
1923  memcpy(tbuf, pDHdr->szName, cbNameLen);
1924  }
1925  else
1926  {
1927  memcpy(tbuf, pDHdr->szName, sizeof(tbuf) - 1);
1928  }
1929 
1930  pCur = (char *)&(pDHdr->szName) + ((cbNameLen + 7) & (~3));
1931 
1932  switch(pDHdr->cSettingType)
1933  {
1934  case XSettingsTypeInteger:
1935 // WB_ERROR_PRINT("TEMPORARY: integer %s %d\n", tbuf, ((XSETTINGS_DATA_INT *)pCur)->uiValue);
1936 
1937  cbSize += sizeof(XSETTINGS_DATA_INT);
1938  pCur += sizeof(XSETTINGS_DATA_INT);
1939 
1940  break;
1941 
1942  case XSettingsTypeColor:
1943  cbSize += sizeof(XSETTINGS_DATA_COLOR);
1944  pCur += sizeof(XSETTINGS_DATA_COLOR);
1945 
1946  break;
1947 
1948  case XSettingsTypeString:
1949 
1950  if(pCur + sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData)
1951  >= pDataEnd) // make sure string length is readable
1952  {
1953  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small)\n",
1954  __FILE__, __LINE__, __FUNCTION__);
1955  XFree(pData);
1956  return;
1957  }
1958 
1959  cbStrLen = pHdr->cByteOrder ? htons(((XSETTINGS_DATA_STRING *)pCur)->cbLength) : ((XSETTINGS_DATA_STRING *)pCur)->cbLength;
1960 
1961 // WB_ERROR_PRINT("TEMPORARY: string %s length %d\n", tbuf, cbStrLen);
1962 // WB_ERROR_PRINT("TEMPORARY: string %s length %d \"%-.*s\"\n",
1963 // tbuf, cbStrLen, cbStrLen, ((XSETTINGS_DATA_STRING *)pCur)->szData);
1964 
1965  if(cbStrLen > 0)
1966  {
1967  cbSize += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData) + cbStrLen + 1;
1968  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData)
1969  + ((cbStrLen + 3) & (~3));
1970  }
1971  else
1972  {
1973  cbSize += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData);
1974  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData);
1975  }
1976 
1977  break;
1978  }
1979 
1980  // must check this at end of loop
1981 
1982  if(pCur > pDataEnd) // last can have pCurDataEnd == pCur
1983  {
1984  WB_ERROR_PRINT("%s:%d - %s XSETTINGS data corrupt (data buffer too small) %p %p %d of %d\n",
1985  __FILE__, __LINE__, __FUNCTION__, pCur, pDataEnd, i1, nItems);
1986  XFree(pData);
1987  return;
1988  }
1989  }
1990 
1991 // WB_ERROR_PRINT("TEMPORARY: total length needed %d bytes\n", cbSize);
1992 
1993  pCur = (char *)(pHdr + 1); // back to the beginning
1994 
1995  if(pXSettings)
1996  {
1997  WBFree(pXSettings);
1998  pXSettings = NULL;
1999  }
2000 
2001  pXSettings = (CHXSettings *)WBAlloc(cbSize + sizeof(CHXSettings));
2002 
2003  if(!pXSettings)
2004  {
2005  WB_ERROR_PRINT("%s:%d - %s not enough memory for XSettings\n",
2006  __FILE__, __LINE__, __FUNCTION__);
2007  XFree(pData);
2008  return;
2009  }
2010 
2011  pXSettings->pDisplay = pDisplay;
2012  pXSettings->nSettings = nItems;
2013  pXSettings->uiSerial = pHdr->cByteOrder ? htonl(pHdr->uiSerial) : pHdr->uiSerial;
2014 
2015  pXSEnd = (char *)pXSettings + cbSize + sizeof(CHXSettings); // to duplicate 'malloc' size calc
2016  pXSCur = (char *)(pXSettings->aData + nItems); // this is where I put string data
2017 
2018  for(i1=0; i1 < nItems; i1++)
2019  {
2020  XSETTINGS_DATAHDR *pDHdr = (XSETTINGS_DATAHDR *)pCur;
2021  int cbNameLen = pHdr->cByteOrder ? htons(pDHdr->wNameLen) : pDHdr->wNameLen;
2022  CHXSetting *pXS = pXSettings->aData + i1;
2023 
2024  if(pXSCur + cbNameLen + 1 > pXSEnd)
2025  {
2026  WB_ERROR_PRINT("%s:%d - %s CHXSettings data corrupt (data buffer too small)\n",
2027  __FILE__, __LINE__, __FUNCTION__);
2028 
2029  WBFree(pXSettings);
2030  pXSettings = NULL;
2031 
2032  XFree(pData);
2033  return;
2034  }
2035 
2036  pXS->szName = pXSCur;
2037 
2038  memcpy(pXSCur, pDHdr->szName, cbNameLen);
2039  pXSCur += cbNameLen;
2040  *(pXSCur++) = 0;
2041 
2042  pCur = (char *)&(pDHdr->szName) + ((cbNameLen + 7) & (~3));
2043 
2044  pXS->iType = pDHdr->cSettingType;
2045 
2046  switch(pDHdr->cSettingType)
2047  {
2048  case XSettingsTypeInteger:
2049 
2050  pXS->iLen = 0;
2051  pXS->uData.iData = ((XSETTINGS_DATA_INT *)pCur)->uiValue;
2052 
2053  pCur += sizeof(XSETTINGS_DATA_INT);
2054 
2055  break;
2056 
2057  case XSettingsTypeColor:
2058 
2059  pXS->iLen = 0;
2060 
2061  pXS->uData.clrData.sRed = ((XSETTINGS_DATA_COLOR *)pCur)->sRed;
2062  pXS->uData.clrData.sBlue = ((XSETTINGS_DATA_COLOR *)pCur)->sBlue;
2063  pXS->uData.clrData.sGreen = ((XSETTINGS_DATA_COLOR *)pCur)->sGreen;
2064  pXS->uData.clrData.sAlpha = ((XSETTINGS_DATA_COLOR *)pCur)->sAlpha;
2065 
2066  cbSize += sizeof(XSETTINGS_DATA_COLOR);
2067  pCur += sizeof(XSETTINGS_DATA_COLOR);
2068 
2069  break;
2070 
2071  case XSettingsTypeString:
2072 
2073  cbStrLen = pHdr->cByteOrder ? htons(((XSETTINGS_DATA_STRING *)pCur)->cbLength) : ((XSETTINGS_DATA_STRING *)pCur)->cbLength;
2074  pXS->iLen = cbStrLen;
2075 
2076  if(cbStrLen > 0)
2077  {
2078  if(pXSCur + cbStrLen + 1 > pXSEnd)
2079  {
2080  WB_ERROR_PRINT("%s:%d - %s CHXSettings data corrupt (data buffer too small)\n",
2081  __FILE__, __LINE__, __FUNCTION__);
2082 
2083  WBFree(pXSettings);
2084  pXSettings = NULL;
2085 
2086  XFree(pData);
2087  return;
2088  }
2089 
2090  pXS->uData.szData = pXSCur;
2091  memcpy(pXSCur, ((XSETTINGS_DATA_STRING *)pCur)->szData, cbStrLen);
2092 
2093  pXSCur += cbStrLen;
2094  *(pXSCur++) = 0;
2095 
2096  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData)
2097  + ((cbStrLen + 3) & (~3));
2098  }
2099  else
2100  {
2101  pXS->uData.szData = ""; // so that it's not NULL
2102 
2103  pCur += sizeof(XSETTINGS_DATA_STRING) - sizeof(((XSETTINGS_DATA_STRING *)0)->szData);
2104  }
2105 
2106  break;
2107  }
2108  }
2109 
2110  // if I'm debuggin I'll want a copy of what's there
2111 
2112 #ifndef NO_DEBUG
2113  WB_IF_DEBUG_LEVEL(DebugLevel_WARN | DebugSubSystem_Settings)
2114  {
2115  CHDumpConfig();
2116  }
2117 #endif // !NO_DEBUG
2118 }
2119 
2120 static void __settings_cleanup(void)
2121 {
2122  if(pXSettings)
2123  {
2124  WBFree(pXSettings);
2125  pXSettings = NULL;
2126  }
2127 }
2128 
2129 
2130 
2132 // QUERY SYSTEM PARAMETERS
2134 
2135 
2136 // basic 'window_helper' helpers
2137 // NOTE: see page on Specifications/XSettingsRegistry at freedesktop.org
2138 // http://www.freedesktop.org/wiki/Specifications/XSettingsRegistry
2139 
2140 #define DEFAULT_DOUBLE_CLICK_TIME 250 /* msec */
2141 #define DEFAULT_DOUBLE_CLICK_DISTANCE 5 /* pixels */
2142 #define DEFAULT_DRAG_THRESHOLD 9 /* pixels */
2143 #define DEFAULT_CURSOR_BLINK (!0) /* boolean */
2144 #define DEFAULT_CURSOR_BLINK_TIME 1200 /* msec */
2145 
2146 
2147 int CHGetDoubleClickTime(Display *pDisplay)
2148 {
2149 static int iFirstTime = 1, iRval = DEFAULT_DOUBLE_CLICK_TIME;
2150 static unsigned int uiSerial;
2151 const CHXSetting *pXS;
2152 
2153  // These first checks should be optimized for speed. It is likely that
2154  // they will be performed OFTEN, as often as 'every message'
2155 
2156  if(WB_UNLIKELY(!pXSettings))
2157  {
2158  return iRval; // last known value
2159  }
2160 
2161  if(WB_LIKELY(!iFirstTime) &&
2162  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2163  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2164  {
2165  return iRval; // no need to check again
2166  }
2167 
2168  if(pDisplay && pDisplay != pXSettings->pDisplay)
2169  {
2170  return DEFAULT_DOUBLE_CLICK_TIME; // return the default value (for now)
2171  }
2172 
2173  iFirstTime = 0; // make sure
2174  uiSerial = pXSettings->uiSerial; // keep track of serial #
2175 
2176  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/DoubleClickTime");
2177 
2178  if(pXS && pXS->iType == XSettingsTypeInteger)
2179  {
2180  iRval = pXS->uData.iData;
2181  }
2182 
2183  return iRval;
2184 }
2185 
2186 int CHGetDoubleClickDistance(Display *pDisplay)
2187 {
2188 static int iFirstTime = 1, iRval = DEFAULT_DOUBLE_CLICK_DISTANCE;
2189 static unsigned int uiSerial;
2190 const CHXSetting *pXS;
2191 
2192  // These first checks should be optimized for speed. It is likely that
2193  // they will be performed OFTEN, as often as 'every message'
2194 
2195  if(WB_UNLIKELY(!pXSettings))
2196  {
2197  return iRval; // last known value
2198  }
2199 
2200  if(WB_LIKELY(!iFirstTime) &&
2201  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2202  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2203  {
2204  return iRval; // no need to check again
2205  }
2206 
2207  if(pDisplay && pDisplay != pXSettings->pDisplay)
2208  {
2209  return DEFAULT_DOUBLE_CLICK_DISTANCE; // return the default value (for now)
2210  }
2211 
2212  iFirstTime = 0; // make sure
2213  uiSerial = pXSettings->uiSerial; // keep track of serial #
2214 
2215  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/DoubleClickDistance");
2216 
2217  if(pXS && pXS->iType == XSettingsTypeInteger)
2218  {
2219  iRval = pXS->uData.iData;
2220  }
2221 
2222  return iRval;
2223 }
2224 
2225 int CHGetDragThreshold(Display *pDisplay)
2226 {
2227 static int iFirstTime = 1, iRval = DEFAULT_DRAG_THRESHOLD;
2228 static unsigned int uiSerial;
2229 const CHXSetting *pXS;
2230 
2231  // These first checks should be optimized for speed. It is likely that
2232  // they will be performed OFTEN, as often as 'every message'
2233 
2234  if(WB_UNLIKELY(!pXSettings))
2235  {
2236  return iRval; // last known value
2237  }
2238 
2239  if(WB_LIKELY(!iFirstTime) &&
2240  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2241  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2242  {
2243  return iRval; // no need to check again
2244  }
2245 
2246  if(pDisplay && pDisplay != pXSettings->pDisplay)
2247  {
2248  return DEFAULT_DRAG_THRESHOLD; // return the default value (for now)
2249  }
2250 
2251  iFirstTime = 0; // make sure
2252  uiSerial = pXSettings->uiSerial; // keep track of serial #
2253 
2254  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/DndDragThreshold");
2255 
2256  if(pXS && pXS->iType == XSettingsTypeInteger)
2257  {
2258  iRval = pXS->uData.iData;
2259  }
2260 
2261  return iRval;
2262 }
2263 
2264 int CHGetCursorBlink(Display *pDisplay)
2265 {
2266 static int iFirstTime = 1, iRval = DEFAULT_CURSOR_BLINK;
2267 static unsigned int uiSerial;
2268 const CHXSetting *pXS;
2269 
2270  // These first checks should be optimized for speed. It is likely that
2271  // they will be performed OFTEN, as often as 'every message'
2272 
2273  if(WB_UNLIKELY(!pXSettings))
2274  {
2275  return iRval; // last known value
2276  }
2277 
2278  if(WB_LIKELY(!iFirstTime) &&
2279  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2280  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2281  {
2282  return iRval; // no need to check again
2283  }
2284 
2285  if(pDisplay && pDisplay != pXSettings->pDisplay)
2286  {
2287  return DEFAULT_CURSOR_BLINK; // return the default value (for now)
2288  }
2289 
2290  iFirstTime = 0; // make sure
2291  uiSerial = pXSettings->uiSerial; // keep track of serial #
2292 
2293  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/CursorBlink");
2294 
2295  if(pXS && pXS->iType == XSettingsTypeInteger)
2296  {
2297  iRval = pXS->uData.iData ? !0 : 0; // convert to "true boolean"
2298  }
2299 
2300  return iRval;
2301 }
2302 
2303 int CHGetCursorBlinkTime(Display *pDisplay)
2304 {
2305 static int iFirstTime = 1, iRval = DEFAULT_CURSOR_BLINK_TIME;
2306 static unsigned int uiSerial;
2307 const CHXSetting *pXS;
2308 
2309  // These first checks should be optimized for speed. It is likely that
2310  // they will be performed OFTEN, as often as 'every message'
2311 
2312  if(WB_UNLIKELY(!pXSettings))
2313  {
2314  return iRval; // last known value
2315  }
2316 
2317  if(WB_LIKELY(!iFirstTime) &&
2318  WB_LIKELY(uiSerial == pXSettings->uiSerial) &&
2319  WB_LIKELY((!pDisplay || pDisplay == pXSettings->pDisplay)))
2320  {
2321  return iRval; // no need to check again
2322  }
2323 
2324  if(pDisplay && pDisplay != pXSettings->pDisplay)
2325  {
2326  return DEFAULT_CURSOR_BLINK_TIME; // return the default value (for now)
2327  }
2328 
2329  iFirstTime = 0; // make sure
2330  uiSerial = pXSettings->uiSerial; // keep track of serial #
2331 
2332  pXS = CHGetXSetting(pXSettings->pDisplay, "Net/CursorBlinkTime");
2333 
2334  if(pXS && pXS->iType == XSettingsTypeInteger)
2335  {
2336  iRval = pXS->uData.iData;
2337  }
2338 
2339  return iRval;
2340 }
2341 
2342 
2343 
2345 // X M L P A R S I N G //
2347 
2356 static const char *InternalParseXML(CHXMLEntry **ppOrigin, int *pcbOrigin, CHXMLEntry **ppCur,
2357  char **ppData, int *pcbData, char **ppCurData,
2358  const char *ppXMLData, const char *pXMLDataEnd)
2359 {
2360  // parse a section of XML, adding contents to the end of 'ppOrigin', and returning
2361  // a pointer to the next element on success (or NULL otherwise). This function will
2362  // re-allocate '*ppOrigin' as needed, storing the max size in '*pcbOrigin'. It can
2363  // also recurse to embedded sections, and then process them as needed to get the XML
2364  // hierarchy correct in the CHXMLEntry pointed to by 'ppOrigin'.
2365  //
2366  // A recursive call can affect *ppOrigin. It should be explicitly re-loaded on return.
2367 
2368  // this isn't needed per se, but having it here can't hurt..
2369  if(!ppOrigin || !*ppOrigin || !pcbOrigin || !ppCur || !*ppCur ||
2370  !ppXMLData || !*ppXMLData || !pXMLDataEnd ||
2371  (((WB_UINTPTR)pXMLDataEnd) < ((WB_UINTPTR)*ppXMLData))) // warning abatement, use type cast for WB_UINTPTR
2372  {
2373  return NULL; // just reject these possibilities outright and return "error"
2374  }
2375 
2376  // this function will return on error or if it finds and parses the ending tag
2377 
2378  // OK what kind of tag do we have now?
2379 
2380 
2381 // pCur = CHFindNextXMLTag(pCur, cbLength, 0);
2382 //
2383 // if(!pCur) // no more tags
2384 // {
2385 // break;
2386 // }
2387 //
2388 // pCur++; // points past the tag
2389 
2390 
2391  // returned pointer is the next point at which to parse a 'same level' tag
2392 
2393  return pXMLDataEnd; // for now
2394 }
2395 
2396 
2397 // typedef struct _CHXMLEntry_
2398 // {
2399 // int iNextIndex; // 0-based index for next item at this level; <= 0 for none. 0 marks "end of list" for top level
2400 // int iContainer; // 0-based index for container; <= 0 for none.
2401 // int iContentsIndex; // 0-based first array index for 'contents' for this entry; <= 0 for none
2402 //
2403 // int nLabelOffset; // BYTE offset to label (zero-byte-terminated) string (from beginning of array)
2404 // // for this entry; <= 0 for 'no label'
2405 // int nDataOffset; // BYTE offset to data (zero-byte-terminated) string (from beginning of array)
2406 // // for the entry data; <= 0 for 'no data'
2407 //
2408 // } CHXMLEntry;
2409 
2410 CHXMLEntry *CHParseXML(const char *pXMLData, int cbLength)
2411 {
2412 CHXMLEntry *pRval = NULL;
2413 CHXMLEntry *pXE, *pXCur, *pXPrev;
2414 int cbRval, cbData, cbNeed, cbOffs;
2415 const char *pEnd = pXMLData + cbLength;
2416 const char *pCur;
2417 char *pData, *pCurData;
2418 
2419 
2420  if(!pXMLData || !cbLength || !*pXMLData)
2421  {
2422  return NULL;
2423  }
2424 
2425  cbRval = 0x1000 * sizeof(CHXMLEntry); // 64k entries
2426  cbData = 0x100000; // 256k, to start with
2427 
2428  pRval = (CHXMLEntry *)malloc(cbRval);
2429  pData = malloc(cbData);
2430  if(!pRval || !pData)
2431  {
2432  if(pRval)
2433  {
2434  free(pRval);
2435  }
2436 
2437  if(pData)
2438  {
2439  free(pData);
2440  }
2441  return NULL; // not enough memory (oops)
2442  }
2443 
2444 
2445 
2446  pCur = pXMLData;
2447  pXCur = pRval;
2448  pXPrev = NULL;
2449  pCurData = pData;
2450 
2451  *pData = 0; // ending zero byte - must be present at pData[length]
2452 
2453  pXCur->iNextIndex = 0; // marks "end of list"
2454  pXCur->iContainer = 0; // marks it as "top level"
2455  pXCur->iContentsIndex = 0;
2456  pXCur->nLabelOffset = 0;
2457  pXCur->nDataOffset = 0;
2458 
2459 
2460  while(pCur < pEnd)
2461  {
2462  // call recursive function that does "one level" of XML and all of its contents
2463 
2464  pCur = InternalParseXML(&pRval, &cbRval, &pXCur, &pData, &cbData, &pCurData, pCur, pEnd);
2465 
2466  if(!pCur) // error
2467  {
2468  goto error_exit;
2469  }
2470 
2471  // TODO: look at the beginning for things like <?xml version="xx"?> and <!DOCTYPE xxx>
2472 
2473  }
2474 
2475  // at this point 'pXCur' is the pointer to the final entry, and there's room in the array for it.
2476 
2477  pXCur->iNextIndex = 0; // marks "end of list"
2478  pXCur->iContainer = 0; // assign the other zeros by convention
2479  pXCur->iContentsIndex = 0;
2480  pXCur->nLabelOffset = 0;
2481  pXCur->nDataOffset = 0;
2482 
2483  cbOffs = ((char *)(pXCur + 1) - (char *)pRval); // offset to where the data is, for fixups
2484  // I use 'pXCur + 1' here because I'll increment it later. but I also need it
2485  // for a limit pointer in the fixup loop, so I don't increment it YET...
2486  cbNeed = cbOffs + 2 * sizeof(*pXCur)
2487  + (pCurData - pData); // the actual size of the data
2488 
2489  if(cbNeed > cbRval) // need to re-allocate
2490  {
2491  void *pTemp = realloc(pRval, cbNeed);
2492 
2493  if(!pTemp)
2494  {
2495  goto error_exit;
2496  }
2497 
2498  pXCur = (pXCur - pRval) + ((CHXMLEntry *)pTemp); // new 'pXCur'
2499  pRval = (CHXMLEntry *)pTemp; // new 'pRval'
2500  }
2501 
2502  // fix up all of the data indices
2503  for(pXE=pRval; pXE < pXCur; pXE++)
2504  {
2505  if(pXCur->nLabelOffset >= 0) // allow '0' for this part
2506  {
2507  pXCur->nLabelOffset += cbOffs; // fix up the data offset
2508  }
2509  else
2510  {
2511  pXCur->nLabelOffset = 0; // make it zero to mark it 'unused'
2512  }
2513 
2514  if(pXCur->nDataOffset >= 0) // allow '0' for this part
2515  {
2516  pXCur->nDataOffset += cbOffs; // fix up the data offset
2517  }
2518  else
2519  {
2520  pXCur->nDataOffset = 0; // make it zero to mark it 'unused'
2521  }
2522  }
2523 
2524  pXCur++; // this is where the data will start, now
2525 
2526  // copy the data where it needs to be
2527 
2528  memcpy(pXCur, pData, (pCurData - pData) + 1); // copy data, including the final 0-byte at 'pData[length]'
2529 
2530  // and now I'm done!
2531 
2532  goto the_end;
2533 
2534 error_exit:
2535 
2536  free(pRval);
2537  pRval = NULL;
2538 
2539 
2540 the_end:
2541  free(pData); // not needed any more
2542 
2543  return pRval; // the self-contained structure, or NULL on error
2544 }
2545 
2546 char *CHParseXMLTagContents(const char *pTagContents, int cbLength)
2547 {
2548 const char *pCur = pTagContents, *pEnd = pTagContents + cbLength;
2549 const char *p1, *p2, *p3;
2550 char *pRval, *pC, *pE, *p4, *p5;
2551 int i1, cbRval = 4096;
2552 
2553  if(!pCur)
2554  {
2555  return NULL;
2556  }
2557 
2558  if(cbLength < 0)
2559  {
2560  cbLength = strlen(pTagContents);
2561  pEnd = pTagContents + cbLength;
2562  }
2563 
2564  pC = pRval = WBAlloc(cbRval);
2565  if(!pRval)
2566  {
2567  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2568  return NULL;
2569  }
2570 
2571  pE = pRval + cbRval;
2572 
2573  pC[0] = pC[1] = 0;
2574 
2575 
2576  // NOTE: XML spec requires that '>' and '&' be treated special, and quotes ignored for these.
2577  // The strings "&amp;" "&lt;" "&gt;" must also be honored inside or outside of quotes.
2578  // when I find '>' inside of quotes, I could optionally ignore it, but the spec says "NO"
2579  // so the result should be some kind of XML syntax error...
2580 
2581  while(pCur < pEnd && *pCur)
2582  {
2583  // find value name
2584  while(pCur < pEnd && *pCur && *pCur <= ' ')
2585  {
2586  pCur++; // skip white space
2587  }
2588 
2589  p1 = pCur;
2590  while(pCur < pEnd && *pCur > ' ' && *pCur != '=' && *pCur != '>' && *pCur != '[' && *pCur != '(')
2591  {
2592  pCur++; // find end of string
2593  }
2594 
2595  if(pCur >= pEnd)
2596  {
2597  break; // went past the end of the buffer
2598  }
2599 
2600  if(*pCur == '>') // end of tag?
2601  {
2602  if(pCur == p1) // empty value
2603  {
2604  break; // I am done
2605  }
2606  else if(pCur >= p1 + 2 && *(pCur - 1) == '-' && *(pCur - 2) == '-')
2607  {
2608  pCur -= 2; // prior to '-->'
2609  }
2610  else if(*(pCur - 1) == '/')
2611  {
2612  pCur --; // prior to '/>'
2613  }
2614  }
2615 #if 0
2616  else if(*pCur == '(' || *pCur == '[') // an embedded section ??? [this only applies to CDATA]
2617  {
2618  // in this case it's an embedded entity and I want to preserve it in its entirety
2619  char cTemp = *pCur;
2620 
2621  if(cTemp == '(')
2622  {
2623  cTemp = ')';
2624  }
2625  else
2626  {
2627  cTemp = ']';
2628  }
2629 
2630  pCur++;
2631  if(pCur >= pEnd)
2632  {
2633  break; // I'm outa here - past end of buffer
2634  }
2635 
2636  p2 = pCur;
2637  pCur = CHFindEndOfXMLSection(pCur, pEnd - pCur, cTemp, 0); // find end of section
2638 
2639  if(pCur < pEnd)
2640  {
2641  pCur++; // point to next char past the end of this section
2642  }
2643 
2644  p5 = WBCopyStringN(p2, pCur - p2); // make a copy of the section, allocated as p5
2645 
2646  goto value_is_now_p5; // this will check for NULL 'p5' also
2647  }
2648 #endif // 0
2649 
2650  if(pCur == p1)
2651  {
2652  break; // I am done
2653  }
2654 
2655 
2656  p2 = pCur;
2657 
2658  while(pCur < pEnd && *pCur && *pCur <= ' ')
2659  {
2660  pCur++; // skip white space
2661  }
2662 
2663  // NOTE: this function does not handle '&amp;' or '&gt;' etc. outside of quotes
2664 
2665  if(pCur < pEnd && *pCur == '=') // value follows
2666  {
2667  pCur++;
2668 
2669  while(pCur < pEnd && *pCur && *pCur <= ' ')
2670  {
2671  pCur++; // skip white space
2672  }
2673 
2674  p3 = pCur;
2675 
2676  if(*pCur == '"' || *pCur == '\'') // quoted string
2677  {
2678  char c1 = *pCur;
2679  pCur++;
2680  while(pCur < pEnd && *pCur &&
2681  (*pCur != c1 || (pCur < pEnd - 1 && pCur[1] == c1)))
2682  {
2683  if(*pCur == c1) // will be < pEnd - 1
2684  {
2685  pCur += 2;
2686  }
2687  else
2688  {
2689  pCur++;
2690  }
2691  }
2692 
2693  if(*pCur == '"')
2694  {
2695  pCur++; // now past the quote
2696  }
2697 
2698  p5 = WBCopyStringN(p3, pCur - p3); // copy all including start/end quotes
2699 
2700  // make de-quoted normalized version
2701  if(p5)
2702  {
2703  WBNormalizeXMLString(p5); // remove quotes and sub '&gt;' '&amp;' etc.
2704  }
2705  }
2706  else
2707  {
2708  while(pCur < pEnd && *pCur > ' ' && *pCur != '>')
2709  {
2710  pCur++; // find end of string
2711  }
2712 
2713  if(*pCur == '>') // end of tag?
2714  {
2715  if(pCur == p3) // empty value
2716  {
2717  goto no_value;
2718  }
2719  else if(pCur >= p3 + 2 && *(pCur - 1) == '-' && *(pCur - 2) == '-')
2720  {
2721  pCur -= 2; // prior to '-->'
2722  }
2723  else if(*(pCur - 1) == '/')
2724  {
2725  pCur --; // prior to '/>'
2726  }
2727  }
2728 
2729  if(pCur == p3) // 'value= />' yeah, I accept it
2730  {
2731  goto no_value;
2732  }
2733 
2734  p5 = WBCopyStringN(p3, pCur - p3);
2735 
2736  // make normalized version
2737  if(p5)
2738  {
2739  WBNormalizeXMLString(p5); // remove quotes and sub '&gt;' '&amp;' etc.
2740  }
2741  }
2742 
2743 //value_is_now_p5:
2744 
2745  if(!p5)
2746  {
2747  WBFree(pRval);
2748  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2749  return NULL;
2750  }
2751 
2752  // value is now p5
2753 
2754  if(pC + (p2 - p1) + strlen(p5) + 4 >= pE)
2755  {
2756  i1 = 4096;
2757  while((p2 - p1) + strlen(p5) + 4 >= i1)
2758  {
2759  i1 += 4096; // to make sure it's big enough in 4k increments
2760  }
2761 
2762  cbRval += i1;
2763  p4 = WBReAlloc(pRval, cbRval);
2764 
2765  if(!p4)
2766  {
2767  WBFree(p5);
2768  WBFree(pRval);
2769  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2770  return NULL;
2771  }
2772 
2773  if(p4 != pRval)
2774  {
2775  pC = p4 + (pC - pRval);
2776  pRval = p4;
2777  }
2778 
2779  pE = pRval + cbRval;
2780  }
2781 
2782  // now do value=the value\0\0 and point 'pC' to the 2nd '\0'
2783  memcpy(pC, p1, p2 - p1);
2784  pC += p2 - p1;
2785  *(pC++) = '=';
2786  strcpy(pC, p5);
2787  pC += strlen(pC) + 1;
2788  *pC = 0;
2789 
2790  WBFree(p5); // done with it
2791  }
2792  else // no value
2793  {
2794 no_value:
2795  if(pC + (p2 - p1) + 3 >= pE)
2796  {
2797  i1 = 4096;
2798  while((p2 - p1) + 3 >= i1)
2799  {
2800  i1 += 4096; // to make sure it's big enough in 4k increments
2801  }
2802 
2803  cbRval += i1;
2804  p4 = WBReAlloc(pRval, cbRval);
2805 
2806  if(!p4)
2807  {
2808  WBFree(pRval);
2809  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2810  return NULL;
2811  }
2812 
2813  if(p4 != pRval)
2814  {
2815  pC = p4 + (pC - pRval);
2816  pRval = p4;
2817  }
2818 
2819  pE = pRval + cbRval;
2820  }
2821 
2822  // now do value=\0\0 and point 'pC' to the 2nd '\0'
2823  memcpy(pC, p1, p2 - p1);
2824  pC += p2 - p1;
2825  *(pC++) = '=';
2826  *(pC++) = 0;
2827  *pC = 0; // by convention
2828  }
2829  }
2830 
2831 
2832  return pRval;
2833 }
2834 
2835 const char *CHFindNextXMLTag(const char *pTagContents, int cbLength, int nNestingFlags)
2836 {
2837 const char *p1, *pEnd = pTagContents + cbLength;
2838 
2839 
2840  if(!pTagContents || cbLength == 0 || !*pTagContents)
2841  {
2842  return pTagContents;
2843  }
2844 
2845  if(cbLength < 0)
2846  {
2847  cbLength = strlen(pTagContents);
2848  }
2849 
2850  p1 = pTagContents;
2851 
2852  // outside of a tag, we don't check quote marks. however, I do check for parens
2853  // when the bit flags in 'nNestingFlags' tell me to.
2854  // TODO: add a 'quote mark' check to 'nNestingFlags' ?
2855 
2856  // NOTE: XML spec requires that '>' and '&' be treated special, and quotes ignored for these.
2857  // The strings "&amp;" "&lt;" "&gt;" must also be honored inside or outside of quotes.
2858  // when I find '>' inside of quotes, I could optionally ignore it, but the spec says "NO"
2859  // so the result should be some kind of XML syntax error...
2860 
2861  while(p1 < pEnd && *p1)
2862  {
2863  if(*p1 == '<') // next tag (includes comment tags, etc.)
2864  {
2865  break;
2866  }
2867 
2868  // TODO: exit if I find ending tag?
2869 
2870  if((nNestingFlags & CHPARSEXML_PAREN) && (*p1 == '(' || *p1 == ')'))
2871  {
2872  break;
2873  }
2874 
2875  if((nNestingFlags & CHPARSEXML_BRACKET) && (*p1 == '[' || *p1 == ']'))
2876  {
2877  break;
2878  }
2879 
2880  p1++;
2881  }
2882 
2883  return p1;
2884 }
2885 
2886 
2887 const char *CHFindEndOfXMLSection(const char *pTagContents, int cbLength, char cEndChar, int bUseQuotes)
2888 {
2889 register const char *p1 = pTagContents;
2890 const char *pE;
2891 
2892 
2893  if(!p1 || !cbLength)
2894  {
2895  return NULL;
2896  }
2897 
2898  if(cbLength < 0)
2899  {
2900  cbLength = strlen(p1);
2901  }
2902 
2903  pE = p1 + cbLength;
2904 
2905  // in cases of <!CDATA ... > and other tags that might have nesting within them, this
2906  // function needs to pay attention to '[' ']' '(' and ')' that are OUTSIDE of quoted strings
2907 
2908  // NOTE: XML spec requires that '>' and '&' be treated special, and quotes ignored for these.
2909  // The strings "&amp;" "&lt;" "&gt;" must also be honored inside or outside of quotes.
2910  // when I find '>' inside of quotes, I could optionally ignore it, but the spec says "NO"
2911  // so the result should be some kind of XML syntax error...
2912 
2913  while(p1 < pE && *p1)
2914  {
2915  // need to parse out this tag.
2916  if(bUseQuotes && (*p1 == '"' || *p1 == '\'')) //handle quotes
2917  {
2918  char c1 = *p1;
2919  p1++;
2920  if(p1 >= pE)
2921  {
2922  break;
2923  }
2924 
2925  while(p1 < pE && *p1 &&
2926  (*p1 != c1 || ((p1 + 1) < pE && p1[1] == c1)))
2927  {
2928  if(*p1 == c1) // doubled quote?
2929  {
2930  p1 += 2;
2931  }
2932  else
2933  {
2934  p1++;
2935  }
2936  }
2937 
2938  if(p1 >= pE)
2939  {
2940  break;
2941  }
2942 
2943  if(*p1 == c1)
2944  {
2945  p1++; // now past the quote
2946  }
2947  }
2948  else if(*p1 == cEndChar) // end of the tag/section
2949  {
2950  break;
2951  }
2952 
2953  // Assuming we are already "within a tag" it's possible, for some tags, to have
2954  // a bunch of stuff embedded within them using '( )' '[ ]' etc.. This function
2955  // will recurse and allow for nested things like that.
2956 
2957  else if(*p1 == '(') // now we look for embedded things
2958  {
2959  p1++;
2960  if(p1 >= pE)
2961  {
2962  break;
2963  }
2964 
2965  p1 = CHFindEndOfXMLSection(p1, pE - p1, ')', 0);
2966 
2967  if(!p1 || p1 >= pE || !*p1 )
2968  {
2969  break;
2970  }
2971 
2972  p1++; // point it past the ')' I just found
2973  }
2974  else if(*p1 == '[')
2975  {
2976  p1++;
2977  if(p1 >= pE)
2978  {
2979  break;
2980  }
2981 
2982  p1 = CHFindEndOfXMLSection(p1, pE - p1, ']', 0);
2983 
2984  if(!p1 || p1 >= pE || !*p1 )
2985  {
2986  break;
2987  }
2988 
2989  p1++; // point it past the ']' I just found
2990  }
2991  else if(!bUseQuotes && *p1 == '<' && (cEndChar == ']' || cEndChar == ')'))
2992  {
2993  // special case within an embedded section surrounded by '[]' or '()'
2994  // IF I'm searching for an end bracket/paren, and
2995  // IF I've just run across the beginning of a tag, and
2996  // IF I'm currently ignoring quote marks
2997  // THEN, I want to parse the XML tag with respect to quote marks until the end of the tag
2998 
2999  p1++;
3000  if(p1 >= pE)
3001  {
3002  break;
3003  }
3004 
3005  p1 = CHFindEndOfXMLSection(p1, pE - p1, '>', 1);
3006 
3007  if(!p1 || p1 >= pE || !*p1 )
3008  {
3009  break;
3010  }
3011 
3012  p1++; // point it past the '>' I just found (it's embedded)
3013  }
3014  else
3015  {
3016  p1++;
3017  }
3018  }
3019 
3020  return p1;
3021 }
3022 
3023 const char *CHFindEndOfXMLTag(const char *pTagContents, int cbLength)
3024 {
3025  return CHFindEndOfXMLSection(pTagContents, cbLength, '>', 1);
3026 }
3027 
3028 
3029 
3031 // MIME HELPERS
3033 
3034 char *CHGetFileMimeType(const char *szFileName)
3035 {
3036 char *pRval, *p2;
3037 
3038  // for NOW just do this... later check if it's just a file extension
3039  // and get the mime type from just that
3040 
3041  pRval = WBRunResult("xdg-mime","query","filetype",szFileName,NULL);
3042 
3043  // right-trim the result (always)
3044  if(pRval)
3045  {
3046  p2 = pRval + strlen(pRval);
3047  while(p2 > pRval && *(p2 - 1) <= ' ')
3048  {
3049  *(--p2) = 0;
3050  }
3051  }
3052 
3053  return pRval;
3054 }
3055 
3056 char *CHGetMimeDefaultApp(const char *szMimeType)
3057 {
3058 char *pRval, *p2;
3059 
3060  pRval = WBRunResult("xdg-mime","query","default",szMimeType,NULL);
3061 
3062  // right-trim the result (always)
3063 
3064  if(pRval)
3065  {
3066  p2 = pRval + strlen(pRval);
3067  while(p2 > pRval && *(p2 - 1) <= ' ')
3068  {
3069  *(--p2) = 0;
3070  }
3071  }
3072 
3073  return pRval;
3074 }
3075 
3076 // NOTE: according to http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
3077 // the 'Exec' key in a '.desktop' file may have the following parameters:
3078 // %f - a single file name
3079 // %F - one or more file names
3080 // %u - a single URL
3081 // %U - one or more URLs
3082 // %i - the icon key expanded as 2 arguments (see web page)
3083 // %c - the translated application name (see web page)
3084 // %k - the desktop file's name/location/URI/whatever (see web page)
3085 // (all others are deprecated)
3086 
3087 char *CHGetDesktopFileInfo(const char *szDesktopFile, const char *szInfo)
3088 {
3089 char *pRval, *p1, *p2, *pTemp;
3090 int i1;
3091 
3092  p1 = WBSearchPath(szDesktopFile);
3093  if(!p1)
3094  {
3095  // desktop files can be in a couple of OTHER places...
3096 
3097  if(*szDesktopFile != '/')
3098  {
3099  p1 = WBAlloc(PATH_MAX + strlen(szDesktopFile));
3100  if(p1)
3101  {
3102  // NOTE: there may be some system config var that tells me whether to use "/usr/share" or "/usr/local/share"
3103  // may ALSO want to look in '/usr/local/share/applications/*' (xdg-open does)
3104  static const char * const aszPaths[]=
3105  { "/usr/local/share/applications/", "/usr/share/applications/",
3106  "/usr/local/share/app-install/desktop/", "/usr/share/app-install/desktop/" };
3107 
3108  for(i1=0; i1 < sizeof(aszPaths)/sizeof(aszPaths[0]); i1++)
3109  {
3110  strcpy(p1, aszPaths[i1]);
3111  strcat(p1, szDesktopFile);
3112  if(!WBStat(p1, NULL)) // file exists?
3113  {
3114  break;
3115  }
3116  }
3117 
3118  if(i1 >= sizeof(aszPaths)/sizeof(aszPaths[0]))
3119  {
3120  WBFree(p1);
3121  p1 = NULL;
3122  }
3123  }
3124  }
3125  }
3126 
3127  if(!p1)
3128  {
3129  WB_ERROR_PRINT("%s - File \"%s\" does not exist or cannot be located\n", __FUNCTION__, szDesktopFile);
3130  return NULL;
3131  }
3132 
3133  pTemp = WBCopyString("^");
3134 
3135  if(pTemp)
3136  {
3137  WBCatString(&pTemp, szInfo);
3138  }
3139  if(pTemp)
3140  {
3141  WBCatString(&pTemp, "="); // '^whatever=' for szInfo -> "whatever"
3142  }
3143 
3144  if(!pTemp)
3145  {
3146  WBFree(p1);
3147  return NULL;
3148  }
3149 
3150  pRval = WBRunResult("grep",pTemp,p1,NULL);
3151 
3152  WBFree(p1);
3153  p1 = NULL;
3154 
3155  WB_ERROR_PRINT("TEMPORARY: %s - result \"%s\"\n", __FUNCTION__, pRval);
3156 
3157 
3158  if(pRval && strlen(pRval) >= strlen(pTemp))
3159  {
3160  strcpy(pRval, pRval + strlen(pTemp) - 1); // note '^' at beginning won't be in the result
3161  // TODO: find the '=' and use THAT instead?
3162  }
3163 
3164  WBFree(pTemp);
3165 
3166  // right-trim the result (always)
3167  if(pRval)
3168  {
3169  p2 = pRval + strlen(pRval);
3170  while(p2 > pRval && *(p2 - 1) <= ' ')
3171  {
3172  *(--p2) = 0;
3173  }
3174 
3175  if(!*pRval) // if the resulting string is BLANK
3176  {
3177  WBFree(pRval);
3178  pRval = NULL;
3179  }
3180  }
3181 
3182  return pRval;
3183 }
3184 
3185 
3186 
3188 // DEBUG FUNCTIONS
3190 
3191 #ifndef NO_DEBUG
3193 {
3194 int i1, nSettings;
3195 
3196  if(!pXSettings)
3197  {
3198  WBDebugPrint("WARNING: no CHXSettings data in CHDumpConfig\n");
3199  return;
3200  }
3201 
3202  WBDebugPrint("========================================================================\n");
3203  WBDebugPrint("CHXSettings dump - DISP: %p # %d SER: %u (%08xH)\n",
3204  pXSettings->pDisplay, pXSettings->nSettings, pXSettings->uiSerial, pXSettings->uiSerial);
3205 
3206  nSettings = pXSettings->nSettings;
3207 
3208  for(i1=0; i1 < nSettings; i1++)
3209  {
3210  WBDebugPrint("%3d %-32.32s ", i1, pXSettings->aData[i1].szName);
3211 
3212  switch(pXSettings->aData[i1].iType)
3213  {
3214  case XSettingsTypeInteger:
3215  WBDebugPrint(" integer %d (%08xH)\n",
3216  pXSettings->aData[i1].uData.iData,
3217  pXSettings->aData[i1].uData.iData);
3218  break;
3219 
3220  case XSettingsTypeColor:
3221  WBDebugPrint(" color R:%d B:%d G:%d A:%d\n",
3222  pXSettings->aData[i1].uData.clrData.sRed,
3223  pXSettings->aData[i1].uData.clrData.sBlue,
3224  pXSettings->aData[i1].uData.clrData.sGreen,
3225  pXSettings->aData[i1].uData.clrData.sAlpha);
3226  break;
3227 
3228  case XSettingsTypeString:
3229 // if(pXSettings->aData[i1].iLen > 32)
3230 // {
3231 // WBDebugPrint(" \"%-.32s ...\n",
3232 // pXSettings->aData[i1].uData.szData);
3233 // }
3234 // else
3235  {
3236  WBDebugPrint(" \"%s\"\n",
3237  pXSettings->aData[i1].uData.szData);
3238  }
3239  break;
3240  }
3241  }
3242 
3243  WBDebugPrint("========================================================================\n");
3244 
3245 }
3246 
3247 #endif // !NO_DEBUG
3248 
basic &#39;buffered I/O&#39; object structure for &#39;FileBuf&#39; APIs
Definition: file_help.h:140
char * CHGetMimeDefaultApp(const char *szMimeType)
Get the default application for a particular MIME type.
Definition: conf_help.c:3056
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:2186
int nSettings
the total number of settings in aData
Definition: conf_help.h:455
&#39;window helper&#39; main header file for the X11workbench Toolkit API
#define CHPARSEXML_PAREN
Definition: conf_help.h:118
int FBWriteFileBufHandle(int iFile, const file_help_buf_t *pBuf)
Write the file_help_buf_t object&#39;s text data to a file using an open file handle. ...
Definition: file_help.c:303
int iData
The data as an integer value.
Definition: conf_help.h:418
Utilities for copying and drawing text, determining text extents, and so on.
unsigned short sRed
16-bit RGB value for RED
Definition: conf_help.h:371
const CHXSetting * CHGetXSetting(Display *pDisplay, const char *szSettingName)
returns a pointer to a specific X setting entry by name
Definition: conf_help.c:1727
char * WBRunResult(const char *szAppName,...)
Run an application synchronously, returning &#39;stdout&#39; output in a character buffer.
CHXSetting aData[1]
An array of &#39;nSettings&#39; CHXSetting structures. Binary and string data directly follows the array...
Definition: conf_help.h:457
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 &#39;helper&#39; functions
Definition: conf_help.c:166
char ** ppLineBuf
array of pointers to beginning of each line (WBAlloc&#39;d TODO: make it part of &#39;cData&#39;?)
Definition: file_help.h:149
int nLabelOffset
BYTE offset to label (zero-byte-terminated) string (from beginning of array) for this entry; <= 0 for...
Definition: conf_help.h:598
char * WBSearchPath(const char *szFileName)
Run an application asynchronously.
const char * CHFindEndOfXMLTag(const char *pTagContents, int cbLength)
Parses contents of an XML tag to find the end of it.
Definition: conf_help.c:3023
union _CHXSetting_::@7 uData
union of the data alias&#39;
Atom aINCR
Atoms for the clipboard.
void FBDestroyFileBuf(file_help_buf_t *pBuf)
Destroy a file_help_buf_t object.
Definition: file_help.c:186
int iType
The setting type (XSettingsType enumeration)
Definition: conf_help.h:413
unsigned short sAlpha
16-bit ALPHA value. If not used, it will be 0xffff
Definition: conf_help.h:374
struct __XSETTINGS_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:637
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&#39;d string list similar to environment strings...
Definition: conf_help.c:2356
const char * CHFindEndOfXMLSection(const char *pTagContents, int cbLength, char cEndChar, int bUseQuotes)
Parses XML text for the end of a &#39;section&#39;, typically ending in &#39;>&#39; &#39;)&#39; or &#39;]&#39;.
Definition: conf_help.c:2887
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:1223
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
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 &#39;re-allocate&#39;.
int CHGetCursorBlinkTime(Display *pDisplay)
returns default cursor blink time (from X settings)
Definition: conf_help.c:2303
long lLineCount
number of lines in &#39;cData&#39; when ppLineBuf not NULL
Definition: file_help.h:146
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:2835
#define CH_FLAGS_WRITE
Definition: conf_help.h:115
int nDataOffset
BYTE offset to data (zero-byte-terminated) string (from beginning of array) for the entry data; <= 0 ...
Definition: conf_help.h:599
int CHGetArgC()
retrieves the argument count stored by CHRegisterArgs()
Definition: conf_help.c:624
unsigned int uiSerial
serial number from last XSETTINGS query
Definition: conf_help.h:456
Array wrapper for CHXSetting cache.
Definition: conf_help.h:452
char cData[sizeof(char *)]
the data itself (aligned to size of a pointer)
Definition: file_help.h:150
long lBufferCount
number of bytes of valid data
Definition: file_help.h:145
int WBStat(const char *szLinkName, unsigned long *pdwModeAttrReturn)
Obtain the &#39;stat&#39; flags for a file name, resolving links as needed.
Definition: file_help.c:1578
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:1357
const char * szName
pointer to the NAME string within memory that follows CHXSettings::aData
Definition: conf_help.h:412
void CHCloseConfFile(void *pFile)
close configuration file opened by CHOpenConfFile(), but does NOT free memory resources ...
Definition: conf_help.c:831
unsigned short sGreen
16-bit RGB value for Green
Definition: conf_help.h:373
void * WBAlloc(int nSize)
High performance memory sub-allocator &#39;allocate&#39;.
int CHGetResourceInt(Display *pDisplay, const char *szIdentifier)
Queryies desktop resource integer values (from strings) (may become deprecated)
Definition: conf_help.c:598
int iContainer
0-based index for container; <= 0 for none.
Definition: conf_help.h:595
CHXMLEntry * CHParseXML(const char *pXMLData, int cbLength)
Parses contents of an XML tag, returning as WBAlloc&#39;d string list similar to environment strings...
Definition: conf_help.c:2410
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:3192
#define WB_UNLIKELY(x)
#define CH_FLAGS_GLOBAL
Definition: conf_help.h:114
Structure for storing settings information internally.
Definition: conf_help.h:410
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:268
Structure for storing configuration color information, XSettingsTypeColor.
Definition: conf_help.h:369
int iContentsIndex
0-based first array index for &#39;contents&#39; for this entry; <= 0 for none
Definition: conf_help.h:596
unsigned short sBlue
16-bit RGB value for Blue
Definition: conf_help.h:372
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
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
int CHGetDoubleClickTime(Display *pDisplay)
returns default double click time (from X settings)
Definition: conf_help.c:2147
int iLen
length (where applicable)
Definition: conf_help.h:414
The setting is an RGBA Color stored as XSETTINGS_DATA_COLOR.
Definition: conf_help.h:341
int CHGetCursorBlink(Display *pDisplay)
returns default cursor blink &#39;enable&#39; flag (from X settings)
Definition: conf_help.c:2264
int CHWriteConfFileInt(void *hFile, const char *szSection, const char *szIdentifier, int iData)
write an integer value to a configuration file
Definition: conf_help.c:1643
void CHSettingsRefresh(Display *pDisplay)
refresh the internally cached X settings
Definition: conf_help.c:1749
int CHGetConfFileInt(void *hFile, const char *szSection, const char *szIdentifier)
obtain an integer value from a configuration file
Definition: conf_help.c:1621
XSETTINGS_DATA_COLOR clrData
The data as a color value.
Definition: conf_help.h:420
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 &#39;dirty&#39;.
Definition: file_help.h:184
char * CHGetFileMimeType(const char *szFileName)
Get the MIME type for a particular file name or extension.
Definition: conf_help.c:3034
The setting is an integer.
Definition: conf_help.h:339
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...
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
char * WBGetCanonicalPath(const char *szFileName)
Return the canonical path for a file name (similar to POSIX &#39;realpath()&#39; funtion) ...
Definition: file_help.c:807
char * szData
&#39;char&#39; typed pointer to data within string area
Definition: conf_help.h:422
Display * pDisplay
identifies which display it belongs to (reserved)
Definition: conf_help.h:454
#define CHPARSEXML_BRACKET
Definition: conf_help.h:119
int CHGetDragThreshold(Display *pDisplay)
returns default drag threshold (from X settings)
Definition: conf_help.c:2225
const char *const * CHGetArgV()
retrieves the array of arguments stored by CHRegisterArgs()
Definition: conf_help.c:629
unsigned long long WB_UINTPTR
Platform abstract unsigned integer that matches pointer size.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
#define WB_LIKELY(x)
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:863
The setting is a 0-byte terminated ASCII string.
Definition: conf_help.h:340
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 &#39;if block&#39; code.
Definition: debug_helper.h:249
struct _CHXMLEntry_ 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:3087
void WBNormalizeXMLString(char *pString)
De-Quote and &#39;normalize&#39; an XML string &#39;in place&#39;, that is modifying the original string by removing ...
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:594
struct _CHXSetting_ CHXSetting
Structure for storing settings information internally.
char * CHParseXMLTagContents(const char *pTagContents, int cbLength)
Parses contents of a single XML tag, returning as WBAlloc&#39;d string list similar to environment string...
Definition: conf_help.c:2546
void WBCatString(char **ppDest, const char *pSrc)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string...
Descriptor for parsed XML entry.
Definition: conf_help.h:592