X11workbench Toolkit  1.0
platform_helper.c
Go to the documentation of this file.
1 // _ _ __ _ _ //
3 // _ __ | | __ _ | |_ / _| ___ _ __ _ __ ___ | |__ ___ | | _ __ ___ _ __ ___ //
4 // | '_ \ | | / _` || __|| |_ / _ \ | '__|| '_ ` _ \ | '_ \ / _ \| || '_ \ / _ \| '__|/ __| //
5 // | |_) || || (_| || |_ | _|| (_) || | | | | | | | | | | || __/| || |_) || __/| | _| (__ //
6 // | .__/ |_| \__,_| \__||_| \___/ |_| |_| |_| |_|_____|_| |_| \___||_|| .__/ \___||_|(_)\___| //
7 // |_| |_____| |_| //
8 // //
9 // platform-specific code to rectify various issues //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2018 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 
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <stdarg.h>
60 #include <string.h>
61 #include <memory.h>
62 #ifndef WIN23
63 #include <pthread.h>
64 #include <unistd.h>
65 #include <fcntl.h>
66 #include <errno.h>
67 #include <signal.h>
68 #include <time.h>
69 #include <sys/time.h>
70 #include <dlfcn.h> /* dynamic library support */
71 #include <sys/wait.h>
72 #include <sys/types.h>
73 #ifdef __FreeBSD__
74 #include <sys/sysctl.h> // to use the 'sysctlXXX' APIs
75 #endif // __FreeBSD__
76 
77 #include <time.h> // clock_gettime etc.
78 #include <locale.h>
79 #endif // !WIN32
80 
81 #include "platform_helper.h" // includes X11 headers as well
82 #include "window_helper.h"
83 #include "file_help.h"
84 //#include "draw_text.h"
85 
86 #ifdef HAVE_MALLOC_USABLE_SIZE
87 #ifdef __FreeBSD__
88 #include <malloc_np.h>
89 #else
90 #include <malloc.h>
91 #endif // __FreeBSD__
92 #endif // HAVE_MALLOC_USABLE_SIZE
93 
94 
95 static char *pTempFileList = NULL, *pTempFileListEnd = NULL;
96 unsigned long cbTempFileList; // size of pointer
97 
98 static volatile int fInterlockedRWLockInitFlag = 0;
99 static pthread_rwlock_t xInterlockedRWLock;
100 
101 static void WBFreePointerHashes(void);
102 static void __add_to_temp_file_list(const char *szFile);
103 
104 
105 int bQuitFlag = FALSE; // defined here, used globally
106 
107 // within this file debug level is writable. outside of this file, it's (technically) read-only
108 unsigned int iWBDebugLevel = 0; // default is no debug except errors, all subsystems
109 
110 // application name (from argv[0])
111 static char szAppName[PATH_MAX * 2]="";
112 
113 
114 
116 // //
117 // ___ _ _ _ _ _____ _ _ //
118 // |_ _| _ __ (_)| |_ / \ _ __ __| | | ____|__ __(_)| |_ //
119 // | | | '_ \ | || __| / _ \ | '_ \ / _` | | _| \ \/ /| || __| //
120 // | | | | | || || |_ / ___ \ | | | || (_| | | |___ > < | || |_ //
121 // |___||_| |_||_| \__| /_/ \_\|_| |_| \__,_| |_____|/_/\_\|_| \__| //
122 // //
123 // //
125 
127 {
128 char *pEnv;
129 int iType;
130 
131  // NOTE: main thread needs to do this before spawning any threads.
132 
133  if(!fInterlockedRWLockInitFlag)
134  {
135  pthread_rwlock_init(&xInterlockedRWLock, NULL);
136  }
137 
138  iType = LC_ALL;
139  pEnv = getenv("LC_ALL"); // check this one first
140 
141  if(!pEnv || !*pEnv)
142  {
143  pEnv = getenv("LC_CTYPE"); // then this one
144 
145  if(pEnv && *pEnv)
146  {
147  iType = LC_CTYPE; // only adjust LC_CTYPE if I found THIS but no others...
148  }
149  else
150  {
151  pEnv = getenv("LANG"); // overall language setting
152  }
153  }
154 
155  if(pEnv && *pEnv)
156  {
157  if(!setlocale(iType, pEnv)) // should pick the correct one when I do this
158  {
159  WB_ERROR_PRINT("ERROR: %s - cannot set locale \"%s\"\n", __FUNCTION__, pEnv);
160 
161  goto default_locale;
162  }
163  }
164  else
165  {
166 default_locale:
167  setlocale(LC_ALL, ""); // should pick OS default when I do this
168  }
169 }
170 
172 {
173 char *p1;
174 
175 
176  if(pTempFileList) // delete temporary file list
177  {
178  for(p1 = pTempFileList; *p1; p1 += strlen(p1) + 1)
179  {
180  unlink(p1); // delete all of the temp files, ignoring errors
181  }
182 
183  WBFree(pTempFileList);
184  }
185 
186  WBFreePointerHashes(); // delete pointer hashes
187 
188  // finally, free up the RWLOCK that I created for RW-locking 'Interlocked' things
189 
190  if(fInterlockedRWLockInitFlag)
191  {
192  pthread_rwlock_destroy(&xInterlockedRWLock);
193  fInterlockedRWLockInitFlag = 0; // by convention, in case I re-init [unlikely]
194  }
195 
196 }
197 
198 // NOTE: when this function is called first, the arguments will be parsed in order to obtain
199 // information needed by WBInit and WBInitDisplay. Otherwise, default values are used by
200 // the WBInit and WBInitDisplay functions.
201 
202 int WBParseStandardArguments(int *pargc, char ***pargv, char ***penvp)
203 {
204 int argc = *pargc;
205 char **argv = *pargv;
206 char **envp = *penvp;
207 char **p1;
208 char **argvNew = NULL;
209 int argcNew = 0;
210 int i1, i2;
211 int iDebugLevelSeen = 0;
212 int argc_orig = argc;
213 char **argv_orig = argv;
214 
215 static const char * const aszCmdLineOptions[]=
216 {
217  "debug","subsys","display","minimize","maximize","geometry",
218  NULL // marks end of list
219 };
220 
221 static const char * const aszDebugSubSys[]=
222 {
223  "init","application","window","menu","event",
224  "dialog","dialogctrl","frame","keyboard",
225  "mouse","font","settings","selection",
226  "pixmap","expose","editwindow","scrollbar",
227  NULL
228 };
229 
230 enum
231 {
232  option_debug=0,
233  option_subsys,
234  option_display,
235  option_minimize,
236  option_maximize,
237  option_geometry
238 };
239 
240  // grab the name of the program and cache it. I'll need the path info.
241 
242  strncpy(szAppName, argv[0], sizeof(szAppName));
243  szAppName[sizeof(szAppName) - 1] = 0; // make sure
244 
245  // search for DISPLAY environment variable, pre-assign to szStartupDisplayName
246  p1 = envp;
247 
248  if(p1)
249  {
250  while(*p1)
251  {
252  if(!strncmp(*p1, "DISPLAY=", 8))
253  {
254  __internal_startup_display(*p1 + 8);
255  break; // I am done
256  }
257 
258  p1++;
259  }
260  }
261  else // with no environment pointers, use the 'getenv' C language function
262  {
263  char *p1a = getenv("DISPLAY");
264 
265  if(p1a)
266  {
267  __internal_startup_display(p1a);
268  }
269  }
270 
271  // parse arg list and make copy as needed
272 
273  while(argc > 1)
274  {
275  int argc0 = argc; // value at start of loop iteration
276  int iFlag = 0;
277 
278  if(argv[1][0] != '-' || !argv[1][1])
279  {
280  // end of arguments is a '-' by itself or something not starting with '-'
281  break;
282  }
283 
284  if(argv[1][1] != '-') // double '-' is what I'm looking for
285  {
286  if(!argvNew) // aren't making copies yet
287  {
288  argv++; // keep looking but don't touch anything yet
289  argc--;
290 
291 // WB_ERROR_PRINT("TEMPORARY: skipping argument '%s'\n", argv[0]);
292  continue;
293  }
294 
295 // WB_ERROR_PRINT("TEMPORARY: unrecognized argument '%s'\n", argv[1]);
296  }
297  else
298  {
299  // options here start with two '-' and are followed by
300  // text=value or text [space] value
301 
302  for(i1=0; aszCmdLineOptions[i1]; i1++)
303  {
304  const char *szOpt = aszCmdLineOptions[i1];
305  int iLen = strlen(szOpt);
306  const char *szArg = argv[1] + 2;
307  const char *szVal = szArg + iLen;
308 
309  if(!strncmp(szOpt, szArg, iLen) &&
310  (*szVal == '=' || !*szVal))
311  {
312  if(!*szVal)
313  {
314  if(argc > 2)
315  {
316  // skip over arg for loop
317  argv++;
318  argc--;
319  szVal = argv[1];
320  }
321  }
322 
323  switch(i1)
324  {
325  case option_debug:
326  i2 = atoi(szVal);
327 
328  if(*szVal < '0' || *szVal > '9' || i2 < 0 || i2 > DebugLevel_Excessive)
329  {
330  WBDebugPrint("Invalid debug level %s\n", szVal);
331 
332  if(argvNew)
333  {
334  WBFree(argvNew);
335  }
336 
337  return -1;
338  }
339 
340  iDebugLevelSeen = 1;
341  iWBDebugLevel = (iWBDebugLevel & DebugSubSystem_MASK)
342  | (atoi(szVal) & DebugLevel_MASK);
343  break;
344 
345  case option_subsys:
346  if(!strcmp("help", szVal))
347  {
348  // --subsys=help lists all of them on stderr
349 
350  WBDebugPrint("List of '--subsys' options:\n"
351  " Subsystem Name Bit Value (hexadecimal)\n"
352  " ----------------------- ----------- -------------\n");
353 
354  for(i2=0; aszDebugSubSys[i2]; i2++)
355  {
356  WBDebugPrint(" %-24s%10ld %08lxH\n", aszDebugSubSys[i2],
357  1L << (i2 + DebugSubSystem_BITSHIFT),
358  1L << (i2 + DebugSubSystem_BITSHIFT));
359  }
360 
361  WBDebugPrint(" (special '--subsys' options)\n"
362  " all 0 00000000H\n"
363  " restrict 2147483648 80000000H\n\n"
364  "Specify 'restrict' in addition to one or more subsystems in order\n"
365  "to ONLY display debug output for the specified subsystem(s)\n\n");
366 
367  return 1; // advise NOT to display 'usage()' but to definitely exit the application
368  }
369  else if(!strcmp("restrict", szVal))
370  {
371  iWBDebugLevel |= DebugSubSystem_RESTRICT; // means ONLY the subsys that I specify
372  }
373  else if(!strcmp("all", szVal))
374  {
375  iWBDebugLevel &= ~(DebugSubSystem_MASK | DebugSubSystem_RESTRICT); // "all"
376  }
377  else
378  {
379  for(i2=0; aszDebugSubSys[i2]; i2++)
380  {
381  if(!strcasecmp(aszDebugSubSys[i2], szVal))
382  {
383  iWBDebugLevel |= 1L << (i2 + DebugSubSystem_BITSHIFT);
384  break;
385  }
386  }
387  if(!aszDebugSubSys[i2])
388  {
389  i2 = atoi(szVal);
390  if(i2 > 0 && (i2 + DebugSubSystem_BITSHIFT) <= 32)
391  {
392  iWBDebugLevel |= 1L << (i2 + DebugSubSystem_BITSHIFT - 1);
393  }
394  else
395  {
396  WBDebugPrint("unrecognized subsystem %s\n", szVal);
397 
398  if(argvNew)
399  {
400  WBFree(argvNew);
401  }
402 
403  return -1;
404  }
405  }
406  }
407 
408  if(!iDebugLevelSeen)
409  {
410  // assume EXCESSIVE debug level unless otherwise specified
411 
412  iWBDebugLevel = (iWBDebugLevel & DebugSubSystem_MASK)
413  | DebugLevel_Excessive;
414  }
415 
416  break;
417 
418  case option_display:
419  __internal_startup_display(szVal);
420  break;
421 
422  case option_minimize:
423  __internal_startup_minimize();
424  break;
425  case option_maximize:
426  __internal_startup_maximize();
427  break;
428  case option_geometry:
429  __internal_startup_geometry(szVal);
430  break;
431  default:
432  WBDebugPrint("Unrecognized option: --%s\n", szArg);
433 
434  if(argvNew)
435  {
436  WBFree(argvNew);
437  }
438 
439  return -1;
440  }
441 
442  iFlag = 1; // merely skip this entry (I found a match for one of those options)
443  break;
444  }
445  }
446  }
447 
448  // at this point the argument may not have been recognized and so I must
449  // copy it into the destination if it wasn't... as PRIOR to this there
450  // should already be one or more unrecognized arguments
451 
452  if(!argvNew) // need to allocate copy
453  {
454 // WB_ERROR_PRINT("TEMPORARY: allocating argvNew at '%s'\n", argv[1]);
455 
456  argvNew = WBAlloc(sizeof(*argvNew) * (argc + 2));
457  if(!argvNew)
458  {
459  return -1; // not enough memory
460  }
461 
462  i2 = argc_orig; // the original 'argc' value
463  argcNew = 0;
464  while(i2 >= argc0) // I want to do this once on the first pass, etc.
465  {
466 // WB_ERROR_PRINT("TEMPORARY: copying argv at '%s'\n", argv_orig[argcNew]);
467 
468  argvNew[argcNew] = argv_orig[argcNew];
469  argcNew++;
470  i2--;
471  }
472  }
473 
474  if(!iFlag)
475  {
476  argvNew[argcNew++] = argv[1];
477 
478 // WB_ERROR_PRINT("TEMPORARY: (2) copying argv at '%s'\n", argv[1]);
479  }
480 
481  argv++;
482  argc--;
483  }
484 
485  if(argvNew)
486  {
487  // copy the rest of the arguments
488 
489  while(argc > 1)
490  {
491 // WB_ERROR_PRINT("TEMPORARY: (3) copying argv at '%s'\n", argv[1]);
492 
493  argvNew[argcNew++] = argv[1];
494  argv++;
495  argc--;
496  }
497 
498  *pargv = argvNew;
499  *pargc = argcNew;
500  }
501 
502  if((iWBDebugLevel & DebugLevel_MASK) > 0)
503  {
504  WBDebugPrint("DebugLevel set to %d\n", (int)(iWBDebugLevel & DebugLevel_MASK));
505  }
506 
507  if((iWBDebugLevel & DebugSubSystem_MASK) != 0)
508  {
509  i2 = 0;
510  for(i1=0; aszDebugSubSys[i1]; i1++)
511  {
512  if(iWBDebugLevel & (1L << (i1 + DebugSubSystem_BITSHIFT)))
513  {
514  if(!i2)
515  {
516  WBDebugPrint("Debug SubSystem: %s", aszDebugSubSys[i1]);
517  i2 = 1;
518  }
519  else
520  {
521  WBDebugPrint(", %s", aszDebugSubSys[i1]);
522  }
523  }
524  }
525 
526  for(; (i1 + DebugSubSystem_BITSHIFT) < 32; i1++)
527  {
528  if((1L << (i1 + DebugSubSystem_BITSHIFT)) == DebugSubSystem_RESTRICT)
529  {
530  if(!i2)
531  {
532  WBDebugPrint("Debug SubSystem: RESTRICT");
533  i2 = 1;
534  }
535  else
536  {
537  WBDebugPrint(", RESTRICT");
538  }
539  }
540  else if(iWBDebugLevel & (1L << (i1 + DebugSubSystem_BITSHIFT)))
541  {
542  if(!i2)
543  {
544  WBDebugPrint("Debug SubSystem: %d", i1 + 1);
545  i2 = 1;
546  }
547  else
548  {
549  WBDebugPrint(", %d", i1 + 1);
550  }
551  }
552  }
553 
554  if(i2) // must print an additional LF
555  {
556  WBDebugPrint("\n");
557  }
558  }
559 
560  return 0;
561 }
562 
563 void WBToolkitUsage(void)
564 {
565  fputs("X11 WorkBench Toolkit options (these should precede other options)\n"
566  "--debug n debug level (default 0, errors only)\n"
567  " 1 = minimal, 7 is maximum debug level\n"
568  "--subsys xx subsystem to debug (implies --debug 7 if not specified)\n"
569  " 'xx' is a subsystem name or bit value (see window_helper.h)\n"
570  " A bit value of 1 is equivalent to the lowest subsystem bit\n"
571  " NOTE: this option can be specified multiple times\n"
572  " (You can specify '--subsys help' to get a list of subsystems)\n"
573  "--display xx X11 display to use (default is DISPLAY env variable)\n"
574  "--minimize show minimized window on startup\n"
575  "--maximize show maximized window on startup\n"
576  "--geometry geom specify window geometry on startup\n"
577  " geometry spec as per X specification (see X man page)\n"
578  " typical value might be 800x600+100+50\n"
579  " (default centers window with reasonable size)\n"
580  " NOTE: application options with separate parameters should appear after\n"
581  " any X11 Workbench Toolkit options, to avoid confusing the parser.\n"
582  "\n", stderr);
583 }
584 
585 const char *GetStartupAppName(void)
586 {
587  return szAppName;
588 }
589 
590 
591 
593 // //
594 // ____ _ //
595 // | _ \ ___ | |__ _ _ __ _ //
596 // | | | | / _ \| '_ \ | | | | / _` | //
597 // | |_| || __/| |_) || |_| || (_| | //
598 // |____/ \___||_.__/ \__,_| \__, | //
599 // |___/ //
600 // //
602 
603 void WBSetDebugLevel(unsigned int iNew) // pass level as argument, 0 for none, 1 for minimal, 2 for more, etc. up to 7
604 {
605  iWBDebugLevel = iNew;
606 }
607 
608 #if !defined(__GNUC__) && !defined(_MSVC_VER)
609 // querying debug level. non-inline version
610 unsigned int WBGetDebugLevel(void) // debug_helper.h makes this inline when __GNUC__ or _MSVC_VER defined
611 {
612  return iWBDebugLevel;
613 }
614 
615 // checking debug level - non-inline version
616 int WBCheckDebugLevel(unsigned int dwLevel) // debug_helper.h makes this inline when __GNUC__ or _MSVC_VER defined
617 {
618  if(WB_LIKELY((iWBDebugLevel & DebugLevel_MASK) < (dwLevel & DebugLevel_MASK)))
619  {
620  return 0;
621  }
622 
623  if(!WB_UNLIKELY( WBGetDebugLevel() & DebugSubSystem_RESTRICT )) // RESTRICT not specified
624  {
625  if(!(dwLevel & DebugSubSystem_MASK) ) // no subsystem specified in debug output
626  {
627  return 1; // this is acceptable - since no subsystem specified, allow debug output if not 'RESTRICT'
628  }
629  }
630 
631  // at this point I have a debug subsystem 'RESTRICT' specified
632 
633  if(((dwLevel & DebugSubSystem_MASK) & (iWBDebugLevel & DebugSubSystem_MASK))
634  != 0) // check to see that subsystem bits in 'dwLevel' match bits in 'iWBDebugLevel'
635  {
636  // at least one subsystem bit matches from 'dwLevel' and iWBDebugLevel
637  return 1;
638  }
639 
640  return 0;
641 }
642 #endif // !defined(__GNUC__) && !defined(_MSVC_VER)
643 
644 void WBDebugPrint(const char *fmt, ...)
645 {
646 va_list va;
647 
648  va_start(va, fmt);
649 
650  vfprintf(stderr, fmt, va);
651  fflush(stderr); // dump NOW before (possibly) crashing
652 
653  // TODO: log file?
654 
655  va_end(va);
656 }
657 
658 void WBDebugDump(const char *szTitle, void *pData, int cbData)
659 {
660 int i1, i2;
661 unsigned char *pX = (unsigned char *)pData;
662 static const int nCols = 16;
663 
664  WBDebugPrint("==========================================================================================\n"
665  "%s\n", szTitle);
666 
667  for(i1=0; i1 < cbData; i1 += nCols, pX += nCols)
668  {
669  WBDebugPrint("%06x: ", i1); // assume less than 1Mb for now
670 
671  for(i2=0; i2 < nCols && (i1 + i2) < cbData; i2++)
672  {
673  WBDebugPrint("%02x ", pX[i2]);
674  }
675 
676  for(; i2 < nCols; i2++)
677  {
678  WBDebugPrint(" ");
679  }
680 
681  WBDebugPrint("|");
682 
683  for(i2=0; i2 < nCols && (i1 + i2) < cbData; i2++)
684  {
685  if(pX[i2] < ' ' || pX[i2] >= 0x80)
686  {
687  WBDebugPrint(" .");
688  }
689  else
690  {
691  WBDebugPrint(" %c", pX[i2]);
692  }
693  }
694 
695  WBDebugPrint("\n");
696  }
697 
698  WBDebugPrint("==========================================================================================\n");
699 }
700 
701 
702 
704 // //
705 // ____ _ _ _ _ _ _ //
706 // / ___| _ _ ___ | |_ ___ _ __ ___ | | | || |_ (_)| | ___ //
707 // \___ \ | | | |/ __|| __|/ _ \| '_ ` _ \ | | | || __|| || |/ __| //
708 // ___) || |_| |\__ \| |_| __/| | | | | | | |_| || |_ | || |\__ \ //
709 // |____/ \__, ||___/ \__|\___||_| |_| |_| \___/ \__||_||_||___/ //
710 // |___/ //
711 // //
713 
715 {
716 struct timeval tv;
717 
718  gettimeofday(&tv, NULL);
719 
720 #ifdef HAS_WB_UINT64_BUILTIN /* meaning that the WB_UINT64 data type is a 'built-in' */
721 
722  return (WB_UINT64)tv.tv_sec * (WB_UINT64)1000000
723  + (WB_UINT64)tv.tv_usec;
724 
725 #else // it's a structure typedef
726 
727 // TODO: convert to double, do the math, then convert to struct using floor, frac
728 #error not implemented (yet)
729 
730 #endif // WB_UINT64
731 }
732 
733 void WBDelay(uint32_t uiDelay) // approximate delay for specified period (in microseconds). may be interruptible
734 {
735 #ifdef HAVE_NANOSLEEP
736 struct timespec tsp;
737 
738  if(WB_UNLIKELY(uiDelay >= 1000000L))
739  {
740  tsp.tv_sec = uiDelay / 1000000L;
741  uiDelay = uiDelay % 1000000L; // number of microseconds converted to nanoseconds
742  }
743  else
744  {
745  tsp.tv_sec = 0; // it's assumed that this method is slightly faster
746  }
747 
748  tsp.tv_sec = 0;
749  tsp.tv_nsec = uiDelay * 1000; // wait for .1 msec
750 
751  nanosleep(&tsp, NULL);
752 #else // HAVE_NANOSLEEP
753 
754  usleep(uiDelay); // 100 microsecs - a POSIX alternative to 'nanosleep'
755 
756 #endif // HAVE_NANOSLEEP
757 }
758 
759 int WBCPUCount(void)
760 {
761 // determine # of CPUs to get the default # of jobs during compile
762 // FreeBSD: sysctl vars: kern.smp.cpus hw.ncpu (both have the correct count, even in a VM)
763 // Linux: /proc/cpuinfo - filter on 'processor:', one line per active core
764 // (in a VM, only the assigned cores show up, so count them)
765 int iCPU = 0;
766 
767 #if defined(__FreeBSD__)
768 // TODO: a config option to test for 'sysctlbyname' and the sys/sysctl.h file?
769 unsigned long cb;
770 
771  cb = sizeof(iCPU);
772  iCPU = 0;
773  if(sysctlbyname("hw.ncpu", &iCPU, &cb, NULL, 0)) // could also use MIBs CTL_HW and HW_NCPU
774  {
775 // WB_ERROR_PRINT("ERROR - %s - hw.ncpu, errno=%d iCPU=%d cb=%ld\n", __FUNCTION__, errno, iCPU, cb);
776  cb = sizeof(iCPU);
777  iCPU = 0;
778 
779  if(sysctlbyname("kern.smp.cpus", &iCPU, &cb, NULL, 0)) // could also use MIBs CTL_HW and HW_NCPU
780  {
781 // WB_ERROR_PRINT("ERROR - %s - kernel.smp.cpus, errno=%d iCPU=%d cb=%ld\n", __FUNCTION__, errno, iCPU, cb);
782  return 0;
783  }
784  }
785 
786 // WB_ERROR_PRINT("TEMPORARY: %s - iCPU=%d\n", __FUNCTION__, iCPU);
787 
788  return iCPU;
789 
790 #elif defined(linux)
791 
792 FILE *pF;
793 char tbuf[256];
794 char *p1, *p2;
795 
796  pF = fopen("/proc/cpuinfo", "r");
797  if(pF)
798  {
799  while(fgets(tbuf, sizeof(tbuf), pF))
800  {
801  // read each line, then scan for 'processor' and cound 'em up
802  // NOTE: if there's a better way, I'd like to see it...
803 
804  p1 = tbuf, *p2;
805  while(*p1 && *p1 <= ' ')
806  {
807  p1++; // skip leadin white space
808  }
809 
810  p2 = p1 + strlen(p1);
811  while(p2 > p1 && *(p2 - 1) <= ' ')
812  {
813  *(--p2) = 0; // trim trailing white space
814  }
815 
816  if(!strncmp(p1, "processor", 9) && // TODO: check it's unique?
817  p1[9] && p1[9] <= ' ') // typically is followed by a tab then ':'
818  {
819  iCPU++; // count them!
820  }
821  }
822 
823  fclose(pF);
824  }
825 
826 #elif defined(WIN32)
827  iCPU == 0; // just fail (for now)
828 #else
829  iCPU == 0; // just fail
830 #endif // all of that
831 
832  return iCPU;
833 }
834 
835 
836 
837 
839 // //
840 // __ __ _ _ _ ___ ____ //
841 // | \/ | / \ | | | | / _ \ / ___| //
842 // | |\/| | / _ \ | | | | | | | || | //
843 // | | | | / ___ \ | |___ | |___| |_| || |___ //
844 // |_| |_|/_/ \_\|_____||_____|\___/ \____| //
845 // //
846 // //
848 
849 // ********************
850 // MEMORY SUB-ALLOCATOR
851 // ********************
852 
853 // NOTES TO SELF (design notes)
854 // a) allocate Xk blocks of memory for suballocator blocks of a specific size
855 // b) the first 'page' of the memory block is an array of indices (double-link list)
856 // c) the remainder of the block is an ordered set of memory blocks of a fixed size
857 // d) as more blocks are added, they are added to a linked list associated with the size
858 // e) blocks "more than Xk" will be malloc'd and added to the 'malloc' list.
859 // f) malloc'd blocks will have a double-link list and a header with extra info
860 // This way an allocated address will auto-indicate which memory block it belongs to
861 
862 static const char szWBAllocTag[]="WB_M";
863 #define WB_ALLOC_TAG (*((const unsigned int *)szWBAllocTag)) /*< tag indicating it's a __malloc_header__ **/
864 
865 #define PMALLOC_FLAG (&mallocFlagMalloc) /*< when pPrev and pNext point to THIS, it's a "malloc'd" pointer **/
866 
867 struct __malloc_header__
868 {
869  struct __malloc_header__ *pPrev, *pNext;
870  unsigned int iTag;
871  unsigned int cbSize;
872 };
873 
874 static struct __malloc_header__ mallocFlagMalloc; // pointers to THIS indicate "I am a
875 
876 //static struct __malloc_header__ *pMallocList = NULL, *pMallocListEnd = NULL;
877 // TODO: when I go to implement this, uncomment the above line
878 
879 // TODO: sync object for pMallocList etc.
880 
881 void *WBAlloc(int nSize)
882 {
883 unsigned char *pRval;
884 struct __malloc_header__ *pMH;
885 unsigned int nAllocSize, nNewSize, nLimit;
886 
887 
888  if(nSize <= 0)
889  {
890  return NULL;
891  }
892 
893  // TODO: implement allocation of smaller memory blocks as 'power of 2' blocks.
894  //
895 
896  nAllocSize = nSize + sizeof(*pMH);
897  // nAllocSize will be converted to the next higher power of 2
898 
899  nLimit = nAllocSize + (nAllocSize >> 1);
900 
901  for(nNewSize=64; nNewSize < nLimit; nNewSize <<= 1)
902  { } // NOTE: 64 bytes is the smallest allocation unit
903 
904 // if(nNewSize < 4096) TODO: internally sub-allocated blocks
905 // {
906 // TODO: maintain lists of pre-allocated blocks of memory, allocating new memory as needed
907 // (this memory will need to be re-used intelligently so trash mashing can work properly
908 // }
909 
910  // for larger blocks, I use 'malloc'
911 
912  pRval = (unsigned char *)malloc(nNewSize); // for now - later, use sub-allocation stuff
913 
914  if(pRval)
915  {
916 #ifdef HAVE_MALLOC_USABLE_SIZE
917  void *pActual = pRval;
918 #endif // HAVE_MALLOC_USABLE_SIZE
919 
920  pMH = (struct __malloc_header__ *)pRval;
921 
922  pRval += sizeof(*pMH);
923 
924  pMH->pPrev = PMALLOC_FLAG; // this indicates it was 'malloc'd
925  pMH->pNext = PMALLOC_FLAG; // this indicates it was 'malloc'd
926  pMH->iTag = WB_ALLOC_TAG;
927 
928 #ifdef HAVE_MALLOC_USABLE_SIZE
929  nLimit = malloc_usable_size(pActual); // the ACTUAL SIZE of the memory block
930  if(nLimit > nNewSize)
931  {
932  nNewSize = nLimit;
933  }
934 #endif // HAVE_MALLOC_USABLE_SIZE
935  pMH->cbSize = nNewSize - sizeof(*pMH);
936 
937  }
938 
939  return pRval;
940 }
941 
942 int WBAllocUsableSize(void *pBuf)
943 {
944 struct __malloc_header__ *pMH;
945 
946 
947  if(!pBuf)
948  {
949  return -1; // an error
950  }
951 
952  // validate pointer
953  // TODO: check against linked list of 'allocated' blocks first
954 
955  pMH = ((struct __malloc_header__ *)pBuf) - 1;
956 
957  if(pMH->iTag == WB_ALLOC_TAG)
958  {
959  return pMH->cbSize;
960  }
961 
962  return -1; // not valid (error)
963 }
964 
965 void WBFree(void *pBuf)
966 {
967 struct __malloc_header__ *pMH;
968 
969  if(!pBuf)
970  {
971  return;
972  }
973 
974  // validate pointer
975  // TODO: check against linked list of 'allocated' blocks first
976 
977  pMH = ((struct __malloc_header__ *)pBuf) - 1;
978 
979  if(pMH->iTag == WB_ALLOC_TAG)
980  {
981  if(pMH->pPrev == PMALLOC_FLAG &&
982  pMH->pNext == PMALLOC_FLAG)
983  {
984  // assign header values that invalidate re-freeing the same memory
985 
986  pMH->iTag = 0; // make sure it's no longer valid (so I don't try to re-free)
987  pMH->pPrev = NULL; // same with these values
988  pMH->pNext = NULL;
989  pMH->cbSize = 0;
990 
991  free(pMH);
992  }
993  else
994  {
995  // TODO: make sure it's not "already free" somehow since re-freeing free memory is *BAD*
996 
997  WB_ERROR_PRINT("TODO: %s unimplemented - NOT freeing memory %p\n", __FUNCTION__, pBuf);
998  }
999  }
1000  else
1001  {
1002  WB_ERROR_PRINT("ERROR: %s NOT freeing memory %p\n", __FUNCTION__, pBuf);
1003  }
1004 }
1005 
1006 void * WBReAlloc(void *pBuf, int nNewSize)
1007 {
1008 struct __malloc_header__ *pMH;
1009 unsigned char *pRval = NULL;
1010 unsigned int nAllocSize, nNewNewSize, nLimit;
1011 
1012 
1013  if(!pBuf || nNewSize <= 0)
1014  {
1015  return NULL;
1016  }
1017 
1018  // validate pointer
1019  // TODO: check against linked list of 'allocated' blocks first
1020 
1021  pMH = ((struct __malloc_header__ *)pBuf) - 1;
1022 
1023  if(pMH->iTag == WB_ALLOC_TAG)
1024  {
1025  // the whole point of this is to minimize the actual need to re-allocate the
1026  // memory block by maintaining a LARGER block than is actually needed when
1027  // allocated or re-allocated. Gradually increasing size is pretty much assumed.
1028 
1029  if(pMH->cbSize >= nNewSize)
1030  {
1031  return pBuf; // no change (same pointer) since it's large enough already
1032  }
1033 
1034  // TODO: implement re-allocation of smaller memory blocks as 'power of 2' blocks.
1035  //
1036 
1037  nAllocSize = nNewSize + sizeof(*pMH);
1038  // nAllocSize will be converted to the next higher power of 2
1039 
1040  nLimit = nAllocSize + (nAllocSize >> 1);
1041  for(nNewNewSize=64; nNewNewSize < nLimit; nNewNewSize <<= 1) { } // NOTE: 64 bytes is the smallest allocation unit
1042 
1043  if(pMH->pPrev != PMALLOC_FLAG &&
1044  pMH->pNext != PMALLOC_FLAG)
1045  {
1046  if(nNewNewSize >= 4096) // TODO: internally sub-allocated blocks
1047  {
1048  // it WAS an internally sub-alloced block. NOW it is a MALLOC block.
1049 
1050  pRval = WBAlloc(nNewNewSize); // just allocate it with 'WBAlloc' and let 'WBAlloc' do the work
1051  if(!pRval)
1052  {
1053  return NULL; // not enough memory
1054  }
1055 
1056  memcpy(pRval, pBuf, pMH->cbSize); // copy the old data, but not the stuff in the header.
1057  WBFree(pBuf); // free 'pBuf' now that it's not needed
1058 
1059  return pRval; // return the new pointer (old is no longer valid, new one is 'malloc'ed version).
1060  }
1061  else
1062  {
1063  //TODO: maintain lists of pre-allocated blocks of memory, allocating new memory as needed
1064  // (this memory will need to be re-used intelligently so trash mashing can work properly
1065 
1066  WB_ERROR_PRINT("TODO: %s - this feature is NOT implemented. Pointer %p NOT re-allocated\n", __FUNCTION__, pBuf);
1067  return NULL;
1068  }
1069  }
1070 
1071  pRval = realloc(pMH, nNewNewSize); // for now...
1072  if(pRval)
1073  {
1074 #ifdef HAVE_MALLOC_USABLE_SIZE
1075  void *pActual = pRval;
1076 #endif // HAVE_MALLOC_USABLE_SIZE
1077 
1078  pMH = (struct __malloc_header__ *)pRval;
1079  pRval += sizeof(*pMH);
1080 
1081 #ifdef HAVE_MALLOC_USABLE_SIZE
1082  nLimit = malloc_usable_size(pActual); // the ACTUAL SIZE of the memory block
1083  if(nLimit > nNewSize)
1084  {
1085  nNewSize = nLimit;
1086  }
1087 #endif // HAVE_MALLOC_USABLE_SIZE
1088  pMH->cbSize = nNewNewSize - sizeof(*pMH);
1089  }
1090  }
1091  else
1092  {
1093  WB_ERROR_PRINT("ERROR - %s NOT re-allocating memory %p\n", __FUNCTION__, pBuf);
1094  }
1095 
1096  return pRval;
1097 }
1098 
1100 {
1101  // do nothing (for now)
1102  // later, walk memory list to see if any blocks are completely unused, and free them.
1103  // in debug mode, maybe there's a way to validate memory blocks?
1104 }
1105 
1106 
1108 // //
1109 // ____ _ _ //
1110 // / ___| | |_ _ __ (_) _ __ __ _ ___ //
1111 // \___ \ | __|| '__|| || '_ \ / _` |/ __| //
1112 // ___) || |_ | | | || | | || (_| |\__ \ //
1113 // |____/ \__||_| |_||_| |_| \__, ||___/ //
1114 // |___/ //
1115 // //
1117 
1118 
1119 // ************************
1120 // generic string utilities
1121 // ************************
1122 
1123 char *WBCopyString(const char *pSrc)
1124 {
1125 char *pDest;
1126 int iLen;
1127 
1128  if(!pSrc || !*pSrc)
1129  {
1130  pDest = WBAlloc(2);
1131 
1132  if(pDest)
1133  {
1134  *pDest = 0;
1135  }
1136  }
1137  else
1138  {
1139  iLen = strlen(pSrc);
1140 
1141  pDest = WBAlloc(iLen + 1);
1142 
1143  if(pDest)
1144  {
1145  memcpy(pDest, pSrc, iLen);
1146  pDest[iLen] = 0;
1147  }
1148  }
1149 
1150  return pDest;
1151 }
1152 
1153 char *WBCopyStringN(const char *pSrc, unsigned int nMaxChars)
1154 {
1155 char *pDest;
1156 int iLen;
1157 const char *p1;
1158 
1159  if(!pSrc || !*pSrc)
1160  {
1161  pDest = WBAlloc(2);
1162 
1163  if(pDest)
1164  {
1165  *pDest = 0;
1166  }
1167  }
1168  else
1169  {
1170  for(p1 = pSrc, iLen = 0; iLen < nMaxChars && *p1; p1++, iLen++)
1171  { } // determine length of 'pStr' to copy
1172 
1173  pDest = WBAlloc(iLen + 1);
1174 
1175  if(pDest)
1176  {
1177  memcpy(pDest, pSrc, iLen);
1178  pDest[iLen] = 0;
1179  }
1180  }
1181 
1182  return pDest;
1183 }
1184 
1185 
1186 void WBCatString(char **ppDest, const char *pSrc) // concatenate onto WBAlloc'd string
1187 {
1188 int iLen, iLen2;
1189 int iMaxLen;
1190 char *p1, *p2;
1191 
1192  if(!ppDest || !pSrc || !*pSrc)
1193  {
1194  return;
1195  }
1196 
1197  if(*ppDest)
1198  {
1199  p1 = p2 = *ppDest;
1200 
1201  iMaxLen = WBAllocUsableSize(p1);
1202 
1203  if(iMaxLen <= 0) // an error
1204  {
1205  return;
1206  }
1207 
1208  while(*p2
1209  && (p2 - p1) < iMaxLen
1210  )
1211  {
1212  p2++;
1213  }
1214 
1215  iLen2 = strlen(pSrc);
1216  iLen = iLen2 + (p2 - p1);
1217 
1218  if((iLen + 1) > iMaxLen)
1219  {
1220  *ppDest = WBReAlloc(p1, iLen + 1);
1221  if(!*ppDest)
1222  {
1223  *ppDest = p1;
1224  return; // not enough memory
1225  }
1226 
1227  p2 = (p2 - p1) + *ppDest; // re-position end of string
1228  }
1229 
1230  memcpy(p2, pSrc, iLen2);
1231  p2[iLen2] = 0; // make sure last byte is zero
1232  }
1233  else
1234  {
1235  *ppDest = WBCopyString(pSrc);
1236  }
1237 }
1238 
1239 void WBCatStringN(char **ppDest, const char *pSrc, unsigned int nMaxChars)
1240 {
1241 int iLen, iLen2;
1242 int iMaxLen;
1243 char *p1, *p2;
1244 const char *p3;
1245 
1246 
1247  if(!ppDest || !pSrc || !*pSrc)
1248  {
1249  return;
1250  }
1251 
1252  if(*ppDest)
1253  {
1254  p1 = p2 = *ppDest;
1255 
1256  iMaxLen = WBAllocUsableSize(p1);
1257 
1258  if(iMaxLen <= 0)
1259  {
1260  return;
1261  }
1262 
1263 
1264  while(*p2
1265  && (p2 - p1) < iMaxLen
1266  )
1267  {
1268  p2++;
1269  }
1270 
1271  for(iLen2=0, p3 = pSrc; iLen2 < nMaxChars && *p3; p3++, iLen2++)
1272  { } // determine what the length of pSrc is up to a zero byte or 'nMaxChars', whichever is first
1273 
1274  iLen = iLen2 + (p2 - p1);
1275 
1276  if((iLen + 1) > iMaxLen)
1277  {
1278  *ppDest = WBReAlloc(p1, iLen + 1);
1279  if(!*ppDest)
1280  {
1281  *ppDest = p1; // restore the old pointer value
1282  return; // not enough memory
1283  }
1284 
1285  p2 = (p2 - p1) + *ppDest; // re-position end of string
1286  }
1287 
1288  memcpy(p2, pSrc, iLen2);
1289  p2[iLen2] = 0; // make sure last byte is zero
1290  }
1291  else
1292  {
1293  *ppDest = WBCopyStringN(pSrc, nMaxChars);
1294  }
1295 }
1296 
1297 void WBDeQuoteString(char *pString)
1298 {
1299 char *p1, *pDest;
1300 
1301  p1 = pDest = pString;
1302 
1303  while(*p1)
1304  {
1305  if(*p1 == '"' || *p1 == '\'')
1306  {
1307  char c1 = *(p1++);
1308 
1309  while(*p1 &&
1310  (*p1 != c1 || p1[1] == c1))
1311  {
1312  if(*p1 == c1)
1313  {
1314  p1++;
1315  }
1316 
1317  *(pDest++) = *(p1++);
1318  }
1319 
1320  if(*p1 == c1)
1321  {
1322  p1++;
1323  }
1324  }
1325  else
1326  {
1327  if(pDest != p1)
1328  {
1329  *pDest = *p1;
1330  }
1331 
1332  pDest++;
1333  p1++;
1334  }
1335  }
1336 
1337  *pDest = 0; // make sure
1338 }
1339 
1340 static char __amp_char(char **ppSpot)
1341 {
1342 char *pS = *ppSpot;
1343 
1344  if(!memcmp(pS, "&amp;", 5))
1345  {
1346  *ppSpot += 5;
1347  return '&';
1348  }
1349  else if(!memcmp(pS, "&lt;", 4))
1350  {
1351  *ppSpot += 4;
1352  return '<';
1353  }
1354  else if(!memcmp(pS, "&gt;", 4))
1355  {
1356  *ppSpot += 4;
1357  return '>';
1358  }
1359 
1360  return 0;
1361 }
1362 
1363 void WBNormalizeXMLString(char *pString)
1364 {
1365 char *p1, *pDest;
1366 char c1, c2;
1367 
1368 
1369  // not only de-quoting, but converting '&amp;' '&lt;' '&gt;' to '&' '<' and '>'
1370 
1371  p1 = pDest = pString;
1372 
1373  while(*p1)
1374  {
1375  if(*p1 == '"' || *p1 == '\'')
1376  {
1377  c1 = *(p1++);
1378 
1379  while(*p1 &&
1380  (*p1 != c1 || p1[1] == c1))
1381  {
1382  if(*p1 == c1)
1383  {
1384  p1++;
1385  }
1386  else if(*p1 == '&') // substitute
1387  {
1388  c2 = __amp_char(&p1);
1389 
1390  if(c2)
1391  {
1392  *(pDest++) = c2;
1393  continue;
1394  }
1395 
1396  // if it's not recognized, just process it as normal chars
1397  }
1398 
1399  *(pDest++) = *(p1++);
1400  }
1401 
1402  if(*p1 == c1)
1403  {
1404  p1++;
1405  }
1406  }
1407  else if(*p1 == '&') // substitute
1408  {
1409  c2 = __amp_char(&p1);
1410 
1411  if(!c2)
1412  {
1413  goto normal_char; // just process it as normal chars
1414  }
1415 
1416  *(pDest++) = c2;
1417  }
1418  else
1419  {
1420 normal_char:
1421 
1422  if(pDest != p1)
1423  {
1424  *pDest = *p1;
1425  }
1426 
1427  pDest++;
1428  p1++;
1429  }
1430  }
1431 
1432  *pDest = 0; // make sure
1433 }
1434 
1435 int WBStringLineCount(const char *pSrc, unsigned int nMaxChars)
1436 {
1437 int iRval = 1;
1438 const char *p1;
1439 
1440  if(!pSrc || (!nMaxChars && !*pSrc))
1441  {
1442  return 0;
1443  }
1444 
1445  if(!nMaxChars)
1446  {
1447  nMaxChars = strlen(pSrc);
1448  }
1449 
1450  do
1451  {
1452  p1 = WBStringNextLine(pSrc, &nMaxChars);
1453  if(p1 && nMaxChars) // another line remains
1454  {
1455  iRval++;
1456  }
1457 
1458  pSrc = p1;
1459 
1460  } while(pSrc && nMaxChars);
1461 
1462  return iRval;
1463 }
1464 
1465 const char *WBStringNextLine(const char *pSrc, unsigned int *pnMaxChars)
1466 {
1467 int nMaxChars;
1468 
1469  if(!pSrc)
1470  {
1471  if(pnMaxChars)
1472  {
1473  *pnMaxChars = 0;
1474  }
1475 
1476  return NULL;
1477  }
1478 
1479  if(pnMaxChars)
1480  {
1481  nMaxChars = *pnMaxChars;
1482  }
1483  else if(!*pSrc)
1484  {
1485  return NULL; // end of string (no more)
1486  }
1487  else
1488  {
1489  nMaxChars = strlen(pSrc);
1490  }
1491 
1492  // TODO: handle MBCS differently? (for now, no special handling)
1493 
1494  while(nMaxChars > 0)
1495  {
1496  if(*pSrc == '\r')
1497  {
1498  pSrc++;
1499  nMaxChars--;
1500 
1501  if(nMaxChars > 0 && *pSrc == '\n')
1502  {
1503  pSrc++;
1504  nMaxChars--;
1505  }
1506 
1507  break;
1508  }
1509  else if(*pSrc == '\n')
1510  {
1511  pSrc++;
1512  nMaxChars--;
1513 
1514  if(nMaxChars > 0 && *pSrc == '\r')
1515  {
1516  pSrc++;
1517  nMaxChars--;
1518  }
1519 
1520  break;
1521  }
1522  else if(*pSrc == '\f' || // form feed is like a newline
1523  *pSrc == '\v') // vertical tab (similar, for now)
1524  {
1525  pSrc++;
1526  nMaxChars--;
1527 
1528  break;
1529  }
1530 // TODO: check for UTF-8 paragraph and line separators?
1531 // http://www.unicodemap.org/details/0x2028/index.html (line separator - alternate LF?)
1532 // http://www.unicodemap.org/details/0x202A/index.html (paragraph separator - alternate to CTRL+L ?)
1533 //
1534  else if(!*pSrc) // TODO: test for unicode? normally we just assume UTF-8 is compatible with ASCII
1535  {
1536  nMaxChars = 0;
1537  break;
1538  }
1539 
1540  pSrc++;
1541  nMaxChars--;
1542  }
1543 
1544  if(pnMaxChars)
1545  {
1546  *pnMaxChars = nMaxChars;
1547  }
1548 
1549  return pSrc;
1550 }
1551 
1552 
1553 
1554 
1556 // //
1557 // ____ _ _ _ _ _ //
1558 // | _ \ ___ (_) _ __ | |_ ___ _ __ | | | | __ _ ___ | |__ ___ ___ //
1559 // | |_) |/ _ \ | || '_ \ | __|/ _ \| '__| | |_| | / _` |/ __|| '_ \ / _ \/ __| //
1560 // | __/| (_) || || | | || |_| __/| | | _ || (_| |\__ \| | | || __/\__ \ //
1561 // |_| \___/ |_||_| |_| \__|\___||_| |_| |_| \__,_||___/|_| |_| \___||___/ //
1562 // //
1563 // //
1565 
1566 typedef struct __pointer_hash__
1567 {
1568  WB_UINT32 uiHash; // 32-bit pointer hash value, must be zero for 'free' entry
1569  void *pValue; // NULL if unused entry, else the pointer value
1570  WB_UINT32 dwTick; // millis count when I last created/referenced this hash
1571  WB_UINT32 dwRefCount; // reference count (deleted on '0')
1572 } POINTER_HASH;
1573 
1574 static POINTER_HASH *pPointerHashes = NULL; // WBAlloc'd array
1575 static int nPointerHash = 0, nMaxPointerHash = 0; // for now, simple array
1576 
1577 static WB_UINT32 uiPointerHashSpinlock = 0;
1578 
1579 static void WBFreePointerHashes(void)
1580 {
1581  if(pPointerHashes)
1582  {
1583  WBFree(pPointerHashes);
1584  pPointerHashes = NULL;
1585  }
1586 
1587  nPointerHash = 0;
1588  nMaxPointerHash = 0;
1589 
1590  uiPointerHashSpinlock = 0;
1591 }
1592 
1594 {
1595 int i1, iFreeIndex;
1596 WB_UINT32 uiRval = 0;
1597 WB_UINT32 dwTick = (WB_UINT32)(WBGetTimeIndex() >> 10); // fast 'millis', micros / 1024
1598 
1599 
1600  if(!pPointer)
1601  {
1602  return 0; // not valid, just return zero
1603  }
1604 
1605  while(WBInterlockedExchange(&uiPointerHashSpinlock, 1))
1606  {
1607  WBDelay(100);
1608  }
1609 
1610  if(!pPointerHashes)
1611  {
1612  nMaxPointerHash = 512; // initial size (make it a '#define' ?)
1613  pPointerHashes = (POINTER_HASH *)WBAlloc(nMaxPointerHash * sizeof(*pPointerHashes));
1614 
1615  if(!pPointerHashes)
1616  {
1617  goto return_point;
1618  }
1619 
1620  nPointerHash = 0;
1621  }
1622  else if(nPointerHash >= nMaxPointerHash)
1623  {
1624  void *pTemp = WBReAlloc(pPointerHashes, (nMaxPointerHash + 256) * sizeof(*pPointerHashes));
1625 
1626  if(!pTemp)
1627  {
1628  goto return_point;
1629  }
1630 
1631  pPointerHashes = (POINTER_HASH *)pTemp; // new, re-alloc'd pointer
1632  nMaxPointerHash += 256;
1633 
1634 // WB_ERROR_PRINT("TEMPORARY: %s - new # of hashes %d\n", __FUNCTION__, nMaxPointerHash);
1635  }
1636 
1637  // first, check for a match, and the first 'free' index
1638  for(i1=0, iFreeIndex = -1; i1 < nPointerHash; i1++)
1639  {
1640  if(pPointerHashes[i1].pValue == pPointer)
1641  {
1642  if(uiRval) // more than one?
1643  {
1644  WB_ERROR_PRINT("ERROR: %s - matching 'pPointer' %p for multiple hash entries\n", __FUNCTION__, pPointer);
1645  }
1646  else // NOTE: if this entry WAS too old, I'll still re-use it and reset the tick
1647  {
1648  uiRval = pPointerHashes[i1].uiHash;
1649  pPointerHashes[i1].dwTick = dwTick; // new timestamp
1650  pPointerHashes[i1].dwRefCount++; // increase ref count
1651 
1652  // NOTE: I will only use this one. but I'll check the rest for timeout by continuing
1653  }
1654  }
1655  else if(!pPointerHashes[i1].pValue &&
1656  !pPointerHashes[i1].uiHash) // a free entry
1657  {
1658  if(iFreeIndex < 0) // no free index found (yet)
1659  {
1660  iFreeIndex = i1; // first free entry found
1661  }
1662  }
1663  else if((dwTick - pPointerHashes[i1].dwTick) > WB_SECURE_HASH_TIMEOUT) // auto cleanup part
1664  {
1665  // checking for timeout, and erasing things if timed out
1666 
1667 // WB_ERROR_PRINT("TEMPORARY: %s - deleting 'aged' entry, timeout = %u\n", __FUNCTION__,
1668 // (dwTick - pPointerHashes[i1].dwTick));
1669 // WB_ERROR_PRINT(" %2d : %08xH %10u %4u %p\n",
1670 // i1, pPointerHashes[i1].uiHash, pPointerHashes[i1].dwTick,
1671 // pPointerHashes[i1].dwRefCount, pPointerHashes[i1].pValue);
1672 
1673  // too old - erase it, freeing up the location
1674 
1675  pPointerHashes[i1].uiHash = 0; // also needed to mark it 'free'
1676  pPointerHashes[i1].dwTick = 0;
1677  pPointerHashes[i1].pValue = NULL; // thus marking it 'free'
1678  pPointerHashes[i1].dwRefCount = 0; // regardless of ref count, remove it
1679 
1680  if(iFreeIndex < 0) // no free index yet? remember it
1681  {
1682  iFreeIndex = i1;
1683  }
1684  }
1685  }
1686 
1687  if(uiRval) // found one??
1688  {
1689  goto return_point;
1690  }
1691 
1692 
1693  // at this point, did NOT find a match, so I need to create it
1694 
1695  if(iFreeIndex < 0) // not re-using an entry?
1696  {
1697  iFreeIndex = nPointerHash++; // increment total count, use last entry
1698  }
1699 
1700  pPointerHashes[iFreeIndex].pValue = pPointer;
1701  pPointerHashes[iFreeIndex].dwTick = dwTick;
1702  pPointerHashes[iFreeIndex].dwRefCount = 1;
1703 
1704  // see if there's a 'crash' between two identical hashes
1705  // it's not likely, but it IS possible. So test for it.
1706 
1707  while(1) /* we assume it will bust out eventually */
1708  {
1709  uiRval = ((WB_UINT32)((WB_UINT64)pPointer) ^ dwTick) & 0xffffffff;
1710  // NOTE: this should, in theory, work within a very short time
1711 
1712  if(uiRval) // can't allow a zero (this should be RARE, if ever at all)
1713  {
1714  for(i1=0; i1 < nPointerHash; i1++)
1715  {
1716  if(pPointerHashes[i1].uiHash == uiRval)
1717  {
1718  break;
1719  }
1720  }
1721 
1722  if(i1 >= nPointerHash) // no matching entry found
1723  {
1724  break;
1725  }
1726  }
1727 
1728  dwTick -= 113; // decrement it by a prime number so I can test for it
1729  // being there again, but with a different hash value
1730  }
1731 
1732  pPointerHashes[iFreeIndex].uiHash = uiRval; // save this value where the free index resides
1733 
1734 // WB_ERROR_PRINT("TEMOPRARY: %s - adding hash %u for %p at %d\n", __FUNCTION__, uiRval, pPointer, iFreeIndex);
1735 
1736 
1737 return_point:
1738 
1739  WBInterlockedExchange(&uiPointerHashSpinlock, 0);
1740 
1741 // if(!uiRval)
1742 // {
1743 // WB_ERROR_PRINT("TEMPORARY: %s - did NOT create hash for pointer %p\n", __FUNCTION__, pPointer);
1744 // }
1745 
1746  return uiRval;
1747 }
1748 
1750 {
1751 int i1;
1752 WB_UINT32 dwTick = (WB_UINT32)(WBGetTimeIndex() >> 10); // fast 'millis', micros / 1024
1753 
1754 
1755  if(!uiHash)
1756  {
1757  return; // not valid, just return
1758  }
1759 
1760  while(WBInterlockedExchange(&uiPointerHashSpinlock, 1))
1761  {
1762  WBDelay(100);
1763  }
1764 
1765  for(i1=0; i1 < nPointerHash; i1++)
1766  {
1767  if(!pPointerHashes[i1].pValue &&
1768  !pPointerHashes[i1].uiHash) // a free entry
1769  {
1770  continue;
1771  }
1772 
1773  // check timeouts first, THEN destroy via decremented ref count
1774 
1775  if((dwTick - pPointerHashes[i1].dwTick) > WB_SECURE_HASH_TIMEOUT) // check for aging while I'm at it
1776  {
1777 // WB_ERROR_PRINT("TEMPORARY: %s - deleting 'aged' entry, timeout = %u\n", __FUNCTION__,
1778 // (dwTick - pPointerHashes[i1].dwTick));
1779 // WB_ERROR_PRINT(" %2d : %08xH %10u %4u %p\n",
1780 // i1, pPointerHashes[i1].uiHash, pPointerHashes[i1].dwTick,
1781 // pPointerHashes[i1].dwRefCount, pPointerHashes[i1].pValue);
1782 
1783  // erase it, freeing up the location
1784 erase_it:
1785 
1786  pPointerHashes[i1].uiHash = 0; // this zero, together with 'pValue == NULL' means it's "free"
1787  pPointerHashes[i1].dwTick = 0;
1788  pPointerHashes[i1].pValue = NULL; // thus marking it 'free'
1789  pPointerHashes[i1].dwRefCount = 0; // regardless of ref count, remove it
1790  }
1791  else if(pPointerHashes[i1].uiHash == uiHash)
1792  {
1793 // WB_ERROR_PRINT("TEMPORARY: %s - ref count prior to decrement = %d for %p (%08xH)\n", __FUNCTION__,
1794 // pPointerHashes[i1].dwRefCount, pPointerHashes[i1].pValue,
1795 // pPointerHashes[i1].uiHash);
1796 
1797  if(pPointerHashes[i1].dwRefCount)
1798  {
1799  pPointerHashes[i1].dwRefCount --;
1800  }
1801 
1802  if(!(pPointerHashes[i1].dwRefCount))
1803  {
1804 // WB_ERROR_PRINT("TEMPORARY: %s - deleting matching entry\n", __FUNCTION__);
1805 // WB_ERROR_PRINT(" %2d : %08xH %10u %4u %p\n",
1806 // i1, pPointerHashes[i1].uiHash, pPointerHashes[i1].dwTick,
1807 // pPointerHashes[i1].dwRefCount, pPointerHashes[i1].pValue);
1808 
1809  goto erase_it;
1810  }
1811  }
1812  }
1813 
1814  WBInterlockedExchange(&uiPointerHashSpinlock, 0);
1815 }
1816 
1817 void WBDestroyPointerHashPtr(void *pPointer)
1818 {
1819 int i1;
1820 WB_UINT32 dwTick = (WB_UINT32)(WBGetTimeIndex() >> 10); // fast 'millis', micros / 1024
1821 
1822 
1823  if(!pPointer)
1824  {
1825  return; // not valid, just return
1826  }
1827 
1828  while(WBInterlockedExchange(&uiPointerHashSpinlock, 1))
1829  {
1830  WBDelay(100);
1831  }
1832 
1833  for(i1=0; i1 < nPointerHash; i1++)
1834  {
1835  if(!pPointerHashes[i1].uiHash && !pPointerHashes[i1].pValue)
1836  {
1837  continue; // free entry, ignore it
1838  }
1839 
1840  if((dwTick - pPointerHashes[i1].dwTick) > WB_SECURE_HASH_TIMEOUT) // check for aging while I'm at it
1841  {
1842 // WB_ERROR_PRINT("TEMPORARY: %s - deleting 'aged' entry, timeout = %u\n", __FUNCTION__,
1843 // (dwTick - pPointerHashes[i1].dwTick));
1844 // WB_ERROR_PRINT(" %2d : %08xH %10u %4u %p\n",
1845 // i1, pPointerHashes[i1].uiHash, pPointerHashes[i1].dwTick,
1846 // pPointerHashes[i1].dwRefCount, pPointerHashes[i1].pValue);
1847 
1848  // erase it, freeing up the location (hash will be zero)
1849 
1850 erase_it:
1851  pPointerHashes[i1].uiHash = 0; // this zero, together with 'pValue == NULL' means it's "free"
1852  pPointerHashes[i1].dwTick = 0;
1853  pPointerHashes[i1].pValue = NULL; // thus marking it 'free'
1854  pPointerHashes[i1].dwRefCount = 0; // regardless of ref count, remove it
1855 
1856  }
1857  else if(pPointerHashes[i1].pValue == pPointer) // always free it
1858  {
1859 // WB_ERROR_PRINT("TEMPORARY: %s - deleting matching entry\n", __FUNCTION__);
1860 // WB_ERROR_PRINT(" %2d : %08xH %10u %4u %p\n",
1861 // i1, pPointerHashes[i1].uiHash, pPointerHashes[i1].dwTick,
1862 // pPointerHashes[i1].dwRefCount, pPointerHashes[i1].pValue);
1863 
1864  goto erase_it; // regardless of ref count, remove it (buh-bye)
1865  }
1866  }
1867 
1868  WBInterlockedExchange(&uiPointerHashSpinlock, 0);
1869 }
1870 
1872 {
1873 int i1;
1874 void *pRval = NULL;
1875 
1876 
1877  if(!uiHash)
1878  {
1879  return NULL; // not valid, just return NULL
1880  }
1881 
1882  while(WBInterlockedExchange(&uiPointerHashSpinlock, 1))
1883  {
1884  WBDelay(100);
1885  }
1886 
1887  for(i1=0; i1 < nPointerHash; i1++)
1888  {
1889  if(!pPointerHashes[i1].pValue || // for now just ignore these
1890  pPointerHashes[i1].uiHash != uiHash)
1891  {
1892  continue; // this treats NULL pointers as a 'mismatch'
1893  }
1894 
1895  // TODO: check for timeouts?
1896 
1897  pRval = pPointerHashes[i1].pValue;
1898  goto exit_point;
1899  }
1900 
1901 
1902 exit_point:
1903 
1904  WBInterlockedExchange(&uiPointerHashSpinlock, 0);
1905 
1906 // if(!pRval)
1907 // {
1908 // WB_ERROR_PRINT("TEMPORARY: %s - did NOT find pointer for hash %u (%08xH)\n", __FUNCTION__, uiHash, uiHash);
1909 //
1910 // for(i1=0; i1 < nPointerHash; i1++)
1911 // {
1912 // WB_ERROR_PRINT(" %2d : %08xH %10u %4u %p\n",
1913 // i1, pPointerHashes[i1].uiHash, pPointerHashes[i1].dwTick,
1914 // pPointerHashes[i1].dwRefCount, pPointerHashes[i1].pValue);
1915 // }
1916 //
1917 // WB_ERROR_PRINT("---------------------------------------------------------------------\n\n");
1918 // }
1919 
1920  return pRval; // NULL if not found
1921 }
1922 
1923 
1924 
1926 // //
1927 // _ _ //
1928 // / \ | |_ ___ _ __ ___ ___ //
1929 // / _ \ | __|/ _ \ | '_ ` _ \ / __| //
1930 // / ___ \| |_| (_) || | | | | |\__ \ //
1931 // /_/ \_\\__|\___/ |_| |_| |_||___/ //
1932 // //
1933 // //
1935 
1936 //---------------------------------
1937 // INTERNAL ATOM 'HELPER' UTILITIES
1938 //---------------------------------
1939 
1940 // the X11 server maintains a global list of atoms based on unique strings. These are
1941 // useful for inter-application communication, but can rapidly become inefficient when applications
1942 // create large numbers of unique atoms names for their own custom purposes (like the X11 workbench).
1943 // To combat this obvious problem, these next two functions will create application-unique atoms
1944 // when a global atom is not available. Global atoms are NEVER removed. Once there, they are there
1945 // until the X11 server is shut down. For that reason, these functions are really necessary. It should
1946 // help minimize the resource impact of an X11 workbench toolkit application.
1947 
1948 // For more information, see xorg-server source
1949 // specifically: Atom MakeAtom(const char *string, unsigned len, Bool makeit)
1950 // this function is located in dix/atom.c . It allocates atoms SEQUENTIALLY, beginning with XA_LAST_PREDEFINED+1
1951 // because the atoms are assigned SEQUENTIALLY, it is possible to pick a "very very big number" as the minimum
1952 // internal atom's starting point, i.e. WB_INTERNAL_ATOM_MIN_VAL (which is currently FF000000H) which would allow
1953 // for ~16 million internally-defined atoms.
1954 
1955 static volatile WB_UINT32 lInternalAtomSpinner = 0L;
1956 static char **ppInternalAtoms = NULL; // actual atom value starts with WB_INTERNAL_ATOM_MIN_VAL and is index within this array
1957 static char *pszAtomNames = NULL;
1958 static unsigned int cbAtomNames = 0, cbMaxAtomNames = 0;
1959 static unsigned int nInternalAtoms = 0, nMaxInternalAtoms = 0;
1960 
1961 #define INITIAL_INTERNAL_ATOM_SIZE 4096
1962 #define INITIAL_INTERNAL_ATOM_STRING_SIZE 262144
1963 
1964 Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
1965 {
1966 Atom aRval;
1967 //char *p1;
1968 void *pTemp;
1969 int iLen;
1970 
1971 
1972  if(!szAtomName || !*szAtomName)
1973  {
1974  WB_ERROR_PRINT("ERROR: %s - bad 'szAtomName'\n", __FUNCTION__);
1975 
1976  return None; // bad parameter
1977  }
1978 
1979  if(!pDisplay)
1980  {
1981  pDisplay = WBGetDefaultDisplay();
1982 
1983  if(!pDisplay)
1984  {
1985  WB_ERROR_PRINT("ERROR - %s - no display!\n", __FUNCTION__);
1986 
1987  return None;
1988  }
1989  }
1990 
1991  aRval = WBLookupAtom(pDisplay, szAtomName);
1992 
1993  if(aRval != None)
1994  {
1995  return aRval;
1996  }
1997 
1998  // allocate an internal atom
1999 
2000  aRval = None;
2001 
2002  while(WBInterlockedExchange(&lInternalAtomSpinner, 1)) // THIS MUST BE SPIN-LOCKED
2003  {
2004  WBDelay(100); // by convention just do THIS
2005  }
2006 
2007  if(!ppInternalAtoms)
2008  {
2009  ppInternalAtoms = (char **)WBAlloc(INITIAL_INTERNAL_ATOM_SIZE);
2010 
2011  if(!ppInternalAtoms)
2012  {
2013  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
2014 
2015  goto exit_point;
2016  }
2017 
2018  nMaxInternalAtoms = INITIAL_INTERNAL_ATOM_SIZE;
2019  nInternalAtoms = 0; // make sure
2020  }
2021  else if((nInternalAtoms + 1) >= nMaxInternalAtoms)
2022  {
2023  pTemp = WBReAlloc(ppInternalAtoms, nMaxInternalAtoms + INITIAL_INTERNAL_ATOM_SIZE);
2024  if(!pTemp)
2025  {
2026  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
2027 
2028  goto exit_point;
2029  }
2030 
2031  ppInternalAtoms = (char **)pTemp;
2032  nMaxInternalAtoms += INITIAL_INTERNAL_ATOM_SIZE;
2033  }
2034 
2035  iLen = strlen(szAtomName) + 1; // include the '0' byte at the end
2036 
2037  // now for the names buffer
2038 
2039  if(!pszAtomNames)
2040  {
2041  pszAtomNames = WBAlloc(INITIAL_INTERNAL_ATOM_STRING_SIZE);
2042  if(!pszAtomNames)
2043  {
2044  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
2045 
2046  goto exit_point;
2047  }
2048 
2049  cbMaxAtomNames = INITIAL_INTERNAL_ATOM_STRING_SIZE;
2050  cbAtomNames = 0; // make sure
2051  }
2052  else if((cbAtomNames + iLen + 1) >= cbMaxAtomNames) // not enough room?
2053  {
2054  pTemp = WBReAlloc(pszAtomNames, cbMaxAtomNames + INITIAL_INTERNAL_ATOM_STRING_SIZE);
2055  if(!pTemp)
2056  {
2057  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
2058 
2059  goto exit_point;
2060  }
2061 
2062  pszAtomNames = (char *)pTemp;
2063  cbMaxAtomNames += INITIAL_INTERNAL_ATOM_STRING_SIZE;
2064  }
2065 
2066  aRval = (Atom)(nInternalAtoms + WB_INTERNAL_ATOM_MIN_VAL);
2067 
2068  ppInternalAtoms[nInternalAtoms++] = pszAtomNames + cbAtomNames;
2069 
2070  memcpy(pszAtomNames + cbAtomNames, szAtomName, iLen);
2071 
2072  cbAtomNames += iLen;
2073  pszAtomNames[cbAtomNames] = 0; // by convention
2074 
2075 
2076 exit_point:
2077 
2078  WBInterlockedExchange(&lInternalAtomSpinner, 0); // I'm done with it now
2079 
2080  if(aRval == None)
2081  {
2082  WB_ERROR_PRINT("ERROR: %s - could not allocate new atom %u for %s\n", __FUNCTION__, (unsigned int)aRval, szAtomName);
2083  }
2084 // else
2085 // {
2086 // WB_ERROR_PRINT("TEMPORARY: %s - allocating new atom %u for %s\n", __FUNCTION__, (unsigned int)aRval, szAtomName);
2087 // }
2088 
2089  return aRval;
2090 // aRval = XInternAtom(pDisplay, szAtomName, False); // temporarily, just do this
2091 }
2092 
2093 Atom WBLookupAtom(Display *pDisplay, const char *szAtomName)
2094 {
2095 Atom aRval;
2096 unsigned int i1;
2097 
2098 
2099  if(!szAtomName || !*szAtomName)
2100  {
2101  WB_ERROR_PRINT("ERROR: %s - bad 'szAtomName'\n", __FUNCTION__);
2102 
2103  return None; // bad parameter
2104  }
2105 
2106  if(!pDisplay)
2107  {
2108  pDisplay = WBGetDefaultDisplay();
2109  }
2110 
2111 
2112  // look up internal atoms FIRST...
2113 
2114  aRval = None;
2115 
2116  while(WBInterlockedExchange(&lInternalAtomSpinner, 1)) // THIS MUST BE SPIN-LOCKED
2117  {
2118  WBDelay(100); // by convention just do THIS
2119  }
2120 
2121  if(ppInternalAtoms && pszAtomNames && nInternalAtoms > 0)
2122  {
2123  // TODO: use some kind of hashing algorithm? for now, if the list is short enough,
2124  // this actually replicates what the X Server does with its own atoms.
2125 
2126  for(i1=0; i1 < nInternalAtoms; i1++)
2127  {
2128  if(!strcmp(szAtomName, ppInternalAtoms[i1]))
2129  {
2130  aRval = (Atom)(i1 + WB_INTERNAL_ATOM_MIN_VAL);
2131  break;
2132  }
2133  }
2134  }
2135 
2136  WBInterlockedExchange(&lInternalAtomSpinner, 0); // I'm done with it now
2137 
2138  if(aRval != None)
2139  {
2140 // WB_ERROR_PRINT("TEMPORARY: %s - found atom %u for %s\n", __FUNCTION__, (unsigned int)aRval, szAtomName);
2141 
2142  return aRval;
2143  }
2144 
2145  aRval = XInternAtom(pDisplay, szAtomName, True);
2146 
2147  // TODO: anything else?
2148 
2149  return aRval; // regardless
2150 }
2151 
2152 char *WBGetAtomName(Display *pDisplay, Atom aAtom)
2153 {
2154 char *pRval, *pTemp;
2155 unsigned int nAtom;
2156 
2157 
2158  if(aAtom == None)
2159  {
2160  // NOTE: this can happen under normal circumstances, so don't make it an error
2161  // (especially true for dialog controls without IDs assigned, like static text)
2162 
2163  WB_WARN_PRINT("ERROR: %s - passed 'None' for aAtom\n", __FUNCTION__);
2164 
2165  return NULL;
2166  }
2167  else if((unsigned int)aAtom >= (unsigned int)(WB_INTERNAL_ATOM_MIN_VAL + nInternalAtoms))
2168  {
2169  WB_ERROR_PRINT("ERROR: %s - bad (internal) Atom: %u (%08xH)\n", __FUNCTION__, (unsigned int)aAtom, (unsigned int)aAtom);
2170 
2171  return NULL; // bad parameter
2172  }
2173 
2174  if(!pDisplay)
2175  {
2176  pDisplay = WBGetDefaultDisplay();
2177 
2178  if(!pDisplay)
2179  {
2180  WB_ERROR_PRINT("ERROR - %s - no display!\n", __FUNCTION__);
2181  return NULL;
2182  }
2183  }
2184 
2185  if((unsigned int)aAtom < WB_INTERNAL_ATOM_MIN_VAL)
2186  {
2188 
2189  pTemp = XGetAtomName(pDisplay, aAtom);
2190 
2192 
2193  if(pTemp)
2194  {
2195  pRval = WBCopyString(pTemp);
2196  XFree(pTemp);
2197 
2198  return pRval;
2199  }
2200 
2201  WB_DEBUG_PRINT(DebugLevel_Light,
2202  "INFO: %s - unknown Atom: %u (%08xH)\n",
2203  __FUNCTION__, (unsigned int)aAtom, (unsigned int)aAtom);
2204 
2205  return NULL;
2206  }
2207 
2208  pRval = NULL;
2209 
2210  while(WBInterlockedExchange(&lInternalAtomSpinner, 1)) // THIS MUST BE SPIN-LOCKED
2211  {
2212  WBDelay(100); // by convention just do THIS
2213  }
2214 
2215  if(ppInternalAtoms && pszAtomNames && nInternalAtoms > 0)
2216  {
2217  nAtom = (unsigned int)aAtom - WB_INTERNAL_ATOM_MIN_VAL;
2218 
2219  if(nAtom < nInternalAtoms)
2220  {
2221  pRval = WBCopyString(ppInternalAtoms[nAtom]);
2222  }
2223  }
2224 
2225  WBInterlockedExchange(&lInternalAtomSpinner, 0); // I'm done with it now
2226 
2227  if(!pRval)
2228  {
2229  WB_DEBUG_PRINT(DebugLevel_Light,
2230  "INFO: %s - atom index %u (%u) not found, %u atoms stored\n", __FUNCTION__,
2231  (unsigned int)aAtom, (unsigned int)(aAtom - WB_INTERNAL_ATOM_MIN_VAL),
2232  nInternalAtoms);
2233  }
2234 // else
2235 // {
2236 // WB_ERROR_PRINT("TEMPORARY: %s - found %s for Atom %u\n", __FUNCTION__, pRval, (unsigned int)aAtom);
2237 // }
2238 
2239  return pRval;
2240 }
2241 
2242 
2243 
2244 
2246 // //
2247 // _ //
2248 // __ _ ___ ___ _ __ | |_ _ __ //
2249 // / _` |/ __| / _ \ | '__|| __| | '__| //
2250 // | (_| |\__ \| (_) || | | |_ | | //
2251 // \__, ||___/ \___/ |_| \__|_____|_| //
2252 // |_| |_____| //
2253 // //
2255 
2256 // ========================================================
2257 // Dealing with a lack of or improperly supported qsort_r()
2258 // ========================================================
2259 
2260 #if (!defined(QSORT_R_BSD) && !defined(QSORT_R_GNUC)) || defined(__DOXYGEN__)
2261 
2262 struct my_qsort_data
2263 {
2264  void *pData; // pointer to elements
2265  int cbData; // size of elements
2266  void *pThunk; // data to pass to each 'compare' call
2267  int (*compare)(void *, const void *, const void *); // compare function
2268  void *pSW, *pBR;
2269 };
2270 
2271 static void MyQuickSort2(struct my_qsort_data *pData, int iLow, int iHigh);
2272 
2273 // define exactly like BSD version, but with base types
2274 // TODO: multi-thread version? would need to determine optimum threshold for threading
2275 
2276 void my_qsort_r(void *base, int nmemb, int size, void *thunk,
2277  int (*compar)(void *, const void *, const void *))
2278 {
2279 struct my_qsort_data sData;
2280 unsigned char buf[32], buf2[32];
2281 
2282 
2283  if(nmemb <= 1)
2284  {
2285  return; // no need to sort 1 item
2286  }
2287 
2288  sData.pData = base;
2289  sData.cbData = size;
2290  sData.pThunk = thunk;
2291  sData.compare = compar;
2292 
2293  if(sData.cbData <= sizeof(buf))
2294  {
2295  sData.pBR = buf;
2296  sData.pSW = buf2;
2297  }
2298  else
2299  {
2300  sData.pBR = WBAlloc(sData.cbData * 2);
2301  sData.pSW = (unsigned char *)sData.pBR + sData.cbData;
2302  }
2303 
2304  MyQuickSort2(&sData, 0, nmemb - 1);
2305 
2306  if(sData.cbData > sizeof(buf))
2307  {
2308  WBFree(sData.pBR);
2309  }
2310 }
2311 
2312 static __inline__ void *ElementFromIndex(struct my_qsort_data *pData, int iElement)
2313 {
2314  return ((unsigned char *)pData->pData) + pData->cbData * iElement;
2315 }
2316 
2317 static int DoCompareI(struct my_qsort_data *pData, int iLeft, int iRight)
2318 {
2319  void *p1 = ElementFromIndex(pData, iLeft);
2320  void *p2 = ElementFromIndex(pData, iRight);
2321 
2322  return pData->compare(pData->pThunk, p1, p2);
2323 }
2324 
2325 static int DoCompareP(struct my_qsort_data *pData, int iLeft, void *pRight)
2326 {
2327  void *p1 = ElementFromIndex(pData, iLeft);
2328 
2329  return pData->compare(pData->pThunk, p1, pRight);
2330 }
2331 
2332 static void DoSwapData(struct my_qsort_data *pData, int iLeft, int iRight)
2333 {
2334  void *p1 = ElementFromIndex(pData, iLeft);
2335  void *p2 = ElementFromIndex(pData, iRight);
2336 
2337  // TODO: make this faster?
2338  memcpy(pData->pSW, p1, pData->cbData);
2339  memcpy(p1, p2, pData->cbData);
2340  memcpy(p2, pData->pSW, pData->cbData);
2341 }
2342 
2343 void MyQuickSort2(struct my_qsort_data *pData, int iLow, int iHigh )
2344 {
2345 int iUp, iDown, iBreak;
2346 void *pBreak;
2347 unsigned char buf[32], buf2[32]; // if smaller than 32 bytes, use THIS for temp object storage
2348 
2349 
2350  if( iLow < iHigh ) // do not sort 1 element block
2351  {
2352  iBreak = (iLow + iHigh) / 2;
2353  pBreak = ElementFromIndex(pData, iBreak); // initial pivot point
2354 
2355  if( (iHigh - iLow) > 5 ) // 5 or more elements?
2356  {
2357  // do a 'median of 3' optimization when practical
2358  // this ensures a better pivot point, limiting the
2359  // effect of an already sorted or nearly sorted
2360  // array on performance. Pivot points should be
2361  // as close to the median value as practical for
2362  // the current range of data points. Otherwise,
2363  // a series of bad pivot points could degenerate
2364  // the 'quicksort' into a 'slowsort', and possibly
2365  // cause stack overflows on large data sets.
2366 
2367  if(DoCompareI(pData, iLow, iHigh) <= 0)
2368  {
2369  if(DoCompareP(pData, iLow, pBreak) < 0)
2370  {
2371  if(DoCompareP(pData, iHigh, pBreak) < 0)
2372  {
2373  pBreak = ElementFromIndex(pData, iHigh);
2374  }
2375  }
2376  else
2377  {
2378  pBreak = ElementFromIndex(pData, iLow);
2379  }
2380  }
2381  else
2382  {
2383  if(DoCompareP(pData, iHigh, pBreak) < 0)
2384  {
2385  if(DoCompareP(pData, iLow, pBreak) < 0)
2386  {
2387  pBreak = ElementFromIndex(pData, iLow);
2388  }
2389  }
2390  else
2391  {
2392  pBreak = ElementFromIndex(pData, iHigh);
2393  }
2394  }
2395  }
2396 
2397  memcpy(pData->pBR, pBreak, pData->cbData); // make copy of it before swapping anything
2398  pBreak = pData->pBR; // and now point to the buffer (faster this way)
2399 
2400  iUp = iLow; // initialize indices
2401  iDown = iHigh;
2402 
2403  do
2404  {
2405  // Move in from both sides towards the pivot point.
2406 
2407  // note: it may be possible to thread THESE two loops
2408  // though it's likely not to help a whole lot
2409 
2410  while( iUp < iHigh)
2411  {
2412  if(DoCompareP(pData, iUp, pBreak) < 0)
2413  {
2414  iUp++;
2415  }
2416  else
2417  {
2418  break;
2419  }
2420  }
2421 
2422  while(iDown > iLow)
2423  {
2424  if(DoCompareP(pData, iDown, pBreak) > 0)
2425  {
2426  iDown--;
2427  }
2428  else
2429  {
2430  break;
2431  }
2432  }
2433 
2434 
2435  // if low/high boundaries have not crossed, swap current
2436  // 'boundary' values so that the 'iUp' pointer points
2437  // to a value less than or equal to the pivot point,
2438  // and the 'iDown' value points to a value greater than
2439  // or equal to the pivot point, and continue.
2440 
2441  if( iUp <= iDown )
2442  {
2443  if(iUp != iDown)
2444  {
2445  DoSwapData(pData, iUp, iDown);
2446  }
2447 
2448  iUp++;
2449  iDown--;
2450  }
2451 
2452  } while ( iUp <= iDown );
2453 
2454  // the recursive part... [NOTE if I ever want to thread this, I can make work units here]
2455 
2456  if(iLow < iDown ) // everything to the left of the pivot point
2457  {
2458  MyQuickSort2(pData, iLow, iDown );
2459  }
2460  if(iUp < iHigh) // everything to the right of the pivot point
2461  {
2462  MyQuickSort2(pData, iUp, iHigh );
2463  }
2464  }
2465 }
2466 
2467 #endif // !defined(QSORT_R_BSD) && !defined(QSORT_R_GNUC)
2468 
2469 
2470 #if !defined(HAVE_XPM) || defined(__DOXYGEN__)
2471 
2473 // SUBSTITUTE CODE FOR MISSING (or unwanted) libXpm //
2475 
2476 // parse an XPM, and generate data that I can THEN create a bitmap from
2477 //
2478 // The XPM will have 4 numbers at the start (in ASCII): WIDTH HEIGHT nColors chars_per_color
2479 // 'nColors' is the size of the color table (one line each)
2480 // 'chars_per_color' is the # of characters that represent a color
2481 // following this is the bitmap data itself.
2482 // To create a transparency mask look for a color called "None" which is the background color
2483 // for the transparency mask. The mask will be a monochrome bitmap with '0' for transparency,
2484 // and '1' for everything else.
2485 //
2486 // mono bitmap format will be 1 bit per pixel, 16-bit MSB
2487 //
2488 // This function is generally PREFERABLE to the libXpm version as it's a LOT faster
2489 // Additionally, it can be made 'MS Windows Compatible' so that XPM resources can be
2490 // used within WIN32 applications.
2491 
2492 #define MAX_XPM_COLOR_CHAR_SIZE 4
2493 
2494 typedef struct _MY_XPM_COLOR_
2495 {
2496  char c[MAX_XPM_COLOR_CHAR_SIZE]; // up to MAX_XPM_COLOR_CHAR_SIZE chars that represent a color (use 8 instead?)
2497  XColor clrColor; // actual color (32-bit)
2498 } MY_XPM_COLOR;
2499 
2500 
2501 static int MyXPMColorCompare(const void *p1, const void *p2)
2502 {
2503  // always 1st 4 characters only
2504 
2505  return memcmp(p1, p2, MAX_XPM_COLOR_CHAR_SIZE);
2506 }
2507 
2508 static char * MyXPMToData(const char *pXPM[], int *piW, int *piH, char **ppTransparency)
2509 {
2510 MY_XPM_COLOR *pClr, *pC;
2511 int i1, i2, i3, iW, iH;
2512 int nColors, nCharsPerColor;
2513 char cNone[MAX_XPM_COLOR_CHAR_SIZE]={0};
2514 const char *pX, *pY, *pZ;
2515 const char **ppX;
2516 Colormap colormap;
2517 WB_UINT32 *pRval, *pR;
2518 unsigned char *pT;
2519 char tbuf[256];
2520 
2521 
2522  // ------------------
2523  // parse the XPM file
2524  // ------------------
2525 
2526  if(pXPM)
2527  {
2528  ppX = pXPM; // first line is always 4 numbers
2529  }
2530  else
2531  {
2532  ppX = NULL;
2533  }
2534 
2535  if(ppX)
2536  {
2537  pX = *(ppX++);
2538  }
2539  else
2540  {
2541  pX = NULL;
2542  }
2543 
2544  if(!pX)
2545  {
2546  WB_ERROR_PRINT("%s - unexpected parameter, pXPM=%p, ppX=%p, pX=%p\n", __FUNCTION__, pXPM, ppX, pX);
2547  return NULL;
2548  }
2549 
2550  // use a macro to grab 4 integers from text
2551 
2552 #define NEXT_INT(iW,pX,pY,pZ){int iLen; NEXT_WORD(pX,pY,pZ); \
2553  iLen=pZ - pY > sizeof(tbuf) - 1 ? sizeof(tbuf) - 1 : pZ - pY; \
2554  if(iLen>0){memcpy(tbuf,pY,iLen);} tbuf[iLen]=0; iW=atoi(tbuf);}
2555  NEXT_INT(iW,pX,pY,pZ);
2556  NEXT_INT(iH,pX,pY,pZ);
2557  NEXT_INT(nColors,pX,pY,pZ);
2558  NEXT_INT(nCharsPerColor,pX,pY,pZ);
2559 
2560 #undef NEXT_INT
2561 
2562  if(*pX || !iW || !iH || !nColors || !nCharsPerColor || nCharsPerColor > sizeof(pClr[0].c))
2563  {
2564  WB_ERROR_PRINT("%s fail, iW=%d iH=%d nColors=%d nCharsPerColor=%d\n",
2565  __FUNCTION__, iW, iH, nColors, nCharsPerColor);
2566  return NULL;
2567  }
2568 
2569  colormap = WBDefaultColormap(WBGetDefaultDisplay());
2570 
2571  pClr = WBAlloc(nColors * sizeof(MY_XPM_COLOR));
2572 
2573  // ------------------------------------------------------------------------------
2574  // parse the colors - 1-4 chacters, then white space, then the color as '#nnnnnn'
2575  // ------------------------------------------------------------------------------
2576 
2577  for(i1=0; i1 < nColors; i1++)
2578  {
2579  pY = pX = *(ppX++);
2580 
2581  if(!pX)
2582  {
2583  WBFree(pClr);
2584 
2585  WB_ERROR_PRINT("%s NULL pX unexpected, i1=%d\n", __FUNCTION__, i1);
2586  return NULL;
2587  }
2588 
2589  bzero(pClr[i1].c, sizeof(pClr[i1].c));
2590 
2591  for(i2=0; i2 < nCharsPerColor; i2++)
2592  {
2593  pClr[i1].c[i2] = *(pX++);
2594  }
2595 
2596  while(*pX && *pX <= ' ')
2597  {
2598  pX++; // next char should be a 'c'
2599  }
2600 
2601  // next character is 'c' for color
2602  if(*pX != 'c' || pX[1] > ' ' || (pX[2] != '#' && strncmp(pX + 2,"None",4)))
2603  {
2604  WB_ERROR_PRINT("%s fail 1, pX=\"%s\" pY=\"%s\" %d %c %d\n", __FUNCTION__, pX, pY, pX[1], pX[2], strncmp(pX + 2,"None",4));
2605  WBFree(pClr);
2606 
2607  return NULL;
2608  }
2609 
2610  pX += 2;
2611 
2612  if(!strncmp(pX,"None",4))
2613  {
2614  memcpy(cNone, pClr[i1].c, sizeof(pClr[i1].c));
2615  bzero(&(pClr[i1].clrColor), sizeof(XColor));
2616 
2617  pX += 4;
2618  }
2619  else if(*pX == '#')
2620  {
2621  memcpy(tbuf, pX, 7);
2622  tbuf[7] = 0;
2623  XParseColor(WBGetDefaultDisplay(), colormap, tbuf, &(pClr[i1].clrColor));
2624  pX += 7;
2625  }
2626  else
2627  {
2628  WB_ERROR_PRINT("%s fail 2, pX=\"%s\"\n", __FUNCTION__, pX);
2629  WBFree(pClr);
2630  return NULL;
2631  }
2632 
2633  if(*pX)
2634  {
2635  WB_ERROR_PRINT("%s fail 3, pX=\"%s\"\n", __FUNCTION__, pX);
2636  WBFree(pClr);
2637  return NULL;
2638  }
2639  }
2640 
2641  // first usage of 'pRval'
2642  pRval = WBAlloc(iH * iW * 4); // pixel array, always 32-bit-wide values; B is first byte, G is 2nd byte, R is 3rd byte, 4th byte is zero
2643 
2644  if(!pRval)
2645  {
2646  WBFree(pClr);
2647  WB_ERROR_PRINT("%s fail, not enough memory for pRval, size=%d\n", __FUNCTION__, iH * iW * 4);
2648  return NULL;
2649  }
2650 
2651  if(ppTransparency && cNone[0]) // there is a 'None'
2652  {
2653  i3 = (iW + 7) / 8; // total number of bytes needed per row [padded]
2654  pT = WBAlloc(iH * i3 + 2);
2655  if(!pT)
2656  {
2657  WBFree(pRval);
2658  WBFree(pClr);
2659  WB_ERROR_PRINT("%s fail, not enough memory for pT, size=%d\n", __FUNCTION__, (iH * iW) / 8 + 2);
2660  return NULL;
2661  }
2662 
2663  bzero(pT, iH * i3 + 2); // make sure
2664  }
2665  else
2666  {
2667  pT = NULL;
2668  }
2669 
2670  qsort(pClr, nColors, sizeof(*pClr), MyXPMColorCompare);
2671 
2672  // at this point 'pX' points to the actual RBG color data. I shall now create
2673  // Image binary data in XYPixmap format, 32-bits per pixel with 32-bit padding, based on the default display
2674 
2675  for(i2=0, pR=pRval; i2 < iH; i2++)
2676  {
2677  pX = *(ppX++);
2678  if(!pX)
2679  {
2680  WBFree(pRval);
2681  WBFree(pClr);
2682  WB_ERROR_PRINT("%s NULL pX unexpected, i2=%d\n", __FUNCTION__, i2);
2683  return NULL;
2684  }
2685 
2686  pY = pX;
2687 
2688  for(i1=0; i1 < iW; i1++)
2689  {
2690  bzero(tbuf, sizeof(pClr[0].c));
2691 
2692  // read in 'n' characters
2693  for(i3=0; i3 < nCharsPerColor; i3++)
2694  {
2695  if(!*pX)
2696  {
2697  WBFree(pClr);
2698  WB_ERROR_PRINT("%s premature end of string, %ld bytes\n", __FUNCTION__, (long)(pX - pY));
2699  return NULL;
2700  }
2701 
2702  tbuf[i3] = *(pX++);
2703  }
2704 
2705  pC = (MY_XPM_COLOR *)bsearch(tbuf, pClr, nColors, sizeof(*pClr), MyXPMColorCompare);
2706 
2707  if(!pC || memcmp(tbuf, pC->c, sizeof(pC->c)))
2708  {
2709  WBFree(pClr);
2710  WB_ERROR_PRINT("%s fail, did not locate color %-4.4s\n", __FUNCTION__, tbuf);
2711  return NULL;
2712  }
2713 
2714  // pixel color order is B, then G, then R so that R == MSB and B == LSB
2715  // this being 'low endian' might actually matter with the byte order.
2716  // TODO: do I verify this using the 'Visual' structure info? Should I get
2717  // the default visual for the default Display+Screen and verify?
2718 
2719  ((unsigned char *)pR)[0] = pC->clrColor.blue >> 8;
2720  ((unsigned char *)pR)[1] = pC->clrColor.green >> 8;
2721  ((unsigned char *)pR)[2] = pC->clrColor.red >> 8; // these are 16-bit values, so I want 8-bits out of them
2722  ((unsigned char *)pR)[3] = 0;
2723 
2724  pR++;
2725 
2726  if(pT) // bitmap for transparency
2727  {
2728  register unsigned int uiBit;
2729  register unsigned char *pRow;
2730 
2731  // NOTE: in some test code that was written for VMS, the byte order seems to be
2732  // BACKWARDS in a 16-bittedness way. It may be that the machine in question
2733  // used a 16-bit word with byte-order determined by an internal function,
2734  // which could cause a 'swap' of each consecutive pair of bytes. I am going
2735  // to IGNORE THAT for the moment and (by experimentation) determine how I'm
2736  // supposed to order the bits.
2737  // TODO: figure out a system-independent way of doing this that works every time
2738  // even if I have to create an image and manipulate pixels individually
2739 
2740  // width in bytes must be a multiple of 8 bits
2741  i3 = (iW + 7) / 8; // total number of bytes needed per row [padded]
2742 
2743  pRow = pT + i3 * i2; // 'pR' points to the row
2744  i3 = iW - 1 - i1;
2745  uiBit = 1 << (i3 & 7);
2746 
2747  if(!memcmp(tbuf, cNone, sizeof(cNone)))
2748  {
2749  pRow[i3 / 8] &= ~uiBit; // bit is clear
2750  }
2751  else
2752  {
2753  pRow[i3 / 8] |= uiBit; // bit is set
2754  }
2755  }
2756  }
2757  }
2758 
2759  WBFree(pClr);
2760  pClr = NULL;
2761 
2762  if(ppTransparency)
2763  {
2764  *ppTransparency = (char *)pT;
2765  }
2766 
2767  if(piW)
2768  {
2769  *piW = iW;
2770  }
2771 
2772  if(piH)
2773  {
2774  *piH = iH;
2775  }
2776 
2777  return (char *)pRval;
2778 }
2779 
2780 
2781 // function body
2782 int MyLoadPixmapFromData(Display *pDisplay, Window wID, char *aData[],
2783  Pixmap *pPixmap, Pixmap *pMask, XPM_ATTRIBUTES *pAttr)
2784 {
2785 int iW, iH;
2786 char *pTrans = NULL; // transparency data
2787 char *pB;
2788 Pixmap pxImage, pxMask;
2789 int iRval = 0;
2790 
2791 
2792  if(pAttr)
2793  {
2794  bzero(pAttr, sizeof(XPM_ATTRIBUTES));
2795  }
2796 
2797  pB = MyXPMToData((const char **)aData, &iW, &iH, &pTrans);
2798 
2799  if(!pB)
2800  {
2801  WB_ERROR_PRINT("%s - MyXPMToData failed\n", __FUNCTION__);
2802 
2803  if(pPixmap)
2804  {
2805  *pPixmap = None;
2806  }
2807  if(pMask)
2808  {
2809  *pMask = None;
2810  }
2811 
2812  return -1;
2813  }
2814 
2815  if(pTrans && pMask)
2816  {
2817  pxMask = XCreatePixmapFromBitmapData(pDisplay, wID, pTrans, iW, iH,
2818  1,//BlackPixel(pDisplay, DefaultScreen(pDisplay)), mask 'allow' foreground = 1
2819  0,//WhitePixel(pDisplay, DefaultScreen(pDisplay)), transparent background = 0
2820  1); // depth of one, always
2821  WBFree(pTrans);
2822 
2823  if(!pxMask)
2824  {
2825  iRval = -2; // sort of error, but do the rest anyway
2826  }
2827  }
2828  else
2829  {
2830  pxMask = None; // no mask
2831  }
2832 
2833  if(pPixmap)
2834  {
2835  pxImage = XCreatePixmap(pDisplay, wID, iW, iH, DefaultDepth(pDisplay, DefaultScreen(pDisplay)));
2836 
2837  if(!pxImage)
2838  {
2839  WB_ERROR_PRINT("%s - XCreatePixmap failed\n", __FUNCTION__);
2840 
2841  if(pxMask)
2842  {
2843  XFreePixmap(pDisplay, pxMask);
2844  pxMask = None;
2845  }
2846 
2847  iRval = -1;
2848  }
2849  else
2850  {
2851  // create an image from the pB data
2852  XImage *pImage = XCreateImage(pDisplay, DefaultVisual(pDisplay, DefaultScreen(pDisplay)),
2853  DefaultDepth(pDisplay, DefaultScreen(pDisplay)),
2854  ZPixmap,
2855  0,
2856  pB,
2857  iW, iH, 32, iW * 4);
2858 
2859  if(!pImage)
2860  {
2861  WB_ERROR_PRINT("%s - XCreateImage failed\n", __FUNCTION__);
2862 
2863  if(pxMask)
2864  {
2865  XFreePixmap(pDisplay, pxMask);
2866  pxMask = None;
2867  }
2868 
2869  XFreePixmap(pDisplay, pxImage);
2870  pxImage = None;
2871 
2872  iRval = -1;
2873  }
2874  else
2875  {
2876  GC gc;
2877  XGCValues gcv;
2878 
2879  gcv.foreground = BlackPixel(pDisplay, DefaultScreen(pDisplay));
2880  gcv.background = WhitePixel(pDisplay, DefaultScreen(pDisplay));
2881 
2882  gc = XCreateGC(pDisplay, wID, (GCForeground | GCBackground), &gcv);
2883 
2884  if(gc == None)
2885  {
2886  WB_ERROR_PRINT("%s - XCreateGC failed\n", __FUNCTION__);
2887 
2888  if(pxMask)
2889  {
2890  XFreePixmap(pDisplay, pxMask);
2891  pxMask = None;
2892  }
2893 
2894  XFreePixmap(pDisplay, pxImage);
2895  pxImage = None;
2896 
2897  iRval = -1;
2898  }
2899  else
2900  {
2901  // I will need to create a GC
2902  XPutImage(pDisplay, pxImage, gc, pImage, 0, 0, 0, 0, iW, iH); // and now I have a copy of it
2903 
2904  if(pImage->data == pB)
2905  {
2906  pImage->data = NULL; // MUST do this before calling XDestroyImage
2907  }
2908 
2909  XFreeGC(pDisplay, gc);
2910  }
2911 
2912  XDestroyImage(pImage);
2913  }
2914  }
2915  }
2916 
2917  WBFree(pB);
2918 
2919  if(pPixmap)
2920  {
2921  *pPixmap = pxImage;
2922  }
2923 
2924  if(pMask)
2925  {
2926  *pMask = pxMask;
2927  }
2928 
2929  if(pAttr)
2930  {
2931  pAttr->width = iW;
2932  pAttr->height = iH;
2933  pAttr->depth = DefaultDepth(pDisplay, DefaultScreen(pDisplay));
2934  }
2935 
2936  return iRval;
2937 }
2938 
2939 
2940 
2941 
2942 
2943 #if 0
2944 
2946 // TEST FUNCTIONS FOR XPM SUBSTITUTE //
2948 
2949 void TestFunc1(Display *pDisplay, GC gc, Window wID, int iX, int iY)
2950 {
2951 #define gray_width 16
2952 #define gray_height 16
2953 #define gray_x_hot 8
2954 #define gray_y_hot 8
2955 static char gray_bits0[] = { // this failed
2956  0xf8, 0x1f, 0xe3, 0xc7, 0xcf, 0xf3, 0x9f, 0xf9, 0xbf,
2957  0xfd, 0x33, 0xcc, 0x7f, 0xfe, 0x7f, 0xfe, 0x7e, 0x7e,
2958  0x7f, 0xfe, 0x37, 0xec, 0xbb, 0xdd, 0x9c, 0x39, 0xcf,
2959  0xf3, 0xe3, 0xc7, 0xf8, 0x1f};
2960 
2961 static char gray_bits[] = // this worked (MSB byte-level)
2962 {
2963  0x1f, 0xf8, 0xc7, 0xe3, 0xf3, 0xcf, 0xf9, 0x9f,
2964  0xfd, 0xbf, 0xcc, 0x33, 0xfe, 0x7f, 0xfe, 0x7f,
2965  0x7e, 0x7e, 0xfe, 0x7f, 0xec, 0x37, 0xdd, 0xbb,
2966  0x39, 0x9c, 0xf3, 0xcf, 0xc7, 0xe3, 0x1f, 0xf8
2967 };
2968 unsigned long foreground, background;
2969 unsigned int depth;
2970 Pixmap pxTemp;
2971 
2972  /* open display, determine colors and depth */
2973 
2974  foreground = BlackPixel(pDisplay, DefaultScreen(pDisplay));
2975  background = WhitePixel(pDisplay, DefaultScreen(pDisplay)); // foreground
2976  depth = DefaultDepth(pDisplay, DefaultScreen(pDisplay));
2977 
2978  fprintf(stderr, "Creating pixmap, %ld, %ld, %d\n", foreground, background, depth);
2979  fflush(stderr);
2980 
2981  // on this system a bitmap is 8 bit's per pixel, MSB bit (hopefully the same everywhere)
2982 
2983  pxTemp = XCreatePixmapFromBitmapData(pDisplay, wID, gray_bits, gray_width, gray_height,
2984  foreground, background, depth);
2985 
2986  XCopyArea(pDisplay, pxTemp, wID, gc, 0, 0, gray_width, gray_height, iX, iY);
2987 
2988  XFreePixmap(pDisplay, pxTemp);
2989 }
2990 
2991 #include "icon_finger.xpm" // it's a static, so I have to
2992 
2993 void TestFunc(Display *pDisplay, GC gc, Window wID, int iX, int iY)
2994 {
2995 int iW, iH;
2996 char *pTrans = NULL;
2997 char *pB;
2998 Pixmap pxTemp;
2999 
3000  pB = MyXPMToData((const char **)icon_finger_xpm, &iW, &iH, &pTrans);
3001 
3002  if(!pB)
3003  {
3004  fprintf(stderr, "Oh, by the way, it failed\n");
3005  fflush(stderr);
3006  return;
3007  }
3008 
3009  if(!pTrans)
3010  {
3011  WBFree(pB);
3012  fprintf(stderr, "Oh, by the way, no transparency\n");
3013  fflush(stderr);
3014  return;
3015  }
3016 
3017 
3018 // pxTemp = XCreatePixmapFromBitmapData(pDisplay, wID, pTrans, iW, iH,
3019 // BlackPixel(pDisplay, DefaultScreen(pDisplay)),
3020 // WhitePixel(pDisplay, DefaultScreen(pDisplay)),
3021 // DefaultDepth(pDisplay, DefaultScreen(pDisplay)));
3022 
3023  pxTemp = XCreatePixmap(pDisplay, wID, iW, iH, DefaultDepth(pDisplay, DefaultScreen(pDisplay)));
3024 
3025  if(!pxTemp)
3026  {
3027  fprintf(stderr, "Oh, by the way, no pixmap\n");
3028  fflush(stderr);
3029  }
3030  else
3031  {
3032  // create an image from the pB data
3033  XImage *pImage = XCreateImage(pDisplay, DefaultVisual(pDisplay, DefaultScreen(pDisplay)),
3034  DefaultDepth(pDisplay, DefaultScreen(pDisplay)),
3035  ZPixmap,
3036  0,
3037  pB,
3038  iW, iH, 32, iW * 4);
3039 
3040  if(!pImage)
3041  {
3042  fprintf(stderr, "Oh, by the way, no Image\n");
3043  fflush(stderr);
3044  }
3045  else
3046  {
3047  XPutImage(pDisplay, pxTemp, gc, pImage, 0, 0, 0, 0, iW, iH); // and now I have a copy of it
3048 
3049 
3050  XCopyArea(pDisplay, pxTemp, wID, gc, 0, 0, iW, iH, iX, iY); // and now I do this, proof of concept
3051 
3052 
3053  if(pImage->data == pB)
3054  {
3055  pImage->data = NULL;
3056  }
3057  XDestroyImage(pImage);
3058  }
3059 
3060  XFreePixmap(pDisplay, pxTemp);
3061  }
3062 
3063  WBFree(pB);
3064  WBFree(pTrans);
3065 
3066 
3067 // XImage *XCreateImage(Display *display,
3068 // Visual *visual, // DefaultVisual(pDisplay, DefaultScreen(pDisplay))
3069 // unsigned int depth, // compatible depth
3070 // int format, // XYBitmap, XYPixmap, ZPixmap (one of the 3)
3071 // int offset, // # pixels to ignore at start of scanline
3072 // char *data, // obvious
3073 // unsigned int width, // obvious
3074 // unsigned int height, // obvious
3075 // int bitmap_pad, // scanline 'quantum', 8, 16, or 32
3076 // int bytes_per_line); // # of bytes in a scan line
3077 
3078 }
3079 
3080 #endif // 0
3081 
3082 
3083 // some code may have been adapted from libXpm source - SEE ORIGINAL COPYRIGHT NOTICE BELOW
3084 // I actually DID re-write it from scratch, after studying their code _AND_ doing a bunch of
3085 // online research for standard X11 documentation, but just in case here's their copyright
3086 
3087 /********************** libXpm ORIGINAL SOURCE COPYRIGHT **************************/
3088 /*
3089  * Copyright (C) 1989-95 GROUPE BULL
3090  *
3091  * Permission is hereby granted, free of charge, to any person obtaining a copy
3092  * of this software and associated documentation files (the "Software"), to
3093  * deal in the Software without restriction, including without limitation the
3094  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
3095  * sell copies of the Software, and to permit persons to whom the Software is
3096  * furnished to do so, subject to the following conditions:
3097  *
3098  * The above copyright notice and this permission notice shall be included in
3099  * all copies or substantial portions of the Software.
3100  *
3101  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3102  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3103  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
3104  * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
3105  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3106  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3107  *
3108  * Except as contained in this notice, the name of GROUPE BULL shall not be
3109  * used in advertising or otherwise to promote the sale, use or other dealings
3110  * in this Software without prior written authorization from GROUPE BULL.
3111  */
3112 
3113 
3114 
3115 #endif // !defined(HAVE_XPM)
3116 
3117 
3118 
3119 
3121 // //
3122 // ____ _ _ _ _____ _____ _ _ //
3123 // | _ \ __ _ | |_ | |__ ___ __ _ _ __ __| | |_ _|___ _ __ ___ _ __ | ___|(_)| | ___ ___ //
3124 // | |_) |/ _` || __|| '_ \ / __| / _` || '_ \ / _` | | | / _ \| '_ ` _ \ | '_ \ | |_ | || | / _ \/ __| //
3125 // | __/| (_| || |_ | | | |\__ \ | (_| || | | || (_| | | || __/| | | | | || |_) | | _| | || || __/\__ \ //
3126 // |_| \__,_| \__||_| |_||___/ \__,_||_| |_| \__,_| |_| \___||_| |_| |_|| .__/ |_| |_||_| \___||___/ //
3127 // |_| //
3128 // //
3130 
3131 
3132 char * WBSearchPath(const char *szFileName)
3133 {
3134 char *pRval = NULL;
3135 const char *p1, *pCur, *pPath;
3136 char *p2;
3137 
3138  if(0 > WBStat(szFileName, NULL)) // file does not exist?
3139  {
3140  if(*szFileName == '/') // absolute path
3141  {
3142 no_stat:
3143  WB_ERROR_PRINT("%s - File does not exist: \"%s\"\n", __FUNCTION__, szFileName);
3144  return NULL;
3145  }
3146 
3147  // check PATH environment variable, and locate first match
3148 
3149  pRval = WBAlloc(2 * PATH_MAX + strlen(szFileName));
3150 
3151  if(pRval)
3152  {
3153  pPath = getenv("PATH"); // not malloc'd, but should not modify
3154  if(pPath)
3155  {
3156  pCur = pPath;
3157 
3158  while(*pCur)
3159  {
3160  *pRval = 0; // reset
3161 
3162  p1 = pCur;
3163  while(*p1 && *p1 != ':')
3164  {
3165  p1++;
3166  }
3167 
3168  if((p1 - pCur) + 2 < 2 * PATH_MAX) // only if not a buffer overrun
3169  {
3170  // build path name
3171  memcpy(pRval, pCur, p1 - pCur);
3172 
3173  if(pRval[(p1 - pCur) - 1] != '/')
3174  {
3175  pRval[(p1 - pCur)] = '/';
3176  strcpy(pRval + (p1 - pCur) + 1, szFileName);
3177  }
3178  else
3179  {
3180  strcpy(pRval + (p1 - pCur), szFileName);
3181  }
3182 
3183 // fprintf(stderr, "TEMPORARY: trying \"%s\"\n", pRval);
3184 
3185  if(!WBStat(pRval, NULL))
3186  {
3187  return pRval; // FOUND!
3188  }
3189  }
3190 
3191  if(*p1)
3192  {
3193  p1++;
3194  }
3195 
3196  pCur = p1;
3197  }
3198  }
3199 
3200  pPath = pCur = p1 = NULL; // make sure I NULL them out (prevent pointer re-use)
3201 
3202  // if I get here I should check ONE MORE TIME at the location of X11workbench in case
3203  // it was installed into a non-standard path someplace and I need one of its utilities
3204 
3206  if(p2)
3207  {
3208  if(*p2 && strlen(p2) < 2 * PATH_MAX) // so I don't overflow
3209  {
3210  p1 = strrchr(p2, '/'); // find the last '/'
3211  if(p1)
3212  {
3213  p2[p1 - p2 + 1] = 0; // terminate with 0 byte (p1 is const)
3214  }
3215  else
3216  {
3217  WBFree(p2);
3218  p2 = NULL;
3219  }
3220  }
3221  else
3222  {
3223  WBFree(p2);
3224  p2 = NULL;
3225  }
3226  }
3227 
3228  p1 = NULL; // prevents pointer re-use
3229 
3230  if(p2)
3231  {
3232  strcpy(pRval, p2); // the path for X11workbench's install directory
3233  strcat(pRval, szFileName); // use path of X11workbench executable with szFileName
3234  }
3235  else // could not find, nor get path info
3236  {
3237  WBFree(pRval);
3238  pRval = NULL;
3239  }
3240  }
3241 
3242  if(!pRval || 0 > WBStat(pRval, NULL))
3243  {
3244  if(pRval)
3245  {
3246  WBFree(pRval);
3247  }
3248 
3249  goto no_stat;
3250  }
3251  }
3252  else
3253  {
3254  pRval = WBCopyString(szFileName); // file exists, so return as-is
3255  }
3256 
3257  return pRval;
3258 }
3259 
3260 
3261 char * WBTempFile0(const char *szExt)
3262 {
3263 char *pRval = NULL;
3264 const char *szDir = NULL;
3265 int i1;
3266 WB_FILE_HANDLE h1;
3267 union
3268 {
3269  WB_UINT64 ullTime;
3270  unsigned short sA[4];
3271 } uX;
3272 static const char szH[16]="0123456789ABCDEF";
3273 
3274 
3275 #ifdef WIN32
3276  // TODO: the windows code, which uses the TEMP and TMP environment variables as well as the registry
3277 #error windows version not implemented
3278 #else // !WIN32
3279 
3280  // On POSIX systems, first use /var/tmp and if not available, use /tmp
3281 
3282  szDir = "/var/tmp";
3283 
3284  if(0 > WBStat(szDir, NULL))
3285  {
3286  szDir = "/tmp";
3287  if(0 > WBStat(szDir, NULL))
3288  {
3289  return NULL; // unable to 'stat' the temp file directory
3290  }
3291  }
3292 
3293 #endif // !WIN32
3294 
3295  for(i1=0; i1 < 256; i1++) // don't try forever
3296  {
3297  pRval = WBCopyString(szDir);
3298 
3299  if(pRval)
3300  {
3301 #ifdef WIN32
3302  WBCatString(&pRval, "\\wbtk0000");
3303 #else // !WIN32
3304  WBCatString(&pRval, "/wbtk0000");
3305 #endif // !WIN32
3306  }
3307 
3308 
3309  uX.ullTime = WBGetTimeIndex();
3310  uX.sA[0] ^= uX.sA[1];
3311  uX.sA[0] ^= uX.sA[2];
3312  uX.sA[0] ^= uX.sA[3];
3313 
3314  if(pRval)
3315  {
3316  char *pX = pRval + strlen(pRval) - 4; // point to first '0'
3317 
3318  pX[0] = szH[(uX.sA[0] >> 12) & 0xf];
3319  pX[1] = szH[(uX.sA[0] >> 8) & 0xf];
3320  pX[2] = szH[(uX.sA[0] >> 4) & 0xf];
3321  pX[3] = szH[uX.sA[0] & 0xf];
3322 
3323  if(szExt && *szExt)
3324  {
3325  if(*szExt != '.')
3326  {
3327  WBCatString(&pRval, ".");
3328  }
3329  if(pRval)
3330  {
3331  WBCatString(&pRval, szExt);
3332  }
3333  }
3334  }
3335 
3336  if(pRval)
3337  {
3338 #ifdef WIN32
3339 #error windows code not written yet
3340 #else // !WIN32
3341  h1 = open(pRval, O_CREAT | O_EXCL | O_RDWR, 0644); // create file, using '644' permissions, fail if exists
3342 
3343  if(h1 < 0) // error
3344  {
3345  WBFree(pRval);
3346  pRval = NULL;
3347 
3348  if(errno == EEXIST)
3349  {
3350  WBDelay(499);
3351  continue; // try again with a different name
3352  }
3353 
3354  if(errno == ENOTDIR || errno == ENOENT || errno == EACCES ||
3355  errno == EPERM || errno == EROFS || errno == EMFILE || errno == ENFILE)
3356  {
3357  // these errors are fatal, so I exit now
3358  break;
3359  }
3360  }
3361  else
3362  {
3363  close(h1);
3364 
3365  // add this file to the existing list of temp files to be destroyed
3366  // on exit from the program.
3367 
3368  break; // file name is valid and ready for use
3369  }
3370 #endif // !WIN32
3371  }
3372  }
3373 
3374  return pRval;
3375 }
3376 
3377 char * WBTempFile(const char *szExt)
3378 {
3379 char *pRval = WBTempFile0(szExt);
3380 
3381  if(pRval)
3382  {
3383  __add_to_temp_file_list(pRval);
3384  }
3385 
3386  return pRval;
3387 }
3388 
3389 static void __add_to_temp_file_list(const char *szFile)
3390 {
3391 int i1 = strlen(szFile);
3392 char *pTemp;
3393 
3394  // TODO: thread-safe?
3395 
3396  if(!pTempFileList)
3397  {
3398  cbTempFileList = PATH_MAX * 256;
3399  pTempFileList = WBAlloc(cbTempFileList);
3400 
3401  if(!pTempFileList)
3402  {
3403  return;
3404  }
3405 
3406  pTempFileListEnd = pTempFileList;
3407  }
3408  else if((pTempFileListEnd - pTempFileList) + i1 + 2 >= cbTempFileList)
3409  {
3410  pTemp = WBReAlloc(pTempFileList, cbTempFileList + (128 * PATH_MAX));
3411  if(!pTemp)
3412  {
3413  return;
3414  }
3415 
3416  cbTempFileList += 128 * PATH_MAX; // using a 'while' loop sanity checks this against i1
3417 
3418  if((pTempFileListEnd - pTempFileList) + i1 + 2 >= cbTempFileList) // sanity check
3419  {
3420  return; // don't bother re-allocating, this is probably NOT sane
3421  }
3422  }
3423 
3424  memcpy(pTempFileListEnd, szFile, i1);
3425  pTempFileListEnd += i1;
3426 
3427  *(pTempFileListEnd++) = 0;
3428  *pTempFileListEnd = 0; // always end with 2 0-bytes, point to 2nd one
3429 }
3430 
3431 
3433 // //
3434 // _____ _ _ _ _ _ _ _ //
3435 // | ____|__ __| |_ ___ _ __ _ __ __ _ | | / \ _ __ _ __ | |(_) ___ __ _ | |_ (_) ___ _ __ ___ //
3436 // | _| \ \/ /| __|/ _ \| '__|| '_ \ / _` || | / _ \ | '_ \ | '_ \ | || | / __|/ _` || __|| | / _ \ | '_ \ / __| //
3437 // | |___ > < | |_| __/| | | | | || (_| || | / ___ \ | |_) || |_) || || || (__| (_| || |_ | || (_) || | | |\__ \ //
3438 // |_____|/_/\_\ \__|\___||_| |_| |_| \__,_||_| /_/ \_\| .__/ | .__/ |_||_| \___|\__,_| \__||_| \___/ |_| |_||___/ //
3439 // |_| |_| //
3440 // //
3441 // _ _ ____ //
3442 // / \ _ __ __| | | _ \ _ __ ___ ___ ___ ___ ___ ___ ___ //
3443 // / _ \ | '_ \ / _` | | |_) || '__|/ _ \ / __|/ _ \/ __|/ __| / _ \/ __| //
3444 // / ___ \ | | | || (_| | | __/ | | | (_) || (__| __/\__ \\__ \| __/\__ \ //
3445 // /_/ \_\|_| |_| \__,_| |_| |_| \___/ \___|\___||___/|___/ \___||___/ //
3446 // //
3448 
3450 // EXTERNAL APPLICATION EXECUTION
3452 
3453 WB_PROCESS_ID WBRunAsyncPipeV(WB_FILE_HANDLE hStdIn, WB_FILE_HANDLE hStdOut, WB_FILE_HANDLE hStdErr,
3454  const char *szAppName, va_list va)
3455 {
3456 const char *pArg;//, *pPath;
3457 char *pCur, *p1, *pAppName = NULL;
3458 char **argv;
3459 int i1, nItems, cbItems;
3460 va_list va2;
3461 WB_PROCESS_ID hRval;
3462 WB_FILE_HANDLE hIn, hOut, hErr;
3463 
3464 
3465  // NOTE: to avoid zombies, must assign SIGCHLD to 'SIG_IGN' or process them correctly
3466  // (this is done in 'WBInit')
3467 
3468  hIn = hOut = hErr = WB_INVALID_FILE_HANDLE; // by convention (WIN32 needs this anyway)
3469 
3470  // FIRST, locate 'szAppName'
3471 
3472  pAppName = WBSearchPath(szAppName);
3473 
3474 // WB_ERROR_PRINT("TEMPORARY: %s - AppName \"%s\"\n", __FUNCTION__, pAppName);
3475 //
3476 // DEBUG-ONLY code - TODO enable with debug level verbosity > ???
3477 // {
3478 // va_copy(va2, va);
3479 // nItems = 1;
3480 // while(1)
3481 // {
3482 // pArg = va_arg(va2, const char *);
3483 // if(!pArg)
3484 // {
3485 // break;
3486 // }
3487 //
3488 // WB_ERROR_PRINT("TEMPORARY: %s - Arg %d -\"%s\"\n", __FUNCTION__, nItems, pArg);
3489 // nItems++;
3490 // }
3491 // }
3492 
3493  if(hStdIn == WB_INVALID_FILE_HANDLE) // re-dir to/from /dev/null
3494  {
3495 #ifndef WIN32
3496  hIn = open("/dev/null", O_RDONLY, 0);
3497 #else // WIN32
3498  SECURITY_DESCRIPTOR *pSD = (SECURITY_DESCRIPTOR *)WBAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
3499 
3500  if(pSD)
3501  {
3502  InitializeSecurityDescriptor(pSD,SECURITY_DESCRIPTOR_REVISION);
3503  pSD->bInheritHandle = TRUE; // what a pain
3504 
3505  hIn = CreateFile("NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
3506  pSD, OPEN_EXISTING, NULL, NULL);
3507  WBFree(pSD);
3508  }
3509 #endif // WIN32
3510  }
3511  else
3512  {
3513 #ifndef WIN32
3514  hIn = dup(hStdIn);
3515 #else // WIN32
3516  if(!DuplicateHandle(GetCurrentProcess(), hStdIn,
3517  GetCurrentProcess(), &hIn, GENERIC_READ,
3518  TRUE, 0))
3519  {
3520  hIn = WB_INVALID_FILE_HANDLE;
3521  }
3522 #endif // WIN32
3523  }
3524 
3525  if(hStdOut == WB_INVALID_FILE_HANDLE) // re-dir to/from /dev/null
3526  {
3527 #ifndef WIN32
3528  hOut = open("/dev/null", O_WRONLY, 0);
3529 #else // WIN32
3530  SECURITY_DESCRIPTOR *pSD = (SECURITY_DESCRIPTOR *)WBAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
3531 
3532  if(pSD)
3533  {
3534  InitializeSecurityDescriptor(pSD,SECURITY_DESCRIPTOR_REVISION);
3535  pSD->bInheritHandle = TRUE; // what a pain
3536 
3537  hOut = CreateFile("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
3538  pSD, OPEN_EXISTING, NULL, NULL);
3539 
3540  // ALTERNATE: use 'SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)'
3541 
3542  WBFree(pSD);
3543  }
3544 #endif // WIN32
3545  }
3546  else
3547  {
3548 #ifndef WIN32
3549  hOut = dup(hStdOut);
3550 #else // WIN32
3551  if(!DuplicateHandle(GetCurrentProcess(), hStdOut,
3552  GetCurrentProcess(), &hOut, GENERIC_WRITE,
3553  TRUE, 0))
3554  {
3555  hOut = WB_INVALID_FILE_HANDLE;
3556  }
3557 #endif // WIN32
3558  }
3559 
3560  if(hStdErr == WB_INVALID_FILE_HANDLE) // re-dir to/from /dev/null
3561  {
3562 #ifndef WIN32
3563  hErr = open("/dev/null", O_WRONLY, 0);
3564 #else // WIN32
3565  SECURITY_DESCRIPTOR *pSD = (SECURITY_DESCRIPTOR *)WBAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
3566 
3567  if(pSD)
3568  {
3569  InitializeSecurityDescriptor(pSD,SECURITY_DESCRIPTOR_REVISION);
3570  pSD->bInheritHandle = TRUE; // what a pain
3571 
3572  hErr = CreateFile("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
3573  pSD, OPEN_EXISTING, NULL, NULL);
3574  WBFree(pSD);
3575  }
3576 #endif // WIN32
3577  }
3578  else
3579  {
3580 #ifndef WIN32
3581  hErr = dup(hStdErr);
3582 #else // WIN32
3583  if(!DuplicateHandle(GetCurrentProcess(), hStdErr,
3584  GetCurrentProcess(), &hErr, GENERIC_WRITE,
3585  TRUE, 0))
3586  {
3587  hErr = WB_INVALID_FILE_HANDLE;
3588  }
3589 #endif // WIN32
3590  }
3591 
3592  // if file handle duplication fails, exit now with an error
3593 
3594  if(hIn == WB_INVALID_FILE_HANDLE ||
3595  hOut == WB_INVALID_FILE_HANDLE ||
3596  hErr == WB_INVALID_FILE_HANDLE)
3597  {
3598 // WB_ERROR_PRINT("TEMPORARY: %s hIn=%d hOut=%d hErr=%d\n", __FUNCTION__, hIn, hOut, hErr);
3599 
3600  if(hIn != WB_INVALID_FILE_HANDLE)
3601  {
3602  close(hIn);
3603  }
3604  if(hOut != WB_INVALID_FILE_HANDLE)
3605  {
3606  close(hOut);
3607  }
3608  if(hErr != WB_INVALID_FILE_HANDLE)
3609  {
3610  close(hErr);
3611  }
3612 
3613  if(pAppName != szAppName)
3614  {
3615  WBFree(pAppName);
3616  }
3617 
3618  return WB_INVALID_FILE_HANDLE;
3619  }
3620 
3621  // count arguments, determine memory requirement
3622 
3623  nItems = 0;
3624  cbItems = 2 * sizeof(char *) + strlen(szAppName) + 1;
3625  va_copy(va2, va);
3626 
3627  while(1)
3628  {
3629  pArg = va_arg(va2, const char *);
3630  if(!pArg)
3631  {
3632  break;
3633  }
3634 
3635  cbItems += strlen(pArg) + 1 + sizeof(char *);
3636  nItems++;
3637  }
3638 
3639  argv = (char **)WBAlloc(64 + cbItems);
3640  if(!argv)
3641  {
3642  close(hIn);
3643  close(hOut);
3644  close(hErr);
3645 
3646  if(pAppName != szAppName)
3647  {
3648  WBFree(pAppName);
3649  }
3650 
3651 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (1)\n", __FUNCTION__);
3652  return WB_INVALID_FILE_HANDLE;
3653  }
3654 
3655  pCur = (char *)(argv + nItems + 2); // enough room for argument pointers
3656 
3657  p1 = strrchr(szAppName, '/');
3658  if(p1)
3659  {
3660  strcpy(pCur, p1 + 1); // just the name
3661  }
3662  else
3663  {
3664  strcpy(pCur, szAppName);
3665  }
3666 
3667  argv[0] = pCur;
3668  pCur += strlen(pCur) + 1;
3669 
3670  for(i1=1; i1 <= nItems; i1++)
3671  {
3672  pArg = va_arg(va, const char *);
3673 
3674  strcpy(pCur, pArg);
3675  argv[i1] = pCur;
3676  pCur += strlen(pCur) + 1;
3677  }
3678 
3679  argv[nItems + 1] = NULL;
3680 
3681 #ifndef WIN32
3682 
3683  // now that I have a valid 'argv' I can spawn the process.
3684  // I will return the PID so that the caller can wait on it
3685 
3686  hRval = vfork();
3687 
3688  if(!hRval) // the 'forked' process
3689  {
3690  // vfork jumps here FIRST and temporarily suspends the calling thread
3691  // it also does NOT make a copy of memory so I must treat it as 'read only'
3692 
3693  if(dup2(hIn, 0) != -1 && dup2(hOut, 1) != -1 && dup2(hErr, 2) != -1) // stdin, stdout, stderr
3694  {
3695  static const char szMsg[]="ERROR: 'execve()' failure\n";
3696  extern char **environ; // this is what the man page says to do (it's part of libc)
3697 
3698  // TODO: customize environment?
3699 
3700  signal(SIGHUP, SIG_IGN); // ignore 'HUP' signal before 'setsid' call ['daemon()' does this]
3701  setsid(); // so that I am my own process group (NOTE doing this might make it impossible to get the exit status... must verify everywhere)
3702  signal(SIGHUP, SIG_DFL); // restore default handling of 'HUP' ['daemon()' does this]
3703 
3704  execve(pAppName, argv, environ); // NOTE: execute clears all existing signal handlers back to 'default' but retains 'ignored' signals
3705 
3706  write(2, szMsg, sizeof(szMsg) - 1); // stderr is still 'the old one' at this point
3707  fsync(2);
3708 
3709  // TODO: if execve fails, should I forcibly close the duplicated handles??
3710 // close(0);
3711 // close(1);
3712 // close(2);
3713  }
3714  else
3715  {
3716  static const char szMsg[]="ERROR: 'dup2()' failure\n";
3717  write(2, szMsg, sizeof(szMsg) - 1); // stderr is still 'the old one' at this point
3718  }
3719 
3720  close(hIn); // explicitly close these if I get here
3721  close(hOut);
3722  close(hErr);
3723 
3724  _exit(-1); // should never get here, but this must be done if execve fails
3725  }
3726 // else if(hRval < 0)
3727 // {
3728 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (2) errno=%d\n", __FUNCTION__, errno);
3729 // }
3730 
3731 
3732 #else
3733 
3734 #error TODO write the WIN32 code for this using ShellExecuteEx or CreateProcess (example below from Setup Gizmo)
3735 #error and pay SPECIFIC ATTENTION to getting the handle re-direction right for the stdin/stdout/stderr so that piping works
3736 #error and don`t forget the registry entries that ShellExecute uses for the PATH (then again...)
3737 
3738 // SEE http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx
3739 // for creating a child process with re-directed stdin/stdout/stderr
3740 
3741 //DWORD RunApplication(LPCSTR szCmdLine, LPCSTR szExecDir,
3742 // LPCSTR szAppName, LPCSTR szWindowTitle /* = NULL */)
3743 //{
3744 // STARTUPINFO si;
3745 // PROCESS_INFORMATION pi;
3746 // CString csTemp;
3747 //
3748 // CString csExecDir = szExecDir;
3749 //
3750 // if(!csExecDir.GetLength())
3751 // csExecDir = PathFromCommandLine(szCmdLine);
3752 //
3753 // memset(&si, 0, sizeof(si));
3754 // si.cb = sizeof(si);
3755 //
3756 // si.lpTitle = (LPTSTR)szWindowTitle;
3757 // si.wShowWindow = SW_SHOWNA; // show but do not activate it!
3758 // si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USEPOSITION;
3759 //
3760 // memset(&pi, 0, sizeof(pi));
3761 //
3762 // DWORD dwRval = 0; // non-zero return is an error
3763 //
3764 //
3765 // UINT uiFlags = CREATE_NEW_PROCESS_GROUP | CREATE_DEFAULT_ERROR_MODE;
3766 //
3767 // if(GetVersion() & 0x80000000) // WIN '9x
3768 // {
3769 // uiFlags |= CREATE_NEW_CONSOLE;
3770 // }
3771 // else
3772 // {
3773 // uiFlags |= CREATE_SEPARATE_WOW_VDM;
3774 // }
3775 //
3776 // NOTE: assign si.hStdError, si.hStdInput, si.hStdOutput and si.dwFlags |= STARTF_USESTDHANDLES
3777 // in the STARTUPINFO structure in order to re-direct I/O. Also the 'inherit' flag must
3778 // be TRUE (not FALSE as in the example below) for this to work
3779 //
3780 // TODO: determine correct environment using 'ShellExecute' registry parameters
3781 //
3782 // if(!CreateProcess(NULL, (LPSTR)szCmdLine, NULL, NULL, FALSE,
3783 // uiFlags | NORMAL_PRIORITY_CLASS,
3784 // NULL, szExecDir, &si, &pi))
3785 // {
3786 // if(pi.hThread)
3787 // CloseHandle(pi.hThread);
3788 //
3789 // if(pi.hProcess)
3790 // CloseHandle(pi.hProcess);
3791 //
3792 // DWORD dwErr = GetLastError();
3793 // csTemp.Format(" - error %08xH (%d)", dwErr, dwErr);
3794 //
3795 // AfxMessageBox("Unable to start "
3796 // + (CString)szAppName
3797 // + csTemp);
3798 //
3799 // dwRval = 0xffffffff;
3800 // }
3801 // else
3802 // {
3803 // // TODO: do I check an 'abandon me' flag and loop?
3804 // // TODO: do I check for a 'WM_QUIT' message?
3805 //
3806 // while(1)
3807 // {
3808 // DWORD dwWait = ::WaitForSingleObject(pi.hProcess, 500); // 1/2 sec wait
3809 //
3810 // if(dwWait == WAIT_OBJECT_0)
3811 // break;
3812 //
3813 // if(dwWait != WAIT_TIMEOUT)
3814 // {
3815 // AfxMessageBox("Process wait failed on " + (CString)szAppName);
3816 //
3817 // dwRval = 0xffffffff;
3818 // break;
3819 // }
3820 //
3821 // Sleep(50);
3822 //
3823 // MSG msg;
3824 // PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); // help 16 bit apps maybe?
3825 // }
3826 //
3827 // DWORD dwExit = 0;
3828 // if(!dwRval && !GetExitCodeProcess(pi.hProcess, &dwExit))
3829 // {
3830 // dwRval = 0xffffffff;
3831 //
3832 // dwExit= GetLastError();
3833 // csTemp.Format("%ld (%08xH)", dwExit, dwExit);
3834 //
3835 // AfxMessageBox("Unable to get return code from "
3836 // + (CString)szAppName
3837 // + ", error code " + csTemp);
3838 // }
3839 // else if(dwExit != 0)
3840 // {
3841 // CString cs1;
3842 // cs1.Format("%d", dwExit);
3843 //
3844 // AfxMessageBox("'" + (CString)szAppName + "' exits with error code " + cs1,
3845 // MB_OK | MB_ICONHAND | MB_SETFOREGROUND);
3846 //
3847 // dwRval = dwExit;
3848 // }
3849 // }
3850 //
3851 // if(pi.hThread)
3852 // CloseHandle(pi.hThread);
3853 //
3854 // if(pi.hProcess)
3855 // CloseHandle(pi.hProcess);
3856 //
3857 // Sleep(500); // this helps Win98 systems "not hang"
3858 //
3859 // return(dwRval);
3860 //}
3861 //
3862 // ** SECONDARY EXAMPLE USING ShellExecuteEx() **
3863 //
3864 // SHELLEXECUTEINFO sei;
3865 // memset(&sei, 0, sizeof(sei));
3866 // sei.cbSize = sizeof(sei);
3867 // sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
3868 // sei.lpFile = csEXE;
3869 // sei.lpParameters = csParm;
3870 // sei.nShow = SW_SHOWMAXIMIZED;
3871 //
3872 // if(!ShellExecuteEx(&sei))
3873 // {
3874 // AfxMessageBox("Unable to execute 'MAKECAB' command",
3875 // MB_OK | MB_ICONHAND | MB_SETFOREGROUND);
3876 // return(FALSE); // failed
3877 // }
3878 // else
3879 // {
3880 // // wait for process to complete
3881 // wait.Restore(); // just in case
3882 //
3883 // WaitForSingleObject(sei.hProcess, INFINITE);
3884 //
3885 // DWORD dwExitCode;
3886 // if(!GetExitCodeProcess(sei.hProcess, &dwExitCode))
3887 // {
3888 // AfxMessageBox("WARNING: cannot get exit code from 'MAKECAB'",
3889 // MB_OK | MB_ICONHAND | MB_SETFOREGROUND);
3890 // bWasExeError = TRUE;
3891 // }
3892 // else if(dwExitCode == STILL_ACTIVE)
3893 // {
3894 // AfxMessageBox("WARNING: 'MAKECAB' process did not terminate",
3895 // MB_OK | MB_ICONHAND | MB_SETFOREGROUND);
3896 // bWasExeError = TRUE;
3897 // }
3898 // else if(dwExitCode)
3899 // {
3900 // CString cs1;
3901 // cs1.Format("%d", dwExitCode);
3902 //
3903 // AfxMessageBox("'MAKECAB' exits with error code " + cs1,
3904 // MB_OK | MB_ICONHAND | MB_SETFOREGROUND);
3905 // bWasExeError = TRUE;
3906 // }
3907 // }
3908 
3909 #endif // WIN32
3910 
3911  // once I've forked, I don't have to worry about copied memory or shared memory
3912  // and it's safe to free the allocated 'argv' array.
3913 
3914  WBFree(argv);
3915  close(hIn);
3916  close(hOut);
3917  close(hErr);
3918 
3919  if(pAppName != szAppName)
3920  {
3921  WBFree(pAppName);
3922  }
3923 
3924 
3925  return hRval;
3926 }
3927 
3928 
3929 WB_PROCESS_ID WBRunAsync(const char *szAppName, ...)
3930 {
3931 WB_PROCESS_ID idRval;
3932 va_list va;
3933 
3934  va_start(va, szAppName);
3935 
3936  idRval = WBRunAsyncPipeV(WB_INVALID_FILE_HANDLE, WB_INVALID_FILE_HANDLE,
3937  WB_INVALID_FILE_HANDLE, szAppName, va);
3938 
3939  va_end(va);
3940 
3941  if(idRval == WB_INVALID_FILE_HANDLE)
3942  {
3943  WB_ERROR_PRINT("Unable to run '%s'\n", szAppName);
3944  }
3945 // else
3946 // {
3947 // WB_ERROR_PRINT("Running '%s' - pid=%d\n", szAppName, idRval);
3948 // }
3949 
3950  return idRval;
3951 }
3952 
3953 WB_PROCESS_ID WBRunAsyncPipe(WB_FILE_HANDLE hStdIn, WB_FILE_HANDLE hStdOut, WB_FILE_HANDLE hStdErr,
3954  const char *szAppName, ...)
3955 {
3956 WB_PROCESS_ID idRval;
3957 va_list va;
3958 
3959  va_start(va, szAppName);
3960 
3961  idRval = WBRunAsyncPipeV(hStdIn, hStdOut, hStdErr, szAppName, va);
3962 
3963  va_end(va);
3964 
3965  return idRval;
3966 }
3967 
3968 
3969 #define WBRUNRESULT_BUFFER_MINSIZE 65536
3970 #define WBRUNRESULT_BYTES_TO_READ 256
3971 
3972 static char * WBRunResultInternal(WB_FILE_HANDLE hStdIn, const char *szAppName, va_list va)
3973 {
3974 WB_PROCESS_ID idRval;
3975 WB_FILE_HANDLE hP[2]; // [0] is read end, [1] is write end
3976 char *p1, *p2, *pRval;
3977 int i1, i2, iStat, iRunning;
3978 unsigned int cbBuf;
3979 
3980 
3981  cbBuf = WBRUNRESULT_BUFFER_MINSIZE;
3982  pRval = WBAlloc(cbBuf);
3983 
3984  if(!pRval)
3985  {
3986 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (1) WBAlloc fail %d\n", __FUNCTION__, cbBuf);
3987  return NULL;
3988  }
3989 
3990  // use WBRunAsyncPipeV to create a process, with all stdout piped to a char * buffer capture
3991  // stdin and stderr still piped to/from /dev/null
3992 
3993  // create an anonymous pipe. pH[0] is the INPUT pipe, pH[1] is the OUTPUT pipe
3994  // this is important in windows. for POSIX it doesn't really matter which one you use,
3995  // but by convention [0] will be input, [1] will be output
3996 
3997  hP[0] = hP[1] = WB_INVALID_FILE_HANDLE;
3998 
3999 #ifdef WIN32 /* the WINDOWS way */
4000  if(!CreatePipe(&(pH[0]), &(pH[1]), NULL, 0))
4001 #else // !WIN32 (everybody else)
4002  if(0 > pipe(hP))
4003 #endif // WIN32
4004  {
4005  WBFree(pRval);
4006 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (2)\n", __FUNCTION__);
4007  return NULL;
4008  }
4009 
4010 
4011  idRval = WBRunAsyncPipeV(hStdIn, hP[1], // the 'write' end is passed as stdout
4012  WB_INVALID_FILE_HANDLE, szAppName, va);
4013 
4014  if(idRval == WB_INVALID_FILE_HANDLE)
4015  {
4016 // WB_ERROR_PRINT("TEMPORARY: %s failed to run \"%s\" errno=%d\n", __FUNCTION__, szAppName, errno);
4017 
4018  close(hP[0]);
4019  close(hP[1]);
4020 
4021  return NULL;
4022  }
4023 
4024  close(hP[1]); // by convention, this will 'widow' the read end of the pipe once the process is done with it
4025 
4026  fcntl(hP[0], F_SETFL, O_NONBLOCK); // set non-blocking I/O
4027 
4028  // so long as the process is alive, read data from the pipe and stuff it into the output buffer
4029  // (the buffer will need to be reallocated periodically if it fills up)
4030 
4031  p1 = pRval;
4032  *p1 = 0; // always do this
4033  iRunning = 1; // iRunning will be used as a flag to indicate the process exited.
4034 
4035  while(1)
4036  {
4037  i2 = WBRUNRESULT_BYTES_TO_READ; // number of bytes to read at one time
4038  if((p1 - pRval) + i2 >= cbBuf) // enough room for it?
4039  {
4040  i2 = cbBuf - (p1 - pRval);
4041  if(i2 < WBRUNRESULT_BYTES_TO_READ / 8) // time to re-allocate
4042  {
4043  p2 = WBReAlloc(pRval, cbBuf + WBRUNRESULT_BUFFER_MINSIZE / 2);
4044  if(!p2)
4045  {
4046  WBFree(pRval);
4047  pRval = NULL;
4048 
4049 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (4)\n", __FUNCTION__);
4050  break;
4051  }
4052 
4053  cbBuf += WBRUNRESULT_BUFFER_MINSIZE / 2;
4054  p1 = p2 + (p1 - pRval);
4055  pRval = p2;
4056  i2 = WBRUNRESULT_BYTES_TO_READ;
4057  }
4058  }
4059 
4060  // if no data available I'll return immediately
4061 
4062  i1 = read(hP[0], p1, i2);
4063 
4064  if(i1 <= 0)
4065  {
4066  if(!iRunning)
4067  {
4068  if(i1 == 0)
4069  {
4070  break; // end of file, process ended, bail out now
4071  }
4072 // else // this could be caused by the process forking, and the program failing to run
4073 // {
4074 // // TODO: allow a few retries, then bail??
4075 //
4076 // break; // for now, bail out on this as well. should still get "all of it" in the output
4077 // }
4078  }
4079 
4080  if(errno == EAGAIN)
4081  {
4082  WBDelay(500); // wait 1/2 msec
4083  }
4084  else
4085  {
4086  break; // an error of some kind, so bail out [pipe closed?]
4087  }
4088  }
4089  else
4090  {
4091  p1 += i1; // point past the # of bytes I just read in
4092  *p1 = 0; // by convention [to make sure the string is ALWAYS terminated with a 0-byte]
4093  }
4094 
4095  if(iRunning) // only if "still running"
4096  {
4097 #ifdef WIN32 /* the windows way */
4098  DWORD dwExitCode;
4099 
4100  if(::WaitForSingleObject(idRval, 5) != WAIT_TIMEOUT)
4101  {
4102  if(!GetExitCodeProcess(hProcess, &dwExitCode) ||
4103  dwExitCode != STILL_ACTIVE)
4104  {
4105  iRunning = 0; // my flag that it's not running
4106 
4107  Sleep(5);
4108  break;
4109  }
4110  else // if(dwExitCode == STILL_ACTIVE)
4111  {
4112  Sleep(1); // lose time slice, continue loop
4113  }
4114  }
4115 #else // !WIN32 (everybody else)
4116  // for waitpid(), if WNOHANG is specified and there are no stopped, continued or exited children, 0 is returned
4117 
4118  if(waitpid(idRval, &iStat, WNOHANG) && // note this might return non-zero for stopped or continued processes
4119  WIFEXITED(iStat)) // so test if process exits also.
4120  {
4121  iRunning = 0; // my flag that it's not running
4122  WBDelay(5000); // wait for a bit to make sure the I/O completes
4123  }
4124  else
4125  {
4126  WBDelay(500); // so I don't 'spin'
4127  }
4128 #endif // WIN32
4129  }
4130  }
4131 
4132  // always kill the process at this point (in case there was an error)
4133 
4134  kill(idRval, SIGKILL); // not so nice way but oh well
4135  WBDelay(5000); // wait 5msec
4136 
4137  close(hP[0]); // done with the pipe - close it now
4138 
4139 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (4) pRval=%p *pRval=%c\n", __FUNCTION__, pRval, (char)(pRval ? *pRval : 0));
4140 
4141  return pRval;
4142 }
4143 
4144 char *WBRunResult(const char *szAppName, ...)
4145 {
4146 char *pRval;
4147 va_list va;
4148 
4149 
4150  va_start(va, szAppName);
4151 
4152  pRval = WBRunResultInternal(WB_INVALID_FILE_HANDLE, szAppName, va);
4153 
4154  va_end(va);
4155 
4156  return pRval;
4157 }
4158 
4159 
4160 
4161 char *WBRunResultPipe(const char *szStdInBuf, const char *szAppName, ...)
4162 {
4163 char *pRval, *pTemp = NULL;
4164 va_list va;
4165 WB_FILE_HANDLE hIn = WB_INVALID_FILE_HANDLE;
4166 
4167 
4168  va_start(va, szAppName);
4169 
4170  if(szStdInBuf && *szStdInBuf)
4171  {
4172 #ifdef WIN32
4173  DWORD dw1;
4174 #endif // WIN32
4175  unsigned int nLen = strlen(szStdInBuf);
4176 
4177  pTemp = WBTempFile0(".tmp");
4178 
4179  if(!pTemp)
4180  {
4181 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (1)\n", __FUNCTION__);
4182 
4183  va_end(va);
4184  return NULL;
4185  }
4186 
4187 #ifdef WIN32
4188  hIn = CreateFile(pTemp, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
4189  NULL, OPEN_EXISTING, NULL, NULL);
4190 
4191  if(hIn == WB_INVALID_FILE_HANDLE)
4192 #else // WIN32
4193  hIn = open(pTemp, O_RDWR, 0);
4194 
4195  if(hIn < 0)
4196 #endif // WIN32
4197  {
4198 bad_file:
4199 #ifdef WIN32
4200  DeleteFile(pTemp);
4201 #else // WIN32
4202  unlink(pTemp);
4203 #endif // WIN32
4204  WBFree(pTemp);
4205 
4206 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (2)\n", __FUNCTION__);
4207 
4208  va_end(va);
4209  return NULL;
4210  }
4211 
4212 #ifdef WIN32
4213  if(!WriteFile(hIn, szStdInBuf, nLen, &dw1, NULL)
4214  || dw1 != nLen)
4215  {
4216  CloseHandle(hIn);
4217  goto bad_file;
4218  }
4219 
4220  SetFilePointer(hIn, 0, NULL, FILE_BEGIN); // rewind file
4221 #else // WIN32
4222  if(write(hIn, szStdInBuf, nLen) != nLen)
4223  {
4224  close(hIn);
4225  goto bad_file;
4226  }
4227 
4228  lseek(hIn, 0, SEEK_SET); // rewind file
4229 #endif // WIN32
4230 
4231 // WB_ERROR_PRINT("TEMPORARY: %s HERE I AM (3) temp file \"%s\"\n", __FUNCTION__, pTemp);
4232  }
4233 
4234  pRval = WBRunResultInternal(hIn, szAppName, va);
4235 
4236  va_end(va);
4237 
4238  if(pTemp)
4239  {
4240 #ifdef WIN32
4241  CloseHandle(hIn);
4242  DeleteFile(pTemp);
4243 #else // WIN32
4244  close(hIn);
4245  unlink(pTemp);
4246 #endif // WIN32
4247 
4248  WBFree(pTemp);
4249  }
4250 
4251  return pRval;
4252 }
4253 
4254 
4256 // //
4257 // ____ _ _ _ _ _ //
4258 // / ___| | |__ __ _ _ __ ___ __| | | | (_)| |__ _ __ __ _ _ __ _ _ //
4259 // \___ \ | '_ \ / _` || '__|/ _ \ / _` | | | | || '_ \ | '__|/ _` || '__|| | | | //
4260 // ___) || | | || (_| || | | __/| (_| | | |___ | || |_) || | | (_| || | | |_| | //
4261 // |____/ |_| |_| \__,_||_| \___| \__,_| |_____||_||_.__/ |_| \__,_||_| \__, | //
4262 // |___/ //
4263 // //
4265 
4266 #ifdef WIN32
4267 
4268 WB_MODULE WBLoadLibrary(const char * szModuleName)
4269 {
4270  return((WB_MODULE)LoadLibrary(szModuleName));
4271 }
4272 
4273 void WBFreeLibrary(WB_MODULE hModule)
4274 {
4275  FreeLibrary(hModule);
4276 }
4277 
4278 WB_PROCADDRESS WBGetProcAddress(WB_MODULE hModule, const char *szProcName)
4279 {
4280  return((WB_PROCADDRESS)GetProcAddress(hModule, szProcName));
4281 }
4282 
4283 #else // POSIX
4284 
4285 WB_MODULE WBLoadLibrary(const char * szModuleName)
4286 {
4287  return((WB_MODULE)dlopen(szModuleName, RTLD_LAZY | RTLD_LOCAL));
4288 }
4289 
4291 {
4292  dlclose(hModule);
4293 }
4294 
4295 WB_PROCADDRESS WBGetProcAddress(WB_MODULE hModule, const char *szProcName)
4296 {
4297 // freebsd has the 'dlfunc' API, which is basically 'dlsym' cast to a function pointer
4298 #ifdef __FreeBSD__
4299  return((WB_PROCADDRESS)dlfunc(hModule, szProcName));
4300 #else // other POSIX systems - TODO, check for 'dlfunc' instead of the OS
4301  return((WB_PROCADDRESS)dlsym(hModule, szProcName));
4302 #endif // 'dlfunc' check
4303 }
4304 
4305 #endif // POSIX, WIN32
4306 
4308 // //
4309 // _____ _ _ ____ _ //
4310 // |_ _|| |__ _ __ ___ __ _ __| | / ___| _ _ _ __ _ __ ___ _ __ | |_ //
4311 // | | | '_ \ | '__|/ _ \ / _` | / _` | \___ \ | | | || '_ \ | '_ \ / _ \ | '__|| __| //
4312 // | | | | | || | | __/| (_| || (_| | ___) || |_| || |_) || |_) || (_) || | | |_ //
4313 // |_| |_| |_||_| \___| \__,_| \__,_| |____/ \__,_|| .__/ | .__/ \___/ |_| \__| //
4314 // |_| |_| //
4315 // //
4317 
4318 
4319 
4321 {
4322 WB_THREAD_KEY keyRval;
4323 #ifdef WIN32
4324 #else // WIN23
4325  if(!pthread_key_create(&keyRval, NULL))
4326  {
4327  return keyRval;
4328  }
4329 
4331 #endif // WIN32
4332 }
4333 
4335 {
4336 #ifdef WIN32
4337 #else // WIN23
4338  pthread_key_delete(keyVal); // TODO: check return?
4339 #endif // WIN32
4340 }
4341 
4343 {
4344 #ifdef WIN32
4345 #else // WIN23
4346  return pthread_getspecific(keyVal);
4347 #endif // WIN32
4348 }
4349 
4350 void WBThreadSetLocal(WB_THREAD_KEY keyVal, void *pValue)
4351 {
4352 #ifdef WIN32
4353 #else // WIN23
4354  pthread_setspecific(keyVal, pValue);
4355 #endif // WIN32
4356 }
4357 
4359 {
4360 #ifdef WIN32
4361 #else // WIN23
4362  return pthread_self();
4363 #endif // WIN32
4364 }
4365 
4366 
4367 #if 0 /* this code reserved for future use - implement it or remove it */
4368 
4369 #ifdef WIN32
4370 
4371 // special WIN32 version of it
4372 
4373 #else // WIN32
4374 
4375 // call my own thread proc that calls the user thread proc
4376 // which lets me set some stuff up. this will require a locked
4377 // set of buffers for that purpose. This sets them up.
4378 
4379 struct __WB_THREAD_PARM__
4380 {
4381  struct __WB_THREAD_PARM__ *pNext;
4382 
4383  void *(*function)(void *);
4384  void *pParam;
4385 };
4386 
4387 
4388 volatile unsigned int dwThreadParmSpinLock = 0; // spinlock on THIS using WBInterlockedExchange
4389 
4390 struct __WB_THREAD_PARM__ aTP[16] =
4391 {
4392  { &(aTP[1]), NULL, 0 }, // this initializer creates a 'chain'
4393  { &(aTP[2]), NULL, 0 },
4394  { &(aTP[3]), NULL, 0 },
4395  { &(aTP[4]), NULL, 0 },
4396  { &(aTP[5]), NULL, 0 },
4397  { &(aTP[6]), NULL, 0 },
4398  { &(aTP[7]), NULL, 0 },
4399  { &(aTP[8]), NULL, 0 },
4400  { &(aTP[9]), NULL, 0 },
4401  { &(aTP[19]), NULL, 0 },
4402  { &(aTP[11]), NULL, 0 },
4403  { &(aTP[12]), NULL, 0 },
4404  { &(aTP[13]), NULL, 0 },
4405  { &(aTP[14]), NULL, 0 },
4406  { &(aTP[15]), NULL, 0 },
4407  { NULL, NULL, 0 }
4408 };
4409 
4410 struct __WB_THREAD_PARM__ *pTPHead = &(aTP[0]); // the head of the 'free' list
4411 
4412 static void *WBInternalThreadProc(void *pParam)
4413 {
4414 void *pRval = NULL;
4415 struct __WB_THREAD_PARM__ *pParm = (struct __WB_THREAD_PARM__ *)pParam;
4416 void *(*function2)(void *);
4417 void *pParam2;
4418 
4419 
4420  if(!pParam)
4421  {
4422  return NULL;
4423  }
4424 
4425  function2 = pParm->function;
4426  pParam2 = pParm->pParam;
4427 
4428  // do a spinlock, rather than owning a global mutex
4429  while(WBInterlockedExchange(&dwThreadParmSpinLock, 1))
4430  {
4431  WBDelay(100);
4432  }
4433 
4434  // spin lock ok, mess with aTP
4435 
4436  pParm->pNext = pTPHead; // place this entry at the beginning of the free list
4437  pTPHead = pParm;
4438 
4439  WBInterlockedExchange(&dwThreadParmSpinLock, 0); // un-spin-lock
4440 
4441  if(function2)
4442  {
4443  signal(SIGTSTP,SIG_IGN); // thread ignores the signal
4444 
4445  pRval = function2(pParam2);
4446  }
4447 
4448  return pRval;
4449 }
4450 
4451 #endif // 0
4452 
4453 #endif // !WIN32
4454 
4455 
4456 
4457 WB_THREAD WBThreadCreate(void *(*function)(void *), void *pParam)
4458 {
4459 #ifdef WIN32
4460 #else // WIN23
4462 
4463  // TODO: call my own thread startup proc, passing a struct that contains
4464  // 'function' and 'pParam' as the param. use a linked list of
4465  // pre-allocated buffers for that.
4466  // see possible implementation, above
4467 
4468  if(!pthread_create(&thrdRval, NULL, function, pParam))
4469  {
4470  return thrdRval;
4471  }
4472 
4474 #endif // WIN32
4475 }
4476 
4477 void *WBThreadWait(WB_THREAD hThread) // closes hThread, returns exit code, waits for thread to terminate (blocks)
4478 {
4479 #ifdef WIN32
4480 #else // WIN23
4481 void *pRval = NULL;
4482 
4483  if(pthread_join(hThread, &pRval))
4484  {
4485  // TODO: error return??
4486  pRval = (void *)-1;
4487  }
4488 
4489  return pRval;
4490 #endif // WIN32
4491 }
4492 
4493 int WBThreadRunning(WB_THREAD hThread) // >0 if thread is running, <0 error - use 'pthread_kill(thread,0)' which returns ESRCH if terminated i.e. 'PS_DEAD'
4494 {
4495 #ifdef WIN32
4496  DWORD dwRval = WaitForSingleObject(hThread, 0);
4497  if(dwRval == WAIT_OBJECT_0) // still running?
4498  {
4499  return 0; // nope the thread terminated
4500  }
4501  else if(dwRval == WAIT_TIMEOUT)
4502  {
4503  return 1; // still running
4504  }
4505  else
4506  {
4507  return -1; // an error
4508  }
4509 #else // WIN23
4510  int iR = pthread_kill(hThread,0);
4511 
4512  if(!iR)
4513  {
4514  return 1; // no signal sent, handle is valid (and did not exit)
4515  }
4516 
4517  if(iR == ESRCH)
4518  {
4519  return 0; // for now, allow this to indicate 'done'
4520  }
4521 
4522  return -1;
4523 #endif // WIN32
4524 }
4525 
4526 void WBThreadExit(void *pRval)
4527 {
4528 #ifdef WIN32
4529 #else // WIN23
4530  pthread_exit(pRval);
4531 #endif // WIN32
4532 }
4533 
4535 {
4536 #ifdef WIN32
4537  CloseHandle(hThread);
4538 #else // WIN23
4539  pthread_detach(hThread);
4540 #endif // WIN32
4541 }
4542 
4543 
4544 
4546 // //
4547 // ____ _ _ _ _ _ _ __ __ _ //
4548 // / ___| ___ _ __ __| |(_)| |_ (_) ___ _ __ ___ / \ _ __ __| | | \/ | _ _ | |_ ___ __ __ ___ ___ //
4549 // | | / _ \ | '_ \ / _` || || __|| | / _ \ | '_ \ / __| / _ \ | '_ \ / _` | | |\/| || | | || __|/ _ \\ \/ // _ \/ __| //
4550 // | |___| (_) || | | || (_| || || |_ | || (_) || | | |\__ \ / ___ \ | | | || (_| | | | | || |_| || |_| __/ > <| __/\__ \ //
4551 // \____|\___/ |_| |_| \__,_||_| \__||_| \___/ |_| |_||___/ /_/ \_\|_| |_| \__,_| |_| |_| \__,_| \__|\___|/_/\_\\___||___/ //
4552 // //
4553 // //
4555 
4557 {
4558 int iRval = -1;
4559 
4560  if(pCond)
4561  {
4562 #ifdef WIN32
4563  // because of problems under Windows, this will be defined as 'unsigned int'
4564  // and I'll use the increment/decrement interlock functions and wait loops to
4565  // monitor the signaling.
4566 
4567  *pCond = 0;
4568 
4569  iRval = 0; // success!
4570 #else // WIN23
4571 // pthread_condattr_t attr;
4572 // iRval = pthread_condattr_init(&attr);
4573 //
4574 // if(!iRval)
4575 // {
4576 // pthread_condattr_setclock(&attr, CLOCK_REALTIME); // *MUST* use *THIS* one
4577 // pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
4578 //
4579 // iRval = pthread_cond_init(pCond, &attr);
4580 //
4581 // pthread_condattr_destroy(&attr);
4582 // }
4583 
4584  // note: Implementing with 'interlock' in POSIX as well
4585 
4586  *pCond = 0;
4587 
4588  iRval = 0; // success!
4589 #endif // WIN32
4590  }
4591 
4592  return iRval;
4593 }
4594 
4596 {
4597  if(pMtx)
4598  {
4599 #ifdef WIN32
4600 #else // WIN23
4601  if(!pthread_mutex_init(pMtx, NULL))
4602  {
4603  return 0;
4604  }
4605 #endif // WIN32
4606  }
4607 
4608  return -1;
4609 }
4610 
4611 
4612 void WBCondFree(WB_COND *pCond)
4613 {
4614 #ifdef WIN32
4615 // CloseHandle(*pCond);
4616  if(pCond)
4617  {
4618  *pCond = 1; // forces a waiting thread to signal (though only one will probably do it)
4619  }
4620 #else // WIN23
4621  if(pCond)
4622  {
4623 // pthread_cond_destroy(pCond);
4624 
4625  // because of problems under Windows, this will be defined as 'unsigned int'
4626  // and I'll use the increment/decrement interlock functions and wait loops to
4627  // monitor the signaling.
4628 
4629  *pCond = 1; // forces a waiting thread to signal (though only one will probably do it)
4630  }
4631 #endif // WIN32
4632 }
4633 
4635 {
4636 #ifdef WIN32
4637  CloseHandle(*pMtx);
4638 #else // WIN23
4639  pthread_mutex_destroy(pMtx);
4640 #endif // WIN32
4641 }
4642 
4643 int WBMutexLock(WB_MUTEX *pMtx, int nTimeout)
4644 {
4645 int iRval;
4646 #ifdef WIN32
4647 #else // WIN23
4648 WB_UINT64 ullTime = WBGetTimeIndex(); // current time index in microseconds
4649 WB_UINT64 ullTemp;
4650 
4651  if(nTimeout < 0)
4652  {
4653  iRval = pthread_mutex_lock(pMtx);
4654 
4655  return iRval ? -1 : 0; // either an error, or success [but never a timeout]
4656  }
4657 
4658  while(1)
4659  {
4660  iRval = pthread_mutex_trylock(pMtx);
4661 
4662  if(!iRval)
4663  {
4664  return 0;
4665  }
4666  else if(iRval != EBUSY)
4667  {
4668  return -1; // an error
4669  }
4670 
4671  ullTemp = WBGetTimeIndex(); // TODO: use clock_gettime instead?
4672 
4673  if((ullTemp - ullTime) > nTimeout)
4674  {
4675  return 1; // timed out
4676  }
4677 
4678  WBDelay(83 + (ullTemp / 13) % 37); // sleep a 'sort of' random wait time
4679  }
4680 #endif // WIN32
4681 
4682  return -1; // should never get here
4683 }
4684 
4686 {
4687 #ifdef WIN32
4688 #else // WIN23
4689  return pthread_mutex_unlock(pMtx);
4690 #endif // WIN32
4691 }
4692 
4693 
4695 {
4696 //#ifdef WIN32
4697 //#else // WIN23
4698 // return pthread_cond_signal(pCond);
4699 
4700  if(pCond)
4701  {
4702  WBInterlockedExchange(pCond, 1); // assign to 1 [this is my trigger]
4703  return 0;
4704  }
4705 //#endif // WIN32
4706 
4707  return -1;
4708 }
4709 
4710 int WBCondWait(WB_COND *pCond, int nTimeout)
4711 {
4712 int iRval = -1;
4713 //#ifdef WIN32
4714 //#else // WIN23
4715 //WB_MUTEX xMtx;
4716 //
4717 // if(WBMutexCreate(&xMtx)) // needed by call to pthread_cond_timedwait
4718 // {
4719 // return -1; // wait fail
4720 // }
4721 //
4722 // WBMutexLock(&xMtx, 0); // should lock immediately as I just created it
4723 //
4724 // iRval = WBCondWaitMutex(pCond, &xMtx, nTimeout);
4725 //
4726 // WBMutexUnlock(&xMtx);
4727 // WBMutexFree(&xMtx);
4728 //
4729 // return iRval;
4730 
4731  if(!pCond)
4732  {
4733  WB_ERROR_PRINT("TEMPORARY: %s - returns -1 (NULL pCond)\n", __FUNCTION__);
4734 
4735  return -1;
4736  }
4737 
4738  if(nTimeout >= 0)
4739  {
4740  struct timespec ts, tsNow;
4741 
4742  clock_gettime(CLOCK_REALTIME, &ts);
4743 
4744  ts.tv_sec += nTimeout / 1000000;
4745  ts.tv_nsec += (nTimeout % 1000000) * 1000L;
4746 
4747  if(ts.tv_nsec > 1000000000L)
4748  {
4749  ts.tv_sec++;
4750  ts.tv_nsec -= 1000000000L;
4751  }
4752 
4753  iRval = 0;
4754 
4755  while(!WBInterlockedExchange(pCond, 0)) // if return is zero, it wasn't 'signaled'
4756  {
4757  clock_gettime(CLOCK_REALTIME, &tsNow);
4758 
4759  if(tsNow.tv_sec > ts.tv_sec ||
4760  (tsNow.tv_sec == ts.tv_sec &&
4761  tsNow.tv_nsec > ts.tv_nsec))
4762  {
4763  iRval = ETIMEDOUT; // timeout error
4764  break;
4765  }
4766 
4767  WBDelay(37 + tsNow.tv_nsec % 29); // a slightly random ~0.05 msec delay
4768  }
4769  }
4770  else
4771  {
4772  while(!WBInterlockedExchange(pCond, 0)) // if return is zero, it wasn't 'signaled'
4773  {
4774  WBDelay(100); // for now, it's the same time period (TODO: adjust like above?)
4775  }
4776  }
4777 
4778 //#endif // WIN32
4779 
4780  return iRval;
4781 }
4782 
4783 int WBCondWaitMutex(WB_COND *pCond, WB_MUTEX *pMtx, int nTimeout)
4784 {
4785 int iRval = -1;
4786 #ifdef WIN32
4787 #else // WIN23
4788 
4789 // WB_ERROR_PRINT("TEMPORARY: %s - here I am\n", __FUNCTION__);
4790 
4791  if(!pCond || !pMtx)
4792  {
4793  WB_ERROR_PRINT("ERROR: %s - returns -1 (NULL pCond)\n", __FUNCTION__);
4794 
4795  return -1;
4796  }
4797 
4798 // if(nTimeout >= 0)
4799 // {
4800 // struct timespec ts;
4801 //
4802 // clock_gettime(CLOCK_REALTIME, &ts);
4803 //
4804 // ts.tv_sec += nTimeout / 1000000;
4805 // ts.tv_nsec += (nTimeout % 1000000) * 1000L;
4806 //
4807 // if(ts.tv_nsec > 1000000000L)
4808 // {
4809 // ts.tv_sec++;
4810 // ts.tv_nsec -= 1000000000L;
4811 // }
4812 //
4813 // iRval = pthread_cond_timedwait(pCond, pMtx, &ts);
4814 // }
4815 // else
4816 // {
4817 // iRval = pthread_cond_wait(pCond, pMtx); // 'infinite wait' version
4818 // }
4819 //
4820 // if(iRval)
4821 // {
4822 // // make sure mutex is owned - some indication is that on timeout that
4823 // // pthread_cond_timedwait may NOT re-own the mutex! or same on error...?
4824 //
4825 // WBMutexUnlock(pMtx); // in case it IS locked
4826 // WBMutexLock(pMtx, -1); // then re-lock it again [bug workaround]
4827 //
4828 // if(nTimeout >= 0 && iRval == ETIMEDOUT) // timeout
4829 // {
4830 // return 1; // indicate 'timeout' but not error
4831 // }
4832 // else
4833 // {
4834 // return -1;
4835 // }
4836 // }
4837 
4838  WBMutexUnlock(pMtx);
4839 
4840  iRval = WBCondWait(pCond, nTimeout);
4841 
4842  WBMutexLock(pMtx, -1); // this is a safe way of replicating the behavior [wait infinitely on mutex]
4843 
4844 #endif // WIN32
4845 
4846  return iRval;
4847 }
4848 
4849 
4850 unsigned int WBInterlockedDecrement(volatile unsigned int *pValue)
4851 {
4852 #ifdef WIN32
4853  return InterlockedDecrement(pValue);
4854 #else // !WIN32
4855 unsigned int iRval;
4856 
4857  pthread_rwlock_wrlock(&xInterlockedRWLock);
4858 
4859  (*pValue)++;
4860  iRval = *pValue;
4861 
4862  pthread_rwlock_unlock(&xInterlockedRWLock);
4863 
4864  return iRval;
4865 #endif // WIN32
4866 }
4867 
4868 unsigned int WBInterlockedIncrement(volatile unsigned int *pValue)
4869 {
4870 #ifdef WIN32
4871  return InterlockedIncrement(pValue);
4872 #else // !WIN32
4873 unsigned int iRval;
4874 
4875  pthread_rwlock_wrlock(&xInterlockedRWLock);
4876 
4877  (*pValue)++;
4878  iRval = *pValue;
4879 
4880  pthread_rwlock_unlock(&xInterlockedRWLock);
4881 
4882  return iRval;
4883 #endif // WIN32
4884 }
4885 
4886 unsigned int WBInterlockedExchange(volatile unsigned int *pValue, unsigned int nNewVal)
4887 {
4888 #ifdef WIN32
4889  return InterlockedExchange(pValue, nNewVal); // pretty sure this is right...
4890 #else // !WIN32
4891 unsigned int iRval;
4892 
4893  pthread_rwlock_wrlock(&xInterlockedRWLock);
4894 
4895  iRval = *pValue;
4896  *pValue = nNewVal;
4897 
4898  pthread_rwlock_unlock(&xInterlockedRWLock);
4899 
4900  return iRval;
4901 }
4902 
4903 unsigned int WBInterlockedRead(volatile unsigned int *pValue)
4904 {
4905 #ifdef WIN32
4906  return *pValue; // for now; later, there might be a better way
4907 #else // !WIN32
4908 unsigned int iRval;
4909 
4910  pthread_rwlock_rdlock(&xInterlockedRWLock); // this only does a read lock
4911 
4912  iRval = *pValue;
4913 
4914  pthread_rwlock_unlock(&xInterlockedRWLock);
4915 
4916  return iRval;
4917 #endif // WIN32
4918 }
4919 
4920 
4921 
4922 
4924 // ____ ____ ___ _ _ _____ ___ _ _ ____ //
4925 // | _ \| _ \|_ _| \ | |_ _|_ _| \ | |/ ___| //
4926 // | |_) | |_) || || \| | | | | || \| | | _ //
4927 // | __/| _ < | || |\ | | | | || |\ | |_| | //
4928 // |_| |_| \_\___|_| \_| |_| |___|_| \_|\____| //
4929 // //
4931 
4932 int WBPrintPostScriptFile(const char *szPrinterName, const char *szFileName)
4933 {
4934 // use 'lpr-cups' if present; /usr/local/bin/lpr if present; then 'lpr'.
4935 // search order will be /usr/local/bin /usr/local/sbin /usr/bin /usr/sbin /bin /sbin for 'lpr'
4936 // prioritize this against the results using 'PATH'. user might want something else...
4937 // (examples might be a lack of 'sbin' in PATH, or specifying system before local)
4938 // alternately COULD use 'which' output if 'which' is present.
4939 
4940 // typical command line will be 'lpr -P"printer name" filename'
4941 
4942 // TODO: postscript to binary-format conversion using 'ghostscript'
4943 
4944 // TODO: check for presence of cups, get printer caps, specify more information on 'lpr' command
4945 
4946 // TODO: use 'internet print protocol' to localhost:631/printers/printername
4947 
4948 
4949 // for Win32, might need to write a simple postscript renderer onto a printer display surface...
4950 
4951 
4952  return -1; // error (for now)
4953 }
4954 
4955 char *WBGetPrinterList(void)
4956 {
4957  // open 'printcap' and list the entries
4958 
4959  // TODO: check for presence of cups web interface, request printer list "somehow"
4960  // from there, I can grab individual printers' capabilities once I have their names
4961 
4962  // TODO: for Win32 generate the list via shell APIs
4963 
4964  // TODO: a 'select printer' dialog box that's standardized for all platforms
4965 
4966 
4967  return NULL; // for now
4968 }
4969 
4970 #endif // WIN32,POSIX
4971 
4972 
void * WBGetPointerFromHash(WB_UINT32 uiHash)
Obtain a pointer from a 32-bit &#39;secure&#39; pointer hash value.
unsigned int WB_UINT32
Platform abstract unsigned 32-bit integer.
#define WB_LIKELY(x)
optimization for code branching when condition is &#39;likely&#39;. use within conditionals ...
const char * GetStartupAppName(void)
returns a pointer to a copy of the application name from argv[0]
void WBPlatformOnExit(void)
Resource &#39;free-up&#39; on exit (must call)
void * WB_MODULE
MODULE HANDLE equivalent.
char * WBTempFile(const char *szExt)
Get the name for a new, unique temporary file, creating the file in the process, and save its name fo...
WB_UINT32 WB_COND
CONDITION HANDLE equivalent (similar to an &#39;event&#39;)
WB_THREAD_KEY WBThreadAllocLocal(void)
Allocate &#39;thread local&#39; storage.
&#39;window helper&#39; main header file for the X11workbench Toolkit API
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
static __inline__ unsigned int WBGetDebugLevel(void)
Returns the current debug level assigned by WBSetDebugLevel.
Definition: debug_helper.h:124
int bQuitFlag
&#39;Quit&#39; Flag - you should check this periodically in your main (message) loop and exit whenever it is ...
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:299
static __inline__ Colormap WBDefaultColormap(Display *pDisplay)
returns the default colormap for the default screen of the specified display
void WBThreadFreeLocal(WB_THREAD_KEY keyVal)
Free &#39;thread local&#39; storage allocated by WBThreadAllocLocal()
int WBMutexUnlock(WB_MUTEX *pMtx)
Unlock a previously locked mutex.
void * WBThreadGetLocal(WB_THREAD_KEY keyVal)
Get &#39;thread local&#39; data identified by &#39;keyVal&#39;.
void WBCatStringN(char **ppDest, const char *pSrc, unsigned int nMaxChars)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string up t...
int WBStringLineCount(const char *pSrc, unsigned int nMaxChars)
Determine how many &#39;lines&#39; are in a block of text by counting &#39;linefeed&#39; characters.
char * WBRunResult(const char *szAppName,...)
Run an application synchronously, returning &#39;stdout&#39; output in a character buffer.
static __inline__ void WBSupressErrorOutput(void)
Supress X11 XErrorEvent output to stderr.
void WBSubAllocTrashMasher(void)
High performance memory sub-allocator &#39;trash masher&#39; - call periodically to minimize wasted memory...
int MyLoadPixmapFromData(Display *pDisplay, Window wID, char *aData[], Pixmap *pPixmap, Pixmap *pMask, XPM_ATTRIBUTES *pAttr)
Alternate for XpmCreatePixmapFromData() whenever libXpm is not being used.
void WBFreeLibrary(WB_MODULE hModule)
Frees a shared library, DLL, module, or whatever, that was loaded via &#39;WBLoadLibrary()&#39;.
WB_MODULE WBLoadLibrary(const char *szModuleName)
Loads a shared library, DLL, module, or whatever you call it on your operating system.
void * WBThreadWait(WB_THREAD hThread)
Wait for a specified threat to exit.
void WBThreadExit(void *pRval)
Exit the current thread immediately, specifying return code.
unsigned int WBInterlockedIncrement(volatile unsigned int *pValue)
Interlocked &#39;atomic&#39; increment of an unsigned integer.
void my_qsort_r(void *base, int nmemb, int size, void *thunk, int(*compar)(void *, const void *, const void *))
Local implementation of qsort_r() for operating systems that do not have it.
static __inline__ int WBCheckDebugLevel(unsigned int dwLevel)
Check specified debug level against the value returned by WBGetDebugLevel() and return non-zero for a...
Definition: debug_helper.h:239
void WBDebugDump(const char *szTitle, void *pData, int cbData)
conditionally dumps binary data to debug message output
static __inline__ void WBAllowErrorOutput(void)
Restore X11 XErrorEvent output to stderr.
pthread_key_t WB_THREAD_KEY
THREAD LOCAL STORAGE &#39;key&#39; equivalent.
WB_UINT32 WBCreatePointerHash(void *pPointer)
Create/obtain a 32-bit &#39;secure&#39; hash for a pointer.
WB_PROCESS_ID WBRunAsyncPipe(WB_FILE_HANDLE hStdIn, WB_FILE_HANDLE hStdOut, WB_FILE_HANDLE hStdErr, const char *szAppName,...)
Run an application asynchronously, specifying file handles for STDIN, STDOUT, and STDERR...
unsigned int WBInterlockedExchange(volatile unsigned int *pValue, unsigned int nNewVal)
Interlocked &#39;atomic&#39; exchange of an unsigned integer with a specified value.
void * WBReAlloc(void *pBuf, int nNewSize)
High performance memory sub-allocator &#39;re-allocate&#39;.
pthread_mutex_t WB_MUTEX
MUTEX HANDLE equivalent.
int depth
depth of the returned &#39;image&#39; pixmap. The mask pixmap always has a depth of &#39;1&#39;.
WB_UINT64 WBGetTimeIndex(void)
Returns the current &#39;time index&#39; (in microseconds)
WB_THREAD WBThreadCreate(void *(*function)(void *), void *pParam)
Create a new thread, returning its WB_THREAD identifier.
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:1574
void WBDeQuoteString(char *pString)
De-Quote a string &#39;in place&#39;, that is modifying the original string by removing quotes.
void WBToolkitUsage(void)
Displays &#39;usage&#39; for toolkit options to stderr.
void WBThreadSetLocal(WB_THREAD_KEY keyVal, void *pValue)
Get &#39;thread local&#39; data identified by &#39;keyVal&#39;.
void WBDestroyPointerHashPtr(void *pPointer)
Destroy a 32-bit &#39;secure&#39; hash for a pointer regardless of reference count.
void(* WB_PROCADDRESS)(void)
PROC ADDRESS equivalent.
void * WBAlloc(int nSize)
High performance memory sub-allocator &#39;allocate&#39;.
int WBCondSignal(WB_COND *pCond)
Signal a condition (event)
unsigned long long WB_UINT64
Platform abstract unsigned 64-bit integer.
WB_PROCESS_ID WBRunAsync(const char *szAppName,...)
Run an application asynchronously.
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:356
char * WBGetPrinterList(void)
Get a list of printer names.
Atom WBLookupAtom(Display *pDisplay, const char *szAtomName)
Lookup (but do not allocate) an internal (or X11) Atom for a named string.
WB_THREAD WBThreadGetCurrent(void)
Get &#39;current thread&#39; identifier.
char * WBSearchPath(const char *szFileName)
search for a file using the PATH environment variable
char * WBRunResultPipe(const char *szStdInBuf, const char *szAppName,...)
Run an application synchronously, returning &#39;stdout&#39; output in a character buffer.
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
void WBDelay(uint32_t uiDelay)
Delay for a specified period in microseconds.
int WBParseStandardArguments(int *pargc, char ***pargv, char ***penvp)
parses standard C arguments as passed to &#39;main()&#39;
void WBPlatformOnInit(void)
Resource initialization on startup.
unsigned int WBInterlockedDecrement(volatile unsigned int *pValue)
Interlocked &#39;atomic&#39; decrement of an unsigned integer.
int WBAllocUsableSize(void *pBuf)
High performance memory sub-allocator, similar to &#39;malloc_usable_size&#39;.
void WBSetDebugLevel(unsigned int iNew)
set debug level
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms) ...
#define WB_INTERNAL_ATOM_MIN_VAL
The minimum &#39;internal&#39; Atom value used by the toolkit.
#define WB_SECURE_HASH_TIMEOUT
WB_PROCADDRESS WBGetProcAddress(WB_MODULE hModule, const char *szProcName)
Loads a shared library, DLL, module, or whatever you call it on your operating system.
int WBMutexCreate(WB_MUTEX *pMtx)
Create a lockable mutex.
int WBMutexLock(WB_MUTEX *pMtx, int nTimeout)
Wait for and lock a mutex, blocking until it is available.
int WBThreadRunning(WB_THREAD hThread)
Determine whether a thread is running (can be suspended)
const char * WBStringNextLine(const char *pSrc, unsigned int *pnMaxChars)
Locate the next line in a block of text, returning its pointer (and updating remaining length) ...
WB_PROCESS_ID WBRunAsyncPipeV(WB_FILE_HANDLE hStdIn, WB_FILE_HANDLE hStdOut, WB_FILE_HANDLE hStdErr, const char *szAppName, va_list va)
Run an application asynchronously, specifying file handles for STDIN, STDOUT, and STDERR...
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...
int WBCondWait(WB_COND *pCond, int nTimeout)
Wait for a signal on a condition (event)
void WBMutexFree(WB_MUTEX *pMtx)
Free a lockable mutex.
void WBDestroyPointerHash(WB_UINT32 uiHash)
Destroy a 32-bit &#39;secure&#39; hash for a pointer.
int height
height of the returned pixmaps
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
int width
The width of the returned pixmaps.
pthread_t WB_THREAD
THREAD HANDLE equivalent.
#define INVALID_HANDLE_VALUE
INVALID HANDLE VALUE equivalent.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
char * WBTempFile0(const char *szExt)
Get the name for a new, unique temporary file, creating the file in the process.
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
Definition file for platform-specific utility functions.
int WBCondWaitMutex(WB_COND *pCond, WB_MUTEX *pMtx, int nTimeout)
Wait for a signal on a condition (event)
Compatibility structure for use with MyLoadPixmapFromData() whenever libXpm is not in use...
void WBThreadClose(WB_THREAD hThread)
Close the specified WB_THREAD identifier.
int WBCPUCount(void)
Get the number of available CPU cores.
unsigned int WBInterlockedRead(volatile unsigned int *pValue)
Interlocked &#39;atomic&#39; read of an unsigned integer.
#define WB_WARN_PRINT(...)
Preferred method of implementing a &#39;warning level&#39; debug message for all subsystems.
Definition: debug_helper.h:349
int WBPrintPostScriptFile(const char *szPrinterName, const char *szFileName)
Send postscript file to a printer (using lpr-cups or lpr on POSIX systems)
int WBCondCreate(WB_COND *pCond)
Create a signallable condition.
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 ...
#define WB_UNLIKELY(x)
optimization for code branching when condition is &#39;unlikely&#39;. use within conditionals ...
void WBCatString(char **ppDest, const char *pSrc)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string...
void WBCondFree(WB_COND *pCond)
Free a signallable condition.