more documentation
This commit is contained in:
parent
67173fb971
commit
47e9c33362
7 changed files with 217 additions and 96 deletions
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* file: cat.c
|
* file: cat.c
|
||||||
|
* command: cat
|
||||||
* author: Tyler Murphy
|
* author: Tyler Murphy
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* file: chmod.c
|
* file: chmod.c
|
||||||
|
* command: chmod
|
||||||
* author: Tyler Murphy
|
* author: Tyler Murphy
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* file: chown.c
|
* file: chown.c
|
||||||
|
* command: chown
|
||||||
* author: Tyler Murphy
|
* author: Tyler Murphy
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
151
command/cp.c
151
command/cp.c
|
@ -1,3 +1,9 @@
|
||||||
|
/**
|
||||||
|
* file: cp.c
|
||||||
|
* command: cp
|
||||||
|
* author: Tyler Murphy
|
||||||
|
*/
|
||||||
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "lslib.h"
|
#include "lslib.h"
|
||||||
|
|
||||||
|
@ -10,14 +16,20 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command flags to be used with cp
|
||||||
|
*/
|
||||||
static struct {
|
static struct {
|
||||||
bool recurse;
|
bool recurse; /* Recurse copy directorys */
|
||||||
bool preserve;
|
bool preserve; /* Preserve file attributes */
|
||||||
bool sym_link;
|
bool sym_link; /* Symlink files instead of copy */
|
||||||
bool hard_link;
|
bool hard_link; /* Hardlink files instead of copy */
|
||||||
bool verbose;
|
bool verbose; /* Verbose the output */
|
||||||
} flags;
|
} flags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help function for cp
|
||||||
|
*/
|
||||||
static void help(void) {
|
static void help(void) {
|
||||||
printf("Usage: cp [-rplsv] SOURCE... DEST\n\n");
|
printf("Usage: cp [-rplsv] SOURCE... DEST\n\n");
|
||||||
printf("Copy SOURCEs to DEST\n\n");
|
printf("Copy SOURCEs to DEST\n\n");
|
||||||
|
@ -27,6 +39,12 @@ static void help(void) {
|
||||||
printf("\t-v\tVerbose\n");
|
printf("\t-v\tVerbose\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) {
|
static int short_arg(char c, char* next) {
|
||||||
UNUSED(next);
|
UNUSED(next);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
@ -54,24 +72,33 @@ static int short_arg(char c, char* next) {
|
||||||
return ARG_UNUSED;
|
return ARG_UNUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes in two files paths and copies the data from one path to another
|
||||||
|
* @param from the file to copy from
|
||||||
|
* @param to the file to copy to
|
||||||
|
* @returns if the file successfully coppied
|
||||||
|
*/
|
||||||
static bool copy_file(char* from, char* to) {
|
static bool copy_file(char* from, char* to) {
|
||||||
#define BS 1024
|
#define BS 1024 /* The block size of copy with */
|
||||||
|
|
||||||
FILE *from_f, *to_f;
|
FILE *from_f, *to_f; /* file pointers to cope data from and to */
|
||||||
char buf[BS];
|
char buf[BS]; /* te file copy buffer */
|
||||||
int read;
|
int read; /* the amount of bytes read */
|
||||||
|
|
||||||
|
/* attempt to get a read stream from the src file */
|
||||||
from_f = get_file_s(from, "r");
|
from_f = get_file_s(from, "r");
|
||||||
if (from_f == NULL) { return false; }
|
if (from_f == NULL) { return false; }
|
||||||
|
|
||||||
|
/* attempt to get a write streams from the dest file */
|
||||||
to_f = get_file_s(to, "w");
|
to_f = get_file_s(to, "w");
|
||||||
if (to_f == NULL) { fclose(from_f); return false; }
|
if (to_f == NULL) { fclose(from_f); return false; }
|
||||||
|
|
||||||
|
/* while data is read copy the data to the dest */
|
||||||
while ((read = fread(buf, 1, BS, from_f)) > 0) {
|
while ((read = fread(buf, 1, BS, from_f)) > 0) {
|
||||||
fwrite(buf, 1, read, to_f);
|
fwrite(buf, 1, read, to_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* output if verbose */
|
||||||
if (flags.verbose) {
|
if (flags.verbose) {
|
||||||
output("copied '%s'", from);
|
output("copied '%s'", from);
|
||||||
}
|
}
|
||||||
|
@ -79,164 +106,206 @@ static bool copy_file(char* from, char* to) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes in two file paths and symlinks from one to the other path
|
||||||
|
* @param from the file to symlink
|
||||||
|
* @param to the path to put the symlink
|
||||||
|
* @returns if the file successfully symlinked
|
||||||
|
*/
|
||||||
static bool symlink_file(char* from, char* to) {
|
static bool symlink_file(char* from, char* to) {
|
||||||
|
/* symlink the file and error if failed */
|
||||||
if (symlink(from, to) < 0) {
|
if (symlink(from, to) < 0) {
|
||||||
error_s("failed to symlink '%s'", from);
|
error_s("failed to symlink '%s'", from);
|
||||||
return false;
|
return false;
|
||||||
} else if (flags.verbose) {
|
} else if (flags.verbose) {
|
||||||
output("symlinked '%s'", from);
|
output("symlinked '%s'", from); /* output if verbose */
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes in two file paths and hardlinks from one to the other path
|
||||||
|
* @param from the file to hardlink
|
||||||
|
* @param the path to put the hardlink
|
||||||
|
* @returns fi the fule successfully hardlinked
|
||||||
|
*/
|
||||||
static bool hardlink_file(char* from, char* to) {
|
static bool hardlink_file(char* from, char* to) {
|
||||||
|
/* hard link the file and error if failed */
|
||||||
if (link(from, to) < 0) {
|
if (link(from, to) < 0) {
|
||||||
error_s("failed to hardlink '%s'", from);
|
error_s("failed to hardlink '%s'", from);
|
||||||
return false;
|
return false;
|
||||||
} else if (flags.verbose) {
|
} else if (flags.verbose) {
|
||||||
output("hardlinked '%s'", from);
|
output("hardlinked '%s'", from); /* output if verbose */
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies data from one path to another using the set path buffers and
|
||||||
|
* flags. Make sure to set these before running this function.
|
||||||
|
* @param s the stat of the file being coppied
|
||||||
|
*/
|
||||||
static void run_copy(struct stat* s) {
|
static void run_copy(struct stat* s) {
|
||||||
|
|
||||||
|
/* read file paths from the path buffers */
|
||||||
char* from = get_path_buffer();
|
char* from = get_path_buffer();
|
||||||
char* to = get_path_buffer_2();
|
char* to = get_path_buffer_2();
|
||||||
|
|
||||||
|
/* choose copy method based on given flags */
|
||||||
bool result;
|
bool result;
|
||||||
if (flags.sym_link) {
|
if (flags.sym_link) {
|
||||||
result = symlink_file(from, to);
|
result = symlink_file(from, to); /* symlink */
|
||||||
} else if (flags.hard_link) {
|
} else if (flags.hard_link) {
|
||||||
result = hardlink_file(from, to);
|
result = hardlink_file(from, to); /* hardlink */
|
||||||
} else {
|
} else {
|
||||||
result = copy_file(from, to);
|
result = copy_file(from, to); /* copy */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if failed return */
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
if (!flags.preserve) return;
|
if (!flags.preserve) return;
|
||||||
|
|
||||||
|
/* if set to preserve file attribs, copy over ownership and permissions */
|
||||||
if (chmod(to, s->st_mode) < 0) {
|
if (chmod(to, s->st_mode) < 0) {
|
||||||
error_s("cannot chmod '%s'", to);
|
error_s("cannot chmod '%s'", to); /* error if failed */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chown(to, s->st_uid, s->st_gid) < 0) {
|
if (chown(to, s->st_uid, s->st_gid) < 0) {
|
||||||
error_s("cannot chown '%s'", to);
|
error_s("cannot chown '%s'", to); /* error if failed */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* predefine function definition */
|
||||||
static void cp_file(char* path);
|
static void cp_file(char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all files in a diretory specified in the path buffer, to the dest.
|
||||||
|
* Make sure both the src and dest path buffers are set before calling.
|
||||||
|
* @param s the stat of the directory
|
||||||
|
*/
|
||||||
static void cp_directory(struct stat* s) {
|
static void cp_directory(struct stat* s) {
|
||||||
|
|
||||||
DIR* d;
|
DIR* d;
|
||||||
struct dirent* file;
|
struct dirent* file;
|
||||||
|
|
||||||
|
/* if were not recursing ignore directory and error */
|
||||||
if (!flags.recurse) {
|
if (!flags.recurse) {
|
||||||
error_s("-r not specified; omitting directory '%s'", get_path_buffer());
|
error_s("-r not specified; omitting directory '%s'", get_path_buffer());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if we failed to make the directory to copy data into error and return */
|
||||||
if (mkdir(get_path_buffer_2(), s->st_mode) < 0 && errno != EEXIST) {
|
if (mkdir(get_path_buffer_2(), s->st_mode) < 0 && errno != EEXIST) {
|
||||||
error_s("cannot create directory '%s'", get_path_buffer_2());
|
error_s("cannot create directory '%s'", get_path_buffer_2());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* open directory, if failed error and return */
|
||||||
d = opendir(get_path_buffer());
|
d = opendir(get_path_buffer());
|
||||||
|
|
||||||
if (d == NULL) {
|
if (d == NULL) {
|
||||||
error_s("cannot open directory '%s'", get_path_buffer());
|
error_s("cannot open directory '%s'", get_path_buffer());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* copy each file in the directory */
|
||||||
while ((file = readdir(d)) != NULL) {
|
while ((file = readdir(d)) != NULL) {
|
||||||
if (is_dot_dir(file->d_name)) continue;
|
if (is_dot_dir(file->d_name)) continue;
|
||||||
cp_file(file->d_name);
|
cp_file(file->d_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* get_file_name(char* path) {
|
/**
|
||||||
int last = 0;
|
* Copies a given file path to the destination given in the 2nd path buffer
|
||||||
int i = 0;
|
* Make sure path buffer 2 is set before running this function
|
||||||
|
* @param path the file to copy
|
||||||
if (path[0] == '\0') return path;
|
*/
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (path[i+1] == '\0') break;
|
|
||||||
if (path[i] == '/') {
|
|
||||||
last = i;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last == 0) return path;
|
|
||||||
else return path + last + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cp_file(char* path) {
|
static void cp_file(char* path) {
|
||||||
|
|
||||||
|
/* load src and dest into path buffers */
|
||||||
int save = push_path_buffer(path);
|
int save = push_path_buffer(path);
|
||||||
int save2 = push_path_buffer_2(get_file_name(path));
|
int save2 = push_path_buffer_2(get_last_component(path));
|
||||||
|
|
||||||
|
/* if we cannot get file info error and return */
|
||||||
struct stat s;
|
struct stat s;
|
||||||
if (lstat(get_path_buffer(), &s) < 0) {
|
if (lstat(get_path_buffer(), &s) < 0) {
|
||||||
pop_path_buffer(save);
|
pop_path_buffer(save);
|
||||||
error_s("cannot stat '%s'", get_path_buffer());
|
error_s("cannot stat '%s'", get_path_buffer());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if the file is a directory copy it as a directory, else copy it as a file */
|
||||||
if (S_ISDIR(s.st_mode)) {
|
if (S_ISDIR(s.st_mode)) {
|
||||||
cp_directory(&s);
|
cp_directory(&s);
|
||||||
} else {
|
} else {
|
||||||
run_copy(&s);
|
run_copy(&s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
pop_path_buffer(save);
|
pop_path_buffer(save);
|
||||||
pop_path_buffer_2(save2);
|
pop_path_buffer_2(save2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copes file from a src path to a dest path
|
||||||
|
*/
|
||||||
COMMAND(cp_main) {
|
COMMAND(cp_main) {
|
||||||
|
|
||||||
int start, i;
|
int start, i;
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
|
||||||
|
/* define default flags */
|
||||||
flags.hard_link = false;
|
flags.hard_link = false;
|
||||||
flags.sym_link = false;
|
flags.sym_link = false;
|
||||||
flags.preserve = false;
|
flags.preserve = false;
|
||||||
flags.recurse = false;
|
flags.recurse = false;
|
||||||
flags.verbose = false;
|
flags.verbose = false;
|
||||||
|
|
||||||
|
/* parse arguments */
|
||||||
start = parse_args(argc, argv, help, short_arg, NULL);
|
start = parse_args(argc, argv, help, short_arg, NULL);
|
||||||
|
|
||||||
|
/* if not enough arguments passed show help message and quit */
|
||||||
if (argc - start < 2) {
|
if (argc - start < 2) {
|
||||||
global_help(help);
|
global_help(help);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* only when 2 args and first is a file, the 2nd will be a file */
|
/* only when 2 args and first is a file, the 2nd will be a file */
|
||||||
if (argc - start == 2) {
|
if (argc - start == 2) {
|
||||||
|
|
||||||
|
/* if cannot read file info error and exit */
|
||||||
struct stat s;
|
struct stat s;
|
||||||
if (lstat(argv[start], &s) < 0) {
|
if (lstat(argv[start], &s) < 0) {
|
||||||
error("cannot stat '%s'", argv[start]);
|
error("cannot stat '%s'", argv[start]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* load src and dest into path buffers */
|
||||||
push_path_buffer(argv[argc-2]);
|
push_path_buffer(argv[argc-2]);
|
||||||
push_path_buffer_2(argv[argc-1]);
|
push_path_buffer_2(argv[argc-1]);
|
||||||
|
|
||||||
|
/* if the file is a directory copy it as a directory, else copy it as a file */
|
||||||
if (!S_ISDIR(s.st_mode)) {
|
if (!S_ISDIR(s.st_mode)) {
|
||||||
run_copy(&s);
|
run_copy(&s);
|
||||||
} else {
|
} else {
|
||||||
cp_directory(&s);
|
cp_directory(&s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* no need to cleanup path buffers since command is done */
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* otherwise treat the dest argument as a directory */
|
||||||
|
|
||||||
/* push directory */
|
/* push dest directory */
|
||||||
push_path_buffer_2(argv[argc-1]);
|
push_path_buffer_2(argv[argc-1]);
|
||||||
|
|
||||||
|
/* if we cannot read dest info error and return */
|
||||||
if (lstat(get_path_buffer_2(), &s) < 0) {
|
if (lstat(get_path_buffer_2(), &s) < 0) {
|
||||||
error("target: '%s'", get_path_buffer_2());
|
error("target: '%s'", get_path_buffer_2());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* copy each file into dest directory */
|
||||||
for (i = start; i < argc - 1; i++) {
|
for (i = start; i < argc - 1; i++) {
|
||||||
cp_file(argv[i]);
|
cp_file(argv[i]);
|
||||||
}
|
}
|
||||||
|
|
51
command/dd.c
51
command/dd.c
|
@ -1,8 +1,17 @@
|
||||||
|
/**
|
||||||
|
* file: dd.c
|
||||||
|
* command: dd
|
||||||
|
* author: Tyler Murphy
|
||||||
|
*/
|
||||||
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "lslib.h"
|
#include "lslib.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help function for dd
|
||||||
|
*/
|
||||||
static void help(void) {
|
static void help(void) {
|
||||||
printf("Usage: dd [if=FILE] [of=FILE] [bs=N] [count=N]\n\n");
|
printf("Usage: dd [if=FILE] [of=FILE] [bs=N] [count=N]\n\n");
|
||||||
printf("Copy a file with converting and formatting\n\n");
|
printf("Copy a file with converting and formatting\n\n");
|
||||||
|
@ -12,49 +21,57 @@ static void help(void) {
|
||||||
printf("\tcount=N\t\tCopy only N input blocks\n");
|
printf("\tcount=N\t\tCopy only N input blocks\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copys a file with converting and formatting
|
||||||
|
*/
|
||||||
COMMAND(dd_main) {
|
COMMAND(dd_main) {
|
||||||
|
|
||||||
FILE* in_file = stdin;
|
FILE* in_file = stdin; /* the file to copy from */
|
||||||
FILE* out_file = stdout;
|
FILE* out_file = stdout; /* the file to copy to */
|
||||||
int bs = 1024;
|
int bs = 1024; /* the default bs */
|
||||||
int count = -1;
|
int count = -1; /* amount of bs's to copy, if negative go until EOF */
|
||||||
int i;
|
int i;
|
||||||
char* buffer;
|
char* buffer; /* buffer to hold copy data */
|
||||||
size_t read;
|
size_t read;
|
||||||
|
|
||||||
|
/* parse arguments and only check for --help */
|
||||||
parse_help(argc, argv, help);
|
parse_help(argc, argv, help);
|
||||||
|
|
||||||
|
/* dd doesnt use standard arguments, parse though each argument and match each arg */
|
||||||
for (i = 0; i < argc; i++) {
|
for (i = 0; i < argc; i++) {
|
||||||
if (prefix("if=", argv[i])) {
|
if (prefix("if=", argv[i])) { /* sets the input file */
|
||||||
char* path = argv[i] + 3;
|
char* path = argv[i] + 3; /* get data after = */
|
||||||
in_file = get_file(path, "rb");
|
in_file = get_file(path, "rb");
|
||||||
} else if (prefix("of=", argv[i])) {
|
} else if (prefix("of=", argv[i])) { /* sets the output file */
|
||||||
char* path = argv[i] + 3;
|
char* path = argv[i] + 3; /* get data after = */
|
||||||
out_file = get_file(path, "wb");
|
out_file = get_file(path, "wb");
|
||||||
} else if (prefix("bs=", argv[i])) {
|
} else if (prefix("bs=", argv[i])) { /* sets the blocksize */
|
||||||
char* str = argv[i] + 3;
|
char* str = argv[i] + 3; /* get data after = */
|
||||||
bs = get_number(str);
|
bs = get_blkm(str);
|
||||||
if (bs < 1) {
|
if (bs < 1) { /* cannot have a negative bs */
|
||||||
error("block size must be greater than 0");
|
error("block size must be greater than 0");
|
||||||
}
|
}
|
||||||
} else if (prefix("count=", argv[i])) {
|
} else if (prefix("count=", argv[i])) { /* sets the count of bs to do */
|
||||||
char* str = argv[i] + 6;
|
char* str = argv[i] + 6; /* get data after = */
|
||||||
count = get_number(str);
|
count = get_number(str);
|
||||||
if (count < 1) {
|
if (count < 1) {
|
||||||
error("count must be greather than 0");
|
error("count must be greather than 0");
|
||||||
}
|
}
|
||||||
} else {
|
} else { /* error and exit since arg is invalid */
|
||||||
error("unkown option %s", argv[i]);
|
error("unkown option %s", argv[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* allocate buffer of set bs */
|
||||||
buffer = xalloc(bs);
|
buffer = xalloc(bs);
|
||||||
|
|
||||||
|
/* read data until EOF from and to buf */
|
||||||
while ((read = fread(buffer, 1, bs, in_file)) != 0) {
|
while ((read = fread(buffer, 1, bs, in_file)) != 0) {
|
||||||
fwrite(buffer, 1, read, out_file);
|
fwrite(buffer, 1, read, out_file);
|
||||||
if (--count, count == 0) break;
|
if (--count, count == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
free(buffer);
|
free(buffer);
|
||||||
fclose(in_file);
|
fclose(in_file);
|
||||||
fclose(out_file);
|
fclose(out_file);
|
||||||
|
|
|
@ -1,31 +1,47 @@
|
||||||
|
/**
|
||||||
|
* file: echo.c
|
||||||
|
* command: echo
|
||||||
|
* author: Tyler Murphy
|
||||||
|
*/
|
||||||
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "lslib.h"
|
#include "lslib.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags that are to be used with echo
|
||||||
|
*/
|
||||||
static struct {
|
static struct {
|
||||||
bool escape_codes;
|
bool escape_codes;
|
||||||
bool newline;
|
bool newline;
|
||||||
} flags;
|
} flags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a null terminated string to standard out and changes each char based on input flags
|
||||||
|
* @param str the string to print
|
||||||
|
*/
|
||||||
static void print_with_escape_codes(const char* str) {
|
static void print_with_escape_codes(const char* str) {
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0; /* current index in the string */
|
||||||
char n;
|
char c; /* current char being read */
|
||||||
|
char n; /* next char being read */
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
char c = str[index];
|
c = str[index]; /* read current char */
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
if (c == '\0') break;
|
if (c == '\0') break; /* if we hit the NUL, break */
|
||||||
if (c != '\\') {
|
if (c != '\\') { /* if its not a escaped code, print and continue */
|
||||||
putchar(c);
|
putchar(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* read next character */
|
||||||
n = str[index];
|
n = str[index];
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
|
/* check each char against the valid escape codes */
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case '\\':
|
case '\\':
|
||||||
putchar('\\');
|
putchar('\\');
|
||||||
|
@ -47,13 +63,19 @@ static void print_with_escape_codes(const char* str) {
|
||||||
case 'v':
|
case 'v':
|
||||||
putchar('\v');
|
putchar('\v');
|
||||||
break;
|
break;
|
||||||
default:
|
default: /* if none found print both chars as is */
|
||||||
putchar(c);
|
putchar(c);
|
||||||
putchar(n);
|
putchar(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) {
|
static int short_arg(char c, char* next) {
|
||||||
UNUSED(next);
|
UNUSED(next);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
@ -74,19 +96,26 @@ static int short_arg(char c, char* next) {
|
||||||
return ARG_UNUSED;
|
return ARG_UNUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints data in each argument to stdout
|
||||||
|
*/
|
||||||
COMMAND(echo_main) {
|
COMMAND(echo_main) {
|
||||||
|
|
||||||
int start, i;
|
int start, i;
|
||||||
|
|
||||||
|
/* if no arguments supplied exit */
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
return EXIT_SUCCESS;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* set default flags */
|
||||||
flags.escape_codes = false;
|
flags.escape_codes = false;
|
||||||
flags.newline = true;
|
flags.newline = true;
|
||||||
|
|
||||||
|
/* parse arguments, no help message */
|
||||||
start = parse_args(argc, argv, NULL, short_arg, NULL);
|
start = parse_args(argc, argv, NULL, short_arg, NULL);
|
||||||
|
|
||||||
|
/* for each argument either print as is or parse each character for escape codes */
|
||||||
for (i = start; i < argc; i++) {
|
for (i = start; i < argc; i++) {
|
||||||
if (flags.escape_codes) {
|
if (flags.escape_codes) {
|
||||||
print_with_escape_codes(argv[i]);
|
print_with_escape_codes(argv[i]);
|
||||||
|
@ -94,14 +123,17 @@ COMMAND(echo_main) {
|
||||||
printf("%s", argv[i]);
|
printf("%s", argv[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* put a space seperator between arguments */
|
||||||
if (i + 1 != argc) {
|
if (i + 1 != argc) {
|
||||||
putchar(' ');
|
putchar(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if set to put a new line at the end (default), do so */
|
||||||
if (flags.newline) {
|
if (flags.newline) {
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* done */
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
62
command/ed.c
62
command/ed.c
|
@ -53,7 +53,7 @@ static bool read_regex(char** end, re_t* regex, enum RegexDirection dir) {
|
||||||
while(true) {
|
while(true) {
|
||||||
c = *(index++);
|
c = *(index++);
|
||||||
if (c == '\0') {
|
if (c == '\0') {
|
||||||
error_s("missing regex after %c\n", dir == BEFORE ? '?' : '/');
|
error_s("missing regex after %c", dir == BEFORE ? '?' : '/');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (c == (dir == BEFORE ? '?' : '/')) {
|
if (c == (dir == BEFORE ? '?' : '/')) {
|
||||||
|
@ -186,7 +186,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
|
||||||
n_pre = -1;
|
n_pre = -1;
|
||||||
} else {
|
} else {
|
||||||
if (n_pre < 0) {
|
if (n_pre < 0) {
|
||||||
error_s("input cannot be negative\n");
|
error_s("input cannot be negative");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
index = end_pre;
|
index = end_pre;
|
||||||
|
@ -222,7 +222,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (address.data.index.i < 0) {
|
if (address.data.index.i < 0) {
|
||||||
error_s("line number %ld does not exist\n", address.data.index.i + 1);
|
error_s("line number %ld does not exist", address.data.index.i + 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
|
||||||
n = strtol(index, &end, 10) - 1;
|
n = strtol(index, &end, 10) - 1;
|
||||||
|
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
error_s("input cannot be negative\n");
|
error_s("input cannot be negative");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (index == end) {
|
if (index == end) {
|
||||||
|
@ -245,7 +245,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
|
||||||
address.data.index.i = line_current + n;
|
address.data.index.i = line_current + n;
|
||||||
}
|
}
|
||||||
if (address.data.index.i >= (long int) line_count) {
|
if (address.data.index.i >= (long int) line_count) {
|
||||||
error_s("line number %ld does not exist\n", address.data.index.i + 1);
|
error_s("line number %ld does not exist", address.data.index.i + 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -281,7 +281,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
|
||||||
address.data.index.i = n_pre;
|
address.data.index.i = n_pre;
|
||||||
}
|
}
|
||||||
if (address.data.index.i < 0 || address.data.index.i >= (long int) line_count) {
|
if (address.data.index.i < 0 || address.data.index.i >= (long int) line_count) {
|
||||||
error_s("line number %ld does not exist\n", address.data.index.i + 1);
|
error_s("line number %ld does not exist", address.data.index.i + 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,7 +437,7 @@ static bool handle_append(struct LineAddress* address) {
|
||||||
unsigned long cap, size;
|
unsigned long cap, size;
|
||||||
|
|
||||||
if (address->type != INDEX) {
|
if (address->type != INDEX) {
|
||||||
error_s("append command requires index addressing\n");
|
error_s("append command requires index addressing");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,7 +449,7 @@ static bool handle_append(struct LineAddress* address) {
|
||||||
|
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
append_lines(address->data.index.i + 1, buf, size);
|
append_lines(address->data.index.i + 1, buf, size);
|
||||||
printf("ed: appened %lu lines\n", size);
|
output("appened %lu lines", size);
|
||||||
}
|
}
|
||||||
|
|
||||||
line_current += size;
|
line_current += size;
|
||||||
|
@ -459,22 +459,22 @@ static bool handle_append(struct LineAddress* address) {
|
||||||
|
|
||||||
static bool handle_delete(struct LineAddress* address) {
|
static bool handle_delete(struct LineAddress* address) {
|
||||||
if (address->empty && address->data.index.i >= (long int) line_count) {
|
if (address->empty && address->data.index.i >= (long int) line_count) {
|
||||||
error_s("line number %ld does not exist\n", address->data.index.i + 1);
|
error_s("line number %ld does not exist", address->data.index.i + 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (address->type == INDEX) {
|
if (address->type == INDEX) {
|
||||||
delete_lines(address->data.index.i, address->data.index.i);
|
delete_lines(address->data.index.i, address->data.index.i);
|
||||||
output("deleted line %ld\n", address->data.index.i+1);
|
output("deleted line %ld", address->data.index.i+1);
|
||||||
} else if (address->type == RANGE) {
|
} else if (address->type == RANGE) {
|
||||||
delete_lines(address->data.range.a, address->data.range.b);
|
delete_lines(address->data.range.a, address->data.range.b);
|
||||||
output("deleted lines %ld-%ld\n", address->data.range.a+1, address->data.range.b+1);
|
output("deleted lines %ld-%ld", address->data.range.a+1, address->data.range.b+1);
|
||||||
} else if (address->type == SET) {
|
} else if (address->type == SET) {
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
for (i = 0; i < address->data.set.s; i++) {
|
for (i = 0; i < address->data.set.s; i++) {
|
||||||
delete_lines(address->data.set.b[i], address->data.set.b[i]);
|
delete_lines(address->data.set.b[i], address->data.set.b[i]);
|
||||||
}
|
}
|
||||||
output("deleted %lu lines\n", address->data.set.s);
|
output("deleted %lu lines", address->data.set.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -485,7 +485,7 @@ static bool get_file_name(char** filename) {
|
||||||
|
|
||||||
if (len < 1 || (len == 1 && **filename == '\n')) {
|
if (len < 1 || (len == 1 && **filename == '\n')) {
|
||||||
if (default_filename == NULL) {
|
if (default_filename == NULL) {
|
||||||
error_s("no default filename specified\n");
|
error_s("no default filename specified");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*filename = default_filename;
|
*filename = default_filename;
|
||||||
|
@ -510,7 +510,7 @@ static void write_file(char* filename, struct LineAddress* address, char* type)
|
||||||
int wrote;
|
int wrote;
|
||||||
|
|
||||||
if (line_count < 1) {
|
if (line_count < 1) {
|
||||||
error_s("cannot write empty file\n");
|
error_s("cannot write empty file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +546,7 @@ static void write_file(char* filename, struct LineAddress* address, char* type)
|
||||||
|
|
||||||
pending_writes = false;
|
pending_writes = false;
|
||||||
fclose(file);
|
fclose(file);
|
||||||
output("wrote %d lines from %s\n", wrote, filename);
|
output("wrote %d lines from %s", wrote, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_file(char* filename) {
|
static void read_file(char* filename) {
|
||||||
|
@ -566,7 +566,7 @@ static void read_file(char* filename) {
|
||||||
|
|
||||||
if (size < 1) {
|
if (size < 1) {
|
||||||
free(buf);
|
free(buf);
|
||||||
error_s("attempted to read a empty file\n");
|
error_s("attempted to read a empty file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +577,7 @@ static void read_file(char* filename) {
|
||||||
|
|
||||||
append_lines(line, buf, size);
|
append_lines(line, buf, size);
|
||||||
free(buf);
|
free(buf);
|
||||||
output("read and appended %lu lines from %s\n", size, filename);
|
output("read and appended %lu lines from %s", size, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expand_string(char** buf, int* capacity, int* size, char* text, int len) {
|
static void expand_string(char** buf, int* capacity, int* size, char* text, int len) {
|
||||||
|
@ -650,7 +650,7 @@ static void prompt(void) {
|
||||||
struct LineAddress address2;
|
struct LineAddress address2;
|
||||||
|
|
||||||
if (address.type != INDEX) {
|
if (address.type != INDEX) {
|
||||||
error_s("comma range addressing requires two index addresses\n");
|
error_s("comma range addressing requires two index addresses");
|
||||||
free_address(address);
|
free_address(address);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -663,7 +663,7 @@ static void prompt(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (address2.type != INDEX) {
|
if (address2.type != INDEX) {
|
||||||
error_s("comma range addressing requires two index addresses\n");
|
error_s("comma range addressing requires two index addresses");
|
||||||
free_address(address);
|
free_address(address);
|
||||||
free_address(address2);
|
free_address(address2);
|
||||||
return;
|
return;
|
||||||
|
@ -677,7 +677,7 @@ static void prompt(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (address.type == RANGE && address.data.range.a > address.data.range.b) {
|
if (address.type == RANGE && address.data.range.a > address.data.range.b) {
|
||||||
error_s("range addressing must be in ascending order\n");
|
error_s("range addressing must be in ascending order");
|
||||||
free_address(address);
|
free_address(address);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -690,10 +690,10 @@ test:
|
||||||
case '\n':
|
case '\n':
|
||||||
if (address.empty) {
|
if (address.empty) {
|
||||||
if (line_current == line_count) {
|
if (line_current == line_count) {
|
||||||
error_s("line number %ld does not exist\n", line_current + 1);
|
error_s("line number %ld does not exist", line_current + 1);
|
||||||
break;
|
break;
|
||||||
} else if (line_current + 1 == line_count) {
|
} else if (line_current + 1 == line_count) {
|
||||||
error_s("line number %ld does not exist\n", line_current + 2);
|
error_s("line number %ld does not exist", line_current + 2);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
line_current++;
|
line_current++;
|
||||||
|
@ -706,7 +706,7 @@ test:
|
||||||
} else if (address.type == RANGE) {
|
} else if (address.type == RANGE) {
|
||||||
line_current = address.data.range.b;
|
line_current = address.data.range.b;
|
||||||
} else if (address.type == SET) {
|
} else if (address.type == SET) {
|
||||||
error_s("unexpected range addressing\n");
|
error_s("unexpected range addressing");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
printf("%s", lines[line_current]);
|
printf("%s", lines[line_current]);
|
||||||
|
@ -728,7 +728,7 @@ test:
|
||||||
__attribute__((fallthrough));
|
__attribute__((fallthrough));
|
||||||
case 'p':
|
case 'p':
|
||||||
if (address.empty && address.data.index.i >= (long int) line_count) {
|
if (address.empty && address.data.index.i >= (long int) line_count) {
|
||||||
error_s("line number %ld does not exist\n", address.data.index.i + 1);
|
error_s("line number %ld does not exist", address.data.index.i + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (address.type == INDEX) {
|
if (address.type == INDEX) {
|
||||||
|
@ -779,17 +779,17 @@ test:
|
||||||
|
|
||||||
skip_whitespace(&index);
|
skip_whitespace(&index);
|
||||||
if (*(index++) != '/') {
|
if (*(index++) != '/') {
|
||||||
error_s("unexpected character at start of regex\n");
|
error_s("unexpected character at start of regex");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!parse_regex_lines(&index, &address)) { return; }
|
if (!parse_regex_lines(&index, &address)) { return; }
|
||||||
while(*index != '\0' && *index != '/') index++;
|
while(*index != '\0' && *index != '/') index++;
|
||||||
if (*index != '/') {
|
if (*index != '/') {
|
||||||
error_s("/ missing after %c\n", *index);
|
error_s("/ missing after %c", *index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (address.data.set.s < 1) {
|
if (address.data.set.s < 1) {
|
||||||
error_s("no matches found\n");
|
error_s("no matches found");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*(index++) = '\0';
|
*(index++) = '\0';
|
||||||
|
@ -801,11 +801,11 @@ test:
|
||||||
char* end;
|
char* end;
|
||||||
matches = strtol(index, &end, 10);
|
matches = strtol(index, &end, 10);
|
||||||
if (end == index) {
|
if (end == index) {
|
||||||
error_s("invalid number: %s\n", index);
|
error_s("invalid number: %s", index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (matches < 1) {
|
if (matches < 1) {
|
||||||
error_s("matches cannot be less than 1\n");
|
error_s("matches cannot be less than 1");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -815,7 +815,7 @@ test:
|
||||||
for (i = 0; i < address.data.set.s; i++) {
|
for (i = 0; i < address.data.set.s; i++) {
|
||||||
matches_found += substute_string(address.data.set.b[i], matches, last_regex, replace, sub_len);
|
matches_found += substute_string(address.data.set.b[i], matches, last_regex, replace, sub_len);
|
||||||
}
|
}
|
||||||
output("replaced %ld matches over %ld lines\n", matches_found, address.data.set.s);
|
output("replaced %ld matches over %ld lines", matches_found, address.data.set.s);
|
||||||
pending_writes = true;
|
pending_writes = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -865,7 +865,7 @@ test:
|
||||||
printf("%ld\n", line_current + 1);
|
printf("%ld\n", line_current + 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_s("unimplemented command\n");
|
error_s("unimplemented command");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue