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