X11workbench Toolkit  1.0
file_help.c
1 // __ _ _ _ _ //
3 // / _|(_)| | ___ | |__ ___ | | _ __ ___ //
4 // | |_ | || | / _ \ | '_ \ / _ \| || '_ \ / __| //
5 // | _|| || || __/ | | | || __/| || |_) |_| (__ //
6 // |_| |_||_| \___|_____|_| |_| \___||_|| .__/(_)\___| //
7 // |_____| |_| //
8 // //
9 // helper utilities for file I/O //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2019 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17 
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  MIT-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  MIT-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
49 
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <memory.h>
54 #include <string.h>
55 #include <strings.h>
56 #include <fcntl.h>
57 #include <dirent.h>
58 #include <fnmatch.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <sys/param.h> // for MAXPATHLEN and PATH_MAX (also includes limits.h in some cases)
63 
64 #include "file_help.h"
65 #include "draw_text.h" // for string utilities, mostly
66 #include "window_helper.h" // for debug stuff
67 
68 
69 #ifndef NO_DEBUG
70 // debug function to dump parsed contents of file_help_buf_t
71 static void FBDumpParsedFileBuf(file_help_buf_t *pBuf)
72 {
73 int i1;
74 const char *p1;
75 
76  if(WB_UNLIKELY((WBGetDebugLevel() & DebugLevel_MASK) >= ((DebugLevel_Chatty) & DebugLevel_MASK)))
77  {
78  WBDebugPrint("dumping file buf - %d lines\n", (int)pBuf->lLineCount);
79 
80  for(i1=0; i1 < (int)pBuf->lLineCount; i1++)
81  {
82  p1 = pBuf->ppLineBuf[i1 + 1];
83  if(!p1)
84  {
85  p1 = pBuf->ppLineBuf[i1] + strlen(pBuf->ppLineBuf[i1]);
86  }
87 
88  while(p1 > pBuf->ppLineBuf[i1] && (*(p1 - 1) == '\n' || *(p1 - 1) == '\r'))
89  {
90  p1--;
91  }
92 
93  if(p1 > pBuf->ppLineBuf[i1])
94  {
95  WBDebugPrint("%3d: \"%.*s\"..\n", i1, (int)(p1 - pBuf->ppLineBuf[i1]), pBuf->ppLineBuf[i1]);
96  }
97  else
98  {
99  WBDebugPrint("%3d: [empty]\n", i1);
100  }
101  }
102  }
103 }
104 #endif // NO_DEBUG
105 
106 file_help_buf_t *FBGetFileBuf(const char *szFileName)
107 {
108  int iFile;
109  file_help_buf_t *pRval;
110 
111  iFile = open(szFileName, O_RDONLY);
112  if(iFile < 0)
113  {
114  return NULL;
115  }
116 
117  pRval = FBGetFileBufViaHandle(iFile);
118  close(iFile);
119 
120  return pRval;
121 }
122 
124 {
125  file_help_buf_t *pRval;
126  long lFileSize;
127 
128  lFileSize = (long)lseek(iFile, 0, SEEK_END);
129  lseek(iFile, 0, SEEK_SET);
130 
131  if(lFileSize < 0)
132  {
133  return NULL;
134  }
135 
136  pRval = (file_help_buf_t *)WBAlloc(sizeof(*pRval) + ((lFileSize + 256 + 128) & 0xffffff00L));
137 
138  if(pRval)
139  {
140  bzero(pRval, sizeof(*pRval)); // this also takes care of pNext, pPrev
141 
142  pRval->lBufferSize = sizeof(*pRval) + ((lFileSize + 256 + 128) & 0xffffff00L);
143  pRval->lBufferCount = lFileSize;
144  pRval->iFlags = 0;
145 
146  if(lFileSize)
147  {
148  if(read(iFile, pRval->cData, lFileSize) != lFileSize)
149  {
150  WBFree(pRval);
151  pRval = NULL;
152  }
153  }
154  }
155 
156  return pRval;
157 }
158 
159 file_help_buf_t *FBGetFileBufFromBuffer(const char *pBuf, long cbBuf)
160 {
161  file_help_buf_t *pRval;
162 
163  if(cbBuf < 0)
164  {
165  return NULL;
166  }
167 
168  pRval = (file_help_buf_t *)WBAlloc(sizeof(*pRval) + ((cbBuf + 256 + 128) & 0xffffff00L));
169  if(pRval)
170  {
171  bzero(pRval, sizeof(*pRval)); // this also takes care of pNext, pPrev
172 
173  pRval->lBufferSize = sizeof(*pRval) + ((cbBuf + 256 + 128) & 0xffffff00L);
174  pRval->lBufferCount = cbBuf;
175  pRval->iFlags = 0;
176 
177  if(cbBuf && pBuf)
178  {
179  memcpy(pRval->cData, pBuf, cbBuf);
180  }
181  }
182 
183  return pRval;
184 }
185 
187 {
188  if(!pBuf)
189  {
190  return;
191  }
192 
193  if(pBuf->pNext)
194  {
195  FBDestroyFileBuf(pBuf->pNext); // recursively destroy (TODO: loop instead?)
196  pBuf->pNext = NULL;
197  }
198 
199  // TODO: ppLineBuf should be part of 'cData'. For now it's WBAlloc'd
200 
201  if(pBuf->ppLineBuf)
202  {
203  WBFree(pBuf->ppLineBuf);
204  }
205 
206  WBFree(pBuf);
207 }
208 
210 {
211  int i1, iLines;
212  const char *p1, /* *p2,*/ *pEnd;
213 
214  // TODO: ppLineBuf should be part of 'cData'. For now it's WBAlloc'd
215 
216  if(pBuf->ppLineBuf)
217  {
218  WBFree(pBuf->ppLineBuf);
219  pBuf->ppLineBuf = NULL;
220  pBuf->lLineCount = 0;
221  pBuf->lLineBufSize = 0;
222  }
223 
224  // count the lines first
225  for(i1=0, p1 = pBuf->cData, pEnd = pBuf->cData + pBuf->lBufferCount; p1 < pEnd; )
226  {
227 // NOTE: p2 not being used; commented out because of linux gcc warnings
228 // p2 = p1;
229  while(p1 < pEnd && *p1 != '\n')
230  {
231  p1++;
232  }
233  if(p1 < pEnd)
234  {
235  p1++; // skipping past the newline
236  }
237 
238  i1++; // this counts the last line regardless of the '\n' being there or not
239  // and works regardless of compiler behavior outside of a 'for' loop
240  }
241 
242  // now allocate memory and REALLY assign the pointers
243  iLines = i1;
244 
245  // TODO: ppLineBuf should be part of 'cData'. For now it's WBAlloc'd
246  // if I need room for more lines, attach another file_help_buf_t
247  // and chain it to 'pNext'
248 
249  pBuf->ppLineBuf = (char **)WBAlloc((iLines + 1) * sizeof(char **));
250  if(!pBuf->ppLineBuf)
251  {
252  return -1;
253  }
254 
255  pBuf->lLineBufSize = (iLines + 1) * sizeof(char **);
256  pBuf->lLineCount = iLines;
257 
258  for(i1=0, p1 = pBuf->cData, pEnd = pBuf->cData + pBuf->lBufferCount; p1 < pEnd && i1 < iLines; i1++)
259  {
260  pBuf->ppLineBuf[i1] = (char *)p1;
261 
262  while(p1 < pEnd && *p1 != '\n')
263  {
264  p1++;
265  }
266  if(p1 < pEnd)
267  {
268  p1++; // skipping past the newline
269  }
270  }
271 
272  pBuf->ppLineBuf[iLines] = NULL; // always (to mark "the end")
273 
274 #ifndef NO_DEBUG
275  FBDumpParsedFileBuf(pBuf);
276 #endif // NO_DEBUG
277 
278  return 0;
279 }
280 
281 int FBWriteFileBuf(const char *szFileName, const file_help_buf_t *pBuf)
282 {
283  int iFile, iRval;
284 
285  if(!pBuf)
286  {
287  return -1;
288  }
289 
290  iFile = open(szFileName, O_CREAT | O_TRUNC | O_RDWR, 0666); // always create with mode '666' (umask should apply)
291 
292  if(iFile < 0)
293  {
294  return -1;
295  }
296 
297  iRval = FBWriteFileBufHandle(iFile, pBuf);
298  close(iFile);
299 
300  return(iRval);
301 }
302 
303 int FBWriteFileBufHandle(int iFile, const file_help_buf_t *pBuf)
304 {
305  if(!pBuf)
306  {
307  return -1;
308  }
309 
310  lseek(iFile, 0, SEEK_SET); // rewind
311 
312  if(pBuf->lBufferCount > 0)
313  {
314  if(write(iFile, pBuf->cData, pBuf->lBufferCount)
315  != pBuf->lBufferCount)
316  {
317  return -2;
318  }
319  }
320 
321  ftruncate(iFile, pBuf->lBufferCount); // ensure file size is correct
322 
323  ((file_help_buf_t *)pBuf)->iFlags &= ~file_help_buf_dirty;
324  return pBuf->lBufferCount;
325 }
326 
327 static int SanityCheckFileBuf(file_help_buf_t *pBuf, const char *szFunction)
328 {
329  if(!pBuf || pBuf->lBufferSize < sizeof(*pBuf)
330  || pBuf->lBufferCount > pBuf->lBufferSize + sizeof(pBuf->cData) - sizeof(*pBuf))
331  {
332  WB_ERROR_PRINT("%s - error in file buf - %p %ld %ld (%ld)\n",
333  szFunction,
334  pBuf,
335  pBuf ? pBuf->lBufferSize : 0L,
336  pBuf ? pBuf->lBufferCount : 0L,
337  pBuf ? pBuf->lBufferSize + sizeof(pBuf->cData) - sizeof(*pBuf) : 0L);
338  return -1;
339  }
340 
341  return 0;
342 }
343 
344 static int InternalGrowFileBuf(file_help_buf_t **ppBuf, long cbOffset, long cbData)
345 { // return value is negative on error
346  long lNewSize, lActualBufSize;
347  void *pNew;
348 
349  if(!ppBuf)
350  {
351  WB_ERROR_PRINT("NULL 'ppBuf' in InternalGrowFileBuf\n");
352  return -1;
353  }
354 
355  if(SanityCheckFileBuf(*ppBuf, __FUNCTION__) < 0)
356  {
357  return -1;
358  }
359 
360  lActualBufSize = (*ppBuf)->lBufferSize - sizeof(**ppBuf); // exclude 'cData' for the moment
361 
362  if(cbOffset > (*ppBuf)->lBufferCount)
363  {
364  lNewSize = cbOffset + cbData;
365  }
366  else
367  {
368  lNewSize = (*ppBuf)->lBufferCount + cbData;
369  }
370 
371  if(lActualBufSize >= lNewSize)
372  {
373  return 0; // no re-allocation
374  }
375 
376  lNewSize = (lNewSize + sizeof(*ppBuf) + 256 + 128) & 0xffffff00L;
377 
378  WB_DEBUG_PRINT(DebugLevel_Excessive, "TEMPORARY: reallocating - %d %d %d\n",
379  (int)(*ppBuf)->lBufferSize, (int)lActualBufSize, (int)lNewSize);
380 
381  pNew = (char *)WBReAlloc(*ppBuf, lNewSize);
382 
383  if(pNew)
384  {
385  *ppBuf = (file_help_buf_t *)pNew;
386 
387  (*ppBuf)->lBufferSize = lNewSize;
388 
389  // it's been my experience that the additional allocated memory may be uninitialized,
390  // so zero it out, starting with the end of valid data.
391 
392  lActualBufSize = lNewSize - sizeof(**ppBuf) + sizeof((*ppBuf)->cData);
393 
394  if((*ppBuf)->lBufferCount < lActualBufSize) // just in case, test for this
395  {
396  memset((char *)((*ppBuf)->cData) + (*ppBuf)->lBufferCount, 0, lActualBufSize - (*ppBuf)->lBufferCount);
397  }
398 
399  return lNewSize; // returns the NEW size
400  }
401 
402  WB_ERROR_PRINT("error re-allocating file buf\n");
403  return -2; // error re-allocating
404 }
405 
406 void FBInsertIntoFileBuf(file_help_buf_t **ppBuf, long cbOffset, const void *pData, long cbData)
407 {
408  // insert 'cbData' bytes of 'pData' at 'cbOffset' within *ppBuf, possibly re-allocating the
409  // buffer and re-assigning it to *ppBuf.
410 
411  if(InternalGrowFileBuf(ppBuf, cbOffset, cbData) < 0)
412  {
413  return;
414  }
415 
416  if((*ppBuf)->lBufferCount < cbOffset) // insert PAST THE END of the buffer
417  {
418  memset((char *)((*ppBuf)->cData) + (*ppBuf)->lBufferCount, '\n', cbOffset - (*ppBuf)->lBufferCount); // pad with newlines
419  (*ppBuf)->lBufferCount = cbOffset; // since I effectively increased the data count to THIS
420  }
421  else if((*ppBuf)->lBufferCount > cbOffset) // insert into the middle of the buffer
422  {
423  memmove((char *)((*ppBuf)->cData) + cbOffset + cbData, (char *)((*ppBuf)->cData) + cbOffset, (*ppBuf)->lBufferCount - cbOffset);
424  }
425 
426  memcpy((char *)((*ppBuf)->cData) + cbOffset, pData, cbData);
427  (*ppBuf)->lBufferCount += cbData; // new buffer data count
428 
429  SanityCheckFileBuf(*ppBuf, __FUNCTION__);
430 
431  (*ppBuf)->iFlags |= file_help_buf_dirty;
432 }
433 
434 void FBDeleteFromFileBuf(file_help_buf_t *pBuf, long cbOffset, long cbDelFrom)
435 {
436  // remove 'cbDelFrom' bytes of data from 'pBuf' starting at offset 'cbOffset'
437 
438  if(cbOffset >= pBuf->lBufferCount || cbDelFrom <= 0)
439  {
440  return;
441  }
442 
443  if(cbOffset + cbDelFrom > pBuf->lBufferCount)
444  {
445  cbDelFrom = pBuf->lBufferCount - cbOffset;
446  }
447  else if(cbOffset + cbDelFrom < pBuf->lBufferCount)
448  {
449  memcpy(pBuf->cData + cbOffset, pBuf->cData + cbOffset + cbDelFrom,
450  pBuf->lBufferCount - cbOffset - cbDelFrom);
451  }
452 
453  pBuf->lBufferCount -= cbDelFrom;
454  memset(pBuf->cData + pBuf->lBufferCount, 0, cbDelFrom); // zero out what WAS there
455 
456  SanityCheckFileBuf(pBuf, __FUNCTION__);
457 
458  pBuf->iFlags |= file_help_buf_dirty;
459 }
460 
461 void FBInsertLineIntoFileBuf(file_help_buf_t **ppBuf, long lLineNum, const char *szLine)
462 {
463  int i1, i2;
464  long lOffset;
465  char *pDest;
466 
467  // if 'lLineNum' exceeds the current line count, add lines until it matches
468 
469  i2 = strlen(szLine);
470  while(i2 && (szLine[i2 - 1] == '\n' || szLine[i2 - 1] == '\r')) // trim <return> <newline> [will add later]
471  {
472  i2--; // I'll always need to have a newline, so don't include the newline if it already has one
473  }
474 
475  if(!(*ppBuf)->ppLineBuf) // make sure it has been parsed, FIRST
476  {
477  FBParseFileBuf(*ppBuf);
478  if(!(*ppBuf)->ppLineBuf)
479  {
480  WB_ERROR_PRINT("%s - no line buffer!\n", __FUNCTION__);
481  return; // can't (some kind of error)
482  }
483  }
484 
485  i1 = lLineNum - (*ppBuf)->lLineCount; // lLineNum is zero-based
486  if(i1 < 0) // line number is valid
487  {
488  i1 = 0;
489 
490  pDest = (*ppBuf)->ppLineBuf[lLineNum];
491  if(!pDest)
492  {
493  pDest = (*ppBuf)->cData + (*ppBuf)->lBufferCount;
494  }
495  }
496  else
497  {
498  pDest = (*ppBuf)->cData + (*ppBuf)->lBufferCount + i1; // TODO: how do I properly deal with 'in between' lines?
499  }
500 
501  lOffset = (char *)pDest - (char *)((*ppBuf)->cData); // byte offset of the insertion point
502 
503  FBInsertIntoFileBuf(ppBuf, lOffset, szLine, i2); // make room for 2 extra chars, hence 'i2 + 2'
504  FBInsertIntoFileBuf(ppBuf, lOffset + i2, "\r\n", 2); // the trailing CRLF
505 
506  if(i1 > 0)
507  {
508  // fix added newlines, and make sure that 'szLine' ends with a newline as well
509 
510  WB_DEBUG_PRINT(DebugLevel_Chatty, "%s inserting %d blank lines\n", __FUNCTION__, i1);
511  pDest = (*ppBuf)->cData + lOffset; // re-assign since 'ppBuf' may have changed
512 
513  while(i1 > 0) // inserting newline chars for 'in between' blank lines
514  {
515  *(pDest - i1) = '\n';
516  i1--;
517  }
518  }
519 
520  // re-parse file
521 
522  FBParseFileBuf(*ppBuf); // re-parse at the end
523 }
524 
525 void FBDeleteLineFromFileBuf(file_help_buf_t *pBuf, long lLineNum)
526 {
527 // int i1, i2;
528  long lOffset, lEndOffset;
529 // char *pDest;
530 
531  if(!pBuf->ppLineBuf)
532  {
533  FBParseFileBuf(pBuf);
534  }
535 
536  // if 'lLineNum' exceeds the current line count, just return
537 
538  if(!pBuf->ppLineBuf || lLineNum > pBuf->lLineCount)
539  {
540  return;
541  }
542  if(lLineNum == pBuf->lLineCount)
543  {
544  lEndOffset = pBuf->lBufferCount;
545  }
546  else
547  {
548  lEndOffset = (char *)pBuf->ppLineBuf[lLineNum + 1] - (char *)(pBuf->cData);
549  }
550 
551  lOffset = (char *)pBuf->ppLineBuf[lLineNum] - (char *)(pBuf->cData);
552 
553  if(lOffset < lEndOffset)
554  {
555  FBDeleteFromFileBuf(pBuf, lOffset, lEndOffset - lOffset);
556  }
557 
558  FBParseFileBuf(pBuf);
559 }
560 
561 void FBReplaceLineInFileBuf(file_help_buf_t **ppBuf, long lLineNum, const char *szLine)
562 {
563  // TODO: make this more efficient
564  FBDeleteLineFromFileBuf(*ppBuf, lLineNum);
565  FBInsertLineIntoFileBuf(ppBuf, lLineNum, szLine);
566 }
567 
568 
569 // FILE SYSTEM INDEPENDENT FILE AND DIRECTORY UTILITIES
570 // UNIX/LINUX versions - TODO windows versions?
571 
572 size_t WBReadFileIntoBuffer(const char *szFileName, char **ppBuf)
573 {
574 off_t cbLen;
575 size_t cbF;
576 int cb1, iChunk;
577 char *pBuf;
578 int iFile;
579 
580 
581  if(!ppBuf)
582  {
583  return (size_t)-1;
584  }
585 
586  iFile = open(szFileName, O_RDONLY); // open read only (assume no locking for now)
587 
588  if(iFile < 0)
589  {
590  return (size_t)-1;
591  }
592 
593  // how long is my file?
594 
595  cbLen = (unsigned long)lseek(iFile, 0, SEEK_END); // location of end of file
596 
597  if(cbLen == (off_t)-1)
598  {
599  *ppBuf = NULL; // make sure
600  }
601  else
602  {
603  lseek(iFile, 0, SEEK_SET); // back to beginning of file
604 
605  *ppBuf = pBuf = WBAlloc(cbLen + 1);
606 
607  if(!pBuf)
608  {
609  cbLen = (off_t)-1; // to mark 'error'
610  }
611  else
612  {
613  cbF = cbLen;
614 
615  while(cbF > 0)
616  {
617  iChunk = 1048576; // 1MByte at a time
618 
619  if(iChunk > cbF)
620  {
621  iChunk = (int)cbF;
622  }
623 
624  cb1 = read(iFile, pBuf, iChunk);
625 
626  if(cb1 == -1)
627  {
628  if(errno == EAGAIN) // allow this
629  {
630  WBDelay(100);
631  continue; // for now just do this
632  }
633 
634  cbLen = -1;
635  break;
636  }
637  else if(cb1 != iChunk) // did not read enough bytes
638  {
639  iChunk = cb1; // for now
640  }
641 
642  cbF -= iChunk;
643  pBuf += iChunk;
644  }
645  }
646  }
647 
648  close(iFile);
649 
650  return (size_t) cbLen;
651 }
652 
653 int WBWriteFileFromBuffer(const char *szFileName, const char *pBuf, size_t cbBuf)
654 {
655 int iFile, iRval, iChunk;
656 
657 
658  if(!pBuf)
659  {
660  return -1;
661  }
662 
663  iFile = open(szFileName, O_CREAT | O_TRUNC | O_RDWR, 0666); // always create with mode '666' (umask should apply)
664 
665  if(iFile < 0)
666  {
667  return -1;
668  }
669 
670  while(cbBuf > 0)
671  {
672  // write chunks of 1Mb or size remaining
673 
674  iChunk = 1048576;
675  if(iChunk > cbBuf)
676  {
677  iChunk = (int)cbBuf;
678  }
679 
680  iRval = write(iFile, pBuf, iChunk);
681 
682  if(iRval < 0)
683  {
684  if(errno == EAGAIN)
685  {
686  WBDelay(100);
687 
688  // TODO: time limit? for now, no
689 
690  continue; // try again
691  }
692 
693  close(iFile);
694  return -1; // error
695  }
696  else if(iRval != iChunk) // TODO: allow this??
697  {
698  WBDebugPrint("TEMPORARY: writing file, only wrote %d of %d bytes\n", iRval, iChunk);
699  iChunk = iRval;
700  }
701 
702  pBuf += iChunk;
703  cbBuf -= iChunk;
704  }
705 
706  iRval = 0; // at this point, success!
707 
708  close(iFile);
709 
710  return iRval;
711 }
712 
713 int WBReplicateFilePermissions(const char *szProto, const char *szTarget)
714 {
715 struct stat sb;
716 int iRval = 0;
717 
718  iRval = stat(szProto, &sb); // TODO: lstat for symlink?
719  if(!iRval)
720  {
721  // TODO: chflags?
722  // TODO: what if it's a symlink?
723  iRval = chmod(szTarget, sb.st_mode & 0777); // only set the rwx permissions, and ignore others
724  if(!iRval)
725  {
726  if(geteuid() == 0 || getuid() == sb.st_uid) // only do this if owner matches or I'm root
727  {
728  iRval = chown(szTarget, sb.st_uid, sb.st_gid);
729  if(iRval < 0 && geteuid() != 0)
730  {
731  iRval = chown(szTarget, -1, sb.st_gid); // don't change the user
732 
733  if(iRval < 0)
734  {
735  // don't bother changing anything - just warn??
736  iRval = 0; // for now...
737  }
738  }
739  }
740  }
741  }
742 
743  return iRval;
744 }
745 
747 {
748 char *pRval = WBAlloc(MAXPATHLEN + 2);
749 int i1;
750 
751  if(pRval)
752  {
753  if(!getcwd(pRval, MAXPATHLEN))
754  {
755  WBFree(pRval);
756  pRval = NULL;
757  }
758  }
759 
760  // this function will always return something that ends in '/' (except on error)
761 
762  if(pRval)
763  {
764  i1 = strlen(pRval);
765 
766  if(i1 > 0 && pRval[i1 - 1] != '/')
767  {
768  pRval[i1] = '/';
769  pRval[i1 + 1] = 0;
770  }
771  }
772 
773  return pRval;
774 }
775 
776 int WBIsDirectory(const char *szFileName)
777 {
778 int bRval = 0;
779 
780 #ifdef WIN32
781 WIN32_FIND_DATA fd;
782 HANDLE hFF;
783 
784  hFF = FindFirstFile(szFileName, &fd);
785 
786  if(hFF != INVALID_HANDLE_VALUE)
787  {
788  bRval = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
789  FindClose(hFF);
790  }
791 
792 #else // WIN32
793 struct stat sF;
794 
795  if(!stat(szFileName, &sF)) // NOTE: 'stat' returns info about symlink targets, not the link itself
796  bRval = S_ISDIR(sF.st_mode);
797 
798 #endif // WIN32
799 
800  return(bRval);
801 }
802 
803 char *WBGetCanonicalPath(const char *szFileName)
804 {
805 char *pTemp, *p1, *p2, *p3, *p4, *pRval = NULL;
806 struct stat sF;
807 
808  pTemp = WBCopyString(szFileName);
809 
810  if(!pTemp)
811  {
812  return NULL;
813  }
814 
815  // step 1: eliminate // /./
816 
817  p1 = pTemp;
818  while(*p1 && p1[1])
819  {
820  if(*p1 == '/' && p1[1] == '/')
821  {
822  memmove(p1, p1 + 1, strlen(p1 + 1) + 1);
823  }
824  else if(*p1 == '/' && p1[1] == '.' && p1[2] == '/')
825  {
826  memmove(p1, p1 + 2, strlen(p1 + 2) + 1);
827  }
828  else
829  {
830  p1++;
831  }
832  }
833 
834  // step 2: resolve each portion of the path, deal with '~' '.' '..', build new path.
835 
836  if(*pTemp == '~' && (pTemp[1] == '/' || !pTemp[1])) // first look for '~' at the beginning (only allowed there)
837  {
838  p1 = getenv("HOME");
839  if(!p1 || !*p1) // no home directory?
840  {
841  *pTemp = '.'; // for now change it to '.'
842  }
843  else
844  {
845  p3 = WBCopyString(p1);
846  if(!p3)
847  {
848  WBFree(pTemp);
849  return NULL;
850  }
851 
852  if(p3[strlen(p3) - 1] != '/')
853  {
854  WBCatString(&p3, "/");
855  }
856 
857  p2 = pTemp + 1;
858  if(*p2 == '/')
859  {
860  p2++; // already have an ending / on the path
861  }
862 
863  if(*p2)
864  {
865  WBCatString(&p3, p2);
866  }
867 
868  WBFree(pTemp);
869  pTemp = p3;
870  }
871  }
872 
873  p1 = pTemp;
874  while(*p1)
875  {
876  p2 = strchr(p1, '/');
877  if(!p2)
878  {
879  if(*p1 == '.') // check for ending in '.' or '..' and add a '/' so I can handle it correctly
880  {
881  if((p1[1] == '.' && !p1[2]) || !p1[1])
882  {
883  p2 = pTemp; // temporary
884  WBCatString(&pTemp, "/");
885 
886  p1 = (p1 - p2) + pTemp; // restore relative pointer
887 
888  WB_ERROR_PRINT("TEMPORARY: %s %s\n", p1, pTemp);
889 
890  continue; // let's do this again, properly
891  }
892  }
893 
894  // no more paths, so this is "the name".
895  if(!pRval) // no existing path, use CWD
896  {
897  pRval = WBGetCurrentDirectory();
898 
899  if(!pRval)
900  {
901  break;
902  }
903  }
904 
905  WBCatString(&pRval, p1);
906 
907  break;
908  }
909  else if(p2 == p1)
910  {
911  pRval = WBCopyString("/");
912  }
913  else
914  {
915  if(!pRval)
916  {
917  pRval = WBGetCurrentDirectory();
918  if(!pRval)
919  {
920  break;
921  }
922  }
923 
924  // when I assemble these paths together, deal with '..' and
925  // symbolic links. Check for cyclic paths.
926 
927  if(p2 - p1 == 1 && p1[0] == '.') // the ./ path
928  {
929  p1 = p2 + 1; // just ignore this part
930  continue;
931  }
932  else if(p2 - p1 == 2 && p1[0] == '.' && p1[1] == '.') // the ../ path
933  {
934  p1 = p2 + 1; // I need to fix the path while ignoring the '../' part
935 
936  p3 = pRval + strlen(pRval) - 1; // NOTE: pRval ends in '/' and I want the one BEFORE that
937  while(p3 > pRval)
938  {
939  if(*(p3 - 1) == '/')
940  {
941  *p3 = 0;
942  break;
943  }
944 
945  p3--;
946  }
947 
948  if(p3 <= pRval) // did not find a preceding '/' - this is an error
949  {
950  WB_ERROR_PRINT("%s:%d - did not find preceding '/' - %s\n", __FUNCTION__, __LINE__, pRval);
951 
952  WBFree(pRval);
953  pRval = NULL;
954 
955  break;
956  }
957 
958  continue;
959  }
960 
961  // TEMPORARY: just copy as-is to test basic logic
962 
963  WBCatStringN(&pRval, p1, p2 - p1 + 1); // include the '/' at the end
964  if(!pRval)
965  {
966  WB_ERROR_PRINT("%s:%d - WBCatStringN returned NULL pointer\n", __FUNCTION__, __LINE__);
967 
968  break;
969  }
970 
971  // see if this is a symbolic link. exclude testing '/'
972 
973  p3 = pRval + strlen(pRval) - 1;
974  if(p3 > pRval)
975  {
976  *p3 = 0; // temporary
977  if(lstat(pRval, &sF)) // get the file 'stat' and see if we're a symlink
978  {
979  // error, does not exist? - leave it 'as-is' for now
980  *p3 = '/'; // restore it
981  }
982  else if(S_ISDIR(sF.st_mode)) // an actual directory - remains as-is
983  {
984  // don't do anything except restore the '/'
985  *p3 = '/'; // restore it
986  }
987  else if(S_ISLNK(sF.st_mode)) // symlink
988  {
989  // now I get to put the symlink contents "in place". If the symlink is
990  // relative to the current directory, I'll want that.
991 
992  p4 = (char *)WBAlloc(MAXPATHLEN + 2);
993 
994  if(!p4)
995  {
996  WB_ERROR_PRINT("%s:%d - not enough memory for buffer\n", __FUNCTION__, __LINE__);
997 
998  WBFree(pRval);
999  pRval = NULL;
1000  break;
1001  }
1002  else
1003  {
1004  int iLen = readlink(pRval, p4, MAXPATHLEN);
1005 
1006  if(iLen <= 0)
1007  {
1008  WB_ERROR_PRINT("%s:%d - readlink returned %d for %s\n", __FUNCTION__, __LINE__, iLen, pRval);
1009 
1010  WBFree(p4);
1011  WBFree(pRval);
1012  pRval = NULL;
1013 
1014  break;
1015  }
1016 
1017  p4[iLen] = 0; // assume < MAXPATHLEN for now...
1018  if(p4[0] == '/') // it's an absolute path
1019  {
1020  WBFree(pRval);
1021  pRval = p4;
1022  }
1023  else
1024  {
1025  while(p3 > pRval && *(p3 - 1) != '/') // scan back for a '/'
1026  {
1027  p3--;
1028  }
1029 
1030  *p3 = 0;
1031  WBCatString(&pRval, p4); // sub in the relative path
1032  WBFree(p4);
1033  }
1034 
1035  if(!WBIsDirectory(pRval)) // must be a directory!
1036  {
1037  WB_ERROR_PRINT("%s:%d - %s not a directory\n", __FUNCTION__, __LINE__, pRval);
1038 
1039  WBFree(pRval);
1040  pRval = NULL;
1041  break; // this is an error
1042  }
1043  else
1044  {
1045  WBCatString(&pRval, "/");
1046 
1047  if(pRval)
1048  {
1049  p4 = WBGetCanonicalPath(pRval); // recurse
1050 
1051  WBFree(pRval);
1052  pRval = p4; // new canonical version of symlink path
1053  }
1054 
1055  if(!pRval)
1056  {
1057  WB_ERROR_PRINT("%s:%d - NULL pRval\n", __FUNCTION__, __LINE__);
1058 
1059  break;
1060  }
1061  }
1062  }
1063  }
1064  }
1065  }
1066 
1067  p1 = p2 + 1;
1068  }
1069 
1070  // if the resulting path is a symbolic link, fix it
1071  if(pRval)
1072  {
1073  p1 = pRval + strlen(pRval) - 1;
1074 
1075  if(p1 > pRval && *p1 != '/') // does not end in a slash, so it should be a file...
1076  {
1077  while(p1 > pRval && *(p1 - 1) != '/')
1078  {
1079  p1--;
1080  }
1081 
1082  if(!lstat(pRval, &sF)) // get the file 'stat' and see if we're a symlink (ignore errors)
1083  {
1084  if(S_ISDIR(sF.st_mode)) // an actual directory - end with a '/'
1085  {
1086  WBCatString(&pRval, "/"); // add ending '/'
1087  }
1088  else if(S_ISLNK(sF.st_mode)) // symlink
1089  {
1090  // now I get to put the symlink contents "in place". If the symlink is
1091  // relative to the current directory, I'll want that.
1092 
1093  p4 = (char *)WBAlloc(MAXPATHLEN + 2);
1094 
1095  if(!p4)
1096  {
1097  WB_ERROR_PRINT("%s:%d - not enough memory\n", __FUNCTION__, __LINE__);
1098 
1099  // TODO: assign pRval to NULL ?
1100  }
1101  else
1102  {
1103  int iLen = readlink(pRval, p4, MAXPATHLEN);
1104 
1105  if(iLen <= 0)
1106  {
1107  WB_ERROR_PRINT("%s:%d - readlink returned %d for %s\n", __FUNCTION__, __LINE__, iLen, pRval);
1108 
1109  WBFree(p4);
1110  WBFree(pRval);
1111  pRval = NULL;
1112  }
1113  else
1114  {
1115  p4[iLen] = 0; // assume < MAXPATHLEN for now...
1116  if(p4[0] == '/') // it's an absolute path
1117  {
1118  WBFree(pRval); // new path for old
1119  pRval = p4;
1120  }
1121  else
1122  {
1123  p3 = pRval + strlen(pRval); // I won't be ending in '/' for this part so don't subtract 1
1124  while(p3 > pRval && *(p3 - 1) != '/') // scan back for the '/' in symlink's original path
1125  {
1126  p3--;
1127  }
1128 
1129  *p3 = 0;
1130  WBCatString(&pRval, p4); // sub in the relative path
1131  WBFree(p4);
1132  }
1133 
1134  if(pRval && WBIsDirectory(pRval)) // is the result a directory?
1135  {
1136  WBCatString(&pRval, "/");
1137  }
1138 
1139  if(pRval)
1140  {
1141  p4 = WBGetCanonicalPath(pRval); // recurse to make sure I'm canonical (deal with '..' and '.' and so on)
1142 
1143  WBFree(pRval);
1144  pRval = p4; // new canonical version of symlink path
1145  }
1146  }
1147  }
1148  }
1149  }
1150  }
1151  }
1152 
1153  if(pTemp)
1154  {
1155  WBFree(pTemp);
1156  pTemp = NULL; // by convention
1157  }
1158 
1159  if(!pRval)
1160  {
1161  WB_ERROR_PRINT("%s:%d - returning NULL\n", __FUNCTION__, __LINE__);
1162  }
1163 
1164  return pRval;
1165 }
1166 
1167 // reading directories in a system-independent way
1168 
1169 typedef struct __DIRLIST__
1170 {
1171  const char *szPath, *szNameSpec;
1172 #ifdef WIN32 // also true for WIN64
1173  WIN32_FIND_DATA fd;
1174  HANDLE hFF;
1175 #else // !WIN32
1176  DIR *hD;
1177  struct stat sF;
1178  union
1179  {
1180  char cde[sizeof(struct dirent) + NAME_MAX + 2];
1181  struct dirent de;
1182  };
1183 #endif // WIN32 or !WIN32 - that is the question
1184 // actual composite 'search name' follows
1185 } DIRLIST;
1186 
1187 void *WBAllocDirectoryList(const char *szDirSpec)
1188 {
1189 DIRLIST *pRval;
1190 char *p1, *p2;
1191 int iLen, nMaxLen;
1192 char *pBuf;
1193 
1194  if(!szDirSpec || !*szDirSpec)
1195  {
1196  WB_WARN_PRINT("WARNING - %s - invalid directory (NULL or empty)\n", __FUNCTION__);
1197  return NULL;
1198  }
1199 
1200  iLen = strlen(szDirSpec);
1201  nMaxLen = iLen + 32;
1202 
1203  pBuf = WBAlloc(nMaxLen);
1204  if(!pBuf)
1205  {
1206  WB_ERROR_PRINT("ERROR - %s - Unable to allocate memory for buffer size %d\n", __FUNCTION__, nMaxLen);
1207  return NULL;
1208  }
1209 
1210  if(szDirSpec[0] == '/') // path starts from the root
1211  {
1212  memcpy(pBuf, szDirSpec, iLen + 1);
1213  }
1214  else // for now, force a path of './' to be prepended to path spec
1215  {
1216  pBuf[0] = '.';
1217  pBuf[1] = '/';
1218 
1219  memcpy(pBuf + 2, szDirSpec, iLen + 1);
1220  iLen += 2;
1221  }
1222 
1223  // do a reverse scan until I find a '/'
1224  p1 = ((char *)pBuf) + iLen;
1225  while(p1 > pBuf && *(p1 - 1) != '/')
1226  {
1227  p1--;
1228  }
1229 
1230 // WB_ERROR_PRINT("TEMPORARY - \"%s\" \"%s\" \"%s\"\n", pBuf, p1, szDirSpec);
1231 
1232  if(p1 > pBuf)
1233  {
1234  // found, and p1 points PAST the '/'. See if it ends in '/' or if there are wildcards present
1235  if(!*p1) // name ends in '/'
1236  {
1237  if(p1 == (pBuf + 1) && *pBuf == '/') // root dir
1238  {
1239  p1++;
1240  }
1241  else
1242  {
1243  *(p1 - 1) = 0; // trim the final '/'
1244  }
1245 
1246  p1[0] = '*';
1247 #ifdef WIN32
1248  p1[1] = '.';
1249  p1[2] = '*';
1250  p1[3] = 0;
1251 #else // !WIN32
1252  p1[1] = 0;
1253 #endif // WIN32
1254  }
1255  else if(strchr(p1, '*') || strchr(p1, '?'))
1256  {
1257  if(p1 == (pBuf + 1) && *pBuf == '/') // root dir
1258  {
1259  memmove(p1 + 1, p1, strlen(p1) + 1);
1260  *(p1++) = 0; // after this, p1 points to the file spec
1261  }
1262  else
1263  {
1264  *(p1 - 1) = 0; // p1 points to the file spec
1265  }
1266  }
1267  else if(WBIsDirectory(pBuf)) // entire name is a directory
1268  {
1269  // NOTE: root directory should NEVER end up here
1270 
1271  p1 += strlen(p1);
1272  *(p1++) = 0; // end of path (would be '/')
1273  p1[0] = '*';
1274 #ifdef WIN32
1275  p1[1] = '.';
1276  p1[2] = '*';
1277  p1[3] = 0;
1278 #else // !WIN32
1279  p1[1] = 0;
1280 #endif // WIN32
1281  }
1282  else
1283  {
1284  WB_WARN_PRINT("TEMPORARY: I am confused, %s %s\n", pBuf, p1);
1285  }
1286  }
1287  else
1288  {
1289  // this should never happen if I'm always prepending a './'
1290  // TODO: make this more consistent, maybe absolute path?
1291 
1292  WB_WARN_PRINT("TEMPORARY: should not happen, %s %s\n", pBuf, p1);
1293 
1294  if(strchr(pBuf, '*') || strchr(pBuf, '?')) // wildcard spec
1295  {
1296  p1 = (char *)pBuf + 1; // make room for zero byte preceding dir spec
1297  memmove(pBuf, p1, iLen + 1);
1298  *pBuf = 0; // since it's the current working dir just make it a zero byte (empty string)
1299  }
1300  else if(WBIsDirectory(pBuf))
1301  {
1302  p1 = (char *)pBuf + iLen;
1303  *(p1++) = 0; // end of path (would be '/')
1304  p1[0] = '*';
1305 #ifdef WIN32
1306  p1[2] = '*';
1307  p1[3] = 0;
1308 #else // !WIN32
1309  p1[1] = 0;
1310 #endif // WIN32
1311  }
1312  }
1313 
1314  pRval = WBAlloc(sizeof(DIRLIST) + iLen + strlen(p1) + 2);
1315 
1316  if(pRval)
1317  {
1318  pRval->szPath = pBuf;
1319  pRval->szNameSpec = p1;
1320 
1321  p2 = (char *)(pRval + 1);
1322  strcpy(p2, pBuf);
1323  p2 += strlen(p2);
1324  *(p2++) = '/';
1325  strcpy(p2, p1);
1326  p1 = (char *)(pRval + 1);
1327 
1328 #ifdef WIN32
1329  pRval->hFF = FindFirstFile(p2, &(pRval->fd))
1330  if(pRval->hFF == INVALID_HANDLE_VALUE)
1331  {
1332  WBFree(pBuf);
1333  WBFree(pRval);
1334 
1335  pRval = NULL;
1336  }
1337 #else // !WIN32
1338  pRval->hD = opendir(pBuf);
1339 
1340 // WB_ERROR_PRINT("TEMPORARY - opendir for %s returns %p\n", pBuf, pRval->hD);
1341 
1342  if(pRval->hD == NULL)
1343  {
1344  WB_WARN_PRINT("WARNING - %s - Unable to open dir \"%s\", errno=%d\n", __FUNCTION__, pBuf, errno);
1345 
1346  WBFree(pBuf);
1347  WBFree(pRval);
1348 
1349  pRval = NULL;
1350  }
1351 #endif // WIN32,!WIN32
1352  }
1353  else
1354  {
1355  WB_ERROR_PRINT("ERROR - %s - Unable to allocate memory for DIRLIST\n", __FUNCTION__);
1356  WBFree(pBuf); // no need to keep this around
1357  }
1358 
1359  return pRval;
1360 }
1361 
1362 void WBDestroyDirectoryList(void *pDirectoryList)
1363 {
1364  if(pDirectoryList)
1365  {
1366  DIRLIST *pD = (DIRLIST *)pDirectoryList;
1367 
1368 #ifdef WIN32
1369  if(pD->hFF != INVALID_HANDLE_VALUE)
1370  {
1371  FindClose(pD->hFF);
1372  }
1373 #else // !WIN32
1374  if(pD->hD)
1375  {
1376  closedir(pD->hD);
1377  }
1378 #endif // WIN32,!WIN32
1379  if(pD->szPath)
1380  {
1381  WBFree((void *)(pD->szPath));
1382  }
1383 
1384  WBFree(pDirectoryList);
1385  }
1386 }
1387 
1388 // returns < 0 on error, > 0 on EOF, 0 for "found something"
1389 
1390 int WBNextDirectoryEntry(void *pDirectoryList, char *szNameReturn,
1391  int cbNameReturn, unsigned long *pdwModeAttrReturn)
1392 {
1393 #ifdef WIN32
1394 #else // WIN32
1395 struct dirent *pD;
1396 struct stat sF;
1397 #endif // WIN32
1398 char *p1, *pBuf;
1399 //static char *p2; // temporary
1400 int iRval = 1; // default 'EOF'
1401 DIRLIST *pDL = (DIRLIST *)pDirectoryList;
1402 
1403 
1404  if(!pDirectoryList)
1405  {
1406  return -1;
1407  }
1408 
1409  // TODO: improve this, maybe cache buffer or string length...
1410  pBuf = WBAlloc(strlen(pDL->szPath) + 8 + NAME_MAX);
1411 
1412  if(!pBuf)
1413  {
1414  return -2;
1415  }
1416 
1417  strcpy(pBuf, pDL->szPath);
1418  p1 = pBuf + strlen(pBuf);
1419  if(p1 > pBuf && *(p1 - 1) != '/') // it does not already end in /
1420  {
1421  *(p1++) = '/'; // for now assume this
1422  *p1 = 0; // by convention
1423  }
1424 
1425 #ifdef WIN32
1426 
1427  // for WIN32, copy 'previous' data first, then 'FindNextFile'. On EOF mark
1428  // as EOF so that next call will detect it.
1429 
1430 #else // !WIN32
1431 
1432  if(pDL->hD)
1433  {
1434  while((pD = readdir(pDL->hD))
1435  != NULL)
1436  {
1437  // skip '.' and '..'
1438  if(pD->d_name[0] == '.' &&
1439  (!pD->d_name[1] ||
1440  (pD->d_name[1] == '.' && !pD->d_name[2])))
1441  {
1442 // WB_ERROR_PRINT("TEMPORARY: skipping %s\n", pD->d_name);
1443  continue; // no '.' or '..'
1444  }
1445 
1446  strcpy(p1, pD->d_name);
1447 
1448  if(!lstat(pBuf, &sF)) // 'lstat' returns data about a file, and if it's a symlink, returns info about the link itself
1449  {
1450  if(!fnmatch(pDL->szNameSpec, p1, 0/*FNM_PERIOD*/)) // 'tbuf2' is my pattern
1451  {
1452  iRval = 0;
1453 
1454  if(pdwModeAttrReturn)
1455  {
1456  *pdwModeAttrReturn = sF.st_mode;
1457  }
1458 
1459  if(szNameReturn && cbNameReturn > 0)
1460  {
1461  strncpy(szNameReturn, p1, cbNameReturn);
1462  }
1463 
1464  break;
1465  }
1466 // else
1467 // {
1468 // p2 = pDL->szNameSpec;
1469 //
1470 // WB_ERROR_PRINT("TEMPORARY: \"%s\" does not match \"%s\"\n", p1, p2);
1471 // }
1472  }
1473  else
1474  {
1475  WB_WARN_PRINT("%s: can't 'stat' %s, errno=%d (%08xH)\n", __FUNCTION__, pBuf, errno, errno);
1476  }
1477  }
1478  }
1479 
1480 #endif // WIN32,!WIN32
1481 
1482  if(pBuf)
1483  {
1484  WBFree(pBuf);
1485  }
1486 
1487  return iRval;
1488 
1489 }
1490 
1491 char *WBGetDirectoryListFileFullPath(const void *pDirectoryList, const char *szFileName)
1492 {
1493 char *pRval, *pBuf, *p1;
1494 DIRLIST *pDL = (DIRLIST *)pDirectoryList;
1495 
1496  if(!pDirectoryList)
1497  {
1498  if(!szFileName || !*szFileName)
1499  {
1500  return NULL;
1501  }
1502 
1503  return WBGetCanonicalPath(szFileName);
1504  }
1505 
1506  if(szFileName && *szFileName == '/')
1507  {
1508  return WBGetCanonicalPath(szFileName); // don't need relative path
1509  }
1510 
1511  // TODO: improve this, maybe cache buffer or string length...
1512  pBuf = (char *)WBAlloc(strlen(pDL->szPath) + 8 + (szFileName ? strlen(szFileName) : 0) + NAME_MAX);
1513 
1514  if(!pBuf)
1515  {
1516  return NULL;
1517  }
1518 
1519  strcpy(pBuf, pDL->szPath);
1520  p1 = pBuf + strlen(pBuf);
1521  if(p1 > pBuf && *(p1 - 1) != '/') // ends in a slash?
1522  {
1523  *(p1++) = '/'; // for now assume this
1524  *p1 = 0; // by convention (though probably not necessary)
1525  }
1526 
1527  if(szFileName)
1528  {
1529  strcpy(p1, szFileName);
1530  }
1531 
1532  pRval = WBGetCanonicalPath(pBuf);
1533  WBFree(pBuf);
1534 
1535  return pRval;
1536 }
1537 
1538 char *WBGetSymLinkTarget(const char *szFileName)
1539 {
1540 char *pRval = WBAlloc(MAXPATHLEN + 2);
1541 
1542  if(pRval)
1543  {
1544  int iLen = readlink(szFileName, pRval, MAXPATHLEN);
1545  if(iLen <= 0)
1546  {
1547  WBFree(pRval);
1548  return NULL;
1549  }
1550 
1551  pRval[iLen] = 0; // assume < MAXPATHLEN for now...
1552  }
1553 
1554  return pRval;
1555 }
1556 
1557 char *WBGetDirectoryListSymLinkTarget(const void *pDirectoryList, const char *szFileName)
1558 {
1559 char *pTemp, *pRval;
1560 
1561  pTemp = WBGetDirectoryListFileFullPath(pDirectoryList, szFileName);
1562 
1563  if(!pTemp)
1564  {
1565  return NULL;
1566  }
1567 
1568  pRval = WBGetSymLinkTarget(pTemp);
1569  WBFree(pTemp);
1570 
1571  return pRval;
1572 }
1573 
1574 int WBStat(const char *szLinkName, unsigned long *pdwModeAttrReturn)
1575 {
1576 int iRval;
1577 struct stat sF;
1578 
1579 
1580  iRval = stat(szLinkName, &sF);
1581  if(!iRval && pdwModeAttrReturn)
1582  {
1583  *pdwModeAttrReturn = sF.st_mode;
1584  }
1585 
1586  return iRval; // zero on success
1587 }
1588 
1589 int WBGetDirectoryListFileStat(const void *pDirectoryList, const char *szFileName,
1590  unsigned long *pdwModeAttrReturn)
1591 {
1592 char *pTemp;
1593 int iRval;
1594 
1595  pTemp = WBGetDirectoryListFileFullPath(pDirectoryList, szFileName);
1596 
1597  if(!pTemp)
1598  {
1599  return -1;
1600  }
1601 
1602  iRval = WBStat(pTemp, pdwModeAttrReturn);
1603  WBFree(pTemp);
1604 
1605  return iRval;
1606 }
1607 
1608 #if defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1609 unsigned long long WBGetFileModDateTime(const char *szFileName)
1610 #else // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1611 unsigned long WBGetFileModDateTime(const char *szFileName)
1612 #endif // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1613 {
1614 int iRval;
1615 struct stat sF;
1616 
1617 
1618  iRval = stat(szFileName, &sF);
1619 
1620  if(iRval)
1621  {
1622 #if defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1623  return (unsigned long long)((long long)-1);
1624 #else // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1625  return (unsigned long)((long)-1);
1626 #endif // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1627  }
1628 
1629  // TODO: see whether st_mtime or st_ctime is larger, in case of total screwup by something else
1630 
1631  return sF.st_mtime; // mod time (as UNIX time_t value)
1632 }
1633 
1634 #if defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1635 int WBCheckFileModDateTime(const char *szFileName, unsigned long long tVal)
1636 #else // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1637 int WBCheckFileModDateTime(const char *szFileName, unsigned long tVal)
1638 #endif // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1639 {
1640 #if defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1641 unsigned long long tNewVal;
1642 #else // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1643 unsigned long tNewVal;
1644 #endif // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1645 
1646 
1647  tNewVal = WBGetFileModDateTime(szFileName);
1648 
1649  if(
1650 #if defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1651  tNewVal == (unsigned long long)((unsigned long)-1)
1652 #else // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1653  tNewVal == (unsigned long)((long)-1)
1654 #endif // defined(HAVE_LONGLONG) || defined(__DOXYGEN__)
1655  || tNewVal > tVal)
1656  {
1657  return 1;
1658  }
1659  else if(tNewVal < tVal)
1660  {
1661  return -1;
1662  }
1663  else
1664  {
1665  return 0;
1666  }
1667 }
1668 
1669 
char ** ppLineBuf
array of pointers to beginning of each line (WBAlloc'd TODO: make it part of 'cData'?...
Definition: file_help.h:171
'window helper' main header file for the X11workbench Toolkit API
int FBWriteFileBufHandle(int iFile, const file_help_buf_t *pBuf)
Write the file_help_buf_t object's text data to a file using an open file handle.
Definition: file_help.c:303
long lBufferCount
number of bytes of valid data
Definition: file_help.h:167
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:364
file_help_buf_t * FBGetFileBuf(const char *szFileName)
Construct a file_help_buf_t from a file.
Definition: file_help.c:106
Utilities for copying and drawing text, determining text extents, and so on.
void WBCatStringN(char **ppDest, const char *pSrc, unsigned int nMaxChars)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string up t...
void FBInsertLineIntoFileBuf(file_help_buf_t **ppBuf, long lLineNum, const char *szLine)
Insert a line of text into a file_help_buf_t object at a specific line index.
Definition: file_help.c:461
void * WBAllocDirectoryList(const char *szDirSpec)
Allocate a 'Directory List' object for a specified directory spec.
Definition: file_help.c:1187
unsigned long long WBGetFileModDateTime(const char *szFileName)
Obtain the 'time_t' value for a file's modification date/time (unix time, seconds since the epoch)
Definition: file_help.c:1609
int WBWriteFileFromBuffer(const char *szFileName, const char *pBuf, size_t cbBuf)
read a file's contents into a buffer, returning the length of the buffer
Definition: file_help.c:653
void FBDestroyFileBuf(file_help_buf_t *pBuf)
Destroy a file_help_buf_t object.
Definition: file_help.c:186
char cData[sizeof(char *)]
the data itself (aligned to size of a pointer)
Definition: file_help.h:172
struct tag_file_help_buf * pNext
pointer to 'next' item in linked list (NULL for last object)
Definition: file_help.h:165
void FBDeleteLineFromFileBuf(file_help_buf_t *pBuf, long lLineNum)
Delete a line of text from a file_help_buf_t object at a specific line index.
Definition: file_help.c:525
void * WBReAlloc(void *pBuf, int nNewSize)
High performance memory sub-allocator 're-allocate'.
int WBCheckFileModDateTime(const char *szFileName, unsigned long long tVal)
Compare a 64-bit unsigned integer value against a file's modification date/time (unix time,...
Definition: file_help.c:1635
int WBStat(const char *szLinkName, unsigned long *pdwModeAttrReturn)
Obtain the 'stat' flags for a file name, resolving links as needed.
Definition: file_help.c:1574
int WBGetDirectoryListFileStat(const void *pDirectoryList, const char *szFileName, unsigned long *pdwModeAttrReturn)
Obtain the 'stat' flags for a file name, resolving links as needed, with respect to a 'Directory List...
Definition: file_help.c:1589
long lLineCount
number of lines in 'cData' when ppLineBuf not NULL
Definition: file_help.h:168
long lLineBufSize
size of memory block pointed to by 'ppLineBuf'
Definition: file_help.h:169
size_t WBReadFileIntoBuffer(const char *szFileName, char **ppBuf)
read a file's contents into a buffer, returning the length of the buffer
Definition: file_help.c:572
void * WBAlloc(int nSize)
High performance memory sub-allocator 'allocate'.
#define WB_ERROR_PRINT(...)
Preferred method of implementing an 'error level' debug message for all subsystems.
Definition: debug_helper.h:474
int WBIsDirectory(const char *szFileName)
Return whether a file is a directory or a symlink to a directory.
Definition: file_help.c:776
void WBFree(void *pBuf)
High performance memory sub-allocator 'free'.
int FBParseFileBuf(file_help_buf_t *pBuf)
Parse or Re-Parse the data for a file_help_buf_t object.
Definition: file_help.c:209
void WBDelay(uint32_t uiDelay)
Delay for a specified period in microseconds.
void FBInsertIntoFileBuf(file_help_buf_t **ppBuf, long cbOffset, const void *pData, long cbData)
Insert text into a file_help_buf_t object at a specific byte offset.
Definition: file_help.c:406
int WBNextDirectoryEntry(void *pDirectoryList, char *szNameReturn, int cbNameReturn, unsigned long *pdwModeAttrReturn)
Obtain information about the next entry in a 'Directory List'.
Definition: file_help.c:1390
char * WBGetDirectoryListFileFullPath(const void *pDirectoryList, const char *szFileName)
Construct a fully qualified canonical path from a 'Directory List' object and a file name.
Definition: file_help.c:1491
int FBWriteFileBuf(const char *szFileName, const file_help_buf_t *pBuf)
Write the file_help_buf_t object's text data to a file using a filename.
Definition: file_help.c:281
char * WBGetDirectoryListSymLinkTarget(const void *pDirectoryList, const char *szFileName)
Obtain the target of a symbolic link file name with respect to a 'Directory List' object.
Definition: file_help.c:1557
int WBReplicateFilePermissions(const char *szProto, const char *szTarget)
replicate permissions on a target file based on another file's permissions
Definition: file_help.c:713
void FBReplaceLineInFileBuf(file_help_buf_t **ppBuf, long lLineNum, const char *szLine)
Insert a line of text into a file_help_buf_t object at a specific line index.
Definition: file_help.c:561
basic 'buffered I/O' object structure for 'FileBuf' APIs
Definition: file_help.h:162
char * WBGetCanonicalPath(const char *szFileName)
Return the canonical path for a file name (similar to POSIX 'realpath()' funtion)
Definition: file_help.c:803
char * WBGetCurrentDirectory(void)
Return allocated string containing the current working directory.
Definition: file_help.c:746
file_help_buf_t * FBGetFileBufFromBuffer(const char *pBuf, long cbBuf)
Construct a file_help_buf_t from a buffer.
Definition: file_help.c:159
char * WBGetSymLinkTarget(const char *szFileName)
Obtain the target of a symbolic link.
Definition: file_help.c:1538
void WBDestroyDirectoryList(void *pDirectoryList)
Destroy a 'Directory List' object allocated by WBAllocDirectoryList()
Definition: file_help.c:1362
#define INVALID_HANDLE_VALUE
INVALID HANDLE VALUE equivalent.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
long lBufferSize
size of entire buffer
Definition: file_help.h:166
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
file_help_buf_t * FBGetFileBufViaHandle(int iFile)
Construct a file_help_buf_t from a file handle.
Definition: file_help.c:123
void FBDeleteFromFileBuf(file_help_buf_t *pBuf, long cbOffset, long cbDelFrom)
Delete text from a file_help_buf_t object at a specific byte offset.
Definition: file_help.c:434
#define WB_WARN_PRINT(...)
Preferred method of implementing a 'warning level' debug message for all subsystems.
Definition: debug_helper.h:467
#define WB_UNLIKELY(x)
optimization for code branching when condition is 'unlikely'. use within conditionals
int iFlags
various bit flags
Definition: file_help.h:170
static __inline__ WB_UINT64 WBGetDebugLevel(void)
Returns the current debug level assigned by WBSetDebugLevel.
Definition: debug_helper.h:185
void WBCatString(char **ppDest, const char *pSrc)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string.