librnr  1.14.5
RoadNarrows Robotics Common Library 1
path.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Common Library 1
4 //
5 // Library: librnr
6 //
7 // File: path.c
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2011-11-18 13:30:34 -0700 (Fri, 18 Nov 2011) $
12  * $Rev: 1577 $
13  *
14  * \brief General file name and file path utilities.
15  *
16  * Boths posix (the world) and Microsoft's file and path syntax are
17  * supported.
18  *
19  * \author Robin Knight (robin.knight@roadnarrows.com)
20  *
21  * \pkgcopyright{2005-2018,RoadNarrows LLC.,http://www.roadnarrows.com}
22  */
23 // Permission is hereby granted, without written agreement and without
24 // license or royalty fees, to use, copy, modify, and distribute this
25 // software and its documentation for any purpose, provided that
26 // (1) The above copyright notice and the following two paragraphs
27 // appear in all copies of the source code and (2) redistributions
28 // including binaries reproduces these notices in the supporting
29 // documentation. Substantial modifications to this software may be
30 // copyrighted by their authors and need not follow the licensing terms
31 // described here, provided that the new terms are clearly indicated in
32 // all files where they apply.
33 //
34 // IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
35 // OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
36 // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
37 // DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
38 // EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
39 // THE POSSIBILITY OF SUCH DAMAGE.
40 //
41 // THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
42 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
43 // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
44 // "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
45 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
46 //
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 #include <sys/types.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <ctype.h>
53 #include <unistd.h>
54 #include <pwd.h>
55 
56 #include "rnr/rnrconfig.h"
57 #include "rnr/dliststr.h"
58 #include "rnr/log.h"
59 #include "rnr/new.h"
60 #include "rnr/path.h"
61 
62 
63 // ---------------------------------------------------------------------------
64 // Private Interface
65 // ---------------------------------------------------------------------------
66 
67 /*!
68  * Legal identifier character
69  */
70 #define is1stidentifier(c) (isapha((int)c) || ((c) == '_')) ///< first id char
71 #define isidentifier(c) (isalnum((int)c) || ((c) == '_')) ///< id char
72 
73 /*!
74  * Search Pather Iterator Structure
75  */
76 struct path_iter
77 {
78  DListStr_T *m_pSearchPaths; ///< dlist of search paths
79  char *m_sFileCanonical; ///< search canonical file
80  size_t m_nFileLen; ///< length of canonical file
81  char *m_sFilePath; ///< concatenated file path (output)
82  DListStrIter_T m_iterPaths; ///< dlist iterator over paths
83  bool_t m_bIsEos; ///< is [not] end of search flag
84 };
85 
86 /*!
87  * \brief Build a dlist of canonical search paths
88  *
89  * \param sSearchPath Search path of form: path [path_sep path...]
90  *
91  * \return Returns new dlist of path components.
92  */
93 static DListStr_T *NewSearchPathDList(const char *sSearchPath)
94 {
95  DListStr_T *pSearchPaths; // list of search paths
96  char *sExpPath; // expanded path
97  char *sPath; // search path component
98  char *sSave = NULL; // strtok() save pointer
99 
100  // new dlist
101  if( (pSearchPaths = DListStrNewDft()) == NULL )
102  {
103  LOGERROR("DListStrNew() failed");
104  return NULL;
105  }
106 
107  // no search paths
108  if( (sSearchPath == NULL) || (sSearchPath[0] == 0) )
109  {
110  return pSearchPaths;
111  }
112 
113  // canonicalize expanded search path
114  if( (sExpPath = NewSearchPathCanonicalized(sSearchPath)) == NULL )
115  {
116  return pSearchPaths;
117  }
118 
119  // parse search path and append compenents to dlist
120  for(sPath=strtok_r(sExpPath, PATH_SEP_STR, &sSave);
121  sPath!=NULL;
122  sPath=strtok_r(NULL, PATH_SEP_STR, &sSave))
123  {
124  if( DListStrAppend(pSearchPaths, sPath) == NULL )
125  {
126  LOGERROR("DListStrAppend() failed");
127  break;
128  }
129  }
130 
131  delete(sExpPath);
132 
133  LOGDIAG4("%s(%s)", LOGFUNCNAME, sSearchPath);
134  if( LOGABLE(LOG_LEVEL_DIAG4) )
135  {
136  DListStrPrint(pSearchPaths, DListStrDataPrint, LOG_GET_LOGFP());
137  }
138 
139  return pSearchPaths;
140 }
141 
142 
143 // ---------------------------------------------------------------------------
144 // Public Interface
145 // ---------------------------------------------------------------------------
146 
147 /*!
148  * \brief Expand tilde expression.
149  *
150  * \param sTildeExpr '~' [user]
151  *
152  * \return
153  * Return allocated expanded tilde expression path on success.\n
154  * If the expression has invalid syntax or the user does not exist, then
155  * NULL is returned.
156  */
157 char *NewExpandTilde(const char *sTildeExpr)
158 {
159  char *sUser;
160  struct passwd *pPassEntry;
161  uid_t uid;
162  char *sExpanded = NULL;
163 
164  // expression must start with a tilde
165  if( (sTildeExpr == NULL) || (*sTildeExpr != '~') )
166  {
167  return NULL;
168  }
169 
170  // user name
171  sUser = (char *)sTildeExpr + 1;
172 
173  // expand this user's home
174  if( (*sUser == 0) || (*sUser == DIR_SEP_CHAR) || (*sUser == PATH_SEP_CHAR) )
175  {
176  if( (sExpanded = getenv("HOME")) == NULL )
177  {
178  uid = getuid();
179  if( (pPassEntry = getpwuid(uid)) != NULL )
180  {
181  sExpanded = pPassEntry->pw_dir;
182  }
183  }
184  }
185 
186  // expand other user's home
187  else
188  {
189  if( (pPassEntry = getpwnam(sUser)) != NULL )
190  {
191  sExpanded = pPassEntry->pw_dir;
192  }
193  }
194 
195  if( sExpanded != NULL )
196  {
197  return new_strdup(sExpanded);
198  }
199  else
200  {
201  return NULL;
202  }
203 }
204 
205 /*!
206  * \brief Expands search path.
207  *
208  * Expands any environment variables and tilde expressions in search path
209  * while building new search path.
210  *
211  * The path does not have to exist.
212  *
213  * Variable are specified as:\n
214  * $<identifier> or ${<identifier>} where <identifier> is a legal shell
215  * variable name.
216  *
217  * Tilde expressions are specified as:\n
218  * ~ or ~user
219  *
220  * \param sSearchPath Search path of form: path [path_sep path...]
221  *
222  * \return
223  * Return allocated expanded search path on success, NULL if search path is
224  * is not specified. If the search path is too long, a truncated version
225  * will be returned.
226  */
227 char *NewSearchPathExpanded(const char *sSearchPath)
228 {
229  char *sExpPath; // expanded path
230  const char *s, *u; // working source char pointers
231  char *t; // working target char pointers
232  char *sName, *sVal; // environment name=value strings
233  size_t nExpLen = 0; // current length of expanded path
234  // (sans null character)
235  size_t nMaxLen = MAX_SEARCH_PATH-1; // maximum length
236  size_t nNameLen; // name length
237 
238  if( (sSearchPath == NULL) || (*sSearchPath == 0) )
239  {
240  return NULL;
241  }
242 
243  sExpPath = NEWSTR(nMaxLen);
244 
245  for(s=sSearchPath, t=sExpPath; *s && nExpLen<nMaxLen; )
246  {
247  switch(*s)
248  {
249  case '$':
250  ++s;
251  if( *s == '{' )
252  {
253  ++s;
254  }
255  for(u=s; isidentifier(*s); ++s);
256  sName = NULL;
257  nNameLen = (size_t)(s - u);
258  if( nNameLen > 0 )
259  {
260  sName = new_strndup(u, nNameLen);
261  if( (sName != NULL) && ((sVal = getenv(sName)) != NULL) )
262  {
263  for(; *sVal && nExpLen<nMaxLen; ++nExpLen)
264  {
265  *t++ = *sVal++;
266  }
267  }
268  delete(sName);
269  }
270  if( *s == '}' )
271  {
272  ++s;
273  }
274  break;
275  case '~':
276  for(u=s; (*s != DIR_SEP_CHAR) && (*s != 0); ++s);
277  sName = NULL;
278  nNameLen = (size_t)(s - u);
279  if( nNameLen > 0 )
280  {
281  sName = new_strndup(u, nNameLen);
282  if( (sName != NULL) && ((sVal = NewExpandTilde(sName)) != NULL) )
283  {
284  for(u=sVal; *u && nExpLen<nMaxLen; ++nExpLen)
285  {
286  *t++ = *u++;
287  }
288  delete(sVal);
289  }
290  delete(sName);
291  }
292  break;
293  default:
294  *t++ = *s++;
295  ++nExpLen;
296  break;
297  }
298  }
299  *t = 0;
300 
301  if( nExpLen >= nMaxLen )
302  {
303  LOGERROR("Expanded search path truncated");
304  }
305 
306  LOGDIAG4("'%s'=%s('%s')", sExpPath, LOGFUNCNAME, sSearchPath);
307 
308  return sExpPath;
309 }
310 
311 /*!
312  * \brief Expands and canonicalizes a search path.
313  *
314  * The path does not have to exist.
315  *
316  * A canonical search path has: \n
317  * \li any beginning or ending path separators stipped
318  * \li any contiguous sequence of path separators replaced with a single
319  * separator
320  * \li any contiguous sequence of directory separators replaced with a single
321  * separator
322  * \li any terminating directory separator stripped from each path component
323  *
324  * \param sSearchPath Search path of form: path [path_sep path...]
325  *
326  * \return
327  * Return allocated canonical search path on success, NULL if search path is
328  * is not specified. If the search path is too long, a truncated version
329  * will be returned.
330  */
331 char *NewSearchPathCanonicalized(const char *sSearchPath)
332 {
333  char *sExpPath; // expanded search path
334  char *sPath; // expanded path
335  char *sNorm; // normalized path
336  char *sSep; // path separator
337  char *sNew; // canonical search path
338  char *sSave = NULL; // strtok() save pointer
339  char *s; // working string
340  size_t k, m, n, len; // working sizes
341 
342  // expand any environment variables and '~''s listed in search path
343  if( (sExpPath = NewSearchPathExpanded(sSearchPath)) == NULL )
344  {
345  return NULL;
346  }
347 
348  len = strlen(sExpPath);
349  sNew = NEWSTR(len);
350  n = 0;
351  sSep = "";
352  k = 0;
353 
354  for(sPath=strtok_r(sExpPath, PATH_SEP_STR, &sSave);
355  sPath!=NULL;
356  sPath=strtok_r(NULL, PATH_SEP_STR, &sSave))
357  {
358  sNorm = NewNormPath(sPath);
359  m = strlen(sNorm);
360 
361  // grow new search path
362  if( n+k+m >= len )
363  {
364  len = (n + k + m) * 2;
365  s = NEWSTR(len);
366  strcpy(s, sNew);
367  delete(sNew);
368  sNew = s;
369  }
370 
371  sprintf(sNew+n, "%s%s", sSep, sNorm);
372  n += k + m;
373 
374  if( *sSep == 0 )
375  {
376  sSep = PATH_SEP_STR;
377  k = strlen(sSep);
378  }
379  }
380 
381  delete(sExpPath);
382 
383  return sNew;
384 }
385 
386 /*!
387  * \brief Allocates and initilized a new search path iterator.
388  *
389  * This iterator can be reused multiple times with different search files by
390  * calling SearchPathIterFirst(). SearchPathIterDelete() must be called when
391  * finished with the iterator to clean up any allocated memory.
392  *
393  * \param sSearchPath Search path of form: path [path_sep path...]
394  *
395  * \return
396  * Returns pointer to search path iterator on success, NULL on error
397  */
398 PathIter_T *SearchPathIterNew(const char *sSearchPath)
399 {
400  PathIter_T *pIter = NEW(PathIter_T);
401 
402  if( (pIter->m_pSearchPaths = NewSearchPathDList(sSearchPath)) == NULL )
403  {
404  LOGERROR("NewSearchPathDlist() failed");
405  SearchPathIterDelete(pIter);
406  return NULL;
407  }
408 
409  pIter->m_sFileCanonical = NULL;
410  pIter->m_nFileLen = 0;
411  pIter->m_sFilePath = NULL;
412  pIter->m_bIsEos = false;
413 
414  return pIter;
415 }
416 
417 /*!
418  * \brief Deletes search path iterator
419  *
420  * \param pIter Search path iterator (invalid after return from this call).
421  */
423 {
424  if( pIter == NULL )
425  {
426  return;
427  }
428 
429  if( pIter->m_pSearchPaths != NULL )
430  {
431  DListStrDelete(pIter->m_pSearchPaths);
432  }
433 
434  delete(pIter->m_sFileCanonical);
435  delete(pIter->m_sFilePath);
436 
437  delete(pIter);
438 }
439 
440 /*!
441  * \brief Get the first concatenated file path.
442  *
443  * Initialize search path iterator from given search file and returns first
444  * concatenated file path.
445  *
446  * \param pIter Path iterator.
447  * \param sSearchFile Search path.
448  *
449  * \return
450  * Returns first file path on success, NULL if at end of search or on error.
451  */
452 char *SearchPathIterFirst(PathIter_T *pIter, const char *sSearchFile)
453 {
454  const char *sPath; // search path component
455  size_t nLen; // string length
456 
457  // logistics to [re]set iterator
458  delete(pIter->m_sFileCanonical); // clear any old canonical serach file
459  pIter->m_sFileCanonical = NULL;
460  delete(pIter->m_sFilePath); // clear any old constructed file path
461  pIter->m_sFilePath = NULL;
462  pIter->m_nFileLen = 0; // no file, no length
463  pIter->m_bIsEos = true; // assume end-of-search condition until
464  // proven otherwise
465 
466  // no serach file specified
467  if( (sSearchFile == NULL) || (*sSearchFile == 0) )
468  {
469  LOGERROR("%s(): no file specified", LOGFUNCNAME);
470  return NULL;
471  }
472 
473  // cannot canonicalize search file
474  else if( (pIter->m_sFileCanonical = NewSearchPathCanonicalized(sSearchFile))
475  == NULL )
476  {
477  LOGERROR("NewSearchPathCanonicalized() failed");
478  return NULL;
479  }
480 
481  // set canonical file name length - used to speed iteration
482  pIter->m_nFileLen = strlen(pIter->m_sFileCanonical);
483 
484  // no search path - just return canonical file
485  if( DListStrCount(pIter->m_pSearchPaths) == 0 )
486  {
487  LOGDIAG4("'%s'=%s()", pIter->m_sFileCanonical, LOGFUNCNAME);
488 
489  return pIter->m_sFileCanonical;
490  }
491 
492  // construct first file path
493  else if( (sPath = DListStrIterDataFirst(pIter->m_pSearchPaths,
494  &(pIter->m_iterPaths))) != NULL )
495  {
496  pIter->m_bIsEos = false;
497 
498  nLen = strlen(sPath) + strlen(DIR_SEP_STR) + pIter->m_nFileLen;
499 
500  pIter->m_sFilePath = NEWSTR(nLen);
501 
502  sprintf(pIter->m_sFilePath, "%s%s%s",
503  sPath, DIR_SEP_STR, pIter->m_sFileCanonical);
504 
505  LOGDIAG4("'%s'=%s()", pIter->m_sFilePath, LOGFUNCNAME);
506 
507  return pIter->m_sFilePath;
508  }
509 
510  // shouldn't have gotten here - so terminate search
511  else
512  {
513  return NULL;
514  }
515 }
516 
517 /*!
518  * \brief Construct next file path from search path and search file name.
519  *
520  * \param pIter Path iterator.
521  *
522  * \return
523  * Returns next constructed file path on success, NULL if at end of search
524  * or on error
525  */
527 {
528  const char *sPath; // search path component
529  size_t nLen; // string length
530 
531  // end of search
532  if( pIter->m_bIsEos )
533  {
534  return NULL;
535  }
536 
537  // get the next search path component
538  else if( (sPath = DListStrIterDataNext(&(pIter->m_iterPaths))) != NULL )
539  {
540  nLen = strlen(sPath) + strlen(DIR_SEP_STR) + pIter->m_nFileLen;
541 
542  delete(pIter->m_sFilePath); // clear any old constructed file path
543  pIter->m_sFilePath = NEWSTR(nLen); // allocate new file path
544 
545  sprintf(pIter->m_sFilePath, "%s%s%s",
546  sPath, DIR_SEP_STR, pIter->m_sFileCanonical);
547 
548  LOGDIAG4("'%s'=%s()", pIter->m_sFilePath, LOGFUNCNAME);
549 
550  return pIter->m_sFilePath;
551  }
552 
553  // end of search
554  else
555  {
556  pIter->m_bIsEos = true;
557  return NULL;
558  }
559 }
560 
561 /*!
562  * \brief Normalize path.
563  *
564  * Normalize path, eliminating double slashes and simplifying dot "." current
565  * and ".." parent directory references.
566  *
567  * The path does not have to exist.
568  *
569  * \return
570  * Returns allocated normalized path.
571  */
572 char *NewNormPath(const char *sPath)
573 {
574  char *sTopDir;
575  char *sNew;
576  int i, j, k;
577  int slashes;
578 
579  // Degenerative empty string case.
580  if( *sPath == 0 )
581  {
582  return new_strdup(".");
583  }
584 
585  // Normalization results in a smaller length string, except in the
586  // degenerative case above.
587  sNew = NEWSTR(strlen(sPath));
588 
589  // The (implicit) top directory is either the root or current directory.
590  if( PathIsAbsolute(sPath) )
591  {
592  sTopDir = DIR_SEP_STR;
593  }
594  else
595  {
596  sTopDir = ".";
597  }
598 
599  //
600  // Normalize the path.
601  //
602  for(i=0, j=0; sPath[i]!=0; )
603  {
604  switch( sPath[i] )
605  {
606  //
607  // Potential current dot or parent dotdot directory path component.
608  //
609  case '.':
610  // "filename." or "filename.."
611  if( (j > 0) && (sNew[j-1] != DIR_SEP_CHAR) )
612  {
613  sNew[j++] = sPath[i++];
614  }
615 
616  // "../[path]" or ".."
617  else if( (sPath[i+1] == '.') &&
618  ((sPath[i+2] == DIR_SEP_CHAR) || (sPath[i+2] == 0)) )
619  {
620  //
621  // Try to backup to parent directory.
622  // example 1: "path1/pdir/cdir/../path2" -> "pathl/pdir/path2"
623  // example 2: "pdir/cdir/../path2" -> "pdir/path2"
624  // example 3: "cdir/../path2" -> "path2"
625  // example 4: "/cdir/../path2" -> "/path2"
626  // example 5: " cdir/../../path2" -> "../path2"
627  // example 6: "/cdir/../../path2" -> "/path2"
628  // example 7: "dir/../cdir/.." -> "."
629  //
630  for(k=j-1, slashes=0; k>=0 && slashes<2; --k)
631  {
632  if( sNew[k] == DIR_SEP_CHAR )
633  {
634  ++slashes;
635  }
636  }
637  ++k;
638 
639  // i i
640  // k j k j
641  // "[path/]pdir/cdir/..[/path]" or "[path/]pdir/../..[/path]"
642  //
643  if( slashes == 2 )
644  {
645  // if current directory is "..", then "../.."
646  if( (sNew[k+1] == '.') &&
647  (sNew[k+2] == '.') &&
648  (sNew[k+3] == DIR_SEP_CHAR) )
649  {
650  sNew[j++] = '.';
651  sNew[j++] = '.';
652  }
653  // "[path]/pdir"
654  else
655  {
656  j = k;
657  sNew[j] = 0;
658  }
659  }
660 
661  // i i
662  // kj k j
663  // "/..[/path]" or "cdir/..[/path]"
664  //
665  else if( slashes == 1 )
666  {
667  // relative
668  if( sNew[0] != DIR_SEP_CHAR )
669  {
670  j = 0;
671  sNew[j++] = '.';
672  sNew[j] = 0;
673  }
674  // absolute
675  else
676  {
677  j = 0;
678  sNew[j++] = DIR_SEP_CHAR;
679  sNew[j] = 0;
680  }
681  }
682 
683  // i
684  // j
685  // k
686  // "..[/path]"
687  //
688  else // slashes == 0
689  {
690  j = 0;
691  sNew[j++] = '.';
692  sNew[j++] = '.';
693  sNew[j] = 0;
694  }
695 
696  i += 2;
697  }
698 
699  // current dot directory "./[path]" or "."
700  else if( (sPath[i+1] == DIR_SEP_CHAR) || (sPath[i+1] == 0) )
701  {
702  i += sPath[i+1] == DIR_SEP_CHAR? 2: 1;
703  }
704 
705  // dot filename ".filename"
706  else
707  {
708  sNew[j++] = sPath[i++];
709  }
710  break;
711 
712  //
713  // Directory component.
714  //
715  case DIR_SEP_CHAR:
716  // directory separator that's not superfluous "[path1]/path2"
717  if( (sPath[i+1] != DIR_SEP_CHAR) && (sPath[i+1] != 0) )
718  {
719  // "/[path]"
720  if( j == 0 )
721  {
722  sNew[j++] = sPath[i++];
723  }
724  // "//path}
725  else if( sNew[j-1] == DIR_SEP_CHAR )
726  {
727  ++i;
728  }
729  // "[path1/]./[path2]"
730  else if((sNew[j-1] == '.') &&
731  ((j == 1) || (sNew[j-2] == DIR_SEP_CHAR)) )
732  {
733  --j;
734  ++i;
735  }
736  // "[path1]/path2"
737  else
738  {
739  sNew[j++] = sPath[i++];
740  }
741  }
742  // ignore extra or ending separator "//" or "/"
743  else
744  {
745  ++i;
746  }
747  break;
748 
749  //
750  // Non-special file character.
751  //
752  default:
753  sNew[j++] = sPath[i++];
754  break;
755  }
756  }
757 
758  sNew[j] = 0;
759 
760  if( *sNew == 0 )
761  {
762  strcpy(sNew, sTopDir);
763  }
764 
765  return sNew;
766 }
767 
768 /*!
769  * \brief Expands and canonicalizes a real path.
770  *
771  * The expanded path must exist.
772  *
773  * \param sPath Path file name.
774  *
775  * \return
776  * Return allocated canonical path on success, NULL if path doest not exist.
777  */
778 char *NewRealPath(const char *sPath)
779 {
780  char *s, *t;
781 
782  s = NewSearchPathCanonicalized(sPath);
783  t = realpath(s, NULL);
784  delete(s);
785  return t;
786 }
787 
788 /*!
789  * \brief Join two file paths.
790  *
791  * The paths are expanded and canonicalized. The paths do not have to exist.
792  * If the second, appended path is an absolute path, then the joined path is
793  * the second path.
794  *
795  * \param sPath1 Front absolute/relative path.
796  * \param sPath2 Appended, back end path.
797  *
798  * \return
799  * On success, an allocated, canonical joined path name is returned.\n
800  * On failure, NULL is returned.
801  */
802 char *NewJoinedPath(const char *sPath1, const char *sPath2)
803 {
804  char *p1 = NULL;
805  char *p2 = NULL;
806  char *pj = NULL;
807 
808  // canonicalize path 2
809  if( (p2 = NewSearchPathCanonicalized(sPath2)) == NULL )
810  {
811  LOGERROR("NewSearchPathCanonicalized(\"%s\") failed.", sPath2);
812  }
813 
814  // if the second path is an absolute path, cannot join
815  else if( PathIsAbsolute(p2) )
816  {
817  return p2;
818  }
819 
820  // canonicalize path 1
821  else if( (p1 = NewSearchPathCanonicalized(sPath1)) == NULL )
822  {
823  LOGERROR("NewSearchPathCanonicalized(\"%s\") failed.", sPath1);
824  }
825 
826  // join
827  else
828  {
829  pj = NEWSTR(strlen(p1)+strlen(DIR_SEP_STR)+strlen(p2));
830  sprintf(pj, "%s%s%s", p1, DIR_SEP_STR, p2);
831  }
832 
833  delete(p1);
834  delete(p2);
835 
836  return pj;
837 }
#define DIR_SEP_CHAR
directory component separator (char version)
Definition: rnrconfig.h:238
INLINE_IN_H bool_t PathIsAbsolute(const char *sPath)
Check if the given path is an absolute path.
Definition: path.h:91
#define DIR_SEP_STR
directory component separator (string version)
Definition: rnrconfig.h:239
bool_t m_bIsEos
is [not] end of search flag
Definition: path.c:83
char * new_strndup(const char *s, size_t n)
Duplicate not more than n characters of string.
Definition: new.c:208
#define NEWSTR(len)
Allocate new string buffer of length len+1.
Definition: new.h:64
size_t m_nFileLen
length of canonical file
Definition: path.c:80
char * NewSearchPathExpanded(const char *sSearchPath)
Expands search path.
Definition: path.c:227
FILE * LOG_GET_LOGFP()
Get current logging output stream file pointer.
Definition: log.c:301
char * new_strdup(const char *s)
Duplicate a string.
Definition: new.c:176
Doubly linked list of character strings "inherited" from dlistvoid.
char * NewExpandTilde(const char *sTildeExpr)
Expand tilde expression.
Definition: path.c:157
char * m_sFileCanonical
search canonical file
Definition: path.c:79
#define NULL
null pointer
Definition: rnrconfig.h:199
Memory allocation and deallocation declarations.
void SearchPathIterDelete(PathIter_T *pIter)
Deletes search path iterator.
Definition: path.c:422
#define isidentifier(c)
id char
Definition: path.c:71
#define LOGFUNCNAME
function name
Definition: log.h:225
#define LOGERROR(fmt,...)
Standard Error logging.
Definition: log.h:488
#define LOGABLE(level)
Test if given level is logable at current threshold.
Definition: log.h:195
#define LOGDIAG4(fmt,...)
Standard Diagnostic Level 4 logging.
Definition: log.h:386
PathIter_T * SearchPathIterNew(const char *sSearchPath)
Allocates and initilized a new search path iterator.
Definition: path.c:398
DListStr_T * m_pSearchPaths
dlist of search paths
Definition: path.c:78
#define NEW(T)
Allocate new type.
Definition: new.h:49
int bool_t
"boolean" T/F
Definition: rnrconfig.h:187
RoadNarrows Robotics common configuration file.
#define PATH_SEP_STR
path separator (string version)
Definition: rnrconfig.h:237
#define MAX_SEARCH_PATH
Maximum bytes of a PATH_SEP separated search path string (including NULL)
Definition: rnrconfig.h:222
char * m_sFilePath
concatenated file path (output)
Definition: path.c:81
General file name and file path utility declarations.
char * NewSearchPathCanonicalized(const char *sSearchPath)
Expands and canonicalizes a search path.
Definition: path.c:331
char * SearchPathIterNext(PathIter_T *pIter)
Construct next file path from search path and search file name.
Definition: path.c:526
Definition: path.c:76
static DListStr_T * DListStrNewDft()
Allocator and initializer new empty string dlist with default callbacks.
Definition: dliststr.h:91
static void DListStrDataPrint(FILE *fp, char *sData)
Print node data callback.
Definition: dliststr.h:74
char * NewRealPath(const char *sPath)
Expands and canonicalizes a real path.
Definition: path.c:778
DListStrIter_T m_iterPaths
dlist iterator over paths
Definition: path.c:82
Logger declarations.
#define PATH_SEP_CHAR
path separator (char version)
Definition: rnrconfig.h:236
char * NewJoinedPath(const char *sPath1, const char *sPath2)
Join two file paths.
Definition: path.c:802
char * NewNormPath(const char *sPath)
Normalize path.
Definition: path.c:572
#define LOG_LEVEL_DIAG4
diagnostic level 4
Definition: log.h:184
char * SearchPathIterFirst(PathIter_T *pIter, const char *sSearchFile)
Get the first concatenated file path.
Definition: path.c:452
static DListStr_T * NewSearchPathDList(const char *sSearchPath)
Build a dlist of canonical search paths.
Definition: path.c:93