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