From 6d23f11ee6775705de1594bb7492e30ede8d2bd4 Mon Sep 17 00:00:00 2001 From: Sherman Rofeman Date: Tue, 12 Dec 2023 18:18:47 -0300 Subject: [PATCH] prepare v16.2.0 - Added a manual page, thus deprecating `-h` and `-v` options. - Fix type idenfication of socket files. - Ported code to C90 standard. --- README | 32 ----- README.md | 39 ++++++ rvl.1 | 109 +++++++++++++++++ rvl.c | 345 +++++++++++++++++++++++++++--------------------------- 4 files changed, 318 insertions(+), 207 deletions(-) delete mode 100644 README create mode 100644 README.md create mode 100644 rvl.1 diff --git a/README b/README deleted file mode 100644 index f568588..0000000 --- a/README +++ /dev/null @@ -1,32 +0,0 @@ -rvl - reveal -============ -A program that reveals info about entries in the Linux's file system. It is -designed to be a better alternative for scripting. - -Requirements ------------- -In order to build it, you will only need a C compiler. - -Installation ------------- -- Compile the rvl.c file. - - mkdir -p ~/.local/bin - cc -O3 -o ~/.local/bin/rvl rvl.c - -- Add the binary rvl to your system's PATH variable by adding a this line to - your shell config file. - - export PATH=${PATH}:~/.local/bin - -- Reopen your shell. - -Help ----- -Read its help instructions. - - rvl -h - -Copyright ---------- -See LICENSE file for copyright and license details. diff --git a/README.md b/README.md new file mode 100644 index 0000000..598ce57 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# Reveal (rvl) +A program that reveals info about file system entries on Linux. + +## Requirements +In order to build it, you will only need a C90 compiler, like `tcc`. + +## Installation +- Compile the file `rvl.c`. + +```bash +mkdir -p ~/.local/bin +tcc -o ~/.local/bin/rvl rvl.c +``` + +- Install the manual page `rvl.1`. + +```bash +mkdir -p ~/.local/share/man/man1 +cp rvl.1 ~/.local/share/man/man1 +``` + +If necessary, add the `~/.local/bin` and `~/.local/share/man/man1` directories +to your system `PATH` and `MANPATH` environment variables, respectively, in +order to be able to access their files directly. + +```bash +export PATH=${PATH}:~/.local/bin +export MANPATH=${MANPATH}:~/.local/share/man/man1 +``` + +## Documentation +Once installed, you can read its manual. + +```bash +man rvl.1 +``` + +## Copyright +See `LICENSE` for copyright and license details. diff --git a/rvl.1 b/rvl.1 new file mode 100644 index 0000000..2906d3c --- /dev/null +++ b/rvl.1 @@ -0,0 +1,109 @@ +.TH RVL 1 v16.2.0 RVL +.SH NAME +rvl - reveals info about file system entries. + +.SH SYNOPSIS +rvl [\fIOPTION\fP | \fIPATH\fP]... + +.SH DESCRIPTION +It reveals info about each entry PATH(s) given as arguments. Use its OPTION(s) +before them for custom readings. Mix them together to obtain various results at +once. + +.SH DATA TYPE OPTIONS +Use these options before entry paths to set the data type to be retrieved. If +none is used, the one marked as default is considered. + +.TP +.B \-c +(default) reveal its contents. For files, it will be their contents, and for +directories, their entries. +.TP +.B \-t +reveals its type: regular (r), directory (d), symlink (l), character (c), +block (b), socket (s) or fifo (f). +.TP +.B \-s +reveals its byte size. +.TP +.B \-hs +reveals its size using the most convenient unit for a human read. +.TP +.B \-p +reveals its read (r), write (w), execution (x) and lack (-) permissions for +user, group and others. +.TP +.B \-op +reveals its permissions in octal base. +.TP +.B \-u +reveals the user that owns it. +.TP +.B \-ui +reveals the UID of the user that owns it. +.TP +.B \-g +reveals the group that owns it. +.TP +.B \-gi +reveals the GID of the group that owns it. +.TP +.B \-md +reveals its last modified date. + +.SH SYMLINK OPTIONS +Use these options before entry paths to set how symlinks are handled. If +none is used, the one marked as default is considered. + +.TP +.B \-ul +(default) unfollows symlinks. +.TP +.B \-fl +follows symlinks. + +.SH EXAMPLES + +# reveals the entries in the current directory. +.br +rvl . + +# reveals the type of some unknown entries. +.br +rvl -t ../entry{1,2} + +# reveals the permissions of a file and current directory. +.br +rvl -p file.txt -op . + +# combine it with wildcards and utilities. +.br +rvl -t ~/.* +.br +rvl /usr/bin | fmt +.br +date --date="$(rvl -md file.conf)" +"%H:%M" + +.SH EXIT STATUS +It returns 0 on success, and 1 otherwise. + +.SH SOURCE CODE +Its source code is available at . + +.SH BUGS +Report bugs at . + +.SH SEE ALSO +.BR cat (1), +.BR dir (1), +.BR find (1), +.BR ls (1), +.BR lsblk (1), +.BR readlink (1) + +.SH COPYRIGHT +MIT License. +.br +Copyright (c) 2023 Sherman Rofeman . + +See LICENSE for copyright and license details. diff --git a/rvl.c b/rvl.c index 89643c3..53149f7 100644 --- a/rvl.c +++ b/rvl.c @@ -10,234 +10,229 @@ #include #include -#define NAME "rvl" -#define FLAG(f, a) if (!strcmp(v[i], "-" f)) {a;} -#define MFLAG(f, a) FLAG(f, a; return 0) -#define NFLAG(f, a) FLAG(f, a; continue) -#define DTFLAG(f, d) NFLAG(f, dt_g = d) -#define LFLAG(f, l) NFLAG(f, fl_g = l) - -enum {DTC, DTT, DTS, DTHS, DTP, DTOP, DTU, DTUI, DTG, DTGI, DTMD}; - -int alphacmp(const void *, const void *); -void die(char *, ...); -void help(void); -void rvl(char *); -void rvldir(char *); -void rvlg(char *, struct stat *); -void rvlhs(struct stat *); -void rvllnk(char *); -void rvlmd(struct stat *); -void rvlp(struct stat *); -void rvlreg(char *); -void rvlt(struct stat *); -void rvlu(char *, struct stat *); - -int dt_g = DTC, fl_g = 0; - -int alphacmp(const void *a, const void *b) { - return strcmp(*(char **)a, *(char **)b); +#define PARSEDTFLAG(flag, dtval) PARSEFLAG(flag, dt = dtval; continue); +#define PARSEFLAG(flag, act) if (!strcmp("-" flag, argv[i])) {act;} +#define PARSELFLAG(flag, isflval) PARSEFLAG(flag, isfl = isflval; continue); +#define PARSEMETAFLAG(flag, act) PARSEFLAG(flag, act; return 0); + +enum { DT_CTTS, DT_TYPE, DT_SIZE, DT_HSIZE, DT_PERMS, DT_OPERMS, DT_USR, DT_UID, + DT_GRP, DT_GID, DT_MDATE }; + +static int alphacmp(const void *str0, const void *str1); +static void die(char *fmt, ...); +static void rvl(char *path); +static void rvldir(char *path); +static void rvlgrp(char *path, struct stat *s); +static void rvlhsize(struct stat *s); +static void rvllnk(char *path); +static void rvlmdate(struct stat *s); +static void rvlperms(struct stat *s); +static void rvlreg(char *path); +static void rvltype(struct stat *s); +static void rvlusr(char *path, struct stat *s); + +static int dt = DT_CTTS; +static int isfl = 0; + +static int +alphacmp(const void *str0, const void *str1) +{ + return strcmp(*(char **)str0, *(char **)str1); } -void die(char *e, ...) { - va_list a; - va_start(a, e); - fprintf(stderr, NAME ": "); - vfprintf(stderr, e, a); - va_end(a); +static void +die(char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + fprintf(stderr, "rvl: "); + vfprintf(stderr, fmt, args); + va_end(args); exit(1); } -void help(void) { - puts("Usage: " NAME " [OPTIONS | PATHS]..."); - puts("Reveals info about entries in the file system.\n"); - puts("META OPTIONS"); - puts("Use these options to retrieve info about the program.\n"); - puts(" -h show these help instructions."); - puts(" -v show its version.\n"); - puts("DATA TYPE OPTIONS"); - puts("Use one of these options before paths to change the data type to " - "retrieve from"); - puts("them. The default will be used if none is provided.\n"); - puts(" -c (default) reveals its contents."); - puts(" -t reveals its type: regular (r), directory (d), " - "symlink (s),"); - puts(" character (c), block (b), socket (s) or fifo " - "(f)."); - puts(" -s reveals its byte size."); - puts(" -hs reveals its size using a human-readable unit."); - puts(" -p reveals its read (r), write (w) and execution " - "(x) permissions"); - puts(" for user, group and others."); - puts(" -op reveals its octal permissions."); - puts(" -u reveals the user that owns it."); - puts(" -ui reveals the id of the user that owns it."); - puts(" -g reveals the group that owns it."); - puts(" -gi reveals the id of the group that owns it."); - puts(" -md reveals its last modified date.\n"); - puts("SYMLINK OPTIONS"); - puts("Use one of these options before paths to change the way symlinks " - "are handled."); - puts("The default will be used if none is provided.\n"); - puts(" -ul (default) unfollows symlinks."); - puts(" -fl follows symlinks.\n"); - puts("EXIT CODES"); - puts("It returns 0 on success, and 1 otherwise."); -} - -void rvl(char *p) { +static void +rvl(char *path) +{ struct stat s; - if (fl_g ? stat(p, &s) : lstat(p, &s)) - die("can't stat \"%s\".\n", p); - if (dt_g == DTC && S_ISREG(s.st_mode)) - rvlreg(p); - else if (dt_g == DTC && S_ISDIR(s.st_mode)) - rvldir(p); - else if (dt_g == DTC && S_ISLNK(s.st_mode)) - rvllnk(p); - else if (dt_g == DTC) - die("can't reveal contents of \"%s\".\n", p); - else if (dt_g == DTT) - rvlt(&s); - else if (dt_g == DTS) + if (isfl ? stat(path, &s) : lstat(path, &s)) + die("can't stat \"%s\".\n", path); + if (dt == DT_CTTS && S_ISREG(s.st_mode)) + rvlreg(path); + else if (dt == DT_CTTS && S_ISDIR(s.st_mode)) + rvldir(path); + else if (dt == DT_CTTS && S_ISLNK(s.st_mode)) + rvllnk(path); + else if (dt == DT_CTTS) + die("can't reveal contents of \"%s\".\n", path); + else if (dt == DT_TYPE) + rvltype(&s); + else if (dt == DT_SIZE) printf("%ld\n", s.st_size); - else if (dt_g == DTHS) - rvlhs(&s); - else if (dt_g == DTP) - rvlp(&s); - else if (dt_g == DTOP) + else if (dt == DT_HSIZE) + rvlhsize(&s); + else if (dt == DT_PERMS) + rvlperms(&s); + else if (dt == DT_OPERMS) printf("%o\n", s.st_mode & (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH)); - else if (dt_g == DTU) - rvlu(p, &s); - else if (dt_g == DTUI) + else if (dt == DT_USR) + rvlusr(path, &s); + else if (dt == DT_UID) printf("%u\n", s.st_uid); - else if (dt_g == DTG) - rvlg(p, &s); - else if (dt_g == DTGI) + else if (dt == DT_GRP) + rvlgrp(path, &s); + else if (dt == DT_GID) printf("%u\n", s.st_gid); - else if (dt_g == DTMD) - rvlmd(&s); + else if (dt == DT_MDATE) + rvlmdate(&s); } -void rvldir(char *p) { - DIR *d = opendir(p); +static void +rvldir(char *path) +{ + DIR *d = opendir(path); + struct dirent *e; + char *ent; + unsigned long entlen; + int i; + int z; if (!d) - die("can't open directory \"%s\".\n", p); - long unsigned i = 0; - for (; readdir(d); i++); - i -= 2; + die("can't open directory \"%s\".\n", path); + for (i = -2; readdir(d); i++); if (!i) { closedir(d); return; } - char *w[i]; + char *ents[i]; i = 0; rewinddir(d); - for (struct dirent *e; (e = readdir(d));) { + while ((e = readdir(d))) { if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue; - long unsigned s = strlen(e->d_name) + 1; - char *a = malloc(s); - if (!a) + entlen = strlen(e->d_name) + 1; + ent = malloc(entlen); + if (!ent) die("can't alloc memory.\n"); - strcpy(a, e->d_name); - w[i] = a; + strcpy(ent, e->d_name); + ents[i] = ent; i++; } - qsort(w, i, sizeof(char *), alphacmp); - for (long unsigned z = 0; z < i; z++) { - puts(w[z]); - free(w[z]); + qsort(ents, i, sizeof(char *), alphacmp); + for (z = 0; z < i; z++) { + printf("%s\n", ents[z]); + free(ents[z]); } closedir(d); } -void rvlg(char *p, struct stat *s) { - char b[255]; - struct group u, *r; - if (getgrgid_r(s->st_gid, &u, b, sizeof(b), &r) || !r) - die("can't find group that owns \"%s\".\n", p); - puts(u.gr_name); +static void +rvlgrp(char *path, struct stat *s) +{ + char buf[255]; + struct group *res; + struct group grp; + if (getgrgid_r(s->st_gid, &grp, buf, sizeof(buf), &res) || !res) + die("can't find group that owns \"%s\".\n", path); + printf("%s\n", grp.gr_name); } -void rvlhs(struct stat *s) { - float z, m[] = {1e9, 1e6, 1e3}; - char p[] = {'G', 'M', 'k'}; - for (int i = 0; i < 3; i++) - if ((z = s->st_size / m[i]) >= 1) { - printf("%.1f%cB\n", z, p[i]); +static void +rvlhsize(struct stat *s) +{ + char pref[] = { 'G', 'M', 'k' }; + float mult[] = { 1e9, 1e6, 1e3 }; + float size; + int i; + for (i = 0; i < 3; i++) + if ((size = s->st_size / mult[i]) >= 1) { + printf("%.1f%cB\n", size, pref[i]); return; } printf("%ldB\n", s->st_size); } -void rvllnk(char *p) { - char b[100]; - b[readlink(p, b, sizeof(b))] = '\0'; - puts(b); +static void +rvllnk(char *path) +{ + char buf[100]; + buf[readlink(path, buf, sizeof(buf))] = 0; + printf("%s\n", buf); } -void rvlmd(struct stat *s) { - char m[29]; - strftime(m, sizeof(m),"%a %b %d %T %Z %Y", localtime(&s->st_mtime)); - puts(m); +static void +rvlmdate(struct stat *s) +{ + char buf[29]; + strftime(buf, sizeof(buf),"%a %b %d %T %Z %Y", localtime(&s->st_mtime)); + printf("%s\n", buf); } -void rvlp(struct stat *s) { - long unsigned p[] = {S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, - S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH}; - char c[] = {'r', 'w', 'x'}; - for (int i = 0; i < 9; i++) - putchar(s->st_mode & p[i] ? c[i < 3 ? i : (i - 3) % 3] : '-'); +static void +rvlperms(struct stat *s) +{ + char permchars[] = { 'r', 'w', 'x' }; + unsigned long permflags[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, + S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, + S_IXOTH }; + int i; + for (i = 0; i < 9; i++) + putchar(s->st_mode & permflags[i] ? + permchars[i < 3 ? i : (i - 3) % 3] : '-'); putchar('\n'); } -void rvlreg(char *p) { - FILE *f = fopen(p, "r"); +static void +rvlreg(char *path) +{ + FILE *f = fopen(path, "r"); + char c; if (!f) - die("can't open file \"%s\".\n", p); - for (char c; (c = fgetc(f)) != EOF; putchar(c)); + die("can't open file \"%s\".\n", path); + for (; (c = fgetc(f)) != EOF; putchar(c)); fclose(f); } -void rvlt(struct stat *s) { - putchar(S_ISREG(s->st_mode) ? 'r' : S_ISDIR(s->st_mode) ? 'd' : - S_ISLNK(s->st_mode) ? 'l' : S_ISCHR(s->st_mode) ? 'c' : - S_ISBLK(s->st_mode) ? 's' : 'f'); - putchar('\n'); +static void +rvltype(struct stat *s) +{ + printf("%c\n", S_ISREG(s->st_mode) ? 'r' : S_ISDIR(s->st_mode) ? 'd' : + S_ISLNK(s->st_mode) ? 'l' : S_ISCHR(s->st_mode) ? 'c' : + S_ISBLK(s->st_mode) ? 'b' : S_ISFIFO(s->st_mode) ? 'f' : 's'); } -void rvlu(char *p, struct stat *s) { - char b[255]; - struct passwd u, *r; - if (getpwuid_r(s->st_uid, &u, b, sizeof(b), &r) || !r) - die("can't find user that owns \"%s\".\n", p); - puts(u.pw_name); +static void +rvlusr(char *path, struct stat *s) +{ + char buf[255]; + struct passwd *res; + struct passwd usr; + if (getpwuid_r(s->st_uid, &usr, buf, sizeof(buf), &res) || !res) + die("can't find user that owns \"%s\".\n", path); + printf("%s\n", usr.pw_name); } -int main(int c, char **v) { - for (int i = 1; i < c; i++) { - MFLAG("h", help()); - MFLAG("v", puts("v16.1.0")); - } - for (int i = 1; i < c; i++) { - DTFLAG("c", DTC); - DTFLAG("t", DTT); - DTFLAG("s", DTS); - DTFLAG("hs", DTHS); - DTFLAG("p", DTP); - DTFLAG("op", DTOP); - DTFLAG("u", DTU); - DTFLAG("ui", DTUI); - DTFLAG("g", DTG); - DTFLAG("gi", DTGI); - DTFLAG("md", DTMD); - LFLAG("fl", 1); - LFLAG("ul", 0); - rvl(v[i]); +int +main(int argc, char *argv[]) +{ + int i; + for (i = 1; i < argc; i++) { + PARSEDTFLAG("c", DT_CTTS); + PARSEDTFLAG("t", DT_TYPE); + PARSEDTFLAG("s", DT_SIZE); + PARSEDTFLAG("hs", DT_HSIZE); + PARSEDTFLAG("p", DT_PERMS); + PARSEDTFLAG("op", DT_OPERMS); + PARSEDTFLAG("u", DT_USR); + PARSEDTFLAG("ui", DT_UID); + PARSEDTFLAG("g", DT_GRP); + PARSEDTFLAG("gi", DT_GID); + PARSEDTFLAG("md", DT_MDATE); + PARSELFLAG("ul", 0); + PARSELFLAG("fl", 1); + rvl(argv[i]); } return 0; }