25 Commits

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
7 changed files with 274 additions and 137 deletions

View File

@@ -1,6 +1,7 @@
include(CMakeDependentOption)
cmake_minimum_required(VERSION 3.1)
project(roflcat)
cmake_minimum_required(VERSION 3.1)
include(CMakeDependentOption)
include(GNUInstallDirs)
#c++17 without compiler extensions
set(CMAKE_CXX_STANDARD 17)

View File

@@ -1,31 +1,42 @@
# README
## 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.
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 :)
![alt text](https://i.postimg.cc/1tYmJzxd/roflcat-usage.png "Screenshot of --help output")
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/0Qk6k34j/roflcat-usage.png "Screenshot of --help output")
## Dependencies
ncurses
cmake
## 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
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
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")
Truecolor (24 bit):
Truecolor (24 bit):
![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")
[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 <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_ALWAYS 1
@@ -46,8 +37,8 @@ struct cmd_args{
//lolcat options
long seed = 0;
float freq = 0.2f;
float spread = 0.3f;
float freq = 0.3f;
float spread = 0.4f;
unsigned truecol:1;
unsigned color:2;
unsigned invert:1;
@@ -58,6 +49,7 @@ struct cmd_args{
unsigned squeeze:1;
unsigned tabs:1;
unsigned nonprinting:1;
unsigned treatbinary:1;
//flags
unsigned error:1;
@@ -66,8 +58,20 @@ struct cmd_args{
cmd_args(void)noexcept;
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);
#endif

View File

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

View File

@@ -25,11 +25,14 @@
#include <cstdlib> //atol
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){
number = PRINT_LINE_NEVER;
ends = squeeze = tabs = nonprinting = 0;
}
void cmd_args::set_nonprinting(bool i){
nonprinting = treatbinary = i;
}
static constexpr size_t constexpr_strlen(const char* str){
size_t i = 0;
@@ -60,6 +63,10 @@ template<const char* S1, const char* S2>
struct concat_many_strs<S1,S2>{
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 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* U_IGNORED_LONG_OPTION = NO_LONG_OPTION;
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 VERSION_LONG_OPTION[] = "version";
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_SPEED_LONG_OPTION[] = "speed" REQ_FLOAT_ARG;
#define NO_SHORT_OPTION ""
static constexpr const char SHOW_ALL_SHORT_OPTION[] = "A";
static constexpr const char NUMBER_NONBLANK_SHORT_OPTION[] = "b";
static constexpr const char TRUECOLOR_SHORT_OPTION[] = "C";
static constexpr const char VE_SHORT_OPTION[] = "e";
static constexpr const char SHOW_ENDS_SHORT_OPTION[] = "E";
static constexpr const char FORCE_COLOR_SHORT_OPTION[] = "f";
static constexpr const char FREQ_SHORT_OPTION[] = "F";
static constexpr const char USAGE_SHORT_OPTION[] = "h";
static constexpr const char INVERT_SHORT_OPTION[] = "i";
static constexpr const char NUMBER_LINES_SHORT_OPTION[] = "n";
static constexpr const char DISABLE_COLOR_SHORT_OPTION[] = "N";
static constexpr const char SPREAD_SHORT_OPTION[] = "p";
static constexpr const char SQUEEZE_BLANKS_SHORT_OPTION[] = "s";
static constexpr const char SEED_SHORT_OPTION[] = "S";
static constexpr const char VT_SHORT_OPTION[] = "t";
static constexpr const char SHOW_TABS_SHORT_OPTION[] = "T";
static constexpr const char U_IGNORED_SHORT_OPTION[] = "u";
static constexpr const char NONPRINTING_SHORT_OPTION[] = "v";
#define NO_SHORT_OPTION 0
static constexpr char SHOW_ALL_SHORT_OPTION = 'A';
static constexpr char NUMBER_NONBLANK_SHORT_OPTION = 'b';
static constexpr char BINARY_SHORT_OPTION = 'B';
static constexpr char TRUECOLOR_SHORT_OPTION = 'C';
static constexpr char VE_SHORT_OPTION = 'e';
static constexpr char SHOW_ENDS_SHORT_OPTION = 'E';
static constexpr char FORCE_COLOR_SHORT_OPTION = 'f';
static constexpr char FREQ_SHORT_OPTION = 'F';
static constexpr char USAGE_SHORT_OPTION = 'h';
static constexpr char INVERT_SHORT_OPTION = 'i';
static constexpr char NUMBER_LINES_SHORT_OPTION = 'n';
static constexpr char DISABLE_COLOR_SHORT_OPTION = 'N';
static constexpr char SPREAD_SHORT_OPTION = 'p';
static constexpr char SQUEEZE_BLANKS_SHORT_OPTION = 's';
static constexpr char SEED_SHORT_OPTION = 'S';
static constexpr char VT_SHORT_OPTION = 't';
static constexpr char SHOW_TABS_SHORT_OPTION = 'T';
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 const char IGNORE_DURATION_SHORT_OPTION[] = "d";
static constexpr const char* IGNORE_SPEED_SHORT_OPTION = NO_SHORT_OPTION;
static constexpr char IGNORE_ANIMATE_SHORT_OPTION = 'a';
static constexpr char IGNORE_DURATION_SHORT_OPTION = 'd';
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
static constexpr const char IGNORED_DESC[] = "Ignored for compatability";
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 = concat_many_strs<NUMBER_NONBLANK_DESC_BASE,SHOW_TABS_SHORT_OPTION>::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* 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,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 NUMBER_LINES_DESC[] = "number all 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* 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 VERSION_DESC[] = "output version information and exit";
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 FREQ_DESC[] = "Rainbow frequency (default: Not yet decided)";
static constexpr const char SPREAD_DESC[] = "Rainbow spread (default: 0.4)";
static constexpr const char FREQ_DESC[] = "Rainbow frequency (default: 0.3)";
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 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 INVERT_DESC[] = "Change background color instead of foreground";
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\n";
static constexpr const char* IGNORE_ANIMATE_DESC = IGNORED_DESC;
static constexpr const char* IGNORE_DURATION_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}
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){
if(*s != '-' && (*s < '0' || *s > '9')){
@@ -188,10 +229,11 @@ static constexpr size_t strlen_pre_eq(const char* str){
return i;
}
#define CHECK_VALID_LONG_ARG(opt, off, arg, type) \
{ \
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; \
break; \
} \
@@ -202,7 +244,7 @@ static constexpr size_t strlen_pre_eq(const char* str){
#define CHECK_VALID_SHORT_ARG(opt, arg, type) \
{ \
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; \
break; \
} \
@@ -234,13 +276,15 @@ cmd_args process_cmd_args(int argc, char** argv){
for(size_t j = 1;j < arg_len;++j){
switch(argv[i][j]){
case SHORT_OPT(SHOW_ALL):
ret.nonprinting = ret.ends = ret.tabs = 1;
ret.set_nonprinting();
ret.ends = ret.tabs = 1;
break;
case SHORT_OPT(NUMBER_NONBLANK):
ret.number = PRINT_LINE_NONBLANK;
break;
case SHORT_OPT(VE):
ret.nonprinting = ret.ends = 1;
ret.set_nonprinting();
ret.ends = 1;
break;
case SHORT_OPT(SHOW_ENDS):
ret.ends = 1;
@@ -253,6 +297,7 @@ cmd_args process_cmd_args(int argc, char** argv){
ret.squeeze = 1;
break;
case SHORT_OPT(VT):
ret.set_nonprinting();
ret.nonprinting = ret.tabs = 1;
break;
case SHORT_OPT(SHOW_TABS):
@@ -261,7 +306,10 @@ cmd_args process_cmd_args(int argc, char** argv){
case SHORT_OPT(U_IGNORED):
break;
case SHORT_OPT(NONPRINTING):
ret.nonprinting = 1;
ret.set_nonprinting();
break;
case SHORT_OPT(BINARY):
ret.treatbinary = 1;
break;
case SHORT_OPT(USAGE):
ret.usage = 1;
@@ -297,17 +345,16 @@ cmd_args process_cmd_args(int argc, char** argv){
ret.freq = atof(argv[next_arg]);
++next_arg;
break;
case SHORT_OPT(IGNORE_ANIMATE):
break;
case SHORT_OPT(IGNORE_DURATION):
if(next_arg != (argc-1)){
++next_arg;
}
if(next_arg == argc)
break;
++next_arg;
case SHORT_OPT(IGNORE_ANIMATE):
break;
default:
//keep parsing after error so we can still properly output requested color style
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;
@@ -325,7 +372,9 @@ cmd_args process_cmd_args(int argc, char** argv){
}else if(CHECK_LONG_OPTION(SHOW_TABS, arg)){
ret.tabs = 1;
}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)){
ret.usage = 1;
}else if(CHECK_LONG_OPTION(VERSION, arg)){
@@ -340,15 +389,15 @@ cmd_args process_cmd_args(int argc, char** argv){
}else if(CHECK_LONG_OPTION(INVERT, arg)){
ret.invert = 1;
}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);
ret.seed = atol(arg+offset+1);
}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);
ret.spread = atof(arg+offset+1);
}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);
ret.freq = atof(arg+offset+1);
}else if(CHECK_LONG_OPTION(IGNORE_ANIMATE, arg) ||
@@ -358,7 +407,7 @@ cmd_args process_cmd_args(int argc, char** argv){
;
}else{
ret.error = 1;
fwprintf(stderr, L"Unrecognized option '%s'\n", argv[i]);
fprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
}
}else{
ret.filenames.push_back(argv[i]);
@@ -369,32 +418,3 @@ cmd_args process_cmd_args(int argc, char** argv){
}
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 <chrono> //std::chrono::*
#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){
size_t i = 0;
@@ -50,7 +53,7 @@ static constexpr size_t constexpr_strlen(const char* str){
static int detect_term_colors(void){
int term_colors = 16;
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{
term_colors = tigetnum("colors");
if(term_colors < 0){
@@ -91,6 +94,7 @@ constexpr void print_usage(Printer&& p){
p.print(L'\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 is free software, and you are welcome to redistribute it\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();
}
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
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){
if(args.usage){
args.clear_gnu_options();
@@ -115,37 +244,10 @@ int print_files(cmd_args& args){
print_version(Printer{args});
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
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();
}
clearerr(stdin);
}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;
return real_print_files<Printer>(args);
}
auto get_time(void){
auto time = std::chrono::high_resolution_clock::now();
auto eptime = time.time_since_epoch();
@@ -159,7 +261,7 @@ int main(int argc, char** argv){
int avail_colors = detect_term_colors();
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;
}
@@ -174,6 +276,5 @@ int main(int argc, char** argv){
}else if(avail_colors == 256){
return print_files<basic_color_printer<256>>(args);
}
return 0;
}