X11 Work Bench 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-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 
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  usleep(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  WBDebugPrint("TEMPORARY: read %d bytes\n", (int)iChunk);
643 
644  cbF -= iChunk;
645  pBuf += iChunk;
646  }
647  }
648  }
649 
650  close(iFile);
651 
652  WBDebugPrint("TEMPORARY WBReadFileIntoBuffer: return %d buf=%p\n", (int)cbLen, *ppBuf);
653 
654  return (size_t) cbLen;
655 }
656 
657 int WBWriteFileFromBuffer(const char *szFileName, const char *pBuf, size_t cbBuf)
658 {
659 int iFile, iRval, iChunk;
660 
661 
662  if(!pBuf)
663  {
664  return -1;
665  }
666 
667  iFile = open(szFileName, O_CREAT | O_TRUNC | O_RDWR, 0666); // always create with mode '666' (umask should apply)
668 
669  if(iFile < 0)
670  {
671  return -1;
672  }
673 
674  while(cbBuf > 0)
675  {
676  // write chunks of 1Mb or size remaining
677 
678  iChunk = 1048576;
679  if(iChunk > cbBuf)
680  {
681  iChunk = (int)cbBuf;
682  }
683 
684  iRval = write(iFile, pBuf, iChunk);
685 
686  if(iRval < 0)
687  {
688  if(errno == EAGAIN)
689  {
690  usleep(100);
691 
692  // TODO: time limit? for now, no
693 
694  continue; // try again
695  }
696 
697  close(iFile);
698  return -1; // error
699  }
700  else if(iRval != iChunk) // TODO: allow this??
701  {
702  WBDebugPrint("TEMPORARY: writing file, only wrote %d of %d bytes\n", iRval, iChunk);
703  iChunk = iRval;
704  }
705 
706  pBuf += iChunk;
707  cbBuf -= iChunk;
708  }
709 
710  iRval = 0; // at this point, success!
711 
712  close(iFile);
713 
714  return iRval;
715 }
716 
717 int WBReplicateFilePermissions(const char *szProto, const char *szTarget)
718 {
719 struct stat sb;
720 int iRval = 0;
721 
722  iRval = stat(szProto, &sb); // TODO: lstat for symlink?
723  if(!iRval)
724  {
725  // TODO: chflags?
726  // TODO: what if it's a symlink?
727  iRval = chmod(szTarget, sb.st_mode & 0777); // only set the rwx permissions, and ignore others
728  if(!iRval)
729  {
730  if(geteuid() == 0 || getuid() == sb.st_uid) // only do this if owner matches or I'm root
731  {
732  iRval = chown(szTarget, sb.st_uid, sb.st_gid);
733  if(iRval < 0 && geteuid() != 0)
734  {
735  iRval = chown(szTarget, -1, sb.st_gid); // don't change the user
736 
737  if(iRval < 0)
738  {
739  // don't bother changing anything - just warn??
740  iRval = 0; // for now...
741  }
742  }
743  }
744  }
745  }
746 
747  return iRval;
748 }
749 
751 {
752 char *pRval = WBAlloc(MAXPATHLEN + 2);
753 int i1;
754 
755  if(pRval)
756  {
757  if(!getcwd(pRval, MAXPATHLEN))
758  {
759  WBFree(pRval);
760  pRval = NULL;
761  }
762  }
763 
764  // this function will always return something that ends in '/' (except on error)
765 
766  if(pRval)
767  {
768  i1 = strlen(pRval);
769 
770  if(i1 > 0 && pRval[i1 - 1] != '/')
771  {
772  pRval[i1] = '/';
773  pRval[i1 + 1] = 0;
774  }
775  }
776 
777  return pRval;
778 }
779 
780 int WBIsDirectory(const char *szFileName)
781 {
782 int bRval = 0;
783 
784 #ifdef WIN32
785 WIN32_FIND_DATA fd;
786 HANDLE hFF;
787 
788  hFF = FindFirstFile(szFileName, &fd);
789 
790  if(hFF != INVALID_HANDLE_VALUE)
791  {
792  bRval = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
793  FindClose(hFF);
794  }
795 
796 #else // WIN32
797 struct stat sF;
798 
799  if(!stat(szFileName, &sF)) // NOTE: 'stat' returns info about symlink targets, not the link itself
800  bRval = S_ISDIR(sF.st_mode);
801 
802 #endif // WIN32
803 
804  return(bRval);
805 }
806 
807 char *WBGetCanonicalPath(const char *szFileName)
808 {
809 char *pTemp, *p1, *p2, *p3, *p4, *pRval = NULL;
810 struct stat sF;
811 
812  pTemp = WBCopyString(szFileName);
813 
814  if(!pTemp)
815  {
816  return NULL;
817  }
818 
819  // step 1: eliminate // /./
820 
821  p1 = pTemp;
822  while(*p1 && p1[1])
823  {
824  if(*p1 == '/' && p1[1] == '/')
825  {
826  memmove(p1, p1 + 1, strlen(p1 + 1) + 1);
827  }
828  else if(*p1 == '/' && p1[1] == '.' && p1[2] == '/')
829  {
830  memmove(p1, p1 + 2, strlen(p1 + 2) + 1);
831  }
832  else
833  {
834  p1++;
835  }
836  }
837 
838  // step 2: resolve each portion of the path, deal with '~' '.' '..', build new path.
839 
840  if(*pTemp == '~' && (pTemp[1] == '/' || !pTemp[1])) // first look for '~' at the beginning (only allowed there)
841  {
842  p1 = getenv("HOME");
843  if(!p1 || !*p1) // no home directory?
844  {
845  *pTemp = '.'; // for now change it to '.'
846  }
847  else
848  {
849  p3 = WBCopyString(p1);
850  if(!p3)
851  {
852  WBFree(pTemp);
853  return NULL;
854  }
855 
856  if(p3[strlen(p3) - 1] != '/')
857  {
858  WBCatString(&p3, "/");
859  }
860 
861  p2 = pTemp + 1;
862  if(*p2 == '/')
863  {
864  p2++; // already have an ending / on the path
865  }
866 
867  if(*p2)
868  {
869  WBCatString(&p3, p2);
870  }
871 
872  WBFree(pTemp);
873  pTemp = p3;
874  }
875  }
876 
877  p1 = pTemp;
878  while(*p1)
879  {
880  p2 = strchr(p1, '/');
881  if(!p2)
882  {
883  if(*p1 == '.') // check for ending in '.' or '..' and add a '/' so I can handle it correctly
884  {
885  if((p1[1] == '.' && !p1[2]) || !p1[1])
886  {
887  p2 = pTemp; // temporary
888  WBCatString(&pTemp, "/");
889 
890  p1 = (p1 - p2) + pTemp; // restore relative pointer
891 
892  WB_ERROR_PRINT("TEMPORARY: %s %s\n", p1, pTemp);
893 
894  continue; // let's do this again, properly
895  }
896  }
897 
898  // no more paths, so this is "the name".
899  if(!pRval) // no existing path, use CWD
900  {
901  pRval = WBGetCurrentDirectory();
902 
903  if(!pRval)
904  {
905  break;
906  }
907  }
908 
909  WBCatString(&pRval, p1);
910 
911  break;
912  }
913  else if(p2 == p1)
914  {
915  pRval = WBCopyString("/");
916  }
917  else
918  {
919  if(!pRval)
920  {
921  pRval = WBGetCurrentDirectory();
922  if(!pRval)
923  {
924  break;
925  }
926  }
927 
928  // when I assemble these paths together, deal with '..' and
929  // symbolic links. Check for cyclic paths.
930 
931  if(p2 - p1 == 1 && p1[0] == '.') // the ./ path
932  {
933  p1 = p2 + 1; // just ignore this part
934  continue;
935  }
936  else if(p2 - p1 == 2 && p1[0] == '.' && p1[1] == '.') // the ../ path
937  {
938  p1 = p2 + 1; // I need to fix the path while ignoring the '../' part
939 
940  p3 = pRval + strlen(pRval) - 1; // NOTE: pRval ends in '/' and I want the one BEFORE that
941  while(p3 > pRval)
942  {
943  if(*(p3 - 1) == '/')
944  {
945  *p3 = 0;
946  break;
947  }
948 
949  p3--;
950  }
951 
952  if(p3 <= pRval) // did not find a preceding '/' - this is an error
953  {
954  WB_ERROR_PRINT("%s:%d - did not find preceding '/' - %s\n", __FUNCTION__, __LINE__, pRval);
955 
956  WBFree(pRval);
957  pRval = NULL;
958 
959  break;
960  }
961 
962  continue;
963  }
964 
965  // TEMPORARY: just copy as-is to test basic logic
966 
967  WBCatStringN(&pRval, p1, p2 - p1 + 1); // include the '/' at the end
968  if(!pRval)
969  {
970  WB_ERROR_PRINT("%s:%d - WBCatStringN returned NULL pointer\n", __FUNCTION__, __LINE__);
971 
972  break;
973  }
974 
975  // see if this is a symbolic link. exclude testing '/'
976 
977  p3 = pRval + strlen(pRval) - 1;
978  if(p3 > pRval)
979  {
980  *p3 = 0; // temporary
981  if(lstat(pRval, &sF)) // get the file 'stat' and see if we're a symlink
982  {
983  // error, does not exist? - leave it 'as-is' for now
984  *p3 = '/'; // restore it
985  }
986  else if(S_ISDIR(sF.st_mode)) // an actual directory - remains as-is
987  {
988  // don't do anything except restore the '/'
989  *p3 = '/'; // restore it
990  }
991  else if(S_ISLNK(sF.st_mode)) // symlink
992  {
993  // now I get to put the symlink contents "in place". If the symlink is
994  // relative to the current directory, I'll want that.
995 
996  p4 = (char *)WBAlloc(MAXPATHLEN + 2);
997 
998  if(!p4)
999  {
1000  WB_ERROR_PRINT("%s:%d - not enough memory for buffer\n", __FUNCTION__, __LINE__);
1001 
1002  WBFree(pRval);
1003  pRval = NULL;
1004  break;
1005  }
1006  else
1007  {
1008  int iLen = readlink(pRval, p4, MAXPATHLEN);
1009 
1010  if(iLen <= 0)
1011  {
1012  WB_ERROR_PRINT("%s:%d - readlink returned %d for %s\n", __FUNCTION__, __LINE__, iLen, pRval);
1013 
1014  WBFree(p4);
1015  WBFree(pRval);
1016  pRval = NULL;
1017 
1018  break;
1019  }
1020 
1021  p4[iLen] = 0; // assume < MAXPATHLEN for now...
1022  if(p4[0] == '/') // it's an absolute path
1023  {
1024  WBFree(pRval);
1025  pRval = p4;
1026  }
1027  else
1028  {
1029  while(p3 > pRval && *(p3 - 1) != '/') // scan back for a '/'
1030  {
1031  p3--;
1032  }
1033 
1034  *p3 = 0;
1035  WBCatString(&pRval, p4); // sub in the relative path
1036  WBFree(p4);
1037  }
1038 
1039  if(!WBIsDirectory(pRval)) // must be a directory!
1040  {
1041  WB_ERROR_PRINT("%s:%d - %s not a directory\n", __FUNCTION__, __LINE__, pRval);
1042 
1043  WBFree(pRval);
1044  pRval = NULL;
1045  break; // this is an error
1046  }
1047  else
1048  {
1049  WBCatString(&pRval, "/");
1050 
1051  if(pRval)
1052  {
1053  p4 = WBGetCanonicalPath(pRval); // recurse
1054 
1055  WBFree(pRval);
1056  pRval = p4; // new canonical version of symlink path
1057  }
1058 
1059  if(!pRval)
1060  {
1061  WB_ERROR_PRINT("%s:%d - NULL pRval\n", __FUNCTION__, __LINE__);
1062 
1063  break;
1064  }
1065  }
1066  }
1067  }
1068  }
1069  }
1070 
1071  p1 = p2 + 1;
1072  }
1073 
1074  // if the resulting path is a symbolic link, fix it
1075  if(pRval)
1076  {
1077  p1 = pRval + strlen(pRval) - 1;
1078 
1079  if(p1 > pRval && *p1 != '/') // does not end in a slash, so it should be a file...
1080  {
1081  while(p1 > pRval && *(p1 - 1) != '/')
1082  {
1083  p1--;
1084  }
1085 
1086  if(!lstat(pRval, &sF)) // get the file 'stat' and see if we're a symlink (ignore errors)
1087  {
1088  if(S_ISDIR(sF.st_mode)) // an actual directory - end with a '/'
1089  {
1090  WBCatString(&pRval, "/"); // add ending '/'
1091  }
1092  else if(S_ISLNK(sF.st_mode)) // symlink
1093  {
1094  // now I get to put the symlink contents "in place". If the symlink is
1095  // relative to the current directory, I'll want that.
1096 
1097  p4 = (char *)WBAlloc(MAXPATHLEN + 2);
1098 
1099  if(!p4)
1100  {
1101  WB_ERROR_PRINT("%s:%d - not enough memory\n", __FUNCTION__, __LINE__);
1102 
1103  // TODO: assign pRval to NULL ?
1104  }
1105  else
1106  {
1107  int iLen = readlink(pRval, p4, MAXPATHLEN);
1108 
1109  if(iLen <= 0)
1110  {
1111  WB_ERROR_PRINT("%s:%d - readlink returned %d for %s\n", __FUNCTION__, __LINE__, iLen, pRval);
1112 
1113  WBFree(p4);
1114  WBFree(pRval);
1115  pRval = NULL;
1116  }
1117  else
1118  {
1119  p4[iLen] = 0; // assume < MAXPATHLEN for now...
1120  if(p4[0] == '/') // it's an absolute path
1121  {
1122  WBFree(pRval); // new path for old
1123  pRval = p4;
1124  }
1125  else
1126  {
1127  p3 = pRval + strlen(pRval); // I won't be ending in '/' for this part so don't subtract 1
1128  while(p3 > pRval && *(p3 - 1) != '/') // scan back for the '/' in symlink's original path
1129  {
1130  p3--;
1131  }
1132 
1133  *p3 = 0;
1134  WBCatString(&pRval, p4); // sub in the relative path
1135  WBFree(p4);
1136  }
1137 
1138  if(pRval && WBIsDirectory(pRval)) // is the result a directory?
1139  {
1140  WBCatString(&pRval, "/");
1141  }
1142 
1143  if(pRval)
1144  {
1145  p4 = WBGetCanonicalPath(pRval); // recurse to make sure I'm canonical (deal with '..' and '.' and so on)
1146 
1147  WBFree(pRval);
1148  pRval = p4; // new canonical version of symlink path
1149  }
1150  }
1151  }
1152  }
1153  }
1154  }
1155  }
1156 
1157  if(pTemp)
1158  {
1159  WBFree(pTemp);
1160  pTemp = NULL; // by convention
1161  }
1162 
1163  if(!pRval)
1164  {
1165  WB_ERROR_PRINT("%s:%d - returning NULL\n", __FUNCTION__, __LINE__);
1166  }
1167 
1168  return pRval;
1169 }
1170 
1171 // reading directories in a system-independent way
1172 
1173 typedef struct __DIRLIST__
1174 {
1175  const char *szPath, *szNameSpec;
1176 #ifdef WIN32 // also true for WIN64
1177  WIN32_FIND_DATA fd;
1178  HANDLE hFF;
1179 #else // !WIN32
1180  DIR *hD;
1181  struct stat sF;
1182  union
1183  {
1184  char cde[sizeof(struct dirent) + NAME_MAX + 2];
1185  struct dirent de;
1186  };
1187 #endif // WIN32 or !WIN32 - that is the question
1188 // actual composite 'search name' follows
1189 } DIRLIST;
1190 
1191 void *WBAllocDirectoryList(const char *szDirSpec)
1192 {
1193 DIRLIST *pRval;
1194 char *p1, *p2;
1195 int iLen, nMaxLen;
1196 char *pBuf;
1197 
1198  if(!szDirSpec || !*szDirSpec)
1199  {
1200  WB_WARN_PRINT("WARNING - %s - invalid directory (NULL or empty)\n", __FUNCTION__);
1201  return NULL;
1202  }
1203 
1204  iLen = strlen(szDirSpec);
1205  nMaxLen = iLen + 32;
1206 
1207  pBuf = WBAlloc(nMaxLen);
1208  if(!pBuf)
1209  {
1210  WB_ERROR_PRINT("ERROR - %s - Unable to allocate memory for buffer size %d\n", __FUNCTION__, nMaxLen);
1211  return NULL;
1212  }
1213 
1214  if(szDirSpec[0] == '/') // path starts from the root
1215  {
1216  memcpy(pBuf, szDirSpec, iLen + 1);
1217  }
1218  else // for now, force a path of './' to be prepended to path spec
1219  {
1220  pBuf[0] = '.';
1221  pBuf[1] = '/';
1222 
1223  memcpy(pBuf + 2, szDirSpec, iLen + 1);
1224  iLen += 2;
1225  }
1226 
1227  // do a reverse scan until I find a '/'
1228  p1 = ((char *)pBuf) + iLen;
1229  while(p1 > pBuf && *(p1 - 1) != '/')
1230  {
1231  p1--;
1232  }
1233 
1234 // WB_ERROR_PRINT("TEMPORARY - \"%s\" \"%s\" \"%s\"\n", pBuf, p1, szDirSpec);
1235 
1236  if(p1 > pBuf)
1237  {
1238  // found, and p1 points PAST the '/'. See if it ends in '/' or if there are wildcards present
1239  if(!*p1) // name ends in '/'
1240  {
1241  if(p1 == (pBuf + 1) && *pBuf == '/') // root dir
1242  {
1243  p1++;
1244  }
1245  else
1246  {
1247  *(p1 - 1) = 0; // trim the final '/'
1248  }
1249 
1250  p1[0] = '*';
1251 #ifdef WIN32
1252  p1[1] = '.';
1253  p1[2] = '*';
1254  p1[3] = 0;
1255 #else // !WIN32
1256  p1[1] = 0;
1257 #endif // WIN32
1258  }
1259  else if(strchr(p1, '*') || strchr(p1, '?'))
1260  {
1261  if(p1 == (pBuf + 1) && *pBuf == '/') // root dir
1262  {
1263  memmove(p1 + 1, p1, strlen(p1) + 1);
1264  *(p1++) = 0; // after this, p1 points to the file spec
1265  }
1266  else
1267  {
1268  *(p1 - 1) = 0; // p1 points to the file spec
1269  }
1270  }
1271  else if(WBIsDirectory(pBuf)) // entire name is a directory
1272  {
1273  // NOTE: root directory should NEVER end up here
1274 
1275  p1 += strlen(p1);
1276  *(p1++) = 0; // end of path (would be '/')
1277  p1[0] = '*';
1278 #ifdef WIN32
1279  p1[1] = '.';
1280  p1[2] = '*';
1281  p1[3] = 0;
1282 #else // !WIN32
1283  p1[1] = 0;
1284 #endif // WIN32
1285  }
1286  else
1287  {
1288  WB_WARN_PRINT("TEMPORARY: I am confused, %s %s\n", pBuf, p1);
1289  }
1290  }
1291  else
1292  {
1293  // this should never happen if I'm always prepending a './'
1294  // TODO: make this more consistent, maybe absolute path?
1295 
1296  WB_WARN_PRINT("TEMPORARY: should not happen, %s %s\n", pBuf, p1);
1297 
1298  if(strchr(pBuf, '*') || strchr(pBuf, '?')) // wildcard spec
1299  {
1300  p1 = (char *)pBuf + 1; // make room for zero byte preceding dir spec
1301  memmove(pBuf, p1, iLen + 1);
1302  *pBuf = 0; // since it's the current working dir just make it a zero byte (empty string)
1303  }
1304  else if(WBIsDirectory(pBuf))
1305  {
1306  p1 = (char *)pBuf + iLen;
1307  *(p1++) = 0; // end of path (would be '/')
1308  p1[0] = '*';
1309 #ifdef WIN32
1310  p1[2] = '*';
1311  p1[3] = 0;
1312 #else // !WIN32
1313  p1[1] = 0;
1314 #endif // WIN32
1315  }
1316  }
1317 
1318  pRval = WBAlloc(sizeof(DIRLIST) + iLen + strlen(p1) + 2);
1319 
1320  if(pRval)
1321  {
1322  pRval->szPath = pBuf;
1323  pRval->szNameSpec = p1;
1324 
1325  p2 = (char *)(pRval + 1);
1326  strcpy(p2, pBuf);
1327  p2 += strlen(p2);
1328  *(p2++) = '/';
1329  strcpy(p2, p1);
1330  p1 = (char *)(pRval + 1);
1331 
1332 #ifdef WIN32
1333  pRval->hFF = FindFirstFile(p2, &(pRval->fd))
1334  if(pRval->hFF == INVALID_HANDLE_VALUE)
1335  {
1336  WBFree(pBuf);
1337  WBFree(pRval);
1338 
1339  pRval = NULL;
1340  }
1341 #else // !WIN32
1342  pRval->hD = opendir(pBuf);
1343 
1344 // WB_ERROR_PRINT("TEMPORARY - opendir for %s returns %p\n", pBuf, pRval->hD);
1345 
1346  if(pRval->hD == NULL)
1347  {
1348  WB_WARN_PRINT("WARNING - %s - Unable to open dir \"%s\", errno=%d\n", __FUNCTION__, pBuf, errno);
1349 
1350  WBFree(pBuf);
1351  WBFree(pRval);
1352 
1353  pRval = NULL;
1354  }
1355 #endif // WIN32,!WIN32
1356  }
1357  else
1358  {
1359  WB_ERROR_PRINT("ERROR - %s - Unable to allocate memory for DIRLIST\n", __FUNCTION__);
1360  WBFree(pBuf); // no need to keep this around
1361  }
1362 
1363  return pRval;
1364 }
1365 
1366 void WBDestroyDirectoryList(void *pDirectoryList)
1367 {
1368  if(pDirectoryList)
1369  {
1370  DIRLIST *pD = (DIRLIST *)pDirectoryList;
1371 
1372 #ifdef WIN32
1373  if(pD->hFF != INVALID_HANDLE_VALUE)
1374  {
1375  FindClose(pD->hFF);
1376  }
1377 #else // !WIN32
1378  if(pD->hD)
1379  {
1380  closedir(pD->hD);
1381  }
1382 #endif // WIN32,!WIN32
1383  if(pD->szPath)
1384  {
1385  WBFree((void *)(pD->szPath));
1386  }
1387 
1388  WBFree(pDirectoryList);
1389  }
1390 }
1391 
1392 // returns < 0 on error, > 0 on EOF, 0 for "found something"
1393 
1394 int WBNextDirectoryEntry(void *pDirectoryList, char *szNameReturn,
1395  int cbNameReturn, unsigned long *pdwModeAttrReturn)
1396 {
1397 #ifdef WIN32
1398 #else // WIN32
1399 struct dirent *pD;
1400 struct stat sF;
1401 #endif // WIN32
1402 char *p1, *pBuf;
1403 //static char *p2; // temporary
1404 int iRval = 1; // default 'EOF'
1405 DIRLIST *pDL = (DIRLIST *)pDirectoryList;
1406 
1407 
1408  if(!pDirectoryList)
1409  {
1410  return -1;
1411  }
1412 
1413  // TODO: improve this, maybe cache buffer or string length...
1414  pBuf = WBAlloc(strlen(pDL->szPath) + 8 + NAME_MAX);
1415 
1416  if(!pBuf)
1417  {
1418  return -2;
1419  }
1420 
1421  strcpy(pBuf, pDL->szPath);
1422  p1 = pBuf + strlen(pBuf);
1423  if(p1 > pBuf && *(p1 - 1) != '/') // it does not already end in /
1424  {
1425  *(p1++) = '/'; // for now assume this
1426  *p1 = 0; // by convention
1427  }
1428 
1429 #ifdef WIN32
1430 
1431  // for WIN32, copy 'previous' data first, then 'FindNextFile'. On EOF mark
1432  // as EOF so that next call will detect it.
1433 
1434 #else // !WIN32
1435 
1436  if(pDL->hD)
1437  {
1438  while((pD = readdir(pDL->hD))
1439  != NULL)
1440  {
1441  // skip '.' and '..'
1442  if(pD->d_name[0] == '.' &&
1443  (!pD->d_name[1] ||
1444  (pD->d_name[1] == '.' && !pD->d_name[2])))
1445  {
1446 // WB_ERROR_PRINT("TEMPORARY: skipping %s\n", pD->d_name);
1447  continue; // no '.' or '..'
1448  }
1449 
1450  strcpy(p1, pD->d_name);
1451 
1452  if(!lstat(pBuf, &sF)) // 'lstat' returns data about a file, and if it's a symlink, returns info about the link itself
1453  {
1454  if(!fnmatch(pDL->szNameSpec, p1, 0/*FNM_PERIOD*/)) // 'tbuf2' is my pattern
1455  {
1456  iRval = 0;
1457 
1458  if(pdwModeAttrReturn)
1459  {
1460  *pdwModeAttrReturn = sF.st_mode;
1461  }
1462 
1463  if(szNameReturn && cbNameReturn > 0)
1464  {
1465  strncpy(szNameReturn, p1, cbNameReturn);
1466  }
1467 
1468  break;
1469  }
1470 // else
1471 // {
1472 // p2 = pDL->szNameSpec;
1473 //
1474 // WB_ERROR_PRINT("TEMPORARY: \"%s\" does not match \"%s\"\n", p1, p2);
1475 // }
1476  }
1477  else
1478  {
1479  WB_WARN_PRINT("%s: can't 'stat' %s, errno=%d (%08xH)\n", __FUNCTION__, pBuf, errno, errno);
1480  }
1481  }
1482  }
1483 
1484 #endif // WIN32,!WIN32
1485 
1486  if(pBuf)
1487  {
1488  WBFree(pBuf);
1489  }
1490 
1491  return iRval;
1492 
1493 }
1494 
1495 char *WBGetDirectoryListFileFullPath(const void *pDirectoryList, const char *szFileName)
1496 {
1497 char *pRval, *pBuf, *p1;
1498 DIRLIST *pDL = (DIRLIST *)pDirectoryList;
1499 
1500  if(!pDirectoryList)
1501  {
1502  if(!szFileName || !*szFileName)
1503  {
1504  return NULL;
1505  }
1506 
1507  return WBGetCanonicalPath(szFileName);
1508  }
1509 
1510  if(szFileName && *szFileName == '/')
1511  {
1512  return WBGetCanonicalPath(szFileName); // don't need relative path
1513  }
1514 
1515  // TODO: improve this, maybe cache buffer or string length...
1516  pBuf = (char *)WBAlloc(strlen(pDL->szPath) + 8 + (szFileName ? strlen(szFileName) : 0) + NAME_MAX);
1517 
1518  if(!pBuf)
1519  {
1520  return NULL;
1521  }
1522 
1523  strcpy(pBuf, pDL->szPath);
1524  p1 = pBuf + strlen(pBuf);
1525  if(p1 > pBuf && *(p1 - 1) != '/') // ends in a slash?
1526  {
1527  *(p1++) = '/'; // for now assume this
1528  *p1 = 0; // by convention (though probably not necessary)
1529  }
1530 
1531  if(szFileName)
1532  {
1533  strcpy(p1, szFileName);
1534  }
1535 
1536  pRval = WBGetCanonicalPath(pBuf);
1537  WBFree(pBuf);
1538 
1539  return pRval;
1540 }
1541 
1542 char *WBGetSymLinkTarget(const char *szFileName)
1543 {
1544 char *pRval = WBAlloc(MAXPATHLEN + 2);
1545 
1546  if(pRval)
1547  {
1548  int iLen = readlink(szFileName, pRval, MAXPATHLEN);
1549  if(iLen <= 0)
1550  {
1551  WBFree(pRval);
1552  return NULL;
1553  }
1554 
1555  pRval[iLen] = 0; // assume < MAXPATHLEN for now...
1556  }
1557 
1558  return pRval;
1559 }
1560 
1561 char *WBGetDirectoryListSymLinkTarget(const void *pDirectoryList, const char *szFileName)
1562 {
1563 char *pTemp, *pRval;
1564 
1565  pTemp = WBGetDirectoryListFileFullPath(pDirectoryList, szFileName);
1566 
1567  if(!pTemp)
1568  {
1569  return NULL;
1570  }
1571 
1572  pRval = WBGetSymLinkTarget(pTemp);
1573  WBFree(pTemp);
1574 
1575  return pRval;
1576 }
1577 
1578 int WBStat(const char *szLinkName, unsigned long *pdwModeAttrReturn)
1579 {
1580 int iRval;
1581 struct stat sF;
1582 
1583 
1584  iRval = stat(szLinkName, &sF);
1585  if(!iRval && pdwModeAttrReturn)
1586  {
1587  *pdwModeAttrReturn = sF.st_mode;
1588  }
1589 
1590  return iRval; // zero on success
1591 }
1592 
1593 int WBGetDirectoryListFileStat(const void *pDirectoryList, const char *szFileName,
1594  unsigned long *pdwModeAttrReturn)
1595 {
1596 char *pTemp;
1597 int iRval;
1598 
1599  pTemp = WBGetDirectoryListFileFullPath(pDirectoryList, szFileName);
1600 
1601  if(!pTemp)
1602  {
1603  return -1;
1604  }
1605 
1606 // WB_ERROR_PRINT("TEMPORARY: stat on '%s' - \"%s\"\n", szFileName, pTemp);
1607 
1608  iRval = WBStat(pTemp, pdwModeAttrReturn);
1609  WBFree(pTemp);
1610 
1611  return iRval;
1612 }
1613 
basic &#39;buffered I/O&#39; object structure for &#39;FileBuf&#39; APIs
Definition: file_help.h:140
int iFlags
various bit flags
Definition: file_help.h:148
&#39;window helper&#39; main header file for the X11workbench Toolkit API
static __inline__ unsigned int WBGetDebugLevel(void)
Returns the current debug level assigned by WBSetDebugLevel.
Definition: debug_helper.h:68
int FBWriteFileBufHandle(int iFile, const file_help_buf_t *pBuf)
Write the file_help_buf_t object&#39;s text data to a file using an open file handle. ...
Definition: file_help.c:303
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:196
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 &#39;Directory List&#39; object for a specified directory spec.
Definition: file_help.c:1191
char ** ppLineBuf
array of pointers to beginning of each line (WBAlloc&#39;d TODO: make it part of &#39;cData&#39;?)
Definition: file_help.h:149
int WBWriteFileFromBuffer(const char *szFileName, const char *pBuf, size_t cbBuf)
read a file&#39;s contents into a buffer, returning the length of the buffer
Definition: file_help.c:657
void FBDestroyFileBuf(file_help_buf_t *pBuf)
Destroy a file_help_buf_t object.
Definition: file_help.c:186
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 &#39;re-allocate&#39;.
long lLineCount
number of lines in &#39;cData&#39; when ppLineBuf not NULL
Definition: file_help.h:146
char cData[sizeof(char *)]
the data itself (aligned to size of a pointer)
Definition: file_help.h:150
long lBufferCount
number of bytes of valid data
Definition: file_help.h:145
int WBStat(const char *szLinkName, unsigned long *pdwModeAttrReturn)
Obtain the &#39;stat&#39; flags for a file name, resolving links as needed.
Definition: file_help.c:1578
int WBGetDirectoryListFileStat(const void *pDirectoryList, const char *szFileName, unsigned long *pdwModeAttrReturn)
Obtain the &#39;stat&#39; flags for a file name, resolving links as needed, with respect to a &#39;Directory List...
Definition: file_help.c:1593
#define INVALID_HANDLE_VALUE
INVALID HANDLE VALUE equivalent.
size_t WBReadFileIntoBuffer(const char *szFileName, char **ppBuf)
read a file&#39;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 &#39;allocate&#39;.
struct __file_help_buf__ * pNext
pointer to &#39;next&#39; item in linked list (NULL for last object)
Definition: file_help.h:143
#define WB_UNLIKELY(x)
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:268
int WBIsDirectory(const char *szFileName)
Return whether a file is a directory or a symlink to a directory.
Definition: file_help.c:780
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
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 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 &#39;Directory List&#39;.
Definition: file_help.c:1394
char * WBGetDirectoryListFileFullPath(const void *pDirectoryList, const char *szFileName)
Construct a fully qualified canonical path from a &#39;Directory List&#39; object and a file name...
Definition: file_help.c:1495
int FBWriteFileBuf(const char *szFileName, const file_help_buf_t *pBuf)
Write the file_help_buf_t object&#39;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 &#39;Directory List&#39; object...
Definition: file_help.c:1561
int WBReplicateFilePermissions(const char *szProto, const char *szTarget)
replicate permissions on a target file based on another file&#39;s permissions
Definition: file_help.c:717
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
char * WBGetCanonicalPath(const char *szFileName)
Return the canonical path for a file name (similar to POSIX &#39;realpath()&#39; funtion) ...
Definition: file_help.c:807
char * WBGetCurrentDirectory(void)
Return allocated string containing the current working directory.
Definition: file_help.c:750
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:1542
void WBDestroyDirectoryList(void *pDirectoryList)
Destroy a &#39;Directory List&#39; object allocated by WBAllocDirectoryList()
Definition: file_help.c:1366
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
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
long lLineBufSize
size of memory block pointed to by &#39;ppLineBuf&#39;
Definition: file_help.h:147
#define WB_WARN_PRINT(...)
Preferred method of implementing a &#39;warning level&#39; debug message for all subsystems.
Definition: debug_helper.h:261
void WBCatString(char **ppDest, const char *pSrc)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string...
long lBufferSize
size of entire buffer
Definition: file_help.h:144