Author | Jamozed <[email protected]> |
Date | 2020-11-12 11:17:34 |
Commit | 70ce5b01e577b7b480ce65f9c471de1336660d47 |
Parent | bcc3fa8913bea92f3d2a168c0523ada66e1853ec |
chmod: Add chmod utility
Diffstat
M | CMakeLists.txt | | | 1 | + |
M | README.md | | | 1 | + |
A | src/chmod.c | | | 138 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 140 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a008c6..c8c3b54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ LINK_LIBRARIES(lib) ADD_EXECUTABLE(basename ${PROJECT_SOURCE_DIR}/src/basename.c) ADD_EXECUTABLE(cat ${PROJECT_SOURCE_DIR}/src/cat.c) +ADD_EXECUTABLE(chmod ${PROJECT_SOURCE_DIR}/src/chmod.c) ADD_EXECUTABLE(cksum ${PROJECT_SOURCE_DIR}/src/cksum.c) ADD_EXECUTABLE(dirname ${PROJECT_SOURCE_DIR}/src/dirname.c) ADD_EXECUTABLE(echo ${PROJECT_SOURCE_DIR}/src/echo.c) diff --git a/README.md b/README.md index cfc5002..2f5457e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ UNIX-like systems. | ---------------- | ---------------------------------------- | -------- | | basename | Return non-directory portion of a path | POSIX | | cat | Concatenate and print files | POSIX | +| chmod | Change the file modes | POSIX | | cksum | Write file checksums and sizes | POSIX | | dirname | Return the directory portion of a path | POSIX | | echo | Write arguments to standard output | POSIX | diff --git a/src/chmod.c b/src/chmod.c new file mode 100644 index 0000000..c6dc1e0 --- /dev/null +++ b/src/chmod.c @@ -0,0 +1,138 @@ +// chmod.c, version 1.0.0 +// OMKOV coreutils implementation of POSIX chmod +// Copyright (C) 2020, Jakob Wakeling +// All rights reserved. + +/* +OMKOV Permissive Licence, version 1.0 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimers. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimers in the documentation and/or + other materials provided with the distribution. +* Neither the names of the copyright holders, nor the names of its contributors + may be used to endorse or promote products derived from this Software without + specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +*/ + +/* + TODO Handle R flag + TODO Handle copying permission +*/ + +#include "lib/error.h" +#include "lib/mode.h" +#include "lib/optget.h" + +#include <sys/stat.h> + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#define VERSION "1.0.0" + +static struct lop lops[] = { + { "help", ARG_NUL, 256 }, + { "version", ARG_NUL, 257 }, + { NULL, 0, 0 } +}; + +static bool Rflag; + +static mode_t mask; + +static inline int setmode(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 = "R"; opt.lops = lops; + for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) { + case 'R': { Rflag = true; break; } + case 256: { hlp(); return 0; } + case 257: { ver(); return 0; } + default: { return 1; } + } + + if (opt.ind >= ac - 1) { error(1, "missing operand"); } + + mask = umask(0); chmod_t *m = strmode(av[opt.ind]); + if (!m) { error(1, "%s: invalid mode", av[opt.ind]); } + + bool warned = false; + + for (char **p = &av[opt.ind + 1]; *p; ++p) { + if (setmode(*p, m)) { warn("%s: %s", *p, serr()); warned = true; } + } + + if (m) { free(m); } return warned; +} + +/* Set the mode of the specified file */ +static inline int setmode(const char *path, chmod_t *m) { + // Get current permissions of file + struct stat st; if (stat(path, &st)) { return 1; }; + mode_t mode = st.st_mode; + + // 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 accordingly + if (m[i].flag == MF_XIFX) { + if (S_ISDIR(mode) || mode & M_EX) { mode |= m[i].ref & M_EX; } + } break; + } + case MF_COPY: { /* TODO */ } + case MF_NULL: { goto end; } + } end:; + + // Set permissions of the file to the calculated mode + if (chmod(path, mode)) { return 1; } return 0; +} + +/* Print help information */ +static void hlp(void) { + puts("chmod - change the file modes\n"); + puts("usage: chmod [-R] mode file...\n"); + puts("options:"); + puts(" -R Recursively change file mode bits"); + puts(" --help Display help information"); + puts(" --version Display version information"); + return; +} + +/* Print version information */ +static void ver(void) { + puts("OMKOV coreutils chmod, version " VERSION); + puts("Copyright (C) 2020, Jakob Wakeling"); + puts("All rights reserved."); + puts("OMKOV Permissive Licence (https://www.omkov.net/OLPE)"); + return; +}