diff --git a/LibC/stdio.cpp b/LibC/stdio.cpp index 7394819c94..e017e98adf 100644 --- a/LibC/stdio.cpp +++ b/LibC/stdio.cpp @@ -155,6 +155,50 @@ int getchar() return getc(stdin); } +ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream) +{ + char *ptr, *eptr; + if (*lineptr == nullptr || *n == 0) { + *n = BUFSIZ; + if ((*lineptr = static_cast(malloc(*n))) == nullptr) { + return -1; + } + } + + for (ptr = *lineptr, eptr = *lineptr + *n;;) { + int c = fgetc(stream); + if (c == -1) { + if (feof(stream)) { + return ptr == *lineptr ? -1 : ptr - *lineptr; + } else { + return -1; + } + } + *ptr++ = c; + if (c == delim) { + *ptr = '\0'; + return ptr - *lineptr; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbuf_sz = *n * 2; + ssize_t d = ptr - *lineptr; + if ((nbuf = static_cast(realloc(*lineptr, nbuf_sz))) == nullptr) { + return -1; + } + *lineptr = nbuf; + *n = nbuf_sz; + eptr = nbuf + nbuf_sz; + ptr = nbuf + d; + } + } +} + +ssize_t getline(char **lineptr, size_t *n, FILE *stream) +{ + return getdelim(lineptr, n, '\n', stream); +} + int ungetc(int c, FILE* stream) { ASSERT(stream); diff --git a/LibC/stdio.h b/LibC/stdio.h index a8d4c7269f..b1878b5fd8 100644 --- a/LibC/stdio.h +++ b/LibC/stdio.h @@ -57,6 +57,8 @@ int fileno(FILE*); int fgetc(FILE*); int getc(FILE*); int getchar(); +ssize_t getdelim(char**, size_t*, int, FILE*); +ssize_t getline(char**, size_t*, FILE*); int ungetc(int c, FILE*); int remove(const char* pathname); FILE* fdopen(int fd, const char* mode); diff --git a/Userland/wc.cpp b/Userland/wc.cpp new file mode 100644 index 0000000000..f9f77f5a7e --- /dev/null +++ b/Userland/wc.cpp @@ -0,0 +1,128 @@ +#include +#include +#include + +#include +#include + +bool output_chars = false; +bool output_words = false; +bool output_lines = false; + +struct Count { + String file; + unsigned long chars = 0; + unsigned long words = 0; + unsigned long lines = 0; +}; + +void report(const Count& count) +{ + if (output_lines) { + printf("%lu ", count.lines); + } + if (output_words) { + printf("%lu ", count.words); + } + if (output_chars) { + printf("%lu ", count.chars); + } + printf("%s\n", count.file.characters()); +} + +void report(const Vector& counts) +{ + Count total { "total" }; + for (const auto& c : counts) { + report(c); + total.lines += c.lines; + total.words += c.words; + total.chars += c.chars; + } + if (counts.size() > 1) { + report(total); + } + fflush(stdout); +} + +int count_words(const char* s) +{ + int n = 0; + bool in_word = false; + for (; *s; ++s) { + if (!isspace(*s)) { + if (!in_word) { + in_word = true; + ++n; + } + } else if (in_word) { + in_word = false; + } + } + return n; +} + +int main(int argc, char** argv) +{ + if (argc < 2) { + printf("usage: wc [-c|-m] [-lw] [file...]"); + return 0; + } + + CArgsParser args_parser("wc"); + args_parser.add_arg("l", "Include lines in count"); + args_parser.add_arg("c", "Include bytes in count"); + args_parser.add_arg("m", "Include chars in count"); + args_parser.add_arg("w", "Include words in count"); + CArgsParserResult args = args_parser.parse(argc, (const char**)argv); + + if (args.is_present("l")) { + output_lines = true; + } + if (args.is_present("c")) { + output_chars = true; + } + if (args.is_present("m")) { + output_chars = true; + } + if (args.is_present("w")) { + output_words = true; + } + if (!output_lines && !output_words && !output_chars) { + output_lines = output_chars = output_words = true; + } + + Vector files = args.get_single_values(); + if (files.is_empty()) { + fprintf(stderr, "wc: No files provided"); + return 1; + } + + Vector counts; + for (const auto& f : files) { + FILE* fp = fopen(f.characters(), "r"); + if (fp == nullptr) { + fprintf(stderr, "wc: Could not open file '%s'\n", f.characters()); + return 1; + } + + Count count { f }; + char* line = nullptr; + size_t len = 0; + ssize_t n_read = 0; + while ((n_read = getline(&line, &len, fp)) != -1) { + count.lines++; + count.words += count_words(line); + count.chars += n_read; + } + + counts.append(count); + fclose(fp); + if (line) { + free(line); + } + } + + report(counts); + return 0; +}