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