toaruos/apps/chmod.c

137 lines
2.4 KiB
C
Raw Permalink Normal View History

/**
* @brief chmod - change file permissions
*
* This implementation is likely non-compliant, though it does
* attempt to look similar to the standard POSIX syntax,
* supporting both octal mode setings and +/-rwx flavors.
*
* @copyright
2018-08-14 11:13:38 +03:00
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2018 K. Lange
*/
2018-07-17 13:44:23 +03:00
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
enum mode_set {
MODE_SET,
MODE_ADD,
MODE_REMOVE,
};
static int calc(int mode, int users) {
int out = 0;
if (users & 1) {
out |= (mode << 6);
}
if (users & 2) {
out |= (mode << 3);
}
if (users & 4) {
out |= (mode << 0);
}
return out;
}
int main(int argc, char * argv[]) {
if (argc < 3) {
fprintf(stderr, "usage: %s OCTAL-MODE FILE...\n", argv[0]);
return 1;
}
/* Parse mode */
int mode = 0;
enum mode_set mode_set = MODE_SET;
char * c = argv[1];
int user_modes = 0;
2018-07-17 13:53:50 +03:00
int all_users = 7;
2018-07-17 13:44:23 +03:00
while (*c) {
switch (*c) {
case '0':
c++; /* 0 */
while (*c >= '0' && *c <= '7') {
2018-07-17 13:44:23 +03:00
mode *= 8;
mode += (*c - '0');
c++;
}
break;
case 'u':
2018-07-17 13:53:50 +03:00
all_users = 0;
2018-07-17 13:44:23 +03:00
user_modes |= 1;
c++;
break;
case 'g':
2018-07-17 13:53:50 +03:00
all_users = 0;
2018-07-17 13:44:23 +03:00
user_modes |= 2;
c++;
break;
case 'o':
2018-07-17 13:53:50 +03:00
all_users = 0;
2018-07-17 13:44:23 +03:00
user_modes |= 4;
c++;
break;
case 'a':
2018-07-17 13:53:50 +03:00
all_users = 7;
2018-07-17 13:44:23 +03:00
user_modes = 7;
c++;
break;
case '-':
mode_set = MODE_REMOVE;
c++;
break;
case '+':
mode_set = MODE_ADD;
c++;
break;
case '=':
mode_set = MODE_SET;
c++;
break;
case 'r':
2018-07-17 13:53:50 +03:00
mode |= calc(S_IROTH, user_modes | all_users);
2018-07-17 13:44:23 +03:00
c++;
break;
case 'w':
2018-07-17 13:53:50 +03:00
mode |= calc(S_IWOTH, user_modes | all_users);
2018-07-17 13:44:23 +03:00
c++;
break;
case 'x':
2018-07-17 13:53:50 +03:00
mode |= calc(S_IXOTH, user_modes | all_users);
2018-07-17 13:44:23 +03:00
c++;
break;
}
}
int i = 2;
while (i < argc) {
int actual_mode = 0;
struct stat _stat;
if (stat(argv[i], &_stat) < 0) {
fprintf(stderr, "%s: %s: error with stat\n", argv[0], argv[i]);
}
switch (mode_set) {
case MODE_SET:
actual_mode = mode;
break;
case MODE_ADD:
actual_mode = _stat.st_mode | mode;
break;
case MODE_REMOVE:
actual_mode = _stat.st_mode &= ~(mode);
break;
}
if (chmod(argv[i], actual_mode) < 0) {
fprintf(stderr, "%s: %s: error with chmod\n", argv[0], argv[i]);
return 1;
}
i++;
}
return 0;
}