logo

utils-std

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

rmdir.c (2670B)


  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 <stdbool.h>
  7. #include <stdio.h> // fprintf
  8. #include <stdlib.h> // abort
  9. #include <string.h> // strerror, strrchr
  10. #include <unistd.h> // getopt, rmdir
  11. #ifdef HAS_GETOPT_LONG
  12. #include <getopt.h>
  13. #endif
  14. static void
  15. usage(void)
  16. {
  17. #ifdef HAS_GETOPT_LONG
  18. fprintf(stderr, "Usage: rmdir [-pv] [--ignore-fail-on-non-empty] directory...\n");
  19. #else
  20. fprintf(stderr, "Usage: rmdir [-pv] directory...\n");
  21. #endif
  22. }
  23. int
  24. main(int argc, char *argv[])
  25. {
  26. bool parents = false, verbose = false, ign_enotempty = false;
  27. int c = -1;
  28. #ifdef HAS_GETOPT_LONG
  29. // clang-format off
  30. enum long_opt_vals {
  31. IGN_ENOTEMPTY = 1
  32. };
  33. static struct option opts[] = {
  34. {"ignore-fail-on-non-empty", no_argument, 0, IGN_ENOTEMPTY},
  35. {"parents", no_argument, 0, 'p'},
  36. {"verbose", no_argument, 0, 'v'},
  37. {0, 0, 0, 0},
  38. };
  39. // clang-format on
  40. // Need + as first character to get POSIX-style option parsing
  41. while((c = getopt_long(argc, argv, "+:pv", opts, NULL)) != -1)
  42. #else
  43. while((c = getopt(argc, argv, ":pv")) != -1)
  44. #endif
  45. {
  46. switch(c)
  47. {
  48. #ifdef HAS_GETOPT_LONG
  49. case IGN_ENOTEMPTY:
  50. ign_enotempty = true;
  51. break;
  52. #endif
  53. case 'p':
  54. parents = true;
  55. break;
  56. case 'v':
  57. verbose = true;
  58. break;
  59. case ':':
  60. fprintf(stderr, "rmdir: Error: Missing operand for option: '-%c'\n", optopt);
  61. usage();
  62. return 1;
  63. case '?':
  64. fprintf(stderr, "rmdir: Error: Unrecognised option: '-%c'\n", optopt);
  65. usage();
  66. return 1;
  67. default:
  68. abort();
  69. }
  70. }
  71. argc -= optind;
  72. argv += optind;
  73. if(argc == 0)
  74. {
  75. fprintf(stderr, "rmdir: missing operand\n");
  76. usage();
  77. return 1;
  78. }
  79. int err = 0;
  80. for(int i = 0; i < argc; i++)
  81. {
  82. errno = 0;
  83. char *path = argv[i];
  84. if(rmdir(path) < 0)
  85. {
  86. if(ign_enotempty && (errno == ENOTEMPTY || errno == EEXIST)) continue;
  87. fprintf(stderr, "rmdir: Failed removing '%s': %s\n", path, strerror(errno));
  88. err = 1;
  89. continue;
  90. }
  91. if(verbose) fprintf(stderr, "rmdir: Removed '%s'\n", path);
  92. if(!parents) continue;
  93. while(true)
  94. {
  95. char *sep = strrchr(path, '/');
  96. if(sep == NULL) break;
  97. *sep = 0;
  98. if(*path == 0) break;
  99. errno = 0;
  100. if(rmdir(path) < 0)
  101. {
  102. if(errno == ENOTDIR) break;
  103. if(ign_enotempty && (errno == ENOTEMPTY || errno == EEXIST)) break;
  104. fprintf(stderr, "rmdir: Failed removing '%s': %s\n", path, strerror(errno));
  105. err = 1;
  106. break;
  107. }
  108. if(verbose) fprintf(stderr, "rmdir: Removed '%s'\n", path);
  109. }
  110. }
  111. return err;
  112. }