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