31 Commits
v1.0 ... master

Author SHA1 Message Date
rexy712
03d2f386e1 Fix invert flag not being zero initialized 2020-08-05 13:36:36 -07:00
rexy712
9e1e01bb62 Merge branch 'master' of ssh://rexy712.chickenkiller.com:1995/var/git/repos/rexy712/roflcat 2020-04-09 15:20:44 -07:00
rexy712
911375627f Fix CMakeLists.txt 2020-04-09 15:20:39 -07:00
rexy712
3928766c6b Fix uninitialized variable in cmdargs and incorrect WEOF usage 2019-12-27 10:21:59 -08:00
rexy712
ee4c9a6ea1 add to todo 2019-10-24 12:19:42 -07:00
rexy712
ca79f26d4e Fixed error output on nonexistent file 2019-09-16 16:16:14 -07:00
rexy712
e3586a9e93 Reverted 2a733a0. Fixes binary printing 2019-07-22 14:13:16 -07:00
rexy712
72891834c2 Updated Readme and added public repo to help output 2019-07-22 13:50:46 -07:00
rexy712
c0abcee00b Version bump 2019-07-22 13:32:11 -07:00
rexy712
49a1f0456f Output error on directory 2019-07-22 13:29:53 -07:00
Rexy712
2a733a0224 cleanup of some duplicate code 2019-05-20 11:12:08 -07:00
Rexy712
75aa0c2475 Combine binary and normal printing function to save binary space and remove duplicate code 2019-05-19 13:18:19 -07:00
Rexy712
cb55e21846 Fix help output 2019-05-19 12:52:01 -07:00
Rexy712
e5cb265456 Fixed stderr 2019-05-19 12:46:29 -07:00
Rexy712
f757f23d11 Consistent stderr 2019-05-19 12:41:52 -07:00
Rexy712
c9f39be9d8 Add todo 2019-05-19 12:33:25 -07:00
Rexy712
ad93eab52c Changed argument handling a bit 2019-05-19 12:17:38 -07:00
Rexy712
0b3e857722 Add some speedup 2019-05-19 10:54:15 -07:00
Rexy712
82da8580c5 Changed default frequency and spread 2019-05-19 10:40:35 -07:00
rexy712
ae77265efc added -v functionality 2019-05-18 12:09:43 -07:00
rexy712
df44c75bdb add option to print binary file contents. ie treat a file as ascii 2019-05-18 08:42:29 -07:00
rexy712
af6734f8ef version bump 2019-05-18 08:02:00 -07:00
rexy712
b49c9bd0bb fixed -d 2019-05-18 08:01:38 -07:00
Rexy712
ece4b9668d Merge branch 'master' of gitlab.com:rexy712/roflcat 2019-05-13 18:44:10 -07:00
Rexy712
0b6e86ad02 Added TODO 2019-05-13 18:43:53 -07:00
rexy712
4631d532e0 Fixed handling of '-' for stdin 2019-05-11 08:05:55 -07:00
rexy712
f0247ac9c5 Fix segfault caused by off by one error 2019-01-15 11:39:57 -08:00
rexy712
adea8f719e For the love of god, please 2019-01-14 18:58:24 -08:00
rexy712
c25594ccda i swear 2019-01-14 17:37:37 -08:00
rexy712
138c20ba28 actually fixed tinfo 2019-01-14 17:31:50 -08:00
rexy712
8c561778ee should work on systems where tinfo is separate from ncurses now 2019-01-14 17:06:39 -08:00
7 changed files with 283 additions and 139 deletions

View File

@@ -1,6 +1,7 @@
include(CMakeDependentOption)
cmake_minimum_required(VERSION 3.1)
project(roflcat) project(roflcat)
cmake_minimum_required(VERSION 3.1)
include(CMakeDependentOption)
include(GNUInstallDirs)
#c++17 without compiler extensions #c++17 without compiler extensions
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
@@ -8,12 +9,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Curses REQUIRED) find_package(Curses REQUIRED)
find_library(TINFO_LIBRARY tinfo)
if(NOT TINFO_LIBRARY)
set(TINFO_LIBRARY "")
endif()
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include) set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
add_executable (roflcat src/roflcat.cpp src/cmd.cpp) add_executable (roflcat src/roflcat.cpp src/cmd.cpp)
target_include_directories(roflcat PUBLIC ${INCLUDE_PATH}) target_include_directories(roflcat PUBLIC ${INCLUDE_PATH})
target_include_directories(roflcat PUBLIC ${CURSES_INCLUDE_DIRS}) target_include_directories(roflcat PUBLIC ${CURSES_INCLUDE_DIRS} ${TINFO_INCLUDE_DIRS})
target_link_libraries(roflcat PRIVATE ${CURSES_LIBRARIES}) target_link_libraries(roflcat PRIVATE ${CURSES_LIBRARIES} ${TINFO_LIBRARY})
install(TARGETS roflcat RUNTIME DESTINATION bin) install(TARGETS roflcat RUNTIME DESTINATION bin)
#uninstall target #uninstall target

View File

