// mkdir.c, version 1.0.4 // OMKOV coreutils implementation of POSIX mkdir // Copyright (C) 2020, Jakob Wakeling // MIT Licence /* TODO Cleanup p flag handling TODO Handle copying permissions */ #include "util/error.h" #include "util/mode.h" #include "util/optget.h" #include #include #include #include #include #define VERSION "1.0.4" static struct lop lops[] = { { "help", ARG_NUL, 256 }, { "version", ARG_NUL, 257 }, { NULL, 0, 0 } }; static char *mopt; static bool pflag; static mode_t mask; static inline int makedir(const char *path, chmod_t *m); static void hlp(void); static void ver(void); int main(int ac, char *av[]) { A0 = av[0]; struct opt opt = OPTGET_INIT; opt.str = "m:p"; opt.lops = lops; for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) { case 'm': { mopt = opt.arg; break; } case 'p': { pflag = true; break; } case 256: { hlp(); return 0; } case 257: { ver(); return 0; } default: { return 1; } } if (opt.ind == ac) { error(1, "missing operand"); } chmod_t *m = NULL; if (mopt) { mask = umask(0); m = strmode(mopt); if (!m) { error(1, "%s: invalid mode", mopt); } } bool warned = false; for (char **p = &av[opt.ind]; *p; ++p) { if (pflag) { // Create directory hierarchy if p flag is set char *q = *p; for (; *q == '/'; ++q) {} for (; *q; ++q) { if (*q != '/' || *(q + 1) == '/') { continue; } *q = 0; if (mkdir(*p, (S_IWUSR | S_IXUSR | ~mask) & 0777) && errno != EEXIST) { warn("%s: %s", *p, serr()); continue; } *q = '/'; } } if (makedir(*p, m)) { warn("%s: %s", *p, serr()); warned = true; } } free(m); return warned; } /* Make directory with specified permissions */ static inline int makedir(const char *path, chmod_t *m) { // If no chmod is given, use default mode if (!m) { if (mkdir(path, 0777)) { return 1; } return 0; } // Create directory with no permissions if (mkdir(path, 0)) { return 1; } mode_t mode = 0777; // Handle each chmod_t for (int i = 0;; ++i) switch (m[i].flag) { case MF_NORM: case MF_XIFX: { // If ref is null, use the file mode creation mask if (!m[i].ref) { m[i].ref = M_ALL & ~mask; } switch (m[i].op) { case '+': { mode |= m[i].ref & m[i].mod; break; } case '-': { mode &= (~(m[i].ref & m[i].mod) & 07777); break; } case '=': { mode &= ~m[i].ref; mode |= m[i].ref & m[i].mod; break; } } // If the XIFX flag is set, set execute permission if (m[i].flag == MF_XIFX) { mode |= m[i].ref & M_EX; } break; } case MF_COPY: { /* TODO */ } case MF_NULL: { goto end; } } end:; // Set permissions of directory to the calculated mode if (chmod(path, mode)) { return 1; } return 0; } /* Print help information */ static void hlp(void) { puts("mkdir - make directories\n"); puts("usage: mkdir [-p] [-m mode] dir...\n"); puts("options:"); puts(" -p Create all parent directories as required"); puts(" -m mode Set the file mode of the created directory"); puts(" --help Display help information"); puts(" --version Display version information"); } /* Print version information */ static void ver(void) { puts("OMKOV coreutils mkdir, version " VERSION); puts("Copyright (C) 2020, Jakob Wakeling"); puts("MIT Licence (https://opensource.org/licenses/MIT)"); }