logo

utils-std

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

mkdir.c (2738B)


  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 "../config.h"
  6. #include "../libutils/getopt_nolong.h"
  7. #include "../libutils/lib_mkdir.h"
  8. #include "../libutils/mode.h"
  9. #include <errno.h>
  10. #include <stdbool.h>
  11. #include <stdio.h> // fprintf
  12. #include <stdlib.h> // abort
  13. #include <string.h> // strerror
  14. #include <sys/stat.h> // mkdir
  15. #include <unistd.h> // getopt
  16. #ifdef HAS_GETOPT_LONG
  17. #include <getopt.h>
  18. #endif
  19. const char *argv0 = "mkdir";
  20. bool mkdir_parents_verbose = false;
  21. mode_t mkdir_parents_filemask;
  22. static int
  23. mkdir_simple(char *path, mode_t mode)
  24. {
  25. if(mkdir(path, mode) < 0)
  26. {
  27. fprintf(stderr, "%s: error: Failed making directory '%s': %s\n", argv0, path, strerror(errno));
  28. errno = 0;
  29. return -1;
  30. }
  31. if(mkdir_parents_verbose) fprintf(stderr, "%s: Made directory: %s\n", argv0, path);
  32. return 0;
  33. }
  34. static void
  35. usage(void)
  36. {
  37. fprintf(stderr, "Usage: mkdir [-pv] [-m mode] path ...\n");
  38. }
  39. int
  40. main(int argc, char *argv[])
  41. {
  42. mkdir_parents_filemask = umask(0);
  43. umask(mkdir_parents_filemask);
  44. // clang-format off
  45. mode_t mode = (S_IRWXU | S_IRWXG | S_IRWXO | ~mkdir_parents_filemask) & 0777;
  46. bool opt_p = false;
  47. const char *errstr = NULL;
  48. // clang-format on
  49. #ifdef HAS_GETOPT_LONG
  50. // Strictly for GNUisms compatibility so no long-only options
  51. // clang-format off
  52. static struct option opts[] = {
  53. {"mode", required_argument, NULL, 'm'},
  54. {"parents", no_argument, NULL, 'p'},
  55. {"verbose", no_argument, NULL, 'v'},
  56. {0, 0, 0, 0},
  57. };
  58. // clang-format on
  59. // Need + as first character to get POSIX-style option parsing
  60. for(int c = -1; (c = getopt_long(argc, argv, "+:pvm:", opts, NULL)) != -1;)
  61. #else
  62. for(int c = -1; (c = getopt_nolong(argc, argv, ":pvm:")) != -1;)
  63. #endif
  64. {
  65. switch(c)
  66. {
  67. case 'p':
  68. opt_p = true;
  69. break;
  70. case 'v':
  71. mkdir_parents_verbose = true;
  72. break;
  73. case 'm':
  74. mode = new_mode(optarg, 0777, &errstr);
  75. if(errstr != NULL)
  76. {
  77. fprintf(stderr, "%s: error: Failed parsing mode '%s': %s\n", argv0, optarg, errstr);
  78. return 1;
  79. }
  80. break;
  81. case ':':
  82. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  83. usage();
  84. return 1;
  85. case '?':
  86. GETOPT_UNKNOWN_OPT
  87. usage();
  88. return 1;
  89. default:
  90. abort();
  91. }
  92. }
  93. argc -= optind;
  94. argv += optind;
  95. if(argc < 1)
  96. {
  97. fprintf(stderr, "%s: error: Missing operand\n", argv0);
  98. usage();
  99. return 1;
  100. }
  101. for(int i = 0; i < argc; i++)
  102. {
  103. int ret = 0;
  104. if(opt_p)
  105. ret = mkdir_parents(argv[i], mode);
  106. else
  107. ret = mkdir_simple(argv[i], mode);
  108. if(ret < 0) return 1;
  109. }
  110. return 0;
  111. }