logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git

pathchk.c (3102B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include <assert.h>
  6. #include <errno.h>
  7. #include <limits.h> // PATH_MAX
  8. #include <stdbool.h>
  9. #include <stdio.h> // fprintf
  10. #include <string.h> // strerror
  11. #include <sys/stat.h> // lstat
  12. #include <unistd.h> // getopt
  13. // POSIX Portable Character Set
  14. // Returns 0 on success, or the first invalid character found
  15. static char
  16. str_pcs(char *str)
  17. {
  18. for(size_t i = 0; i < strlen(str); i++)
  19. {
  20. char c = str[i];
  21. if(c >= 0x07 && c <= 0x0D) continue;
  22. if(c >= 0x20 && c <= 0x7E) continue;
  23. return c;
  24. }
  25. return 0;
  26. }
  27. static void
  28. usage(void)
  29. {
  30. fprintf(stderr, "Usage: pathchk [-p] [-P] pathname...\n");
  31. }
  32. int
  33. main(int argc, char *argv[])
  34. {
  35. bool opt_P = false, opt_p = false;
  36. size_t path_max = PATH_MAX - 1;
  37. size_t name_max = NAME_MAX;
  38. int c = -1;
  39. while((c = getopt(argc, argv, ":pP")) != -1)
  40. {
  41. switch(c)
  42. {
  43. case 'P':
  44. opt_P = true;
  45. break;
  46. case 'p':
  47. opt_p = true;
  48. path_max = _POSIX_PATH_MAX - 1;
  49. name_max = _POSIX_NAME_MAX;
  50. break;
  51. case ':':
  52. fprintf(stderr, "pathchk: error: Missing operand for option: '-%c'\n", optopt);
  53. usage();
  54. return 1;
  55. case '?':
  56. fprintf(stderr, "pathchk: error: Unrecognised option: '-%c'\n", optopt);
  57. usage();
  58. return 1;
  59. }
  60. }
  61. argv += optind;
  62. argc -= optind;
  63. if(argc != 1)
  64. {
  65. usage();
  66. return 1;
  67. }
  68. int err = 0;
  69. for(int i = 0; i < argc; i++)
  70. {
  71. char *path = argv[i];
  72. size_t len = strlen(path);
  73. if(opt_P && len == 0)
  74. {
  75. fprintf(stderr, "pathchk: error: Operand number %d is empty\n", i);
  76. err = 1;
  77. }
  78. // PATH_MAX includes terminating NULL
  79. if(len > path_max)
  80. {
  81. fprintf(stderr,
  82. "pathchk: error: Path (%zd octets) is over the maximum size (%zd octets): %s\n",
  83. len,
  84. path_max,
  85. path);
  86. err = 1;
  87. }
  88. char *p = strtok(path, "/");
  89. do
  90. {
  91. if(p == NULL || p[0] == 0) break;
  92. if(p[0] == '-' && opt_P)
  93. {
  94. fprintf(stderr, "pathchk: error: Path component starts with an hyphen: %s\n", p);
  95. err = 1;
  96. }
  97. // NAME_MAX doesn't includes terminating NULL
  98. size_t name_len = strlen(p);
  99. if(name_len > name_max)
  100. {
  101. fprintf(stderr,
  102. "pathchk: error: Path component (%zd octets) is over the maximum size (%zd "
  103. "octets): %s\n",
  104. name_len,
  105. name_max,
  106. p);
  107. err = 1;
  108. }
  109. } while((p = strtok(NULL, "/")) != NULL);
  110. if(!opt_p)
  111. {
  112. assert(errno == 0);
  113. if(access(path, F_OK) < 0 && errno != ENOENT)
  114. {
  115. fprintf(stderr,
  116. "pathchk: error: Failed checking '%s' against filesystem: %s\n",
  117. path,
  118. strerror(errno));
  119. errno = 0;
  120. err = 1;
  121. }
  122. }
  123. else
  124. {
  125. char c_pcs = str_pcs(path);
  126. if(c_pcs != 0)
  127. {
  128. fprintf(stderr,
  129. "pathchk: error: Non-portable character '%c' (0x%02x) found in: %s\n",
  130. c_pcs,
  131. c_pcs,
  132. path);
  133. err = 1;
  134. }
  135. }
  136. }
  137. return err;
  138. }