path.c
Go to the documentation of this file.
1 /**
2  * @file path.c
3  * @brief Path manipulation helper functions
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 2.5.2
27  **/
28 
29 //Dependencies
30 #include <string.h>
31 #include <ctype.h>
32 #include "path.h"
33 
34 
35 /**
36  * @brief Test if the path is absolute
37  * @param[in] path NULL-terminated string that contains the path
38  * @return TRUE is the path is absolute, else FALSE
39  **/
40 
42 {
43  //Determine if the path is absolute or relative
44  if(path[0] == '/' || path[0] == '\\')
45  {
46  return TRUE;
47  }
48  else
49  {
50  return FALSE;
51  }
52 }
53 
54 
55 /**
56  * @brief Test if the path is relative
57  * @param[in] path NULL-terminated string that contains the path
58  * @return TRUE is the path is relative, else FALSE
59  **/
60 
62 {
63  //Determine if the path is absolute or relative
64  if(path[0] == '/' || path[0] == '\\')
65  {
66  return FALSE;
67  }
68  else
69  {
70  return TRUE;
71  }
72 }
73 
74 
75 /**
76  * @brief Extract the file name from the supplied path
77  * @param[in] path NULL-terminated string that contains the path
78  * @return Pointer to the file name
79  **/
80 
81 const char_t *pathGetFilename(const char_t *path)
82 {
83  size_t n;
84 
85  //Retrieve the length of the path
86  n = osStrlen(path);
87 
88  //Skip trailing slash or backslash characters
89  while(n > 0)
90  {
91  //Forward slash or backslash character found?
92  if(path[n - 1] != '/' && path[n - 1] != '\\')
93  {
94  break;
95  }
96 
97  //Previous character
98  n--;
99  }
100 
101  //Search the string for the last separator
102  while(n > 0)
103  {
104  //Forward slash or backslash character found?
105  if(path[n - 1] == '/' || path[n - 1] == '\\')
106  {
107  break;
108  }
109 
110  //Previous character
111  n--;
112  }
113 
114  //Return a pointer to the file name
115  return path + n;
116 }
117 
118 
119 /**
120  * @brief Remove the trailing file name from the supplied path
121  * @param[in] path NULL-terminated string that contains the path
122  **/
123 
125 {
126  char_t *p;
127 
128  //Remove the trailing file name and backslash from a path
129  p = (char_t *) pathGetFilename(path);
130  *p = '\0';
131 }
132 
133 
134 /**
135  * @brief Copy a path
136  * @param[out] dest Pointer to the destination buffer
137  * @param[in] src Pointer to the source path
138  * @param[in] maxLen Maximum pathname length
139  **/
140 
141 void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
142 {
143  size_t n;
144 
145  //Get the length of the source path
146  n = osStrlen(src);
147  //Limit the number of characters to be copied
148  n = MIN(n, maxLen);
149 
150  //Copy the string
151  osMemcpy(dest, src, n);
152  //Properly terminate the string with a NULL character
153  dest[n] = '\0';
154 }
155 
156 
157 /**
158  * @brief Simplify a path
159  * @param[in] path NULL-terminated string containing the path to be canonicalized
160  **/
161 
163 {
164  size_t i;
165  size_t j;
166  size_t k;
167 
168  //Move to the beginning of the string
169  i = 0;
170  k = 0;
171 
172  //Replace backslashes with forward slashes
173  while(path[i] != '\0')
174  {
175  //Forward slash or backslash separator found?
176  if(path[i] == '/' || path[i] == '\\')
177  {
178  path[k++] = '/';
179 
180  while(path[i] == '/' || path[i] == '\\')
181  {
182  i++;
183  }
184  }
185  else
186  {
187  path[k++] = path[i++];
188  }
189  }
190 
191  //Properly terminate the string with a NULL character
192  path[k] = '\0';
193 
194  //Move back to the beginning of the string
195  i = 0;
196  j = 0;
197  k = 0;
198 
199  //Parse the entire string
200  do
201  {
202  //Forward slash separator found?
203  if(path[i] == '/' || path[i] == '\0')
204  {
205  //"." element found?
206  if((i - j) == 1 && osStrncmp(path + j, ".", 1) == 0)
207  {
208  //Check whether the pathname is empty?
209  if(k == 0)
210  {
211  if(path[i] == '\0')
212  {
213  path[k++] = '.';
214  }
215  else if(path[i] == '/' && path[i + 1] == '\0')
216  {
217  path[k++] = '.';
218  path[k++] = '/';
219  }
220  }
221  else if(k > 1)
222  {
223  //Remove the final slash if necessary
224  if(path[i] == '\0')
225  {
226  k--;
227  }
228  }
229  }
230  //".." element found?
231  else if((i - j) == 2 && osStrncmp(path + j, "..", 2) == 0)
232  {
233  //Check whether the pathname is empty?
234  if(k == 0)
235  {
236  path[k++] = '.';
237  path[k++] = '.';
238 
239  //Append a slash if necessary
240  if(path[i] == '/')
241  {
242  path[k++] = '/';
243  }
244  }
245  else if(k > 1)
246  {
247  //Search the path for the previous slash
248  for(j = 1; j < k; j++)
249  {
250  if(path[k - j - 1] == '/')
251  {
252  break;
253  }
254  }
255 
256  //Slash separator found?
257  if(j < k)
258  {
259  if(osStrncmp(path + k - j, "..", 2) == 0)
260  {
261  path[k++] = '.';
262  path[k++] = '.';
263  }
264  else
265  {
266  k = k - j - 1;
267  }
268 
269  //Append a slash if necessary
270  if(k == 0 && path[0] == '/')
271  {
272  path[k++] = '/';
273  }
274  else if(path[i] == '/')
275  {
276  path[k++] = '/';
277  }
278  }
279  //No slash separator found?
280  else
281  {
282  if(k == 3 && osStrncmp(path, "..", 2) == 0)
283  {
284  path[k++] = '.';
285  path[k++] = '.';
286 
287  //Append a slash if necessary
288  if(path[i] == '/')
289  path[k++] = '/';
290  }
291  else if(path[i] == '\0')
292  {
293  k = 0;
294  path[k++] = '.';
295  }
296  else if(path[i] == '/' && path[i + 1] == '\0')
297  {
298  k = 0;
299  path[k++] = '.';
300  path[k++] = '/';
301  }
302  else
303  {
304  k = 0;
305  }
306  }
307  }
308  }
309  else
310  {
311  //Copy directory name
312  osMemmove(path + k, path + j, i - j);
313  //Advance write pointer
314  k += i - j;
315 
316  //Append a slash if necessary
317  if(path[i] == '/')
318  {
319  path[k++] = '/';
320  }
321  }
322 
323  //Move to the next token
324  while(path[i] == '/')
325  {
326  i++;
327  }
328 
329  j = i;
330  }
331  } while(path[i++] != '\0');
332 
333  //Properly terminate the string with a NULL character
334  path[k] = '\0';
335 }
336 
337 
338 /**
339  * @brief Add a slash to the end of a string
340  * @param[in,out] path NULL-terminated string that represents the path
341  * @param[in] maxLen Maximum pathname length
342  **/
343 
344 void pathAddSlash(char_t *path, size_t maxLen)
345 {
346  size_t n;
347 
348  //Retrieve the length of the string
349  n = osStrlen(path);
350 
351  //Add a slash character only if necessary
352  if(!n)
353  {
354  //Check the length of the resulting string
355  if(maxLen >= 1)
356  {
357  osStrcpy(path, "/");
358  }
359  }
360  else if(path[n - 1] != '/' && path[n - 1] != '\\')
361  {
362  //Check the length of the resulting string
363  if(maxLen >= (n + 1))
364  {
365  osStrcat(path, "/");
366  }
367  }
368 }
369 
370 
371 /**
372  * @brief Remove the trailing slash from a given path
373  * @param[in,out] path NULL-terminated string that contains the path
374  **/
375 
377 {
378  char_t *end;
379 
380  //Skip the leading slash character
381  if(pathIsAbsolute(path))
382  {
383  path++;
384  }
385 
386  //Search for the first slash character to be removed
387  for(end = NULL; *path != '\0'; path++)
388  {
389  if(*path != '/' && *path != '\\')
390  {
391  end = NULL;
392  }
393  else if(!end)
394  {
395  end = path;
396  }
397  }
398 
399  //Remove the trailing slash characters
400  if(end != NULL)
401  {
402  *end = '\0';
403  }
404 }
405 
406 
407 /**
408  * @brief Concatenate two paths
409  * @param[in,out] path NULL-terminated string containing the first path
410  * @param[in] more NULL-terminated string containing the second path
411  * @param[in] maxLen Maximum pathname length
412  **/
413 
414 void pathCombine(char_t *path, const char_t *more, size_t maxLen)
415 {
416  size_t n1;
417  size_t n2;
418 
419  //Append a slash character to the first path
420  if(*path != '\0')
421  {
422  pathAddSlash(path, maxLen);
423  }
424 
425  //Skip any slash character at the beginning of the second path
426  while(*more == '/' || *more == '\\')
427  {
428  more++;
429  }
430 
431  //Retrieve the length of the first path
432  n1 = osStrlen(path);
433  //Retrieve the length of second path
434  n2 = osStrlen(more);
435 
436  //Check the length of the resulting string
437  if(n1 < maxLen)
438  {
439  //Limit the number of characters to be copied
440  n2 = MIN(n2, maxLen - n1);
441  //Concatenate the resulting string
442  osMemcpy(path + n1, more, n2);
443  //Properly terminate the string with a NULL character
444  path[n1 + n2] = '\0';
445  }
446 }
447 
448 
449 /**
450  * @brief Check whether a file name matches the specified pattern
451  * @param[in] path NULL-terminated string that contains the path to be matched
452  * @param[in] pattern NULL-terminated string that contains the pattern for
453  * which to search. The pattern may contain wildcard characters
454  * @return TRUE if the path matches the specified pattern, else FALSE
455  **/
456 
457 bool_t pathMatch(const char_t *path, const char_t *pattern)
458 {
459  size_t i = 0;
460  size_t j = 0;
461 
462  //Parse the pattern string
463  while(pattern[j] != '\0')
464  {
465  //Any wildcard character found?
466  if(pattern[j] == '?')
467  {
468  //The question mark matches a single character
469  if(path[i] == '\0')
470  {
471  return FALSE;
472  }
473  else
474  {
475  //Advance position in pathname
476  i++;
477  //Advance position in pattern string
478  j++;
479  }
480  }
481  else if(pattern[j] == '*')
482  {
483  //The asterisk sign matches zero or more characters
484  if(path[i] == '\0')
485  {
486  //Advance position in pattern string
487  j++;
488  }
489  else if(pathMatch(path + i, pattern + j + 1))
490  {
491  return TRUE;
492  }
493  else
494  {
495  //Advance position in pathname
496  i++;
497  }
498  }
499  else
500  {
501  //Case insensitive comparison
502  if(osTolower(path[i]) != osTolower(pattern[j]))
503  {
504  return FALSE;
505  }
506  else
507  {
508  //Advance position in pathname
509  i++;
510  //Advance position in pattern string
511  j++;
512  }
513  }
514  }
515 
516  //Check whether the file name matches the specified pattern
517  if(path[i] == '\0' && pattern[j] == '\0')
518  {
519  return TRUE;
520  }
521  else
522  {
523  return FALSE;
524  }
525 }
Path manipulation helper functions.
int bool_t
Definition: compiler_port.h:61
uint8_t p
Definition: ndp.h:300
#define TRUE
Definition: os_port.h:50
bool_t pathIsRelative(const char_t *path)
Test if the path is relative.
Definition: path.c:61
#define osStrlen(s)
Definition: os_port.h:168
const char_t * pathGetFilename(const char_t *path)
Extract the file name from the supplied path.
Definition: path.c:81
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:162
#define FALSE
Definition: os_port.h:46
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
bool_t pathIsAbsolute(const char_t *path)
Test if the path is absolute.
Definition: path.c:41
bool_t pathMatch(const char_t *path, const char_t *pattern)
Check whether a file name matches the specified pattern.
Definition: path.c:457
void pathAddSlash(char_t *path, size_t maxLen)
Add a slash to the end of a string.
Definition: path.c:344
#define MIN(a, b)
Definition: os_port.h:63
char char_t
Definition: compiler_port.h:55
#define osStrcat(s1, s2)
Definition: os_port.h:222
uint8_t n
#define osTolower(c)
Definition: os_port.h:270
#define osStrncmp(s1, s2, length)
Definition: os_port.h:180
void pathRemoveSlash(char_t *path)
Remove the trailing slash from a given path.
Definition: path.c:376
#define osStrcpy(s1, s2)
Definition: os_port.h:210
void pathRemoveFilename(char_t *path)
Remove the trailing file name from the supplied path.
Definition: path.c:124
#define osMemmove(dest, src, length)
Definition: os_port.h:150
void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
Copy a path.
Definition: path.c:141
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:414