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