pathexpand.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. /*
  5. * Returns the analog of "cd path" from a directory "cwd".
  6. * The root is defined as empty path (instead of "/")
  7. * An attempt to go past the root (with "..") leaves the path at root.
  8. * The cwd is not expanded.
  9. */
  10. char *pathexpand(const char *cwd, const char *path)
  11. {
  12. static const char SEP[] = "/";
  13. if (!*path) /* empty path -> "." (don't move) */
  14. path = ".";
  15. if (!*cwd || *SEP == *path) /* no cwd, or path begins with "/" */
  16. cwd = "";
  17. while (*cwd && *SEP == *cwd)
  18. ++cwd;
  19. size_t len = strlen(cwd);
  20. char *out = malloc(len + 1 + strlen(path) + 1);
  21. char *p = strcpy(out, cwd) + len;
  22. for (; *path; ++path)
  23. {
  24. char *pl;
  25. if (p > out && p[-1] != *SEP)
  26. *p++ = *SEP;
  27. pl = p;
  28. while (*path && *SEP != *path)
  29. *p++ = *path++;
  30. *p = '\0';
  31. /* ..."//"... */
  32. if (p == pl)
  33. ; /* just ignore */
  34. /* ..."/./"... */
  35. else if ( p - pl == 1 && '.' == *pl )
  36. --p; /* just ignore */
  37. /* ..."/../"... */
  38. else if ( p - pl == 2 && '.' == pl[0] && '.' == pl[1] )
  39. {
  40. /* drop the last element of the resulting path */
  41. if (pl > out && --pl > out)
  42. for (--pl; pl > out && *SEP != *pl; --pl)
  43. ;
  44. p = pl > out ? ++pl: out;
  45. }
  46. /* ..."/path/"... */
  47. else if (*path)
  48. *p++ = *path; /* just add the separator */
  49. if (!*path)
  50. break;
  51. }
  52. if (p > out+1 && *SEP == p[-1])
  53. --p;
  54. *p = '\0';
  55. return out;
  56. }
  57. #ifdef CHECK_PATHEXPAND
  58. static void check(const char *cwd, const char *path, const char *good)
  59. {
  60. static int n = 0;
  61. printf("%-2d: %10s$ cd %s", ++n, cwd, path);
  62. char *t = pathexpand(cwd, path);
  63. if ( strcmp(t, good) )
  64. printf(" ____________________failed(%s)\n", t);
  65. else
  66. printf(" \033[32m%s\033[0m\n", t);
  67. free(t);
  68. }
  69. int main(int argc, char **argv)
  70. {
  71. /* 1 */ check("/onelevel", "aa", "onelevel/aa");
  72. /* 2 */ check("/", "..", "");
  73. /* 3 */ check("/", "../..", "");
  74. /* 4 */ check("/one", "aa/../bb", "one/bb");
  75. /* 5 */ check("/one/two", "aa//bb", "one/two/aa/bb");
  76. /* 6 */ check("", "/aa//bb", "aa/bb");
  77. /* 7 */ check("/one/two", "", "one/two");
  78. /* 8 */ check("/one/two", "aa/..bb/x/../cc/", "one/two/aa/..bb/cc");
  79. /* 9 */ check("/one/two", "aa/x/././cc////", "one/two/aa/x/cc");
  80. /* 10 */ check("/one/two", "../../../../aa", "aa");
  81. /* 11 */ check("one/", "../one/two", "one/two");
  82. /* 12 */ check("", "../../two", "two");
  83. /* 13 */ check("a/b/c", "../../two", "a/two");
  84. /* 14 */ check("a/b/", "../two", "a/two");
  85. /* 15 */ check("///", "../two", "two");
  86. return 0;
  87. }
  88. #endif