X11 Work Bench Toolkit  1.0
conf_help.c
Go to the documentation of this file.
1 
2 // __ _ _ //
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
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 
2348 char *CHParseXMLTagContents(const char *pTagContents, int cbLength)
2349 {
2350 const char *pCur = pTagContents, *pEnd = pTagContents + cbLength;
2351 const char *p1, *p2, *p3;
2352 char *pRval, *pC, *pE, *p4, *p5;
2353 int i1, cbRval = 4096;
2354 
2355  if(!pCur)
2356  {
2357  return NULL;
2358  }
2359 
2360  if(cbLength < 0)
2361  {
2362  cbLength = strlen(pTagContents);
2363  pEnd = pTagContents + cbLength;
2364  }
2365 
2366  pC = pRval = WBAlloc(cbRval);
2367  if(!pRval)
2368  {
2369  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2370  return NULL;
2371  }
2372 
2373  pE = pRval + cbRval;
2374 
2375  pC[0] = pC[1] = 0;
2376 
2377 
2378  while(pCur < pEnd && *pCur)
2379  {
2380  // find value name
2381  while(pCur < pEnd && *pCur && *pCur <= ' ')
2382  {
2383  pCur++; // skip white space
2384  }
2385 
2386  p1 = pCur;
2387  while(pCur < pEnd && *pCur > ' ' && *pCur != '=' && *pCur != '>')
2388  {
2389  pCur++; // find end of string
2390  }
2391 
2392  if(*pCur == '>') // end of tag?
2393  {
2394  if(pCur == p1) // empty value
2395  {
2396  break; // I am done
2397  }
2398  else if(pCur >= p1 + 2 && *(pCur - 1) == '-' && *(pCur - 2) == '-')
2399  {
2400  pCur -= 2; // prior to '-->'
2401  }
2402  else if(*(pCur - 1) == '/')
2403  {
2404  pCur --; // prior to '/>'
2405  }
2406  }
2407 
2408  if(pCur == p1)
2409  {
2410  break; // I am done
2411  }
2412 
2413  p2 = pCur;
2414 
2415  while(pCur < pEnd && *pCur && *pCur <= ' ')
2416  {
2417  pCur++; // skip white space
2418  }
2419 
2420  if(pCur < pEnd && *pCur == '=') // value follows
2421  {
2422  pCur++;
2423 
2424  while(pCur < pEnd && *pCur && *pCur <= ' ')
2425  {
2426  pCur++; // skip white space
2427  }
2428 
2429  p3 = pCur;
2430 
2431  if(*pCur == '"' || *pCur == '\'') // quoted string
2432  {
2433  char c1 = *pCur;
2434  pCur++;
2435  while(pCur < pEnd && *pCur &&
2436  (*pCur != c1 || (pCur < pEnd - 1 && pCur[1] == c1)))
2437  {
2438  if(*pCur == c1) // will be < pEnd - 1
2439  {
2440  pCur += 2;
2441  }
2442  else
2443  {
2444  pCur++;
2445  }
2446  }
2447 
2448  if(*pCur == '"')
2449  {
2450  pCur++; // now past the quote
2451  }
2452 
2453  p5 = WBCopyStringN(p3, pCur - p3); // copy all including start/end quotes
2454 
2455  // make de-quoted version
2456  if(p5)
2457  {
2458  WBDeQuoteString(p5); // remove quotes
2459  }
2460  }
2461  else
2462  {
2463  while(pCur < pEnd && *pCur > ' ' && *pCur != '>')
2464  {
2465  pCur++; // find end of string
2466  }
2467 
2468  if(*pCur == '>') // end of tag?
2469  {
2470  if(pCur == p3) // empty value
2471  {
2472  goto no_value;
2473  }
2474  else if(pCur >= p3 + 2 && *(pCur - 1) == '-' && *(pCur - 2) == '-')
2475  {
2476  pCur -= 2; // prior to '-->'
2477  }
2478  else if(*(pCur - 1) == '/')
2479  {
2480  pCur --; // prior to '/>'
2481  }
2482  }
2483 
2484  if(pCur == p3) // 'value= />' yeah, I accept it
2485  {
2486  goto no_value;
2487  }
2488 
2489  p5 = WBCopyStringN(p3, pCur - p3);
2490  }
2491 
2492  if(!p5)
2493  {
2494  WBFree(pRval);
2495  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2496  return NULL;
2497  }
2498 
2499  // value is now p5
2500 
2501  if(pC + (p2 - p1) + strlen(p5) + 4 >= pE)
2502  {
2503  i1 = 4096;
2504  while((p2 - p1) + strlen(p5) + 4 >= i1)
2505  {
2506  i1 += 4096; // to make sure it's big enough in 4k increments
2507  }
2508 
2509  cbRval += i1;
2510  p4 = WBReAlloc(pRval, cbRval);
2511 
2512  if(!p4)
2513  {
2514  WBFree(p5);
2515  WBFree(pRval);
2516  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2517  return NULL;
2518  }
2519 
2520  if(p4 != pRval)
2521  {
2522  pC = p4 + (pC - pRval);
2523  pRval = p4;
2524  }
2525 
2526  pE = pRval + cbRval;
2527  }
2528 
2529  // now do value=the value\0\0 and point 'pC' to the 2nd '\0'
2530  memcpy(pC, p1, p2 - p1);
2531  pC += p2 - p1;
2532  *(pC++) = '=';
2533  strcpy(pC, p5);
2534  pC += strlen(pC) + 1;
2535  *pC = 0;
2536 
2537  WBFree(p5); // done with it
2538  }
2539  else // no value
2540  {
2541 no_value:
2542  if(pC + (p2 - p1) + 3 >= pE)
2543  {
2544  i1 = 4096;
2545  while((p2 - p1) + 3 >= i1)
2546  {
2547  i1 += 4096; // to make sure it's big enough in 4k increments
2548  }
2549 
2550  cbRval += i1;
2551  p4 = WBReAlloc(pRval, cbRval);
2552 
2553  if(!p4)
2554  {
2555  WBFree(pRval);
2556  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
2557  return NULL;
2558  }
2559 
2560  if(p4 != pRval)
2561  {
2562  pC = p4 + (pC - pRval);
2563  pRval = p4;
2564  }
2565 
2566  pE = pRval + cbRval;
2567  }
2568 
2569  // now do value=\0\0 and point 'pC' to the 2nd '\0'
2570  memcpy(pC, p1, p2 - p1);
2571  pC += p2 - p1;
2572  *(pC++) = '=';
2573  *(pC++) = 0;
2574  *pC = 0; // by convention
2575  }
2576  }
2577 
2578 
2579  return pRval;
2580 }
2581 
2582 
2583 
2584 const char *CHFindEndOfXMLTag(const char *pTagContents)
2585 {
2586 register const char *p1 = pTagContents;
2587 
2588  if(!p1)
2589  {
2590  return NULL;
2591  }
2592 
2593  while(*p1)
2594  {
2595  // need to parse out this tag.
2596  if(*p1 == '"' || *p1 == '\'') //handle quotes
2597  {
2598  char c1 = *p1;
2599  p1++;
2600  while(*p1 &&
2601  (*p1 != c1 || p1[1] == c1))
2602  {
2603  if(*p1 == c1)
2604  {
2605  p1 += 2;
2606  }
2607  else
2608  {
2609  p1++;
2610  }
2611  }
2612  if(*p1 == c1)
2613  {
2614  p1++; // now past the quote
2615  }
2616  }
2617  else if(*p1 == '>') // end of XML tag
2618  {
2619  break;
2620  }
2621  else
2622  {
2623  p1++;
2624  }
2625  }
2626 
2627  return p1;
2628 }
2629 
2630 
2631 
2633 // MIME HELPERS
2635 
2636 char *CHGetFileMimeType(const char *szFileName)
2637 {
2638 char *pRval, *p2;
2639 
2640  // for NOW just do this... later check if it's just a file extension
2641  // and get the mime type from just that
2642 
2643  pRval = WBRunResult("xdg-mime","query","filetype",szFileName,NULL);
2644 
2645  // right-trim the result (always)
2646  if(pRval)
2647  {
2648  p2 = pRval + strlen(pRval);
2649  while(p2 > pRval && *(p2 - 1) <= ' ')
2650  {
2651  *(--p2) = 0;
2652  }
2653  }
2654 
2655  return pRval;
2656 }
2657 
2658 char *CHGetMimeDefaultApp(const char *szMimeType)
2659 {
2660 char *pRval, *p2;
2661 
2662  pRval = WBRunResult("xdg-mime","query","default",szMimeType,NULL);
2663 
2664  // right-trim the result (always)
2665 
2666  if(pRval)
2667  {
2668  p2 = pRval + strlen(pRval);
2669  while(p2 > pRval && *(p2 - 1) <= ' ')
2670  {
2671  *(--p2) = 0;
2672  }
2673  }
2674 
2675  return pRval;
2676 }
2677 
2678 // NOTE: according to http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
2679 // the 'Exec' key in a '.desktop' file may have the following parameters:
2680 // %f - a single file name
2681 // %F - one or more file names
2682 // %u - a single URL
2683 // %U - one or more URLs
2684 // %i - the icon key expanded as 2 arguments (see web page)
2685 // %c - the translated application name (see web page)
2686 // %k - the desktop file's name/location/URI/whatever (see web page)
2687 // (all others are deprecated)
2688 
2689 char *CHGetDesktopFileInfo(const char *szDesktopFile, const char *szInfo)
2690 {
2691 char *pRval, *p1, *p2, *pTemp;
2692 int i1;
2693 
2694  p1 = WBSearchPath(szDesktopFile);
2695  if(!p1)
2696  {
2697  // desktop files can be in a couple of OTHER places...
2698 
2699  if(*szDesktopFile != '/')
2700  {
2701  p1 = WBAlloc(PATH_MAX + strlen(szDesktopFile));
2702  if(p1)
2703  {
2704  // NOTE: there may be some system config var that tells me whether to use "/usr/share" or "/usr/local/share"
2705  // may ALSO want to look in '/usr/local/share/applications/*' (xdg-open does)
2706  static const char * const aszPaths[]=
2707  { "/usr/local/share/applications/", "/usr/share/applications/",
2708  "/usr/local/share/app-install/desktop/", "/usr/share/app-install/desktop/" };
2709 
2710  for(i1=0; i1 < sizeof(aszPaths)/sizeof(aszPaths[0]); i1++)
2711  {
2712  strcpy(p1, aszPaths[i1]);
2713  strcat(p1, szDesktopFile);
2714  if(!WBStat(p1, NULL)) // file exists?
2715  {
2716  break;
2717  }
2718  }
2719 
2720  if(i1 >= sizeof(aszPaths)/sizeof(aszPaths[0]))
2721  {
2722  WBFree(p1);
2723  p1 = NULL;
2724  }
2725  }
2726  }
2727  }
2728 
2729  if(!p1)
2730  {
2731  WB_ERROR_PRINT("%s - File \"%s\" does not exist or cannot be located\n", __FUNCTION__, szDesktopFile);
2732  return NULL;
2733  }
2734 
2735  pTemp = WBCopyString("^");
2736 
2737  if(pTemp)
2738  {
2739  WBCatString(&pTemp, szInfo);
2740  }
2741  if(pTemp)
2742  {
2743  WBCatString(&pTemp, "="); // '^whatever=' for szInfo -> "whatever"
2744  }
2745 
2746  if(!pTemp)
2747  {
2748  WBFree(p1);
2749  return NULL;
2750  }
2751 
2752  pRval = WBRunResult("grep",pTemp,p1,NULL);
2753 
2754  WBFree(p1);
2755  p1 = NULL;
2756 
2757  WB_ERROR_PRINT("TEMPORARY: %s - result \"%s\"\n", __FUNCTION__, pRval);
2758 
2759 
2760  if(pRval && strlen(pRval) >= strlen(pTemp))
2761  {
2762  strcpy(pRval, pRval + strlen(pTemp) - 1); // note '^' at beginning won't be in the result
2763  // TODO: find the '=' and use THAT instead?
2764  }
2765 
2766  WBFree(pTemp);
2767 
2768  // right-trim the result (always)
2769  if(pRval)
2770  {
2771  p2 = pRval + strlen(pRval);
2772  while(p2 > pRval && *(p2 - 1) <= ' ')
2773  {
2774  *(--p2) = 0;
2775  }
2776 
2777  if(!*pRval) // if the resulting string is BLANK
2778  {
2779  WBFree(pRval);
2780  pRval = NULL;
2781  }
2782  }
2783 
2784  return pRval;
2785 }
2786 
2787 
2788 
2790 // DEBUG FUNCTIONS
2792 
2793 #ifndef NO_DEBUG
2795 {
2796 int i1, nSettings;
2797 
2798  if(!pXSettings)
2799  {
2800  WBDebugPrint("WARNING: no CHXSettings data in CHDumpConfig\n");
2801  return;
2802  }
2803 
2804  WBDebugPrint("========================================================================\n");
2805  WBDebugPrint("CHXSettings dump - DISP: %p # %d SER: %u (%08xH)\n",
2806  pXSettings->pDisplay, pXSettings->nSettings, pXSettings->uiSerial, pXSettings->uiSerial);
2807 
2808  nSettings = pXSettings->nSettings;
2809 
2810  for(i1=0; i1 < nSettings; i1++)
2811  {
2812  WBDebugPrint("%3d %-32.32s ", i1, pXSettings->aData[i1].szName);
2813 
2814  switch(pXSettings->aData[i1].iType)
2815  {
2816  case XSettingsTypeInteger:
2817  WBDebugPrint(" integer %d (%08xH)\n",
2818  pXSettings->aData[i1].uData.iData,
2819  pXSettings->aData[i1].uData.iData);
2820  break;
2821 
2822  case XSettingsTypeColor:
2823  WBDebugPrint(" color R:%d B:%d G:%d A:%d\n",
2824  pXSettings->aData[i1].uData.clrData.sRed,
2825  pXSettings->aData[i1].uData.clrData.sBlue,
2826  pXSettings->aData[i1].uData.clrData.sGreen,
2827  pXSettings->aData[i1].uData.clrData.sAlpha);
2828  break;
2829 
2830  case XSettingsTypeString:
2831 // if(pXSettings->aData[i1].iLen > 32)
2832 // {
2833 // WBDebugPrint(" \"%-.32s ...\n",
2834 // pXSettings->aData[i1].uData.szData);
2835 // }
2836 // else
2837  {
2838  WBDebugPrint(" \"%s\"\n",
2839  pXSettings->aData[i1].uData.szData);
2840  }
2841  break;
2842  }
2843  }
2844 
2845  WBDebugPrint("========================================================================\n");
2846 
2847 }
2848 
2849 #endif // !NO_DEBUG
2850