diff --git a/README.md b/README.md index b4c01d1..1175d17 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ https://trivial.technology/ Unportable: proc_parser.h + ps kill dmesg diff --git a/TODO b/TODO index fdd121f..fbca899 100644 --- a/TODO +++ b/TODO @@ -26,7 +26,6 @@ getty insmod rmmod lsmod -grep find FIX: diff --git a/libmu/parse_mode.c b/libmu/parse_mode.c index 322fd65..e7dbcce 100644 --- a/libmu/parse_mode.c +++ b/libmu/parse_mode.c @@ -93,4 +93,3 @@ mode_t mu_parse_mode(const char *s, mode_t cur_mode) { else return (cur_mode & ~mask) | (mode & mask); } - diff --git a/src/grep.c b/src/grep.c index c9590a2..889b4ec 100644 --- a/src/grep.c +++ b/src/grep.c @@ -2,79 +2,314 @@ #include #include #include +#include #include +#include -typedef struct { - char *pattern; - regex_t rgex; -} PATTERN; -static PATTERN *regexs; -static size_t r_size; +static struct pattern { + char *str; + + regex_t reg; + char reg_set; +} **ptrns; +static size_t ptrns_size = 0; static char i_flag; static char F_flag; static char H_flag; +static char E_flag; +static char v_flag; +static char x_flag; +static char w_flag; +static char mode_flag; +static int reg_flag; -static int addpattern(char *str) { - if (regexs == NULL) { - regexs = malloc(sizeof(PATTERN)); - if (regexs == NULL) { - fprintf(stderr, "grep: malloc failed\n"); +static void free_patterns(void) { + if (ptrns == NULL) + return; + + for (size_t i = 0; i < ptrns_size; i++) { + if (ptrns[i] == NULL) + continue; + + if (ptrns[i]->str != NULL) + free(ptrns[i]->str); + + if (ptrns[i]->reg_set) + regfree(&ptrns[i]->reg); + + free(ptrns[i]); + } + + free(ptrns); + ptrns = NULL; +} + +static int addpattern(const char *str, const size_t size) { + if (ptrns == NULL) { + ptrns = malloc(sizeof(struct pattern *)); + if (ptrns == NULL) { + fprintf(stderr, "grep: malloc: %s\n", strerror(errno)); return 1; } } - PATTERN *regexs_tmp = realloc(regexs, (r_size + 1) * sizeof(PATTERN)); - if (regexs_tmp == NULL) { - free(regexs); + struct pattern **bckp = realloc(ptrns, sizeof(struct pattern *) * (ptrns_size + 1)); + if (bckp == NULL) + goto ADDP_ERROR; + ptrns = bckp; - fprintf(stderr, "grep: realloc failed\n"); + ptrns[ptrns_size] = malloc(sizeof(struct pattern)); + if (ptrns[ptrns_size] == NULL) + goto ADDP_ERROR; + + ptrns[ptrns_size]->str = strdup(str); + if (ptrns[ptrns_size]->str == NULL) + goto ADDP_ERROR; + + if (!F_flag) { + char *reg_str = ptrns[ptrns_size]->str; + size_t rs_size = size; + + char bol = (ptrns[ptrns_size]->str[0] == '^'); + char eol = (ptrns[ptrns_size]->str[size - 1] == '^'); + + if (x_flag || w_flag) { + if (w_flag) + rs_size += 5 + ((E_flag) ? 2 : 4); + + reg_str = malloc(rs_size + 4); + if (reg_str == NULL) + goto ADDP_ERROR; + } + + if (x_flag) + snprintf(reg_str, rs_size + 4, "%s%s%s", (bol) ? "" : "^", ptrns[ptrns_size]->str, (eol) ? "" : "$"); + + else if (w_flag) + snprintf(reg_str, rs_size + 4, "%s\\<%s%.*s%s\\>%s", (bol) ? "^" : "", (E_flag) ? "(" : "\\(", (int)size - bol - eol, ptrns[ptrns_size]->str + bol, (E_flag) ? ")" : "\\)", (eol) ? "$" : ""); + + if (regcomp(&ptrns[ptrns_size]->reg, reg_str, reg_flag) < 0) + goto ADDP_ERROR; + + ptrns[ptrns_size]->reg_set = 1; + if (x_flag || w_flag) + free(reg_str); + } + + ptrns_size++; + return 0; + +ADDP_ERROR: + ptrns_size++; + free_patterns(); + fprintf(stderr, "grep: %s\n", strerror(errno)); + return 1; +} + +static int addpattern_file(const char *file) { + FILE *fp = fopen(file, "r"); + if (fp == NULL) { + fprintf(stderr, "grep: %s: %s\n", file, strerror(errno)); return 1; } - regexs = regexs_tmp; - /* Add new pattern */ - regexs[r_size].pattern = str; - - r_size++; - return 0; + int ret = 0; + + char *buf = NULL; + size_t n = 0; + + ssize_t size = 0; + while ((size = getline(&buf, &n, fp)) > 0) { + if (size && buf[size - 1] == '\n') + buf[size - 1] = '\0'; + + if (addpattern(buf, (size_t)size)) { + ret = 1; + break; + } + } + + if (buf != NULL) + free(buf); + + fclose(fp); + return ret; +} + +static int cmp(const char *str1, const char *str2) { + if (x_flag) + return !((i_flag) ? strcasecmp : strcmp)(str1, str2); + + else + return ((i_flag) ? strcasestr : strstr)(str1, str2) != NULL; +} + +static int grep(FILE *fp, const char *file) { + int ret = 1; + size_t matched_files = 0; + regmatch_t m; + + char *buf = NULL; + size_t n = 0; + + ssize_t size = 0; + while ((size = getline(&buf, &n, fp)) > 0) { + if (size && buf[size - 1] == '\n') + buf[size - 1] = '\0'; + + char match = 0; + size_t i = 0; + for (; i < ptrns_size; i++) { + if (F_flag && cmp(buf, ptrns[i]->str)) { + match = 1; + break; + } + + else if (regexec(&ptrns[i]->reg, buf, 1, &m, 0) == 0) { + match = 1; + break; + } + } + + if (match != v_flag) { + ret = 0; + switch (mode_flag) { + case 'q': + break; + + case 'o': + if (ptrns[i]->reg_set) { + unsigned int start = m.rm_so; + unsigned int finish = m.rm_eo; + printf("%.*s\n", finish - start, buf + start); + } + + else + puts(ptrns[i]->str); + + break; + + case 'c': + matched_files++; + break; + + default: + if (H_flag) + printf("%s: ", file); + + puts(buf); + } + } + } + + if (mode_flag == 'c') + printf("%zu\n", matched_files); + + if (buf != NULL) + free(buf); + + return ret; } int main(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "e:iFH")) != -1) { + while ((opt = getopt(argc, argv, "e:f:iFHEvxwqoc")) != -1) { switch (opt) { case 'e': - if (addpattern(optarg)) + if (addpattern(optarg, strlen(optarg))) + return 1; + + break; + + case 'f': + if (addpattern_file(optarg)) return 1; break; case 'i': i_flag = 1; + reg_flag |= REG_ICASE; break; case 'F': F_flag = 1; + E_flag = 0; + reg_flag &= ~REG_EXTENDED; break; case 'H': H_flag = 1; break; + case 'E': + E_flag = 1; + F_flag = 0; + reg_flag |= REG_EXTENDED; + break; + + case 'v': + v_flag = 1; + break; + + case 'x': + x_flag = 1; + break; + + case 'w': + w_flag = 1; + break; + + case 'q': + mode_flag = 'q'; + break; + + case 'o': + mode_flag = 'o'; + break; + + case 'c': + mode_flag = 'c'; + break; + default: - printf("grep [eiFH] [FILE]\n\t-e PTRN Pattern to match\n\t-i Ignore case\n\t-H Add 'filename:' prefix\n\t-F PATTERN is a literal (not regexp)\n"); + puts("grep [efiFHvxwqoc] [FILE]\n\t-e PTRN Pattern to match\n\t-f FILE Read pattern from file\n\t-i Ignore case\n\t-H Add 'filename:' prefix\n\t-F PATTERN is a literal (not regexp)\n\t-E PATTERN is an extended regexp\n\t-v Select non-matching lines\n\t-x Match whole lines only\n\t-w Match whole words only\n\t-q Quiet\n\t-o Show only the matching part of line\n\t-c Show only count of matching lines"); return 0; } } argv += optind; argc -= optind; - if (r_size == 0) { + if (ptrns_size == 0) { fprintf(stderr, "grep: no patterns specified\n"); return 1; } - free(regexs); - return 0; + int ret = 0; + if (argc == 0) + ret = grep(stdin, "-"); + + else { + for (int i = 0; i < argc; i++) { + FILE *fp = stdin; + if (strcmp(argv[i], "-")) { + fp = fopen(argv[i], "r"); + if (fp == NULL) { + ret = 1; + fprintf(stderr, "grep: %s: %s\n", argv[i], strerror(errno)); + continue; + } + } + + if (grep(fp, argv[i])) + ret = 1; + + if (fp != stdin) + fclose(fp); + } + } + + free_patterns(); + return ret; } diff --git a/src/xargs.c b/src/xargs.c index 6c26b98..e02cfe7 100644 --- a/src/xargs.c +++ b/src/xargs.c @@ -82,17 +82,10 @@ static int add_arg(const char *str, size_t chars, int flag) { return ERROR; if (!flag && I_flag) { - for (int i = 0; i < args; i++) { - while (1) { - if (strstr(cmd[i], I_flag)) { - if (Iflag_push(str, &cmd[i])) - return ERROR; - } - - else - break; - } - } + for (int i = 0; i < args; i++) + if (strstr(cmd[i], I_flag)) + if (Iflag_push(str, &cmd[i])) + return ERROR; return I_FLAG; } @@ -290,23 +283,18 @@ int main(int argc, char **argv) { break; default: - puts("xargs [tnsrP0Idt] [cmd [arg1] [arg2...]\n\t-t Print the command before start\n\t-n Pass no more than N args\n\t-r Don't run command if input is empty\n\t-s Pass command line of no more than N bytes\n\t-0 NUL terminated input\n\t-I STR Replace STR within PROG ARGS with input line\n\t-d CHR Use char as delimeter\n\t-i Ignore quotes"); + puts("xargs [tnsrP0Idti] [cmd [arg1] [arg2...]\n\t-t Print the command before start\n\t-n Pass no more than N args\n\t-r Don't run command if input is empty\n\t-s Pass command line of no more than N bytes\n\t-0 NUL terminated input\n\t-I STR Replace STR within PROG ARGS with input line\n\t-d CHR Use char as delimeter\n\t-i Ignore quotes"); return 0; } } - if (ret) { - if (d_flag) - free(d_flag); - - return 1; - } + if (ret) + goto CLEAN_UP; argv += optind; argc -= optind; while (1) { - /* Arg */ if (argc) { for (int i = 0; i < argc; i++) @@ -326,7 +314,9 @@ int main(int argc, char **argv) { if (cmd[i] == NULL) { fprintf(stderr, "xargs: strdup failed\n"); clear_cmd(); - return 1; + + ret = 1; + goto CLEAN_UP; } } @@ -344,6 +334,7 @@ int main(int argc, char **argv) { break; } +CLEAN_UP: if (d_flag) free(d_flag);