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