1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 19:19:53 +00:00

Add os/perm-int and os/perm-str.

This helps address #331. While we could also
make os/stat return an integer, we don't do that yet
for api breakage reasons.

This also lets us use this logic on other functions
that take permission strings.
This commit is contained in:
Calvin Rose 2020-04-03 15:02:12 -05:00
parent f2815d7068
commit 464fb73d83

View File

@ -880,40 +880,23 @@ static Janet os_readlink(int32_t argc, Janet *argv) {
} }
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
static const uint8_t *janet_decode_permissions(unsigned short m) {
uint8_t flags[9] = {0}; typedef struct _stat jstat_t;
flags[0] = flags[3] = flags[6] = (m & S_IREAD) ? 'r' : '-'; typedef unsigned short jmode_t;
flags[1] = flags[4] = flags[7] = (m & S_IWRITE) ? 'w' : '-';
flags[2] = flags[5] = flags[8] = (m & S_IEXEC) ? 'x' : '-'; static int32_t janet_perm_to_unix(unsigned short m) {
return janet_string(flags, sizeof(flags)); int32_t ret = 0;
if (m & S_IEXEC) ret |= 0111;
if (m & S_IWRITE) ret |= 0222;
if (m & S_IREAD) ret |= 0444;
return ret;
} }
static unsigned short janet_encode_permissions(Janet *argv, int32_t n) { static unsigned short janet_perm_from_unix(int32_t x) {
if (janet_checkint(argv[n])) {
int32_t x = janet_unwrap_integer(argv[n]);
if (x < 0 || x > 0777) {
janet_panicf("expected integer in range [0, 8r777], got %v", argv[n]);
}
unsigned short m = 0; unsigned short m = 0;
if (x & 1 || x & 010 || x & 0100) m |= S_IEXEC; if (x & 111) m |= S_IEXEC;
if (x & 2 || x & 020 || x & 0200) m |= S_IWRITE; if (x & 222) m |= S_IWRITE;
if (x & 4 || x & 040 || x & 0400) m |= S_IREAD; if (x & 444) m |= S_IREAD;
return m;
}
JanetString perm = janet_getstring(argv, n);
if (janet_string_length(perm) != 9) {
janet_panicf("expected string of length 9, got %S", perm);
}
unsigned short m = 0;
if (perm[0] == 'r') m |= S_IREAD;
if (perm[1] == 'w') m |= S_IWRITE;
if (perm[2] == 'x') m |= S_IEXEC;
if (perm[3] == 'r') m |= S_IREAD;
if (perm[4] == 'w') m |= S_IWRITE;
if (perm[5] == 'x') m |= S_IEXEC;
if (perm[6] == 'r') m |= S_IREAD;
if (perm[7] == 'w') m |= S_IWRITE;
if (perm[8] == 'x') m |= S_IEXEC;
return m; return m;
} }
@ -924,45 +907,23 @@ static const uint8_t *janet_decode_mode(unsigned short m) {
else if (m & _S_IFCHR) str = "character"; else if (m & _S_IFCHR) str = "character";
return janet_ckeyword(str); return janet_ckeyword(str);
} }
#else
static const uint8_t *janet_decode_permissions(mode_t m) { static int32_t janet_decode_permissions(jmode_t mode) {
uint8_t flags[9] = {0}; return (int32_t)(mode & (S_IEXEC | S_IWRITE | S_IREAD));
flags[0] = (m & S_IRUSR) ? 'r' : '-';
flags[1] = (m & S_IWUSR) ? 'w' : '-';
flags[2] = (m & S_IXUSR) ? 'x' : '-';
flags[3] = (m & S_IRGRP) ? 'r' : '-';
flags[4] = (m & S_IWGRP) ? 'w' : '-';
flags[5] = (m & S_IXGRP) ? 'x' : '-';
flags[6] = (m & S_IROTH) ? 'r' : '-';
flags[7] = (m & S_IWOTH) ? 'w' : '-';
flags[8] = (m & S_IXOTH) ? 'x' : '-';
return janet_string(flags, sizeof(flags));
} }
static mode_t janet_encode_permissions(Janet *argv, int32_t n) { #else
if (janet_checkint(argv[n])) {
int32_t x = janet_unwrap_integer(argv[n]); typedef struct stat jstat_t;
if (x < 0 || x > 0777) { typedef mode_t jmode_t;
janet_panicf("expected integer in range [0, 8r777], got %v", argv[n]);
static int32_t janet_perm_to_unix(mode_t m) {
return (int32_t) m;
} }
static mode_t janet_perm_from_unix(int32_t x) {
return (mode_t) x; return (mode_t) x;
} }
JanetString perm = janet_getstring(argv, n);
if (janet_string_length(perm) != 9) {
janet_panicf("expected string of length 9, got %S", perm);
}
mode_t m = 0;
if (perm[0] == 'r') m |= S_IRUSR;
if (perm[1] == 'w') m |= S_IWUSR;
if (perm[2] == 'x') m |= S_IXUSR;
if (perm[3] == 'r') m |= S_IRGRP;
if (perm[4] == 'w') m |= S_IWGRP;
if (perm[5] == 'x') m |= S_IXGRP;
if (perm[6] == 'r') m |= S_IROTH;
if (perm[7] == 'w') m |= S_IWOTH;
if (perm[8] == 'x') m |= S_IXOTH;
return m;
}
static const uint8_t *janet_decode_mode(mode_t m) { static const uint8_t *janet_decode_mode(mode_t m) {
const char *str = "other"; const char *str = "other";
@ -975,13 +936,64 @@ static const uint8_t *janet_decode_mode(mode_t m) {
else if (S_ISCHR(m)) str = "character"; else if (S_ISCHR(m)) str = "character";
return janet_ckeyword(str); return janet_ckeyword(str);
} }
static int32_t janet_decode_permissions(jmode_t mode) {
return (int32_t)(mode & 0777);
}
#endif #endif
#ifdef JANET_WINDOWS static int32_t os_parse_permstring(const uint8_t *perm) {
typedef struct _stat jstat_t; int32_t m = 0;
#else if (perm[0] == 'r') m |= 0400;
typedef struct stat jstat_t; if (perm[1] == 'w') m |= 0200;
#endif if (perm[2] == 'x') m |= 0100;
if (perm[3] == 'r') m |= 0040;
if (perm[4] == 'w') m |= 0020;
if (perm[5] == 'x') m |= 0010;
if (perm[6] == 'r') m |= 0004;
if (perm[7] == 'w') m |= 0002;
if (perm[8] == 'x') m |= 0001;
return m;
}
static Janet os_make_permstring(int32_t permissions) {
uint8_t bytes[9] = {0};
bytes[0] = (permissions & 0400) ? 'r' : '-';
bytes[1] = (permissions & 0200) ? 'w' : '-';
bytes[2] = (permissions & 0100) ? 'x' : '-';
bytes[3] = (permissions & 0040) ? 'r' : '-';
bytes[4] = (permissions & 0020) ? 'w' : '-';
bytes[5] = (permissions & 0010) ? 'x' : '-';
bytes[6] = (permissions & 0004) ? 'r' : '-';
bytes[7] = (permissions & 0002) ? 'w' : '-';
bytes[8] = (permissions & 0001) ? 'x' : '-';
return janet_stringv(bytes, sizeof(bytes));
}
static int32_t os_get_unix_mode(const Janet *argv, int32_t n) {
int32_t unix_mode;
if (janet_checkint(argv[n])) {
/* Integer mode */
int32_t x = janet_unwrap_integer(argv[n]);
if (x < 0 || x > 0777) {
janet_panicf("bad slot #%d, expected integer in range [0, 8r777], got %v", n, argv[n]);
}
unix_mode = x;
} else {
/* Bytes mode */
JanetByteView bytes = janet_getbytes(argv, n);
if (bytes.len != 9) {
janet_panicf("bad slot #%d: expected byte sequence of length 9, got %v", n, argv[n]);
}
unix_mode = os_parse_permstring(bytes.bytes);
}
return unix_mode;
}
static jmode_t os_getmode(const Janet *argv, int32_t n) {
return janet_perm_from_unix(os_get_unix_mode(argv, n));
}
/* Getters */ /* Getters */
static Janet os_stat_dev(jstat_t *st) { static Janet os_stat_dev(jstat_t *st) {
@ -994,7 +1006,7 @@ static Janet os_stat_mode(jstat_t *st) {
return janet_wrap_keyword(janet_decode_mode(st->st_mode)); return janet_wrap_keyword(janet_decode_mode(st->st_mode));
} }
static Janet os_stat_permissions(jstat_t *st) { static Janet os_stat_permissions(jstat_t *st) {
return janet_wrap_string(janet_decode_permissions(st->st_mode)); return os_make_permstring(janet_perm_to_unix(janet_decode_permissions(st->st_mode)));
} }
static Janet os_stat_uid(jstat_t *st) { static Janet os_stat_uid(jstat_t *st) {
return janet_wrap_number(st->st_uid); return janet_wrap_number(st->st_uid);
@ -1122,9 +1134,9 @@ static Janet os_chmod(int32_t argc, Janet *argv) {
janet_fixarity(argc, 2); janet_fixarity(argc, 2);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
int res = _chmod(path, janet_encode_permissions(argv, 1)); int res = _chmod(path, os_getmode(argv, 1));
#else #else
int res = chmod(path, janet_encode_permissions(argv, 1)); int res = chmod(path, os_getmode(argv, 1));
#endif #endif
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); if (-1 == res) janet_panicf("%s: %s", strerror(errno), path);
return janet_wrap_nil(); return janet_wrap_nil();
@ -1191,6 +1203,16 @@ static Janet os_realpath(int32_t argc, Janet *argv) {
#endif #endif
} }
static Janet os_permission_string(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
return os_make_permstring(os_get_unix_mode(argv, 0));
}
static Janet os_permission_int(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
return janet_wrap_integer(os_get_unix_mode(argv, 0));
}
#endif /* JANET_REDUCED_OS */ #endif /* JANET_REDUCED_OS */
static const JanetReg os_cfuns[] = { static const JanetReg os_cfuns[] = {
@ -1249,7 +1271,7 @@ static const JanetReg os_cfuns[] = {
" only that information from stat. If the file or directory does not exist, returns nil. The keys are\n\n" " only that information from stat. If the file or directory does not exist, returns nil. The keys are\n\n"
"\t:dev - the device that the file is on\n" "\t:dev - the device that the file is on\n"
"\t:mode - the type of file, one of :file, :directory, :block, :character, :fifo, :socket, :link, or :other\n" "\t:mode - the type of file, one of :file, :directory, :block, :character, :fifo, :socket, :link, or :other\n"
"\t:permissions - A unix permission string like \"rwx--x--x\". On windows, a string like \"rwx\".\n" "\t:permissions - A Unix permission integer like 8r740\n"
"\t:uid - File uid\n" "\t:uid - File uid\n"
"\t:gid - File gid\n" "\t:gid - File gid\n"
"\t:nlink - number of links to file\n" "\t:nlink - number of links to file\n"
@ -1270,9 +1292,9 @@ static const JanetReg os_cfuns[] = {
"os/chmod", os_chmod, "os/chmod", os_chmod,
JDOC("(os/chmod path mode)\n\n" JDOC("(os/chmod path mode)\n\n"
"Change file permissions, where mode is a permission string as returned by " "Change file permissions, where mode is a permission string as returned by "
"os/stat, or an integer. " "os/perm-str, or an integer as returned by os/perm-int. "
"When mode is an integer, it is interpreted as a unix permission value, best specified in octal, like " "When mode is an integer, it is interpreted as a Unix permission value, best specified in octal, like "
"8r666 or 8r400. Windows will not differentiate between user, group, and other permissions. Returns nil.") "8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil.")
}, },
{ {
"os/touch", os_touch, "os/touch", os_touch,
@ -1289,7 +1311,7 @@ static const JanetReg os_cfuns[] = {
"os/mkdir", os_mkdir, "os/mkdir", os_mkdir,
JDOC("(os/mkdir path)\n\n" JDOC("(os/mkdir path)\n\n"
"Create a new directory. The path will be relative to the current directory if relative, otherwise " "Create a new directory. The path will be relative to the current directory if relative, otherwise "
"it will be an absolute path. Returns true if the directory was create, false if the directoyr already exists, and " "it will be an absolute path. Returns true if the directory was created, false if the directory already exists, and "
"errors otherwise.") "errors otherwise.")
}, },
{ {
@ -1305,7 +1327,7 @@ static const JanetReg os_cfuns[] = {
{ {
"os/link", os_link, "os/link", os_link,
JDOC("(os/link oldpath newpath &opt symlink)\n\n" JDOC("(os/link oldpath newpath &opt symlink)\n\n"
"Create a symlink from oldpath to newpath, returning nil. The 3rd optional paramater " "Create a symlink from oldpath to newpath, returning nil. The 3rd optional parameter "
"enables a symlink iff truthy, hard link otherwise or if not provided. Does not work on Windows.") "enables a symlink iff truthy, hard link otherwise or if not provided. Does not work on Windows.")
}, },
{ {
@ -1376,14 +1398,14 @@ static const JanetReg os_cfuns[] = {
{ {
"os/cryptorand", os_cryptorand, "os/cryptorand", os_cryptorand,
JDOC("(os/cryptorand n &opt buf)\n\n" JDOC("(os/cryptorand n &opt buf)\n\n"
"Get or append n bytes of good quality random data provided by the os. Returns a new buffer or buf.") "Get or append n bytes of good quality random data provided by the OS. Returns a new buffer or buf.")
}, },
{ {
"os/date", os_date, "os/date", os_date,
JDOC("(os/date &opt time local)\n\n" JDOC("(os/date &opt time local)\n\n"
"Returns the given time as a date struct, or the current time if no time is given. " "Returns the given time as a date struct, or the current time if no time is given. "
"Returns a struct with following key values. Note that all numbers are 0-indexed. " "Returns a struct with following key values. Note that all numbers are 0-indexed. "
"Date is given in UTC unless local is truthy, in which case the date is formated for " "Date is given in UTC unless local is truthy, in which case the date is formatted for "
"the local timezone.\n\n" "the local timezone.\n\n"
"\t:seconds - number of seconds [0-61]\n" "\t:seconds - number of seconds [0-61]\n"
"\t:minutes - number of minutes [0-59]\n" "\t:minutes - number of minutes [0-59]\n"
@ -1406,6 +1428,19 @@ static const JanetReg os_cfuns[] = {
"Get the absolute path for a given path, following ../, ./, and symlinks. " "Get the absolute path for a given path, following ../, ./, and symlinks. "
"Returns an absolute path as a string. Will raise an error on Windows.") "Returns an absolute path as a string. Will raise an error on Windows.")
}, },
{
"os/perm-str", os_permission_string,
JDOC("(os/perm-str int)\n\n"
"Convert a Unix octal permission value from a permission integer as returned by os/stat "
"to a human readable string, that follows the formatting "
"of unix tools like ls. Returns the string as a 9 character string of r, w, x and - characters. Does not "
"include the file/directory/symlink character as rendered by `ls`.")
},
{
"os/perm-int", os_permission_int,
JDOC("(os/perm-int bytes)\n\n"
"Parse a 9 character permission string and return an integer that can be used by chmod.")
},
#endif #endif
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };