commit 2f168b8b4ec0698a76e930df021d1812f5c9a913 Author: Rexy712 Date: Sun May 5 01:44:17 2019 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4734bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +strlen +*.o diff --git a/makefile b/makefile new file mode 100644 index 0000000..621af88 --- /dev/null +++ b/makefile @@ -0,0 +1,19 @@ +.PHONY: all +all: strlen + +strlen: src/strlen.cpp + g++ -o strlen -O2 -Wall -Wextra -pedantic -std=c++17 src/strlen.cpp + strip --strip-all strlen + + +.PHONY: install +install: + install -m755 -s strlen "$(DESTDIR)/usr/bin/" + +.PHONY: uninstall +uninstall: + rm "$(DESTDIR)/usr/bin/strlen" + +.PHONY: clean +clean: + rm -f strlen diff --git a/src/strlen.cpp b/src/strlen.cpp new file mode 100644 index 0000000..6356e00 --- /dev/null +++ b/src/strlen.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include + +namespace cmd{ + struct flags{ + int retval; + unsigned abort:1; + unsigned cumulative:1; + unsigned ignorenl:1; + std::vector args; + }; + + struct options{ + const char* lopt; + char sopt; + const char* desc; + int (*func)(int, char**, int, flags&); + }; +} + +static cmd::options cmd_options[] = { + {"--cumulative", 'c', "suppress normal output and sum all the lengths together", [](int, char**, int, cmd::flags& f)->int{f.cumulative = 1;return 0;}}, + {"--ignorenewline", 'n', "do not count newlines toward the character count", [](int, char**, int, cmd::flags& f)->int{f.ignorenl = 1;return 0;}}, + {"--help", 'h', "print this help message and exit", [](int, char**, int, cmd::flags& f)->int{f.abort = 1;return 1;}} +}; + +//print usage message +[[noreturn]] +void usage(char* programname, int code){ + wprintf(L"Usage: %s [OPTION]... [STRING]...\n", programname); + wprintf(L"Print the length of STRING(s) to standard output\n"); + wprintf(L"\n"); + wprintf(L"With no STRING, or when STRING is -, read standard input.\n"); + wprintf(L"\n"); + + for(size_t i = 0;i < sizeof(cmd_options)/sizeof(cmd_options[0]);++i){ + size_t spacing = 20; + spacing -= strlen(cmd_options[i].lopt); + wprintf(L" "); + wprintf(L"-%c, %s", cmd_options[i].sopt, cmd_options[i].lopt); + wprintf(L"%*s", spacing, ""); + wprintf(L"%s\n", cmd_options[i].desc); + } + + wprintf(L"\n"); + wprintf(L"strlen Copyright (C) 2019 rexy712\n"); + wprintf(L"This program comes with ABSOLUTELY NO WARRANTY\n"); + wprintf(L"This is free software, and you are welcome to redistribute it\n"); + wprintf(L"under certain conditions; see the GNU GPLv3 for details.\n"); + wprintf(L"A copy of the GPLv3 is available with the source in the file 'LICENSE'\n"); + + exit(code); +} + +namespace cmd{ + //Turn command line arguments into flags and individual strings + flags handle_args(int argc, char** argv){ + flags return_val = {}; + bool ignore_args = false; + + for(int i = 1;i < argc;++i){ + //non option arguments + if(argv[i][0] != '-' || ignore_args){ + return_val.args.push_back(argv[i]); + continue; + } + if(argv[i][1] == 0){ + return_val.args.push_back(argv[i]); + continue; + } + //everything after '--' is treated as not an argument + if(argv[i][1] == '-' && argv[i][2] == 0){ + ignore_args = true; + continue; + } + + size_t arglen = strlen(argv[i]); + //handle short options + if(argv[i][1] != '-'){ + for(size_t j = 1;j < arglen;++j){ + bool found = false; + for(size_t k = 0;k < sizeof(cmd_options)/sizeof(cmd_options[0]);++k){ + if(argv[i][j] == cmd_options[k].sopt){ + found = true; + return_val.retval = cmd_options[k].func(argc, argv, i, return_val); + if(return_val.retval) + return return_val; + break; + } + } + if(!found){ + fwprintf(stderr, L"Unrecognized argument -%c\n", argv[i][j]); + return_val.abort = 1; + return_val.retval = -1; + return return_val; + } + } + //handle long options + }else{ + bool found = false; + for(size_t j = 0;j < sizeof(cmd_options)/sizeof(cmd_options[0]);++j){ + if(!strcmp(argv[i], cmd_options[j].lopt)){ + found = true; + return_val.retval = cmd_options[j].func(argc, argv, i, return_val); + if(return_val.retval) + return return_val; + break; + } + } + if(!found){ + fwprintf(stderr, L"Unrecognized argument %s\n", argv[i]); + return_val.abort = 1; + return_val.retval = -1; + return return_val; + } + } + } + return return_val; + } +} + +//handle user input or piped input +int do_stdin(bool ignore_newlines){ + int len = 0; + if(feof(stdin)){ + return -1; + } + for(wint_t in;(in = fgetwc(stdin)) != WEOF;++len){ + if(in == L'\n'){ + if(!ignore_newlines) ++len; + return len; + } + } + if(feof(stdin) && !len) + return -1; + return len; +} + +//Sum of all strlens +int do_cumulative(const cmd::flags& flags){ + int total = 0; + if(flags.args.size() == 0){ + for(int len = 0;(len = do_stdin(flags.ignorenl)) >= 0;) + total += len; + wprintf(L"%d\n", total); + return 0; + } + for(size_t i = 0;i < flags.args.size();++i){ + if(!strcmp(flags.args[i], "-")){ + for(int len;(len = do_stdin(flags.ignorenl)) >= 0;){ + total += len; + } + }else{ + total += strlen(flags.args[i]); + } + } + wprintf(L"%d\n", total); + return 0; +} + +//Non cumulative +int do_basic(const cmd::flags& flags){ + if(flags.args.size() == 0){ + for(int len = 0;(len = do_stdin(flags.ignorenl)) >= 0;) + wprintf(L"%d\n", len); + return 0; + } + for(size_t i = 0;i < flags.args.size();++i){ + if(!strcmp(flags.args[i], "-")){ + for(int len;(len = do_stdin(flags.ignorenl)) >= 0;){ + wprintf(L"%d\n", len); + } + }else{ + wprintf(L"%d\n", strlen(flags.args[i])); + } + } + return 0; +} + +int main(int argc, char** argv){ + setlocale(LC_ALL, ""); + + cmd::flags flags = cmd::handle_args(argc, argv); + if(flags.abort) + usage(argv[0], flags.retval); + + if(flags.cumulative) + return do_cumulative(flags); + else + return do_basic(flags); +}