logo

utils-std

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

pathchk.c (3057B)


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