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-2024 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.4.0
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  return TRUE;
46  else
47  return FALSE;
48 }
49 
50 
51 /**
52  * @brief Test if the path is relative
53  * @param[in] path NULL-terminated string that contains the path
54  * @return TRUE is the path is relative, else FALSE
55  **/
56 
58 {
59  //Determine if the path is absolute or relative
60  if(path[0] == '/' || path[0] == '\\')
61  return FALSE;
62  else
63  return TRUE;
64 }
65 
66 
67 /**
68  * @brief Extract the file name from the supplied path
69  * @param[in] path NULL-terminated string that contains the path
70  * @return Pointer to the file name
71  **/
72 
73 const char_t *pathGetFilename(const char_t *path)
74 {
75  size_t n;
76 
77  //Retrieve the length of the path
78  n = osStrlen(path);
79 
80  //Skip trailing slash or backslash characters
81  while(n > 0)
82  {
83  //Forward slash or backslash character found?
84  if(path[n - 1] != '/' && path[n - 1] != '\\')
85  break;
86 
87  //Previous character
88  n--;
89  }
90 
91  //Search the string for the last separator
92  while(n > 0)
93  {
94  //Forward slash or backslash character found?
95  if(path[n - 1] == '/' || path[n - 1] == '\\')
96  break;
97 
98  //Previous character
99  n--;
100  }
101 
102  //Return a pointer to the file name
103  return path + n;
104 }
105 
106 
107 /**
108  * @brief Remove the trailing file name from the supplied path
109  * @param[in] path NULL-terminated string that contains the path
110  **/
111 
113 {
114  char_t *p;
115 
116  //Remove the trailing file name and backslash from a path
117  p = (char_t *) pathGetFilename(path);
118  *p = '\0';
119 }
120 
121 
122 /**
123  * @brief Copy a path
124  * @param[out] dest Pointer to the destination buffer
125  * @param[in] src Pointer to the source path
126  * @param[in] maxLen Maximum pathname length
127  **/
128 
129 void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
130 {
131  size_t n;
132 
133  //Get the length of the source path
134  n = osStrlen(src);
135  //Limit the number of characters to be copied
136  n = MIN(n, maxLen);
137 
138  //Copy the string
139  osMemcpy(dest, src, n);
140  //Properly terminate the string with a NULL character
141  dest[n] = '\0';
142 }
143 
144 
145 /**
146  * @brief Simplify a path
147  * @param[in] path NULL-terminated string containing the path to be canonicalized
148  **/
149 
151 {
152  size_t i;
153  size_t j;
154  size_t k;
155 
156  //Move to the beginning of the string
157  i = 0;
158  k = 0;
159 
160  //Replace backslashes with forward slashes
161  while(path[i] != '\0')
162  {
163  //Forward slash or backslash separator found?
164  if(path[i] == '/' || path[i] == '\\')
165  {
166  path[k++] = '/';
167  while(path[i] == '/' || path[i] == '\\') i++;
168  }
169  else
170  {
171  path[k++] = path[i++];
172  }
173  }
174 
175  //Properly terminate the string with a NULL character
176  path[k] = '\0';
177 
178  //Move back to the beginning of the string
179  i = 0;
180  j = 0;
181  k = 0;
182 
183  //Parse the entire string
184  do
185  {
186  //Forward slash separator found?
187  if(path[i] == '/' || path[i] == '\0')
188  {
189  //"." element found?
190  if((i - j) == 1 && !osStrncmp(path + j, ".", 1))
191  {
192  //Check whether the pathname is empty?
193  if(k == 0)
194  {
195  if(path[i] == '\0')
196  {
197  path[k++] = '.';
198  }
199  else if(path[i] == '/' && path[i + 1] == '\0')
200  {
201  path[k++] = '.';
202  path[k++] = '/';
203  }
204  }
205  else if(k > 1)
206  {
207  //Remove the final slash if necessary
208  if(path[i] == '\0')
209  k--;
210  }
211  }
212  //".." element found?
213  else if((i - j) == 2 && !osStrncmp(path + j, "..", 2))
214  {
215  //Check whether the pathname is empty?
216  if(k == 0)
217  {
218  path[k++] = '.';
219  path[k++] = '.';
220 
221  //Append a slash if necessary
222  if(path[i] == '/')
223  path[k++] = '/';
224  }
225  else if(k > 1)
226  {
227  //Search the path for the previous slash
228  for(j = 1; j < k; j++)
229  {
230  if(path[k - j - 1] == '/')
231  break;
232  }
233 
234  //Slash separator found?
235  if(j < k)
236  {
237  if(!osStrncmp(path + k - j, "..", 2))
238  {
239  path[k++] = '.';
240  path[k++] = '.';
241  }
242  else
243  {
244  k = k - j - 1;
245  }
246 
247  //Append a slash if necessary
248  if(k == 0 && path[0] == '/')
249  path[k++] = '/';
250  else if(path[i] == '/')
251  path[k++] = '/';
252  }
253  //No slash separator found?
254  else
255  {
256  if(k == 3 && !osStrncmp(path, "..", 2))
257  {
258  path[k++] = '.';
259  path[k++] = '.';
260 
261  //Append a slash if necessary
262  if(path[i] == '/')
263  path[k++] = '/';
264  }
265  else if(path[i] == '\0')
266  {
267  k = 0;
268  path[k++] = '.';
269  }
270  else if(path[i] == '/' && path[i + 1] == '\0')
271  {
272  k = 0;
273  path[k++] = '.';
274  path[k++] = '/';
275  }
276  else
277  {
278  k = 0;
279  }
280  }
281  }
282  }
283  else
284  {
285  //Copy directory name
286  osMemmove(path + k, path + j, i - j);
287  //Advance write pointer
288  k += i - j;
289 
290  //Append a slash if necessary
291  if(path[i] == '/')
292  path[k++] = '/';
293  }
294 
295  //Move to the next token
296  while(path[i] == '/') i++;
297  j = i;
298  }
299  } while(path[i++] != '\0');
300 
301  //Properly terminate the string with a NULL character
302  path[k] = '\0';
303 }
304 
305 
306 /**
307  * @brief Add a slash to the end of a string
308  * @param[in,out] path NULL-terminated string that represents the path
309  * @param[in] maxLen Maximum pathname length
310  **/
311 
312 void pathAddSlash(char_t *path, size_t maxLen)
313 {
314  size_t n;
315 
316  //Retrieve the length of the string
317  n = osStrlen(path);
318 
319  //Add a slash character only if necessary
320  if(!n)
321  {
322  //Check the length of the resulting string
323  if(maxLen >= 1)
324  osStrcpy(path, "/");
325  }
326  else if(path[n - 1] != '/' && path[n - 1] != '\\')
327  {
328  //Check the length of the resulting string
329  if(maxLen >= (n + 1))
330  osStrcat(path, "/");
331  }
332 }
333 
334 
335 /**
336  * @brief Remove the trailing slash from a given path
337  * @param[in,out] path NULL-terminated string that contains the path
338  **/
339 
341 {
342  char_t *end;
343 
344  //Skip the leading slash character
345  if(pathIsAbsolute(path))
346  path++;
347 
348  //Search for the first slash character to be removed
349  for(end = NULL; *path != '\0'; path++)
350  {
351  if(*path != '/' && *path != '\\')
352  end = NULL;
353  else if(!end)
354  end = path;
355  }
356 
357  //Remove the trailing slash characters
358  if(end)
359  *end = '\0';
360 }
361 
362 
363 /**
364  * @brief Concatenate two paths
365  * @param[in,out] path NULL-terminated string containing the first path
366  * @param[in] more NULL-terminated string containing the second path
367  * @param[in] maxLen Maximum pathname length
368  **/
369 
370 void pathCombine(char_t *path, const char_t *more, size_t maxLen)
371 {
372  size_t n1;
373  size_t n2;
374 
375  //Append a slash character to the first path
376  if(*path != '\0')
377  pathAddSlash(path, maxLen);
378 
379  //Skip any slash character at the beginning of the second path
380  while(*more == '/' || *more == '\\') more++;
381 
382  //Retrieve the length of the first path
383  n1 = osStrlen(path);
384  //Retrieve the length of second path
385  n2 = osStrlen(more);
386 
387  //Check the length of the resulting string
388  if(n1 < maxLen)
389  {
390  //Limit the number of characters to be copied
391  n2 = MIN(n2, maxLen - n1);
392  //Concatenate the resulting string
393  osMemcpy(path + n1, more, n2);
394  //Properly terminate the string with a NULL character
395  path[n1 + n2] = '\0';
396  }
397 }
398 
399 
400 /**
401  * @brief Check whether a file name matches the specified pattern
402  * @param[in] path NULL-terminated string that contains the path to be matched
403  * @param[in] pattern NULL-terminated string that contains the pattern for
404  * which to search. The pattern may contain wildcard characters
405  * @return TRUE if the path matches the specified pattern, else FALSE
406  **/
407 
408 bool_t pathMatch(const char_t *path, const char_t *pattern)
409 {
410  size_t i = 0;
411  size_t j = 0;
412 
413  //Parse the pattern string
414  while(pattern[j] != '\0')
415  {
416  //Any wildcard character found?
417  if(pattern[j] == '?')
418  {
419  //The question mark matches a single character
420  if(path[i] == '\0')
421  {
422  return FALSE;
423  }
424  else
425  {
426  //Advance position in pathname
427  i++;
428  //Advance position in pattern string
429  j++;
430  }
431  }
432  else if(pattern[j] == '*')
433  {
434  //The asterisk sign matches zero or more characters
435  if(path[i] == '\0')
436  {
437  //Advance position in pattern string
438  j++;
439  }
440  else if(pathMatch(path + i, pattern + j + 1))
441  {
442  return TRUE;
443  }
444  else
445  {
446  //Advance position in pathname
447  i++;
448  }
449  }
450  else
451  {
452  //Case insensitive comparison
453  if(osTolower(path[i]) != osTolower(pattern[j]))
454  {
455  return FALSE;
456  }
457  else
458  {
459  //Advance position in pathname
460  i++;
461  //Advance position in pattern string
462  j++;
463  }
464  }
465  }
466 
467  //Check whether the file name matches the specified pattern
468  if(path[i] == '\0' && pattern[j] == '\0')
469  return TRUE;
470  else
471  return FALSE;
472 }
char char_t
Definition: compiler_port.h:48
int bool_t
Definition: compiler_port.h:53
uint8_t n
uint8_t p
Definition: ndp.h:300
#define osMemmove(dest, src, length)
Definition: os_port.h:147
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define MIN(a, b)
Definition: os_port.h:63
#define osTolower(c)
Definition: os_port.h:261
#define osStrlen(s)
Definition: os_port.h:165
#define osStrncmp(s1, s2, length)
Definition: os_port.h:177
#define osStrcat(s1, s2)
Definition: os_port.h:219
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define osStrcpy(s1, s2)
Definition: os_port.h:207
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:370
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:150
const char_t * pathGetFilename(const char_t *path)
Extract the file name from the supplied path.
Definition: path.c:73
void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
Copy a path.
Definition: path.c:129
void pathAddSlash(char_t *path, size_t maxLen)
Add a slash to the end of a string.
Definition: path.c:312
void pathRemoveFilename(char_t *path)
Remove the trailing file name from the supplied path.
Definition: path.c:112
bool_t pathIsRelative(const char_t *path)
Test if the path is relative.
Definition: path.c:57
bool_t pathIsAbsolute(const char_t *path)
Test if the path is absolute.
Definition: path.c:41
void pathRemoveSlash(char_t *path)
Remove the trailing slash from a given path.
Definition: path.c:340
bool_t pathMatch(const char_t *path, const char_t *pattern)
Check whether a file name matches the specified pattern.
Definition: path.c:408
Path manipulation helper functions.