@@ -1,31 +1,42 @@
# README # README
## About ## About
roflcat is a C++ rewrite of [lolcat][1] which runs much faster and supports all the options of GNU cat. I am not going to support the animation options of lolcat (--animate, --duration, --speed) because there is no real purpose to them. roflcat is a C++ rewrite of [lolcat][1] which runs much faster and supports all the options of GNU cat. I am not going to support the animation options of lolcat (--animate, --duration, --speed) because there is no real purpose to them.
Currently I can only confirm it runs on a linux system and I have no desire to expand to supporting anything else at this time. Currently I can only confirm it runs on a linux system and I have no desire to expand to supporting anything else at this time.
To keep compatability with both GNU cat and lolcat, I wanted all command line options from both to either be implemented or ignored. However there are a few conflicts between the two, namely -s -t and -v. For these I will be implementing the GNU cat functionality. To keep compatability with both GNU cat and lolcat, I wanted all command line options from both to either be implemented or ignored. However there are a few conflicts between the two, namely -s -t and -v. For these I will be implementing the GNU cat functionality.
Current version is 0.1a as of writing this, so if the version is much newer, remind me to update this readme :) Current version is 1.1 as of writing this, so if the version is much newer, remind me to update this readme :)
![alt text](https://i.postimg.cc/1tYmJzxd/roflcat-usage.png "Screenshot of --help output") ![alt text](https://i.postimg.cc/0Qk6k34j/roflcat-usage.png "Screenshot of --help output")
## Dependencies
ncurses
cmake
## Building ## Building
There is no release right now. There is only the debug build which will produce an executable called 'tester'. ```
To build just run `make`. mkdir build
cd build
cmake ..
make
```
## Installing ## Installing
I would not recommend installing at this time. There is no install target in the makefile. From within the build directory:
```
make install
```
## More Screenshots ## More Screenshots
Some test outputs on a file filled with laughing cat emojis Some test outputs on a file filled with laughing cat emojis
256 color: 256 color:
![alt text](https://i.postimg.cc/1tZC2k6r/roflcat-roflcats.png "roflcats in 256 color") ![alt text](https://i.postimg.cc/1tZC2k6r/roflcat-roflcats.png "roflcats in 256 color")
Truecolor (24 bit): Truecolor (24 bit):
![alt text](https://i.postimg.cc/tJF2527m/roflcat-roflcats-truecolor.png "roflcats in truecolor") ![alt text](https://i.postimg.cc/tJF2527m/roflcat-roflcats-truecolor.png "roflcats in truecolor")
16 color (type that will work in a tty): 16 color (type that will work in a tty):
![alt text](https://i.postimg.cc/LX99RbFj/roflcat-roflcats-16color.png "roflcats in 16 color") ![alt text](https://i.postimg.cc/LX99RbFj/roflcat-roflcats-16color.png "roflcats in 16 color")
[1]: https://github.com/busyloop/lolcat [1]: https://github.com/busyloop/lolcat

2
TODO Normal file
View File

@@ -0,0 +1,2 @@
move GNU cat logic out of printer class to make it more modular
fix ^` not printing

View File

@@ -22,15 +22,6 @@
#include <vector> //std::vector #include <vector> //std::vector
#include <cstddef> //size_t #include <cstddef> //size_t
struct cmd_options{
const char* lopt;
char sopt;
const char* desc;
};
extern const cmd_options cmd_arguments_list[];
extern const size_t cmd_arguments_list_size;
#define PRINT_LINE_NEVER 0 #define PRINT_LINE_NEVER 0
#define PRINT_LINE_ALWAYS 1 #define PRINT_LINE_ALWAYS 1
@@ -46,8 +37,8 @@ struct cmd_args{
//lolcat options //lolcat options
long seed = 0; long seed = 0;
float freq = 0.2f; float freq = 0.3f;
float spread = 0.3f; float spread = 0.4f;
unsigned truecol:1; unsigned truecol:1;
unsigned color:2; unsigned color:2;
unsigned invert:1; unsigned invert:1;
@@ -58,6 +49,7 @@ struct cmd_args{
unsigned squeeze:1; unsigned squeeze:1;
unsigned tabs:1; unsigned tabs:1;
unsigned nonprinting:1; unsigned nonprinting:1;
unsigned treatbinary:1;
//flags //flags
unsigned error:1; unsigned error:1;
@@ -66,8 +58,20 @@ struct cmd_args{
cmd_args(void)noexcept; cmd_args(void)noexcept;
void clear_gnu_options(void); void clear_gnu_options(void);
void set_nonprinting(bool = true);
}; };
struct cmd_options{
const char* lopt;
char sopt;
const char* desc;
};
extern const cmd_options cmd_arguments_list[];
extern const size_t cmd_arguments_list_size;
cmd_args process_cmd_args(int argc, char** argv); cmd_args process_cmd_args(int argc, char** argv);
#endif #endif

View File

@@ -96,9 +96,7 @@ auto printer_base<Derived>::print(const wchar_t* s)noexcept(noexcept(std::declva
} }
template<class Derived> template<class Derived>
auto printer_base<Derived>::print(char s) -> Derived&{ auto printer_base<Derived>::print(char s) -> Derived&{
wchar_t tmp; return print(static_cast<wchar_t>(s));
mbstowcs(&tmp, &s, 1);
return print(tmp);
} }
template<class Derived> template<class Derived>
auto printer_base<Derived>::print(const char* s) -> Derived&{ auto printer_base<Derived>::print(const char* s) -> Derived&{

View File

@@ -25,11 +25,14 @@
#include <cstdlib> //atol #include <cstdlib> //atol
cmd_args::cmd_args(void)noexcept: cmd_args::cmd_args(void)noexcept:
truecol(0), color(0), number(0), ends(0), squeeze(0), tabs(0), nonprinting(0), error(0), usage(0), version(0){} truecol(0), color(0), invert(0), number(0), ends(0), squeeze(0), tabs(0), nonprinting(0), treatbinary(0), error(0), usage(0), version(0){}
void cmd_args::clear_gnu_options(void){ void cmd_args::clear_gnu_options(void){
number = PRINT_LINE_NEVER; number = PRINT_LINE_NEVER;
ends = squeeze = tabs = nonprinting = 0; ends = squeeze = tabs = nonprinting = 0;
} }
void cmd_args::set_nonprinting(bool i){
nonprinting = treatbinary = i;
}
static constexpr size_t constexpr_strlen(const char* str){ static constexpr size_t constexpr_strlen(const char* str){
size_t i = 0; size_t i = 0;
@@ -60,6 +63,10 @@ template<const char* S1, const char* S2>
struct concat_many_strs<S1,S2>{ struct concat_many_strs<S1,S2>{
static constexpr const char* str = concat_strs<S1, S2>::str; static constexpr const char* str = concat_strs<S1, S2>::str;
}; };
template<char... Cs>
struct char_to_str{
static constexpr const char str[sizeof...(Cs)+1] = {Cs..., '\0'};
};
#define NO_LONG_OPTION nullptr #define NO_LONG_OPTION nullptr
#define REQ_INTEGER_ARG "=<i>" #define REQ_INTEGER_ARG "=<i>"
@@ -74,6 +81,7 @@ static constexpr const char* VT_LONG_OPTION = NO_LONG_OPTION;
static constexpr const char SHOW_TABS_LONG_OPTION[] = "show-tabs"; static constexpr const char SHOW_TABS_LONG_OPTION[] = "show-tabs";
static constexpr const char* U_IGNORED_LONG_OPTION = NO_LONG_OPTION; static constexpr const char* U_IGNORED_LONG_OPTION = NO_LONG_OPTION;
static constexpr const char NONPRINTING_LONG_OPTION[] = "show-nonprinting"; static constexpr const char NONPRINTING_LONG_OPTION[] = "show-nonprinting";
static constexpr const char BINARY_LONG_OPTION[] = "binary";
static constexpr const char USAGE_LONG_OPTION[] = "help"; static constexpr const char USAGE_LONG_OPTION[] = "help";
static constexpr const char VERSION_LONG_OPTION[] = "version"; static constexpr const char VERSION_LONG_OPTION[] = "version";
static constexpr const char SEED_LONG_OPTION[] = "seed" REQ_INTEGER_ARG; static constexpr const char SEED_LONG_OPTION[] = "seed" REQ_INTEGER_ARG;
@@ -88,64 +96,97 @@ static constexpr const char IGNORE_ANIMATE_LONG_OPTION[] = "animate";
static constexpr const char IGNORE_DURATION_LONG_OPTION[] = "duration" REQ_INTEGER_ARG; static constexpr const char IGNORE_DURATION_LONG_OPTION[] = "duration" REQ_INTEGER_ARG;
static constexpr const char IGNORE_SPEED_LONG_OPTION[] = "speed" REQ_FLOAT_ARG; static constexpr const char IGNORE_SPEED_LONG_OPTION[] = "speed" REQ_FLOAT_ARG;
#define NO_SHORT_OPTION "" #define NO_SHORT_OPTION 0
static constexpr const char SHOW_ALL_SHORT_OPTION[] = "A"; static constexpr char SHOW_ALL_SHORT_OPTION = 'A';
static constexpr const char NUMBER_NONBLANK_SHORT_OPTION[] = "b"; static constexpr char NUMBER_NONBLANK_SHORT_OPTION = 'b';
static constexpr const char TRUECOLOR_SHORT_OPTION[] = "C"; static constexpr char BINARY_SHORT_OPTION = 'B';
static constexpr const char VE_SHORT_OPTION[] = "e"; static constexpr char TRUECOLOR_SHORT_OPTION = 'C';
static constexpr const char SHOW_ENDS_SHORT_OPTION[] = "E"; static constexpr char VE_SHORT_OPTION = 'e';
static constexpr const char FORCE_COLOR_SHORT_OPTION[] = "f"; static constexpr char SHOW_ENDS_SHORT_OPTION = 'E';
static constexpr const char FREQ_SHORT_OPTION[] = "F"; static constexpr char FORCE_COLOR_SHORT_OPTION = 'f';
static constexpr const char USAGE_SHORT_OPTION[] = "h"; static constexpr char FREQ_SHORT_OPTION = 'F';
static constexpr const char INVERT_SHORT_OPTION[] = "i"; static constexpr char USAGE_SHORT_OPTION = 'h';
static constexpr const char NUMBER_LINES_SHORT_OPTION[] = "n"; static constexpr char INVERT_SHORT_OPTION = 'i';
static constexpr const char DISABLE_COLOR_SHORT_OPTION[] = "N"; static constexpr char NUMBER_LINES_SHORT_OPTION = 'n';
static constexpr const char SPREAD_SHORT_OPTION[] = "p"; static constexpr char DISABLE_COLOR_SHORT_OPTION = 'N';
static constexpr const char SQUEEZE_BLANKS_SHORT_OPTION[] = "s"; static constexpr char SPREAD_SHORT_OPTION = 'p';
static constexpr const char SEED_SHORT_OPTION[] = "S"; static constexpr char SQUEEZE_BLANKS_SHORT_OPTION = 's';
static constexpr const char VT_SHORT_OPTION[] = "t"; static constexpr char SEED_SHORT_OPTION = 'S';
static constexpr const char SHOW_TABS_SHORT_OPTION[] = "T"; static constexpr char VT_SHORT_OPTION = 't';
static constexpr const char U_IGNORED_SHORT_OPTION[] = "u"; static constexpr char SHOW_TABS_SHORT_OPTION = 'T';
static constexpr const char NONPRINTING_SHORT_OPTION[] = "v"; static constexpr char U_IGNORED_SHORT_OPTION = 'u';
static constexpr char NONPRINTING_SHORT_OPTION = 'v';
static constexpr const char IGNORE_ANIMATE_SHORT_OPTION[] = "a"; static constexpr char IGNORE_ANIMATE_SHORT_OPTION = 'a';
static constexpr const char IGNORE_DURATION_SHORT_OPTION[] = "d"; static constexpr char IGNORE_DURATION_SHORT_OPTION = 'd';
static constexpr const char* IGNORE_SPEED_SHORT_OPTION = NO_SHORT_OPTION; static constexpr char IGNORE_SPEED_SHORT_OPTION = NO_SHORT_OPTION;
static constexpr const char* VERSION_SHORT_OPTION = NO_SHORT_OPTION; static constexpr char VERSION_SHORT_OPTION = NO_SHORT_OPTION;
#define NO_DESC nullptr #define NO_DESC nullptr
static constexpr const char IGNORED_DESC[] = "Ignored for compatability"; static constexpr const char IGNORED_DESC[] = "Ignored for compatability";
static constexpr const char EQUIV_DESC_BASE[] = "equivalent to -"; static constexpr const char EQUIV_DESC_BASE[] = "equivalent to -";
static constexpr const char* SHOW_ALL_DESC = concat_many_strs<EQUIV_DESC_BASE,NONPRINTING_SHORT_OPTION,SHOW_ENDS_SHORT_OPTION,SHOW_TABS_SHORT_OPTION>::str; static constexpr const char* SHOW_ALL_DESC = concat_many_strs<EQUIV_DESC_BASE,char_to_str<NONPRINTING_SHORT_OPTION>::str,char_to_str<SHOW_ENDS_SHORT_OPTION>::str,char_to_str<SHOW_TABS_SHORT_OPTION>::str>::str;
static constexpr const char NUMBER_NONBLANK_DESC_BASE[] = "number nonempty output lines, overrides -"; static constexpr const char NUMBER_NONBLANK_DESC_BASE[] = "number nonempty output lines, overrides -";
static constexpr const char* NUMBER_NONBLANK_DESC = concat_many_strs<NUMBER_NONBLANK_DESC_BASE,SHOW_TABS_SHORT_OPTION>::str; static constexpr const char* NUMBER_NONBLANK_DESC = concat_many_strs<NUMBER_NONBLANK_DESC_BASE,char_to_str<SHOW_TABS_SHORT_OPTION>::str>::str;
static constexpr const char* VE_DESC = concat_many_strs<EQUIV_DESC_BASE,NONPRINTING_SHORT_OPTION,SHOW_ENDS_SHORT_OPTION>::str; static constexpr const char* VE_DESC = concat_many_strs<EQUIV_DESC_BASE,char_to_str<NONPRINTING_SHORT_OPTION>::str,char_to_str<SHOW_ENDS_SHORT_OPTION>::str>::str;
static constexpr const char SHOW_ENDS_DESC[] = "display $ at end of each line"; static constexpr const char SHOW_ENDS_DESC[] = "display $ at end of each line";
static constexpr const char NUMBER_LINES_DESC[] = "number all output lines"; static constexpr const char NUMBER_LINES_DESC[] = "number all output lines";
static constexpr const char SQUEEZE_BLANKS_DESC[] = "suppress repeated empty output lines"; static constexpr const char SQUEEZE_BLANKS_DESC[] = "suppress repeated empty output lines";
static constexpr const char* VT_DESC = concat_many_strs<EQUIV_DESC_BASE,NONPRINTING_SHORT_OPTION,SHOW_TABS_SHORT_OPTION>::str; static constexpr const char* VT_DESC = concat_many_strs<EQUIV_DESC_BASE,char_to_str<NONPRINTING_SHORT_OPTION>::str,char_to_str<SHOW_TABS_SHORT_OPTION>::str>::str;
static constexpr const char SHOW_TABS_DESC[] = "display TAB characters as ^I"; static constexpr const char SHOW_TABS_DESC[] = "display TAB characters as ^I";
static constexpr const char* U_IGNORED_DESC = IGNORED_DESC; static constexpr const char* U_IGNORED_DESC = IGNORED_DESC;
static constexpr const char NONPRINTING_DESC[] = "should use ^ and M- notation, except for LFD and TAB (not supported as of now)"; static constexpr const char BINARY_DESC[] = "treat FILE as a binary file\n";
static constexpr const char NONPRINTING_DESC_BASE[] = "Use ^ and M- notation, except for LFD and TAB. Implies -";
static constexpr const char* NONPRINTING_DESC = concat_many_strs<NONPRINTING_DESC_BASE,char_to_str<BINARY_SHORT_OPTION>::str,char_to_str<'\n'>::str>::str;
static constexpr const char USAGE_DESC[] = "display this help and exit"; static constexpr const char USAGE_DESC[] = "display this help and exit";
static constexpr const char VERSION_DESC[] = "output version information and exit"; static constexpr const char VERSION_DESC[] = "output version information and exit";
static constexpr const char SEED_DESC[] = "Rainbow seed, 0 = random (default: 0)"; static constexpr const char SEED_DESC[] = "Rainbow seed, 0 = random (default: 0)";
static constexpr const char SPREAD_DESC[] = "Rainbow spread (default: Not yet decided)"; static constexpr const char SPREAD_DESC[] = "Rainbow spread (default: 0.4)";
static constexpr const char FREQ_DESC[] = "Rainbow frequency (default: Not yet decided)"; static constexpr const char FREQ_DESC[] = "Rainbow frequency (default: 0.3)";
static constexpr const char TRUECOLOR_DESC[] = "24-bit (truecolor)"; static constexpr const char TRUECOLOR_DESC[] = "24-bit (truecolor)";
static constexpr const char FORCE_COLOR_DESC[] = "Force color even when stdout is not a tty"; static constexpr const char FORCE_COLOR_DESC[] = "Force color even when stdout is not a tty";
static constexpr const char DISABLE_COLOR_DESC_BASE[] = "Suppress color even when stdout is a tty, overrides -"; static constexpr const char DISABLE_COLOR_DESC_BASE[] = "Suppress color even when stdout is a tty, overrides -";
static constexpr const char* DISABLE_COLOR_DESC = concat_many_strs<DISABLE_COLOR_DESC_BASE,FORCE_COLOR_SHORT_OPTION>::str; static constexpr const char* DISABLE_COLOR_DESC = concat_many_strs<DISABLE_COLOR_DESC_BASE,char_to_str<FORCE_COLOR_SHORT_OPTION>::str>::str;
static constexpr const char INVERT_DESC[] = "Change background color instead of foreground"; static constexpr const char INVERT_DESC[] = "Change background color instead of foreground\n";
static constexpr const char* IGNORE_ANIMATE_DESC = IGNORED_DESC; static constexpr const char* IGNORE_ANIMATE_DESC = IGNORED_DESC;
static constexpr const char* IGNORE_DURATION_DESC = IGNORED_DESC; static constexpr const char* IGNORE_DURATION_DESC = IGNORED_DESC;
static constexpr const char* IGNORE_SPEED_DESC = IGNORED_DESC; static constexpr const char* IGNORE_SPEED_DESC = IGNORED_DESC;
#define SHORT_OPT(x) (x##_SHORT_OPTION)[0] #define SHORT_OPT(x) (x##_SHORT_OPTION)
#define OPTION(x) {x##_LONG_OPTION, SHORT_OPT(x), x##_DESC} #define OPTION(x) {x##_LONG_OPTION, SHORT_OPT(x), x##_DESC}
const cmd_options cmd_arguments_list[] = {
OPTION(SHOW_ALL),
OPTION(NUMBER_NONBLANK),
OPTION(VE),
OPTION(SHOW_ENDS),
OPTION(NUMBER_LINES),
OPTION(SQUEEZE_BLANKS),
OPTION(VT),
OPTION(SHOW_TABS),
OPTION(U_IGNORED),
OPTION(NONPRINTING),
OPTION(BINARY),
OPTION(SEED),
OPTION(SPREAD),
OPTION(FREQ),
OPTION(TRUECOLOR),
OPTION(FORCE_COLOR),
OPTION(DISABLE_COLOR),
OPTION(IGNORE_ANIMATE),
OPTION(IGNORE_DURATION),
OPTION(IGNORE_SPEED),
OPTION(INVERT),
OPTION(USAGE),
OPTION(VERSION)
};
const size_t cmd_arguments_list_size = sizeof(cmd_arguments_list)/sizeof(cmd_arguments_list[0]);
static constexpr bool is_integer(const char* s){ static constexpr bool is_integer(const char* s){
if(*s != '-' && (*s < '0' || *s > '9')){ if(*s != '-' && (*s < '0' || *s > '9')){
@@ -188,10 +229,11 @@ static constexpr size_t strlen_pre_eq(const char* str){
return i; return i;
} }
#define CHECK_VALID_LONG_ARG(opt, off, arg, type) \ #define CHECK_VALID_LONG_ARG(opt, off, arg, type) \
{ \ { \
if(arg[off] != '=' || !is_##type(arg+off+1)){ \ if(arg[off] != '=' || !is_##type(arg+off+1)){ \
fwprintf(stderr, L"'--%.*s' requires an argument of type " #type "\n", off, opt); \ fprintf(stderr, "'--%.*s' requires an argument of type " #type "\n", off, opt); \
ret.error = 1; \ ret.error = 1; \
break; \ break; \
} \ } \
@@ -201,8 +243,8 @@ static constexpr size_t strlen_pre_eq(const char* str){
#define CHECK_VALID_SHORT_ARG(opt, arg, type) \ #define CHECK_VALID_SHORT_ARG(opt, arg, type) \
{ \ { \
if(next_arg == (argc-1) || !is_##type(arg)){ \ if(next_arg == argc || !is_##type(arg)){ \
fwprintf(stderr, L"'-%s' requires an argument of type " #type "\n", opt); \ fprintf(stderr, "'-%c' requires an argument of type " #type "\n", opt); \
ret.error = 1; \ ret.error = 1; \
break; \ break; \
} \ } \
@@ -226,19 +268,23 @@ cmd_args process_cmd_args(int argc, char** argv){
} }
if(!strcmp(argv[i], "--")){ if(!strcmp(argv[i], "--")){
escaped = true; escaped = true;
}else if(!strcmp(argv[i], "-")){
ret.filenames.push_back(argv[i]);
}else if(IS_SHORT_OPTION(argv[i])){ }else if(IS_SHORT_OPTION(argv[i])){
size_t arg_len = strlen(argv[i]); size_t arg_len = strlen(argv[i]);
int next_arg = i+1; int next_arg = i+1;
for(size_t j = 1;j < arg_len;++j){ for(size_t j = 1;j < arg_len;++j){
switch(argv[i][j]){ switch(argv[i][j]){
case SHORT_OPT(SHOW_ALL): case SHORT_OPT(SHOW_ALL):
ret.nonprinting = ret.ends = ret.tabs = 1; ret.set_nonprinting();
ret.ends = ret.tabs = 1;
break; break;
case SHORT_OPT(NUMBER_NONBLANK): case SHORT_OPT(NUMBER_NONBLANK):
ret.number = PRINT_LINE_NONBLANK; ret.number = PRINT_LINE_NONBLANK;
break; break;
case SHORT_OPT(VE): case SHORT_OPT(VE):
ret.nonprinting = ret.ends = 1; ret.set_nonprinting();
ret.ends = 1;
break; break;
case SHORT_OPT(SHOW_ENDS): case SHORT_OPT(SHOW_ENDS):
ret.ends = 1; ret.ends = 1;
@@ -251,6 +297,7 @@ cmd_args process_cmd_args(int argc, char** argv){
ret.squeeze = 1; ret.squeeze = 1;
break; break;
case SHORT_OPT(VT): case SHORT_OPT(VT):
ret.set_nonprinting();
ret.nonprinting = ret.tabs = 1; ret.nonprinting = ret.tabs = 1;
break; break;
case SHORT_OPT(SHOW_TABS): case SHORT_OPT(SHOW_TABS):
@@ -259,7 +306,10 @@ cmd_args process_cmd_args(int argc, char** argv){
case SHORT_OPT(U_IGNORED): case SHORT_OPT(U_IGNORED):
break; break;
case SHORT_OPT(NONPRINTING): case SHORT_OPT(NONPRINTING):
ret.nonprinting = 1; ret.set_nonprinting();
break;
case SHORT_OPT(BINARY):
ret.treatbinary = 1;
break; break;
case SHORT_OPT(USAGE): case SHORT_OPT(USAGE):
ret.usage = 1; ret.usage = 1;
@@ -295,17 +345,16 @@ cmd_args process_cmd_args(int argc, char** argv){
ret.freq = atof(argv[next_arg]); ret.freq = atof(argv[next_arg]);
++next_arg; ++next_arg;
break; break;
case SHORT_OPT(IGNORE_ANIMATE):
break;
case SHORT_OPT(IGNORE_DURATION): case SHORT_OPT(IGNORE_DURATION):
if(next_arg != (argc-1)){ if(next_arg == argc)
++next_arg; break;
} ++next_arg;
case SHORT_OPT(IGNORE_ANIMATE):
break; break;
default: default:
//keep parsing after error so we can still properly output requested color style //keep parsing after error so we can still properly output requested color style
ret.error = 1; ret.error = 1;
fwprintf(stderr, L"Unrecognized option '-%c'\n", argv[i][j]); fprintf(stderr, "Unrecognized option '-%c'\n", argv[i][j]);
}; };
} }
i = next_arg-1; i = next_arg-1;
@@ -323,7 +372,9 @@ cmd_args process_cmd_args(int argc, char** argv){
}else if(CHECK_LONG_OPTION(SHOW_TABS, arg)){ }else if(CHECK_LONG_OPTION(SHOW_TABS, arg)){
ret.tabs = 1; ret.tabs = 1;
}else if(CHECK_LONG_OPTION(NONPRINTING, arg)){ }else if(CHECK_LONG_OPTION(NONPRINTING, arg)){
ret.nonprinting = 1; ret.set_nonprinting();
}else if(CHECK_LONG_OPTION(BINARY, arg)){
ret.treatbinary = 1;
}else if(CHECK_LONG_OPTION(USAGE, arg)){ }else if(CHECK_LONG_OPTION(USAGE, arg)){
ret.usage = 1; ret.usage = 1;
}else if(CHECK_LONG_OPTION(VERSION, arg)){ }else if(CHECK_LONG_OPTION(VERSION, arg)){
@@ -338,15 +389,15 @@ cmd_args process_cmd_args(int argc, char** argv){
}else if(CHECK_LONG_OPTION(INVERT, arg)){ }else if(CHECK_LONG_OPTION(INVERT, arg)){
ret.invert = 1; ret.invert = 1;
}else if(CHECK_LONG_OPTION_WITH_ARG(SEED, arg)){ }else if(CHECK_LONG_OPTION_WITH_ARG(SEED, arg)){
constexpr size_t offset = strlen_pre_eq(SEED_LONG_OPTION); constexpr int offset = strlen_pre_eq(SEED_LONG_OPTION);
CHECK_VALID_LONG_INT_ARG(SEED, offset, arg); CHECK_VALID_LONG_INT_ARG(SEED, offset, arg);
ret.seed = atol(arg+offset+1); ret.seed = atol(arg+offset+1);
}else if(CHECK_LONG_OPTION_WITH_ARG(SPREAD, arg)){ }else if(CHECK_LONG_OPTION_WITH_ARG(SPREAD, arg)){
constexpr size_t offset = strlen_pre_eq(SPREAD_LONG_OPTION); constexpr int offset = strlen_pre_eq(SPREAD_LONG_OPTION);
CHECK_VALID_LONG_FLOAT_ARG(SPREAD, offset, arg); CHECK_VALID_LONG_FLOAT_ARG(SPREAD, offset, arg);
ret.spread = atof(arg+offset+1); ret.spread = atof(arg+offset+1);
}else if(CHECK_LONG_OPTION_WITH_ARG(FREQ, arg)){ }else if(CHECK_LONG_OPTION_WITH_ARG(FREQ, arg)){
constexpr size_t offset = strlen_pre_eq(FREQ_LONG_OPTION); constexpr int offset = strlen_pre_eq(FREQ_LONG_OPTION);
CHECK_VALID_LONG_FLOAT_ARG(FREQ, offset, arg); CHECK_VALID_LONG_FLOAT_ARG(FREQ, offset, arg);
ret.freq = atof(arg+offset+1); ret.freq = atof(arg+offset+1);
}else if(CHECK_LONG_OPTION(IGNORE_ANIMATE, arg) || }else if(CHECK_LONG_OPTION(IGNORE_ANIMATE, arg) ||
@@ -356,7 +407,7 @@ cmd_args process_cmd_args(int argc, char** argv){
; ;
}else{ }else{
ret.error = 1; ret.error = 1;
fwprintf(stderr, L"Unrecognized option '%s'\n", argv[i]); fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
} }
}else{ }else{
ret.filenames.push_back(argv[i]); ret.filenames.push_back(argv[i]);
@@ -367,32 +418,3 @@ cmd_args process_cmd_args(int argc, char** argv){
} }
return ret; return ret;
} }
const cmd_options cmd_arguments_list[] = {
OPTION(SHOW_ALL),
OPTION(NUMBER_NONBLANK),
OPTION(VE),
OPTION(SHOW_ENDS),
OPTION(NUMBER_LINES),
OPTION(SQUEEZE_BLANKS),
OPTION(VT),
OPTION(SHOW_TABS),
OPTION(U_IGNORED),
OPTION(NONPRINTING),
{nullptr,0,nullptr}, //add newline to output
OPTION(SEED),
OPTION(SPREAD),
OPTION(FREQ),
OPTION(TRUECOLOR),
OPTION(FORCE_COLOR),
OPTION(DISABLE_COLOR),
OPTION(IGNORE_ANIMATE),
OPTION(IGNORE_DURATION),
OPTION(IGNORE_SPEED),
OPTION(INVERT),
{nullptr,0,nullptr},
OPTION(USAGE),
OPTION(VERSION)
};
const size_t cmd_arguments_list_size = sizeof(cmd_arguments_list)/sizeof(cmd_arguments_list[0]);

View File

@@ -33,8 +33,11 @@
#include <cstddef> //size_t #include <cstddef> //size_t
#include <chrono> //std::chrono::* #include <chrono> //std::chrono::*
#include <cstdlib> //srand #include <cstdlib> //srand
#include <string> //char_traits
#include <sys/types.h> //struct stat
#include <sys/stat.h> //struct stat
constexpr const char version_string[] = "roflcat version 1.0"; constexpr const char version_string[] = "roflcat version 1.1";
static constexpr size_t constexpr_strlen(const char* str){ static constexpr size_t constexpr_strlen(const char* str){
size_t i = 0; size_t i = 0;
@@ -50,7 +53,7 @@ static constexpr size_t constexpr_strlen(const char* str){
static int detect_term_colors(void){ static int detect_term_colors(void){
int term_colors = 16; int term_colors = 16;
if(setupterm(NULL, fileno(stdout), NULL) == ERR){ if(setupterm(NULL, fileno(stdout), NULL) == ERR){
fwprintf(stderr, L"could not set up term\n"); fprintf(stderr, "could not set up term\n");
}else{ }else{
term_colors = tigetnum("colors"); term_colors = tigetnum("colors");
if(term_colors < 0){ if(term_colors < 0){
@@ -91,6 +94,7 @@ constexpr void print_usage(Printer&& p){
p.print(L'\n'); p.print(L'\n');
} }
p.print(L"\nroflcat Copyright (C) 2019 rexy712\n"); p.print(L"\nroflcat Copyright (C) 2019 rexy712\n");
p.print(L"Source code can be found at https://gitlab.com/rexy712/roflcat\n");
p.print(L"This program comes with ABSOLUTELY NO WARRANTY\n"); p.print(L"This program comes with ABSOLUTELY NO WARRANTY\n");
p.print(L"This is free software, and you are welcome to redistribute it\n"); p.print(L"This is free software, and you are welcome to redistribute it\n");
p.print(L"under certain conditions; see the GNU GPLv3 for details.\n"); p.print(L"under certain conditions; see the GNU GPLv3 for details.\n");
@@ -103,8 +107,133 @@ constexpr void print_version(Printer&& p){
p.reset(); p.reset();
} }
void to_caret_notation(int in, char* dest){
dest[0] = '^';
dest[1] = in ^ 0x40;
dest[2] = 0;
}
void to_mdash_notation(int in, char* dest){
int corin = in & 0x7F;
dest[0] = 'M';
dest[1] = '-';
if(corin > 32 && corin < 127){
dest[2] = corin;
dest[3] = 0;
}else{
char tmp[3];
to_caret_notation(corin, tmp);
dest[2] = tmp[0];
dest[3] = tmp[1];
dest[4] = 0;
}
}
void nonprinting_notation(int in, char* dest){
if(in & 0x80){
to_mdash_notation(in, dest);
}else if(in == '\n' || in == '\t' || (in > 31 && in < 127)){
dest[0] = in;
dest[1] = 0;
}else{
to_caret_notation(in, dest);
}
}
bool is_directory(const char* file){
struct stat st;
if(stat(file, &st) != 0){
return false;
}
return S_ISDIR(st.st_mode);
}
bool file_exists(const char* file){
struct stat st;
return stat(file, &st) == 0;
}
FILE* open_file(const char* file, const char* mode){
if(is_directory(file)){
fflush(stdout);
fprintf(stderr, "Unable to open file \"%s\": Is a directory \n", file);
return nullptr;
}
if(!file_exists(file)){
fflush(stdout);
fprintf(stderr, "Unable to open file \"%s\": No such file\n", file);
return nullptr;
}
FILE* fp = fopen(file, mode);
if(!fp){
fflush(stdout);
fprintf(stderr, "Unable to open file for reading: \"%s\"\n", file);
}
return fp;
}
template<class T, class Printer, class Func>
inline void do_stdin(Printer&& p, Func&& f){
for(T in;(in = f(stdin)) != std::char_traits<T>::eof();){
p.print(in);
if(in == '\n')
p.reset();
}
clearerr(stdin);
}
//Use the given type of printer to output the contents of files and/or stdin //Use the given type of printer to output the contents of files and/or stdin
template<class Printer> template<class Printer>
int real_print_files(const cmd_args& args){
int ret = 0;
Printer p(args);
for(size_t i = 0;i < args.filenames.size();++i){
if(!strcmp(args.filenames[i], "-")){ //stdin
if(args.treatbinary){
//Don't check if buf is empty because that's not how GNU cat handles Ctrl+D in middle of line
for(int in;(in = fgetc(stdin)) != EOF;){
p.print(static_cast<char>(in));
if(in == L'\n') //Only reset on newline to save print overhead
p.reset();
}
}else{
for(wint_t in;(in = fgetwc(stdin)) != WEOF;){
p.print(static_cast<wchar_t>(in));
if(in == L'\n')
p.reset();
}
}
clearerr(stdin);
}else{ //everything besides stdin
FILE* fp = open_file(args.filenames[i], "r");
if(!fp){
ret = 2;
continue;
}
if(args.treatbinary){
if(args.nonprinting){
for(int in;(in = fgetc(fp)) != EOF;){
char tmp[5];
nonprinting_notation(in, tmp);
p.print(tmp);
}
}else{
for(int in;(in = fgetc(fp)) != EOF;)
p.print(static_cast<char>(in));
}
}else{
//set to wide character mode before anything
fwide(fp, 1);
for(wint_t in;(in = fgetwc(fp)) != WEOF;)
p.print(static_cast<wchar_t>(in));
}
p.reset();
fclose(fp);
}
}
p.reset();
return ret;
}
template<class Printer>
int print_files(cmd_args& args){ int print_files(cmd_args& args){
if(args.usage){ if(args.usage){
args.clear_gnu_options(); args.clear_gnu_options();
@@ -115,36 +244,10 @@ int print_files(cmd_args& args){
print_version(Printer{args}); print_version(Printer{args});
return 0; return 0;
} }
int ret = 0;
Printer p(args);
for(size_t i = 0;i < args.filenames.size();++i){
if(!strcmp(args.filenames[i], "-")){ //stdin
//Don't check if buf is empty because that's not how GNU cat handles Ctrl+D in middle of line return real_print_files<Printer>(args);
for(wint_t in;(in = fgetwc(stdin)) != WEOF;){
p.print(static_cast<wchar_t>(in));
if(in == L'\n') //Only reset on newline to save print overhead
p.reset();
}
}else{ //everything besides stdin
FILE* fp = fopen(args.filenames[i], "r");
if(!fp){
fflush(stdout); //force color update before error
fwprintf(stderr, L"Unable to open file %s\n", args.filenames[i]);
ret = 2;
continue;
}
//set to wide character mode before anything
fwide(fp, 1);
for(wint_t in;(in = fgetwc(fp)) != WEOF;)
p.print(static_cast<wchar_t>(in));
p.reset();
fclose(fp);
}
}
p.reset();
return ret;
} }
auto get_time(void){ auto get_time(void){
auto time = std::chrono::high_resolution_clock::now(); auto time = std::chrono::high_resolution_clock::now();
auto eptime = time.time_since_epoch(); auto eptime = time.time_since_epoch();
@@ -158,7 +261,7 @@ int main(int argc, char** argv){
int avail_colors = detect_term_colors(); int avail_colors = detect_term_colors();
if(args.error){ if(args.error){
fwprintf(stderr, L"Try '%s --help' for more information.\n", argv[0]); fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
return 1; return 1;
} }
@@ -173,6 +276,5 @@ int main(int argc, char** argv){
}else if(avail_colors == 256){ }else if(avail_colors == 256){
return print_files<basic_color_printer<256>>(args); return print_files<basic_color_printer<256>>(args);
} }
return 0; return 0;
} }