slight refactor and start documenting
This commit is contained in:
parent
6458a0f9e0
commit
67173fb971
7 changed files with 655 additions and 214 deletions
|
@ -1,27 +1,28 @@
|
|||
/**
|
||||
* file: cat.c
|
||||
* author: Tyler Murphy
|
||||
*/
|
||||
|
||||
#include "command.h"
|
||||
#include "lslib.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Flags that are to be used with cat
|
||||
*/
|
||||
static struct {
|
||||
bool number_lines;
|
||||
bool number_non_empty;
|
||||
bool change_non_print;
|
||||
bool change_tabs;
|
||||
bool end_lines_dollar;
|
||||
bool number_lines; /* if to number the lines */
|
||||
bool number_non_empty; /* if to number empty lines */
|
||||
bool change_non_print; /* if to change non printable characters to be printable */
|
||||
bool change_tabs; /* if to change tabs to ^I */
|
||||
bool end_lines_dollar; /* if to print a $ at the end of lines */
|
||||
} flags;
|
||||
|
||||
static bool printable(char c) {
|
||||
switch (c) {
|
||||
case '\n': return true;
|
||||
case '\r': return true;
|
||||
case '\b': return true;
|
||||
case '\t': return true;
|
||||
default: return isprint(c) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Help function for cat
|
||||
*/
|
||||
static void help(void) {
|
||||
printf("Usage: cat [-nbvteA] [FILE]...\n\n");
|
||||
printf("Print FILEs to stdout\n\n");
|
||||
|
@ -33,52 +34,68 @@ static void help(void) {
|
|||
printf("\t-A Same as -vte\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a file pointer, cat all its contents
|
||||
* @param file the file to cat
|
||||
*/
|
||||
static void cat_file(FILE* file) {
|
||||
char c;
|
||||
size_t read;
|
||||
char c; /* current character read */
|
||||
size_t read; /* amount read */
|
||||
|
||||
size_t line = 1;
|
||||
bool empty = true;
|
||||
bool newline = true;
|
||||
/* default arguments */
|
||||
size_t line = 1; /* what line we are on, 1 indexed */
|
||||
bool empty = true; /* if the current line is empty (nothing printed) */
|
||||
bool newline = true; /* if we are on a new line */
|
||||
|
||||
/* read file a character at a time */
|
||||
while ((read = fread(&c, 1, 1, file)) != 0) {
|
||||
/* if its a new line, update the new line pointer */
|
||||
if (c == '\n') {
|
||||
if (empty && flags.number_lines) {
|
||||
if (empty && flags.number_lines) { /* print line number if set to */
|
||||
printf("\t%ld ", line);
|
||||
}
|
||||
if (flags.end_lines_dollar) {
|
||||
if (flags.end_lines_dollar) { /* pint $ if set to */
|
||||
printf("$");
|
||||
}
|
||||
line++;
|
||||
newline = true;
|
||||
empty = true;
|
||||
newline = true; /* tell that the new line number is set to be printed */
|
||||
empty = true; /* set line to empty */
|
||||
goto print;
|
||||
} else {
|
||||
empty = false;
|
||||
}
|
||||
|
||||
/* skip next check if not newline */
|
||||
if (!newline) {
|
||||
goto print;
|
||||
}
|
||||
|
||||
/* if on a new line and is a valid char, print line number */
|
||||
if (!empty && (flags.number_non_empty || flags.number_lines)) {
|
||||
printf("\t%ld ", line);
|
||||
newline = false;
|
||||
}
|
||||
print:
|
||||
if (!flags.change_non_print || printable(c)) {
|
||||
/* print character */
|
||||
if (!flags.change_non_print || printable_char(c)) {
|
||||
if (flags.change_tabs && c == '\t') {
|
||||
fwrite("^I", 1, 2, stdout);
|
||||
fwrite("^I", 1, 2, stdout); /* print ^I instead of tab */
|
||||
} else {
|
||||
fwrite(&c, 1, 1, stdout);
|
||||
fwrite(&c, 1, 1, stdout); /* print character to stdout */
|
||||
}
|
||||
} else {
|
||||
} else { /* if set to change non print, fix char code */
|
||||
c |= '@';
|
||||
fwrite(&c, 1, 1, stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in each argument that has a single - and parses it
|
||||
* @param c the character after the -
|
||||
* @param next the next argument in argv that hasnt been parsed
|
||||
* @reutrn if the next arg was used or if the arg was invalid
|
||||
*/
|
||||
static int short_arg(char c, char* next) {
|
||||
UNUSED(next);
|
||||
switch (c) {
|
||||
|
@ -110,33 +127,41 @@ static int short_arg(char c, char* next) {
|
|||
return ARG_UNUSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a files contents
|
||||
*/
|
||||
COMMAND(cat_main) {
|
||||
|
||||
int start;
|
||||
int arg_len;
|
||||
int i;
|
||||
|
||||
/* set flag defaults */
|
||||
flags.number_lines = false;
|
||||
flags.number_non_empty = false;
|
||||
flags.change_non_print = false;
|
||||
flags.change_tabs = false;
|
||||
flags.end_lines_dollar = false;
|
||||
|
||||
/* parse user arguments */
|
||||
start = parse_args(argc, argv, help, short_arg, NULL);
|
||||
|
||||
/* get the number of arguments to cat */
|
||||
arg_len = argc - start;
|
||||
|
||||
/* if not read from stdin */
|
||||
if (arg_len < 1) {
|
||||
cat_file(stdin);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* foreach file, try to open it, and then cat it */
|
||||
for (i = start; i < argc; i++) {
|
||||
FILE* in = get_file(argv[i], "r");
|
||||
cat_file(in);
|
||||
FILE* in = get_file(argv[i], "r"); /* open file as read */
|
||||
cat_file(in); /* print it out */
|
||||
if (in != stdin)
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_SUCCESS; /* success */
|
||||
}
|
||||
|
|
120
command/chmod.c
120
command/chmod.c
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* file: chmod.c
|
||||
* author: Tyler Murphy
|
||||
*/
|
||||
|
||||
#include "command.h"
|
||||
#include "lslib.h"
|
||||
|
||||
|
@ -8,21 +13,30 @@
|
|||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* If to add remove or directly set bits to a file
|
||||
*/
|
||||
enum method {
|
||||
ADD,
|
||||
SUB,
|
||||
SET
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags to set in chmod
|
||||
*/
|
||||
static struct {
|
||||
bool recurse;
|
||||
bool list_changed;
|
||||
bool verbose;
|
||||
bool quiet;
|
||||
enum method method;
|
||||
mode_t mode;
|
||||
bool recurse; /* if to recurse directorys */
|
||||
bool list_changed; /* if to list what was updated */
|
||||
bool verbose; /* if to list all output */
|
||||
bool quiet; /* if to silence errors */
|
||||
enum method method; /* the method to apply to the mode */
|
||||
mode_t mode; /* the mode to apply with the method */
|
||||
} flags;
|
||||
|
||||
/**
|
||||
* Help function for chmod
|
||||
*/
|
||||
static void help (void) {
|
||||
printf("Usage: chmod [-Rcvf] MODE[,MODE]... FILE...\n\n");
|
||||
printf("MODE is octal number (bit pattern sstrwxrwxrwx) or {+|-|=}[rwxXst]\n\n");
|
||||
|
@ -32,6 +46,12 @@ static void help (void) {
|
|||
printf("\t-f\tHide errors\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in each argument that has a single - and parses it
|
||||
* @param c the character after the -
|
||||
* @param next the next argument in argv that hasnt been parsed
|
||||
* @reutrn if the next arg was used or if the arg was invalid
|
||||
*/
|
||||
static int short_arg(char c, char* next) {
|
||||
UNUSED(next);
|
||||
switch (c) {
|
||||
|
@ -60,23 +80,31 @@ static int short_arg(char c, char* next) {
|
|||
return ARG_UNUSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a file path, modify its mode, and recurse if set to
|
||||
* @param path the file path to modify
|
||||
*/
|
||||
static void chmod_file(char* path) {
|
||||
int save;
|
||||
struct stat s;
|
||||
DIR* d;
|
||||
struct dirent* file;
|
||||
mode_t mode = 0;
|
||||
/* allocate arguments on stack */
|
||||
int save; /* path buffer save */
|
||||
struct stat s; /* file info */
|
||||
DIR* d; /* if recursing, open dir */
|
||||
struct dirent* file; /* if recursing, file being read */
|
||||
mode_t mode = 0; /* silence the uninitalized var error */
|
||||
|
||||
/* add path to path buffer */
|
||||
save = push_path_buffer(path);
|
||||
|
||||
/* get file statics, if failed return */
|
||||
if (lstat(get_path_buffer(), &s) < 0) {
|
||||
if (!flags.quiet) {
|
||||
error_s("cannot stat '%s'", get_path_buffer());
|
||||
error_s("cannot stat '%s'", get_path_buffer()); /* error if failed */
|
||||
}
|
||||
pop_path_buffer(save);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get the new mode given the method and proved mode */
|
||||
if (flags.method == SET) {
|
||||
mode = flags.mode;;
|
||||
} else if (flags.method == ADD) {
|
||||
|
@ -85,48 +113,60 @@ static void chmod_file(char* path) {
|
|||
mode = s.st_mode & ~flags.mode;
|
||||
}
|
||||
|
||||
/* attempt to modify the file mode, error and return if failed */
|
||||
if (chmod(get_path_buffer(), mode) < 0) {
|
||||
if (!flags.quiet) {
|
||||
error_s("cannot chmod '%s'", get_path_buffer());
|
||||
error_s("cannot chmod '%s'", get_path_buffer()); /* error if failed */
|
||||
}
|
||||
pop_path_buffer(save);
|
||||
pop_path_buffer(save); /* clean up */
|
||||
return;
|
||||
} else if (flags.list_changed) {
|
||||
printf("chmod: changed '%s' to %o\n", get_path_buffer(), mode);
|
||||
output("changed '%s' to %o", get_path_buffer(), mode); /* print if set to be verbose */
|
||||
}
|
||||
|
||||
/* if not set to recurse, dont bother to do future checks */
|
||||
if (!flags.recurse) {
|
||||
pop_path_buffer(save);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if not a dir, cant recurse and then return */
|
||||
if (!S_ISDIR(s.st_mode)) {
|
||||
pop_path_buffer(save);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get the directory, if failed, error and return */
|
||||
d = opendir(get_path_buffer());
|
||||
if (d == NULL) {
|
||||
if (!flags.quiet) {
|
||||
error_s("cannot open dir '%s'", get_path_buffer());
|
||||
error_s("cannot open dir '%s'", get_path_buffer()); /* error if failed */
|
||||
}
|
||||
pop_path_buffer(save);
|
||||
pop_path_buffer(save); /* clean up */
|
||||
return;
|
||||
}
|
||||
|
||||
/* chmod each file read */
|
||||
while ((file = readdir(d)) != NULL) {
|
||||
if (is_dot_dir(file->d_name)) continue;
|
||||
if (is_dot_dir(file->d_name)) continue; /* ignore dot dirs */
|
||||
chmod_file(file->d_name);
|
||||
}
|
||||
|
||||
/* clean up */
|
||||
closedir(d);
|
||||
pop_path_buffer(save);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a given mode and its method
|
||||
* @param input a pointer to the string to check
|
||||
* @return the mode parsed, mode parsing could not be done if reached , if so call again
|
||||
*/
|
||||
static mode_t parse_mode(char** input) {
|
||||
mode_t mode = 00000;
|
||||
char* str = *input;
|
||||
mode_t mode = 00000; /* default mode */
|
||||
char* str = *input; /* get the input string */
|
||||
|
||||
/* check method for mode */
|
||||
switch (*str) {
|
||||
case '=':
|
||||
flags.method = SET;
|
||||
|
@ -139,69 +179,76 @@ static mode_t parse_mode(char** input) {
|
|||
break;
|
||||
default:
|
||||
flags.method = SET;
|
||||
mode = get_mode(str);
|
||||
mode = get_mode(str); /* get mode and skip next checks */
|
||||
goto end;
|
||||
}
|
||||
|
||||
next:
|
||||
next: /* check what bits to update */
|
||||
str++;
|
||||
switch (*str) {
|
||||
case 'r':
|
||||
case 'r': /* read */
|
||||
mode |= 00444;
|
||||
goto next;
|
||||
case 'w':
|
||||
case 'w': /* read */
|
||||
mode |= 00200;
|
||||
goto next;
|
||||
case 'x':
|
||||
case 'x': /* execute */
|
||||
mode |= 00111;
|
||||
goto next;
|
||||
case 'X':
|
||||
case 'X': /* empty */
|
||||
mode |= 00000;
|
||||
goto next;
|
||||
case 's':
|
||||
case 's': /* setuid and setgid */
|
||||
mode |= 06000;
|
||||
goto next;
|
||||
case 't':
|
||||
case 't': /* sticky */
|
||||
mode |= 01000;
|
||||
goto next;
|
||||
case '\0':
|
||||
case ',':
|
||||
case ',': /* stop reading args */
|
||||
goto end;
|
||||
default:
|
||||
error("invalid option: %c", *str);
|
||||
error("invalid option: %c", *str); /* error when invalid */
|
||||
}
|
||||
|
||||
end:
|
||||
end: /* read till next argument and update input string pointer */
|
||||
|
||||
while (true) {
|
||||
|
||||
if (*str == '\0') {
|
||||
*input = NULL;
|
||||
*input = NULL; /* no mroe args */
|
||||
break;
|
||||
}
|
||||
|
||||
if (*str == ',') {
|
||||
*input = ++str;
|
||||
*input = ++str; /* another argument found */
|
||||
break;
|
||||
}
|
||||
|
||||
str++;
|
||||
}
|
||||
|
||||
return mode;
|
||||
return mode; /* return mode */
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a files permission mode
|
||||
*/
|
||||
COMMAND(chmod_main) {
|
||||
|
||||
int start, i;
|
||||
|
||||
/* get default flags */
|
||||
flags.recurse = false;
|
||||
flags.list_changed = false;
|
||||
flags.verbose = false;
|
||||
flags.quiet = false;
|
||||
flags.mode = 0;
|
||||
|
||||
/* parse arguments */
|
||||
start = parse_args(argc, argv, help, short_arg, NULL);
|
||||
|
||||
/* if not arguments error and return */
|
||||
if (argc - start < 1) {
|
||||
if (!flags.quiet) {
|
||||
error("no mode provided");
|
||||
|
@ -210,10 +257,12 @@ COMMAND(chmod_main) {
|
|||
}
|
||||
}
|
||||
|
||||
/* parse mode continusally until no more mode arguments found */
|
||||
do {
|
||||
flags.mode |= parse_mode(&argv[start]);
|
||||
} while (argv[start] != NULL);
|
||||
|
||||
/* if no files found error and return */
|
||||
if (argc - start < 2) {
|
||||
if (!flags.quiet) {
|
||||
error("no files passed");
|
||||
|
@ -222,9 +271,10 @@ COMMAND(chmod_main) {
|
|||
}
|
||||
}
|
||||
|
||||
/* for each file passed, chmod */
|
||||
for (i = start + 1; i < argc; i++) {
|
||||
chmod_file(argv[i]);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_SUCCESS; /* return */
|
||||
}
|
||||
|
|
119
command/chown.c
119
command/chown.c
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* file: chown.c
|
||||
* author: Tyler Murphy
|
||||
*/
|
||||
|
||||
#include "command.h"
|
||||
#include "lslib.h"
|
||||
|
||||
|
@ -10,15 +15,21 @@
|
|||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
/**
|
||||
* File flags to be used with chown
|
||||
*/
|
||||
static struct {
|
||||
bool recurse;
|
||||
bool list_changed;
|
||||
bool verbose;
|
||||
bool quiet;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
bool recurse; /* -R if to recurse directorys */
|
||||
bool list_changed; /* -c if to list what was changed */
|
||||
bool verbose; /* -v if to list all output */
|
||||
bool quiet; /* -f if to silence errors */
|
||||
uid_t uid; /* the uid to set the file to */
|
||||
gid_t gid; /* the gid to set the file to */
|
||||
} flags;
|
||||
|
||||
/**
|
||||
* Help function for chown
|
||||
*/
|
||||
static void help (void) {
|
||||
printf("Usage: chown [-Rcvf]... USER[:[GRP]] FILE...\n\n");
|
||||
printf("Change the owner and/or group of FILEs to USER and/or GRP\n\n");
|
||||
|
@ -28,6 +39,12 @@ static void help (void) {
|
|||
printf("\t-f\tHide errors\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in each argument that has a single - and parses it
|
||||
* @param c the character after the -
|
||||
* @param next the next argument in argv that hasnt been parsed
|
||||
* @reutrn if the next arg was used or if the arg was invalid
|
||||
*/
|
||||
static int short_arg(char c, char* next) {
|
||||
UNUSED(next);
|
||||
switch (c) {
|
||||
|
@ -49,68 +66,86 @@ static int short_arg(char c, char* next) {
|
|||
return ARG_UNUSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a file path change the filw ownership with the given flags
|
||||
* @param path the file to change the ownership
|
||||
*/
|
||||
static void chown_file(char* path) {
|
||||
int save;
|
||||
struct stat s;
|
||||
DIR* d;
|
||||
struct dirent* file;
|
||||
|
||||
/* allocate arguments on the stack */
|
||||
int save; /* path buffer save */
|
||||
struct stat s; /* file stats */
|
||||
DIR* d; /* recursing dir if recursing */
|
||||
struct dirent* file; /*if recursing current file being read */
|
||||
|
||||
save = push_path_buffer(path);
|
||||
|
||||
/* attempt to change file ownership, if failed error and return */
|
||||
if (chown(get_path_buffer(), flags.uid, flags.gid) < 0) {
|
||||
if (!flags.quiet) {
|
||||
error_s("cannot chown '%s'", get_path_buffer());
|
||||
error_s("cannot chown '%s'", get_path_buffer()); /* error if failed */
|
||||
}
|
||||
pop_path_buffer(save);
|
||||
pop_path_buffer(save); /* cleanup */
|
||||
return;
|
||||
} else if (flags.list_changed) {
|
||||
printf("chown: changed '%s' to %u:%u\n", get_path_buffer(), flags.uid, flags.gid);
|
||||
} else if (flags.list_changed) { /* list changed if verbose */
|
||||
output("changed '%s' to %u:%u", get_path_buffer(), flags.uid, flags.gid);
|
||||
}
|
||||
|
||||
/* if not recurse dont do future checks and return */
|
||||
if (!flags.recurse) {
|
||||
pop_path_buffer(save);
|
||||
return;
|
||||
}
|
||||
|
||||
/* stat file info and return if failed */
|
||||
if (lstat(get_path_buffer(), &s) < 0) {
|
||||
if (!flags.quiet) {
|
||||
error_s("cannot stat '%s'", get_path_buffer());
|
||||
error_s("cannot stat '%s'", get_path_buffer()); /* error if failed */
|
||||
}
|
||||
pop_path_buffer(save);
|
||||
pop_path_buffer(save); /* clean up */
|
||||
return;
|
||||
}
|
||||
|
||||
/* if not dir, cannot recurse so return */
|
||||
if (!S_ISDIR(s.st_mode)) {
|
||||
pop_path_buffer(save);
|
||||
pop_path_buffer(save); /* clean up */
|
||||
return;
|
||||
}
|
||||
|
||||
/* open dir, if failed return and error */
|
||||
d = opendir(get_path_buffer());
|
||||
if (d == NULL) {
|
||||
if (!flags.quiet) {
|
||||
error_s("cannot open dir '%s'", get_path_buffer());
|
||||
error_s("cannot open dir '%s'", get_path_buffer()); /* error if failed */
|
||||
}
|
||||
pop_path_buffer(save);
|
||||
pop_path_buffer(save); /* clean up */
|
||||
return;
|
||||
}
|
||||
|
||||
/* read each file in directory, and modify its ownership recursivly */
|
||||
while ((file = readdir(d)) != NULL) {
|
||||
if (is_dot_dir(file->d_name)) continue;
|
||||
if (is_dot_dir(file->d_name)) continue; /* if dot dir skip */
|
||||
chown_file(file->d_name);
|
||||
}
|
||||
|
||||
/* clean up */
|
||||
closedir(d);
|
||||
pop_path_buffer(save);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a given user and group from a string
|
||||
* @param str the stirng to parse
|
||||
*/
|
||||
static void parse_ownership(char* str) {
|
||||
char* user = str;
|
||||
char* group = NULL;
|
||||
char* end = NULL;
|
||||
unsigned long i;
|
||||
struct passwd* p = NULL;
|
||||
struct group* g = NULL;
|
||||
char* user = str; /* a copy of the input string */
|
||||
char* group = NULL; /* the group name found in str */
|
||||
char* end = NULL; /* the end of a string to parse a number if passed */
|
||||
unsigned long i; /* a number i */
|
||||
struct passwd* p = NULL; /* a user if found */
|
||||
struct group* g = NULL; /* a group if found */
|
||||
|
||||
/* attempy to find a colon seperating the user and group in the string, if not parse it as a user only */
|
||||
for (i = 0; i < strlen(str); i++) {
|
||||
if (str[i] == ':') {
|
||||
str[i] = '\0';
|
||||
|
@ -119,33 +154,38 @@ static void parse_ownership(char* str) {
|
|||
}
|
||||
}
|
||||
|
||||
/* attempt to parse input as a number */
|
||||
flags.uid = strtol(user, &end, 10);
|
||||
if (end != user) goto group;
|
||||
if (end != user) goto group; /* if successfull they ave a direct uid */
|
||||
|
||||
/* if not try to get the user by the string */
|
||||
if ((p = getpwnam(user)) == NULL) {
|
||||
error("invalid user '%s'", user);
|
||||
error("invalid user '%s'", user); /* error and abort if failed */
|
||||
} else {
|
||||
flags.uid = p->pw_uid;
|
||||
flags.uid = p->pw_uid; /* update flags */
|
||||
}
|
||||
|
||||
group:
|
||||
|
||||
/* if the group string was not found set the gorup to the user */
|
||||
if (group == NULL) {
|
||||
if (p == NULL) {
|
||||
flags.gid = flags.uid;
|
||||
flags.gid = flags.uid; /* if a int was passed set that int to the group as well */
|
||||
} else {
|
||||
flags.gid = p->pw_gid;
|
||||
flags.gid = p->pw_gid; /* if a user was passed set the group to the users gid */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* if a group was passed, attempt to parse it as a gid first */
|
||||
flags.gid = strtol(group, &end, 10);
|
||||
if (end != group) return;
|
||||
if (end != group) return; /* if succssfull return were done */
|
||||
|
||||
/* otherwise try to get the group from the name */
|
||||
if ((g = getgrnam(group)) == NULL) {
|
||||
error("invalid group '%s'", group);
|
||||
error("invalid group '%s'", group); /* error and abort if failed */
|
||||
} else {
|
||||
flags.gid = g->gr_gid;
|
||||
flags.gid = g->gr_gid; /* update gid */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,15 +193,18 @@ COMMAND(chown_main) {
|
|||
|
||||
int start, i;
|
||||
|
||||
/* set default flags for chown */
|
||||
flags.recurse = false;
|
||||
flags.list_changed = false;
|
||||
flags.verbose = false;
|
||||
flags.quiet = false;
|
||||
|
||||
/* parse argument */
|
||||
start = parse_args(argc, argv, help, short_arg, NULL);
|
||||
|
||||
/* if no more arguments error and abort since no ownership passed */
|
||||
if (argc - start < 1) {
|
||||
if (!flags.quiet) {
|
||||
if (!flags.quiet) { /* if not set to hide errors */
|
||||
error("no onwership passed");
|
||||
} else {
|
||||
return EXIT_FAILURE;
|
||||
|
@ -170,17 +213,19 @@ COMMAND(chown_main) {
|
|||
|
||||
parse_ownership(argv[start]);
|
||||
|
||||
/* if not more arugumnets error and abort since no files passed */
|
||||
if (argc - start < 1) {
|
||||
if (!flags.quiet) {
|
||||
if (!flags.quiet) { /* if not set to hide errors */
|
||||
error("no files passed");
|
||||
} else {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* change ownership for each file passed */
|
||||
for (i = start + 1; i < argc; i++) {
|
||||
chown_file(argv[i]);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_SUCCESS; /* done */
|
||||
}
|
||||
|
|
17
command/ls.c
17
command/ls.c
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* file: ls.c
|
||||
* author: Tyler Murphy
|
||||
*/
|
||||
|
||||
#include "command.h"
|
||||
#include "lslib.h"
|
||||
|
||||
|
@ -37,12 +42,12 @@ enum When {
|
|||
* Flags that are to be used with ls
|
||||
*/
|
||||
static struct {
|
||||
bool hidden; /* -a */
|
||||
bool hide_dot; /* -A */
|
||||
bool more_info; /* -l */
|
||||
bool one_column; /* -1 */
|
||||
bool recurse; /* -R */
|
||||
bool octets; /* -o */
|
||||
bool hidden; /* -a if to show hidden files */
|
||||
bool hide_dot; /* -A if to hide dot dirs */
|
||||
bool more_info; /* -l if to print info as a long list */
|
||||
bool one_column; /* -1 if to print files as a single column */
|
||||
bool recurse; /* -R if to recurse directorys */
|
||||
bool octets; /* -o if to print mode as its octet instead of romanized */
|
||||
enum When colored; /* --color=never/no/yes/always/auto */
|
||||
} flags;
|
||||
|
||||
|
|
|
@ -67,66 +67,3 @@ bool prefix(const char* pre, const char* str) {
|
|||
return strncmp(pre, str, strlen(pre)) == 0;
|
||||
}
|
||||
|
||||
static char fs_types[5] = {'K','M','G','T','P'};
|
||||
void print_file_size(size_t bytes, char buf[5]) {
|
||||
int index, n;
|
||||
float next;
|
||||
|
||||
index = 0;
|
||||
next = bytes;
|
||||
|
||||
while (true) {
|
||||
if (next < 1000) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == 5) {
|
||||
printf("999P");
|
||||
return;
|
||||
}
|
||||
|
||||
next /= 1024;
|
||||
index++;
|
||||
}
|
||||
|
||||
n = snprintf(buf, 4, "%u", (int)(next+.5));
|
||||
|
||||
if (index > 0) {
|
||||
buf[n] = (fs_types[index - 1]);
|
||||
n++;
|
||||
}
|
||||
|
||||
buf[n] = '\0';
|
||||
}
|
||||
|
||||
static char* months[12] =
|
||||
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
void print_date_time(time_t mills, char buf[13]) {
|
||||
struct tm* info;
|
||||
int n;
|
||||
|
||||
info = localtime(&mills);
|
||||
n = snprintf(buf, 5, "%s ", months[info->tm_mon]);
|
||||
|
||||
if (info->tm_mday < 10) {
|
||||
buf[n] = ' ';
|
||||
n++;
|
||||
}
|
||||
|
||||
snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec);
|
||||
}
|
||||
|
||||
void print_file_path(char* path) {
|
||||
if (streql("-", path)) {
|
||||
printf("(standard input)");
|
||||
} else {
|
||||
printf("%s", path);
|
||||
}
|
||||
}
|
||||
|
||||
void nuke_str(char* type) {
|
||||
*type = 0;
|
||||
while(*(++type)) {
|
||||
*type = 0;
|
||||
}
|
||||
}
|
||||
|
|
376
lib/lslib.h
376
lib/lslib.h
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* file: lslib.h
|
||||
* author: Tyler Murphy
|
||||
*/
|
||||
|
||||
#ifndef SHARED_H
|
||||
#define SHARED_H
|
||||
|
||||
|
@ -6,9 +11,9 @@
|
|||
#include <sys/stat.h>
|
||||
#include <pwd.h>
|
||||
|
||||
|
||||
#define ANSCII "\x1b["
|
||||
#define NEXT ";"
|
||||
/* create define flags to be used with color anscii codes */
|
||||
#define ANSCII "\x1b[" /* anscii start escape code */
|
||||
#define NEXT ";" /* anscii seperator */
|
||||
|
||||
#define RESET "0"
|
||||
#define BOLD "1"
|
||||
|
@ -26,119 +31,414 @@
|
|||
#define TURQUOISE "6"
|
||||
#define WHITE "7"
|
||||
|
||||
#define COLOR "m"
|
||||
|
||||
#define COLOR "m" /* color anscii code */
|
||||
|
||||
/**
|
||||
* Define bool cause c89 dont have it
|
||||
*/
|
||||
typedef uint8_t bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
|
||||
/* mark argument unused */
|
||||
#define UNUSED(x) (void)(x)
|
||||
#define ARG_UNUSED 0
|
||||
#define ARG_USED 1
|
||||
#define ARG_IGNORE 2
|
||||
#define ARG_INVALID 3
|
||||
|
||||
/*
|
||||
* Flags to return from parse_args fn pointers
|
||||
*/
|
||||
#define ARG_UNUSED 0 /* state that the next char* wasnt used */
|
||||
#define ARG_USED 1 /* state that the next char* was used */
|
||||
#define ARG_IGNORE 2 /* stop parsing arguments */
|
||||
#define ARG_INVALID 3 /* print error message and die */
|
||||
|
||||
/*
|
||||
* Die is argument is null
|
||||
* @param arg argument to check
|
||||
*/
|
||||
void check_arg (char* arg);
|
||||
|
||||
/**
|
||||
* Print help message with lazysphere title then exit
|
||||
* @param help fn pointer to call to display fn specific help msg
|
||||
*/
|
||||
void global_help(void (*help)(void));
|
||||
|
||||
/**
|
||||
* Parse arguments and only check for --help message otherwise do nothing
|
||||
* @param argc argument count
|
||||
* @param argv argument data
|
||||
* @param help fn pointer to call and display fn specific help msg
|
||||
*/
|
||||
void parse_help (int argc, char** argv, void (*help)(void));
|
||||
|
||||
/**
|
||||
* Parse arguments with given short and long arg pointers, and call help fn if --help supplied
|
||||
* @param argv argument count
|
||||
* @param argv argument data
|
||||
* @param help fn pointer to call and display fn specific help msg
|
||||
* @param short_arg fn pointer to check char after a -, 2nd char* is the next unparsed argv for something like -s [str]
|
||||
* @param long_arg fn pointer to check string after a --, 2nd char* is the next unparsed argv for something like --string [str]
|
||||
* @returns argc where arguments ended
|
||||
*/
|
||||
int parse_args (int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*));
|
||||
|
||||
|
||||
/*
|
||||
* Open a file with a given path and type, and print error if failed
|
||||
* @param path the path to the file
|
||||
* @param type the mode to open the file with
|
||||
* @return the file pointer if opened, NULL if failed
|
||||
*/
|
||||
FILE* get_file_s(const char* path, const char* type);
|
||||
|
||||
/*
|
||||
* Open a file with a given path and type, and print error and die if failed
|
||||
* @param path the path to the file
|
||||
* @param type the mode to open the file with
|
||||
* @return the file pointer
|
||||
*/
|
||||
FILE* get_file(const char* path, const char* type);
|
||||
|
||||
/**
|
||||
* Open a tty and error and die if failed
|
||||
* @return the file descripter to the tty
|
||||
*/
|
||||
int get_tty(void);
|
||||
|
||||
/**
|
||||
* Open a tty stream with the given mode and error and die if failed
|
||||
* @param type the mode to open the tty with
|
||||
* @return the file pointer to the tty
|
||||
*/
|
||||
FILE* get_tty_stream(char* type);
|
||||
void nuke_str(char* type);
|
||||
|
||||
|
||||
/**
|
||||
* Get a reference to the first path buffer
|
||||
* @return the pointer to the first path buffer
|
||||
*/
|
||||
char* get_path_buffer(void);
|
||||
|
||||
/**
|
||||
* Push a string to the first path buffer, a / as added before string if it doesnt allready exist at the end
|
||||
* @param stirng the local file path to push to the first path buffer
|
||||
* @return the integer index to revert to then done
|
||||
*/
|
||||
int push_path_buffer(const char* string);
|
||||
|
||||
/**
|
||||
* Revert the first path buffer to the index provided by push_path_buffer
|
||||
* @param i the index to revert to
|
||||
*/
|
||||
void pop_path_buffer(int i);
|
||||
|
||||
/**
|
||||
* Get a reference to the second path buffer
|
||||
* @return the pointer to the second path buffer
|
||||
*/
|
||||
char* get_path_buffer_2(void);
|
||||
|
||||
/**
|
||||
* Push a string to the second path buffer, a / as added before string if it doesnt allready exist at the end
|
||||
* @param stirng the local file path to push to the first path buffer
|
||||
* @return the integer index to revert to then done
|
||||
*/
|
||||
int push_path_buffer_2(const char* string);
|
||||
|
||||
/**
|
||||
* Revert the second path buffer to the index provided by push_path_buffer_2
|
||||
* @param i the index to revert to
|
||||
*/
|
||||
void pop_path_buffer_2(int i);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a base 10 number from a string and error and die if failed
|
||||
* @param text the string that contains the number
|
||||
* @return the parsed integer if successfull
|
||||
*/
|
||||
long int get_number(const char* text);
|
||||
|
||||
/**
|
||||
* Get a blkm (1024m, 38b, 26G) from a string and error and die if failed
|
||||
* @param text the string that contains the blkm
|
||||
* @return the blkm in bytes if successfull
|
||||
*/
|
||||
long int get_blkm(const char* text);
|
||||
|
||||
/**
|
||||
* Get a mode_t from a string and error and die if failed
|
||||
* @param text the string that contains the mode_t
|
||||
* @return the mode_t if successfull
|
||||
*/
|
||||
mode_t get_mode(const char* text);
|
||||
|
||||
/**
|
||||
* Check if two strings are exactly equal
|
||||
* @param a null terminated string a
|
||||
* @param b null terminated string b
|
||||
* @returns true if the two strings are exactly equal else false
|
||||
*/
|
||||
bool streql(const char* a, const char* b);
|
||||
|
||||
/**
|
||||
* Check if a string has a given prefix
|
||||
* @param pre the null terminated prefix
|
||||
* @param str the null terminated string
|
||||
* @returns true if str is prefixed with pre else false
|
||||
*/
|
||||
bool prefix(const char* pre, const char* str);
|
||||
|
||||
/**
|
||||
* Put a human redable file size (1024M) into buf with the given byte count
|
||||
* @param bytes the byte count
|
||||
* @param buf a char buf of length 5 to contain the parsed size
|
||||
*/
|
||||
void print_file_size(size_t bytes, char buf[5]);
|
||||
|
||||
/**
|
||||
* Put a human redable date into buf with the given mills since unix epoch
|
||||
* @param mills mills since the unix epoch
|
||||
* @param buf a char buf of length 13 to contain the parsed date
|
||||
*/
|
||||
void print_date_time(time_t mills, char buf[13]);
|
||||
|
||||
/**
|
||||
* Print a given file path to stdout or (standard input) if '-'
|
||||
* @param path the file path
|
||||
*/
|
||||
void print_file_path(char* path);
|
||||
|
||||
/**
|
||||
* Nuke a given string in memory
|
||||
* @param the stirng to nuke
|
||||
*/
|
||||
void nuke_str(char* type);
|
||||
|
||||
/**
|
||||
* Checks if a given character is printable symbol
|
||||
* @returns if its printable
|
||||
*/
|
||||
bool printable_char(char c);
|
||||
|
||||
/**
|
||||
* Checks if a directory name is . or ..
|
||||
* @param path the directory name
|
||||
* @return if a directory is one of the . or .. directorys
|
||||
*/
|
||||
bool is_dot_dir(const char* path);
|
||||
|
||||
/**
|
||||
* Get the last component of a path
|
||||
* @param path the path to parse
|
||||
* @return a const reference to the last component
|
||||
*/
|
||||
const char* get_last_component(const char* path);
|
||||
|
||||
|
||||
/**
|
||||
* Die
|
||||
*/
|
||||
void die (void);
|
||||
|
||||
/**
|
||||
* Print error message to stdout
|
||||
* @param format the format string
|
||||
* @param ... the rest of the arguments
|
||||
*/
|
||||
__attribute__ ((__format__(printf, 1, 2)))
|
||||
void error_s(const char* format, ...);
|
||||
|
||||
/**
|
||||
* Print error message to stdout and die
|
||||
* @param format the format string
|
||||
* @param ... the rest of the arguments
|
||||
*/
|
||||
__attribute__ ((__format__(printf, 1, 2)))
|
||||
void error(const char* format, ...);
|
||||
|
||||
/**
|
||||
* Output message as the given command
|
||||
* @param format the format string
|
||||
* @param ... the rest of the arguments
|
||||
*/
|
||||
__attribute__ ((__format__(printf, 1, 2)))
|
||||
void output(const char* format, ...);
|
||||
|
||||
|
||||
/**
|
||||
* Refine the regex as a ADT
|
||||
*/
|
||||
typedef struct regex_t* re_t;
|
||||
|
||||
/**
|
||||
* Compile a given regex pattern
|
||||
* @param pattern the regex to compile
|
||||
* @return the compiled regex
|
||||
*/
|
||||
re_t re_compile(const char* pattern);
|
||||
|
||||
/**
|
||||
* Match a given regex pattern with the string
|
||||
* @param pattern the pattern to match with
|
||||
* @param text the text to match
|
||||
* @param matchlength a pointer to how much was matched
|
||||
* @return the index where the match was found, -1 if nothing matched
|
||||
*/
|
||||
int re_matchp(re_t pattern, const char* text, int* matchlength);
|
||||
|
||||
/**
|
||||
* Compile and match a given regex pattern with the string
|
||||
* @param pattern the pattern to compile and match with
|
||||
* @param text the text to match
|
||||
* @param matchlength a pointer to how much was matched
|
||||
* @return the index where the match was found, -1 if nothing matched
|
||||
*/
|
||||
int re_match(const char* pattern, const char* text, int* matchlength);
|
||||
|
||||
|
||||
/**
|
||||
* A struct containing a stack
|
||||
*/
|
||||
struct Stack {
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
void* data;
|
||||
size_t size; /* The size of the stack */
|
||||
size_t capacity; /* The capacity of the stack */
|
||||
void* data; /* THe data stored in the stack */
|
||||
};
|
||||
|
||||
/**
|
||||
* Initalize a stack with a given size
|
||||
* @param stack a allocated stack struct to inatalize
|
||||
* @param size the initial stack capacity
|
||||
*/
|
||||
void stack_init(struct Stack* stack, size_t size);
|
||||
|
||||
/**
|
||||
* Push data to the stack
|
||||
* @param stack the stack to push to
|
||||
* @param data the data to push
|
||||
* @param len the length of the data to push
|
||||
*/
|
||||
void stack_push(struct Stack* stack, void* data, size_t len);
|
||||
|
||||
/**
|
||||
* Pop the data from the stack (move the pointer back by len doesn't delete)
|
||||
* @param stack the stack to pop from
|
||||
* @param len the length of the data to pop
|
||||
* @return the pointer to the data popped (not yet destroyed until pushed)
|
||||
*/
|
||||
void* stack_pop(struct Stack* stack, size_t len);
|
||||
|
||||
/**
|
||||
* Free a stacks allocated memory
|
||||
* @param stack the stack to free
|
||||
*/
|
||||
void stack_free(struct Stack* stack);
|
||||
|
||||
/**
|
||||
* Wrapper function to push a int to the stack
|
||||
* @param stack the stack to push to
|
||||
* @param value the int to push
|
||||
*/
|
||||
void stack_push_int(struct Stack* stack, int value);
|
||||
|
||||
/**
|
||||
* Wrapper function to pop a int from the stack
|
||||
* @param stack the stack to pop from
|
||||
* @param value the int pointer to pop
|
||||
* @returns if a int was successfully popped
|
||||
*/
|
||||
bool stack_pop_int(struct Stack* stack, int* value);
|
||||
|
||||
|
||||
/* The default shell if no shell is provided */
|
||||
#define DEFAULT_SHELL "/bin/sh"
|
||||
|
||||
/* The default PATH env variable */
|
||||
#define DEFAULT_PATH "/sbin:/usr/sbin:/bin:/usr/bin"
|
||||
|
||||
void setup_environment(const char *shell, bool new_env, bool clear_env, const struct passwd *pw);
|
||||
void exec_shell(const char *shell, bool loginshell, const char **additional_args);
|
||||
/**
|
||||
* Setup the environment variables for a given user
|
||||
* @param shell the shell to set
|
||||
* @param new_env if to update the env variables according to pw
|
||||
* @param clear_env if to remove the old env variables
|
||||
* @param pw the user to update from
|
||||
*/
|
||||
void setup_environment(const char* shell, bool new_env, bool clear_env, const struct passwd *pw);
|
||||
|
||||
/**
|
||||
* Exec a shell using the current environment, function will not return
|
||||
* @param shell the shell to execute
|
||||
* @param loginshell if to set the shell as login
|
||||
* @param additional_args additional arguments to pass to the shell command
|
||||
*/
|
||||
void exec_shell(const char* shell, bool loginshell, const char** additional_args);
|
||||
|
||||
int check_password(const struct passwd *pw, char* plaintext);
|
||||
int prompt_password(const struct passwd *pw, char* prompt);
|
||||
#define PASSWORD_INVALID 0 /* if the password was invalid */
|
||||
#define PASSWORD_VALID 1 /* if the password was valid */
|
||||
#define PASSWORD_EMPTY 2 /* if the user has an empty password */
|
||||
|
||||
/**
|
||||
* Check if a given plaintext password matches the users /etc/shadow hash
|
||||
* @param pw the user to check
|
||||
* @param plaintext the plaintext password to check
|
||||
* @return if the password was invalid, valid, or empty
|
||||
*/
|
||||
int check_password(const struct passwd* pw, char* plaintext);
|
||||
|
||||
/**
|
||||
* Prompt the user for their password and then check the given password
|
||||
* @param pw the user to check
|
||||
* @param prompt the pompt to give to the user
|
||||
* @return if the password was invalid, valid, or empty
|
||||
*/
|
||||
int prompt_password(const struct passwd* pw, char* prompt);
|
||||
|
||||
/**
|
||||
* Set UID and GID to a given user
|
||||
* @param user to change to
|
||||
*/
|
||||
void change_identity(const struct passwd* pw);
|
||||
|
||||
|
||||
/**
|
||||
* Alloc memory and die if failed
|
||||
* @param size the amount of memory to allocate in bytes
|
||||
* @return the pointer to the allocated data
|
||||
*/
|
||||
void* xalloc(size_t size);
|
||||
|
||||
/**
|
||||
* Realloc memory and die if failed
|
||||
* @param ptr the pointer to realloc
|
||||
* @param size the new size of the data
|
||||
* @return the pointer to the reallocated data
|
||||
*/
|
||||
void* xrealloc(void* ptr, size_t size);
|
||||
|
||||
/**
|
||||
* Alloc memory and zero it, die if failed
|
||||
* @param size the amouint of memory to allocate in bytes
|
||||
* @return the zeroed pointer to the allocated data
|
||||
*/
|
||||
void* xzalloc(size_t size);
|
||||
void xsetenv(const char *name, const char *value);
|
||||
|
||||
/**
|
||||
* Set the env, die if failed
|
||||
* @param name the key to set
|
||||
* @param value to value to set the key to
|
||||
*/
|
||||
void xsetenv(const char* name, const char* value);
|
||||
|
||||
/**
|
||||
* Set the uid, die if failed
|
||||
* @param uid the uid to set to
|
||||
*/
|
||||
void xsetuid(uid_t uid);
|
||||
|
||||
/**
|
||||
* Set the gid, die if failed
|
||||
* @param gid the gid to set to
|
||||
*/
|
||||
void xsetgid(gid_t gid);
|
||||
|
||||
/**
|
||||
* Get pw by name, die if failed
|
||||
* @param name the name of the user to get
|
||||
* @return the pw of the given user
|
||||
*/
|
||||
struct passwd* xgetpwnam(char* name);
|
||||
|
||||
|
||||
char* crypt(const char* plaintext, const char* pw_pass);
|
||||
|
||||
|
||||
#define PASSWORD_INVALID 0
|
||||
#define PASSWORD_VALID 1
|
||||
#define PASSWORD_EMPTY 2
|
||||
|
||||
|
||||
#endif
|
||||
#endif /* SHARED_H */
|
||||
|
|
79
lib/string.c
Normal file
79
lib/string.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "lslib.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
|
||||
static char fs_types[5] = {'K','M','G','T','P'};
|
||||
void print_file_size(size_t bytes, char buf[5]) {
|
||||
int index, n;
|
||||
float next;
|
||||
|
||||
index = 0;
|
||||
next = bytes;
|
||||
|
||||
while (true) {
|
||||
if (next < 1000) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == 5) {
|
||||
printf("999P");
|
||||
return;
|
||||
}
|
||||
|
||||
next /= 1024;
|
||||
index++;
|
||||
}
|
||||
|
||||
n = snprintf(buf, 4, "%u", (int)(next+.5));
|
||||
|
||||
if (index > 0) {
|
||||
buf[n] = (fs_types[index - 1]);
|
||||
n++;
|
||||
}
|
||||
|
||||
buf[n] = '\0';
|
||||
}
|
||||
|
||||
static char* months[12] =
|
||||
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
void print_date_time(time_t mills, char buf[13]) {
|
||||
struct tm* info;
|
||||
int n;
|
||||
|
||||
info = localtime(&mills);
|
||||
n = snprintf(buf, 5, "%s ", months[info->tm_mon]);
|
||||
|
||||
if (info->tm_mday < 10) {
|
||||
buf[n] = ' ';
|
||||
n++;
|
||||
}
|
||||
|
||||
snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec);
|
||||
}
|
||||
|
||||
void print_file_path(char* path) {
|
||||
if (streql("-", path)) {
|
||||
printf("(standard input)");
|
||||
} else {
|
||||
printf("%s", path);
|
||||
}
|
||||
}
|
||||
|
||||
void nuke_str(char* type) {
|
||||
*type = 0;
|
||||
while(*(++type)) {
|
||||
*type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool printable_char(char c) {
|
||||
switch (c) {
|
||||
case '\n': return true;
|
||||
case '\r': return true;
|
||||
case '\b': return true;
|
||||
case '\t': return true;
|
||||
default: return isprint(c) == 0;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue