finally finished this. it should be 1:1 with the original rust version...
ci / unix (macos-15) (push) Canceled after 0s
ci / unix (macos-15-intel) (push) Canceled after 0s
ci / unix (ubuntu-24.04) (push) Canceled after 0s
ci / unix (ubuntu-24.04-arm) (push) Canceled after 0s
ci / windows (push) Canceled after 0s
ci / freebsd (push) Canceled after 0s
release / dist (push) Canceled after 0s
release / build (, macos-arm64, macos-15) (push) Canceled after 0s
release / build (, macos-x86_64, macos-15-intel) (push) Canceled after 0s
release / build (-static, linux-aarch64, ubuntu-24.04-arm) (push) Canceled after 0s
release / build (-static, linux-x86_64, ubuntu-24.04) (push) Canceled after 0s
release / windows (push) Canceled after 0s
release / release (push) Canceled after 0s
ci / unix (macos-15) (push) Canceled after 0s
ci / unix (macos-15-intel) (push) Canceled after 0s
ci / unix (ubuntu-24.04) (push) Canceled after 0s
ci / unix (ubuntu-24.04-arm) (push) Canceled after 0s
ci / windows (push) Canceled after 0s
ci / freebsd (push) Canceled after 0s
release / dist (push) Canceled after 0s
release / build (, macos-arm64, macos-15) (push) Canceled after 0s
release / build (, macos-x86_64, macos-15-intel) (push) Canceled after 0s
release / build (-static, linux-aarch64, ubuntu-24.04-arm) (push) Canceled after 0s
release / build (-static, linux-x86_64, ubuntu-24.04) (push) Canceled after 0s
release / windows (push) Canceled after 0s
release / release (push) Canceled after 0s
This commit is contained in:
1 parent
7aff48a428
commit
b5e3328f64
8 files changed
+337
-162
No files matched your search
+3
-1
@@ -6,7 +6,9 @@ mikusays_SOURCES = \
|
||||
src/helper.c \
|
||||
defs/all.h \
|
||||
defs/mikuart.h \
|
||||
defs/helper.h
|
||||
defs/helper.h \
|
||||
defs/colors.h \
|
||||
defs/widechar.h
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra
|
||||
|
||||
|
||||
Generated
+3
-1
@@ -296,7 +296,9 @@ mikusays_SOURCES = \
|
||||
src/helper.c \
|
||||
defs/all.h \
|
||||
defs/mikuart.h \
|
||||
defs/helper.h
|
||||
defs/helper.h \
|
||||
defs/colors.h \
|
||||
defs/widechar.h
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra
|
||||
EXTRA_DIST = LICENSE README.md
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.73 for mikusays 0.3.1.
|
||||
# Generated by GNU Autoconf 2.73 for mikusays 1.0.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2017, 2020-2026 Free Software Foundation,
|
||||
@@ -584,8 +584,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='mikusays'
|
||||
PACKAGE_TARNAME='mikusays'
|
||||
PACKAGE_VERSION='0.3.1'
|
||||
PACKAGE_STRING='mikusays 0.3.1'
|
||||
PACKAGE_VERSION='1.0.0'
|
||||
PACKAGE_STRING='mikusays 1.0.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL='https://git.squeakyrat.moe/mikusays-c'
|
||||
|
||||
@@ -1281,7 +1281,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
'configure' configures mikusays 0.3.1 to adapt to many kinds of systems.
|
||||
'configure' configures mikusays 1.0.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1348,7 +1348,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of mikusays 0.3.1:";;
|
||||
short | recursive ) echo "Configuration of mikusays 1.0.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1443,7 +1443,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
mikusays configure 0.3.1
|
||||
mikusays configure 1.0.0
|
||||
generated by GNU Autoconf 2.73
|
||||
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
@@ -1555,7 +1555,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by mikusays $as_me 0.3.1, which was
|
||||
It was created by mikusays $as_me 1.0.0, which was
|
||||
generated by GNU Autoconf 2.73. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@@ -3171,7 +3171,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='mikusays'
|
||||
VERSION='0.3.1'
|
||||
VERSION='1.0.0'
|
||||
|
||||
|
||||
printf '%s\n' "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
||||
@@ -5437,7 +5437,7 @@ cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by mikusays $as_me 0.3.1, which was
|
||||
This file was extended by mikusays $as_me 1.0.0, which was
|
||||
generated by GNU Autoconf 2.73. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -5497,7 +5497,7 @@ ac_cs_config_escaped=`printf '%s\n' "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
mikusays config.status 0.3.1
|
||||
mikusays config.status 1.0.0
|
||||
configured by $0, generated by GNU Autoconf 2.73,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([mikusays], [0.3.1], [], [mikusays], [https://git.squeakyrat.moe/mikusays-c])
|
||||
AC_INIT([mikusays], [1.0.0], [], [mikusays], [https://git.squeakyrat.moe/mikusays-c])
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall])
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
|
||||
+4
-20
@@ -11,24 +11,6 @@ typedef enum color_mode {
|
||||
MODE_TRUECOLOR
|
||||
}color_mode;
|
||||
|
||||
typedef enum NamedColor {
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
BrightBlack,
|
||||
BrightRed,
|
||||
BrightGreen,
|
||||
BrightYellow,
|
||||
BrightBlue,
|
||||
BrightMagenta,
|
||||
BrightCyan,
|
||||
BrightWhite,
|
||||
}NamedColor;
|
||||
|
||||
typedef struct{
|
||||
unsigned char red;
|
||||
@@ -45,6 +27,9 @@ typedef struct colorconfig {
|
||||
int brightness; //default 50
|
||||
rgbcolor *rainbow_cache; //array of rgbcolor malloced
|
||||
size_t rainbow_count;
|
||||
bool has_gradient;
|
||||
rgbcolor gradient_start;
|
||||
rgbcolor gradient_end;
|
||||
}colorconfig;
|
||||
|
||||
typedef enum ColorParseError {
|
||||
@@ -53,13 +38,11 @@ typedef enum ColorParseError {
|
||||
COLOR_ERR_HEX,
|
||||
COLOR_ERR_RGB,
|
||||
COLOR_ERR_HSL,
|
||||
COLOR_ERR_UNKNOWN_NAME
|
||||
}ColorParseError;
|
||||
|
||||
void colorconfig_init(colorconfig *config);
|
||||
enum color_mode detect_color_mode(void);
|
||||
bool enable_windows_ansi(void);
|
||||
int parse_namedcolors(const char *name, rgbcolor *out_rgb);
|
||||
ColorParseError parse_color(const char *s, rgbcolor *out);
|
||||
const char *color_parse_error_message(ColorParseError error);
|
||||
unsigned char rgb_to_ansi16(rgbcolor rgb);
|
||||
@@ -67,5 +50,6 @@ unsigned char rgb_to_ansi256(rgbcolor rgb);
|
||||
rgbcolor hsl_to_rgb(unsigned short h, unsigned char s, unsigned char l);
|
||||
rgbcolor lerp_color_hsv(rgbcolor start, rgbcolor end, float t);
|
||||
void rgb_to_fg_escape(rgbcolor rgb, color_mode mode, char *out, size_t size);
|
||||
void print_color_debug(const colorconfig *config, bool ansi_enabled);
|
||||
|
||||
#endif
|
||||
+38
-6
@@ -560,17 +560,49 @@ ColorParseError parse_color(const char *s, rgbcolor *out){
|
||||
const char *color_parse_error_message(ColorParseError error){
|
||||
switch(error){
|
||||
case COLOR_ERR_FORMAT:
|
||||
return "Invaliid color format";
|
||||
return "Invalid color format";
|
||||
case COLOR_ERR_HEX:
|
||||
return "Invaliid hex color code";
|
||||
return "Invalid hex color code";
|
||||
case COLOR_ERR_RGB:
|
||||
return "Invaliid RGB format. Expected format: R,G,B";
|
||||
return "Invalid RGB format. Expected format: R,G,B";
|
||||
case COLOR_ERR_HSL:
|
||||
return "Invaliid HSL format. Expected format: hsl(H,S%,L%)";
|
||||
case COLOR_ERR_UNKNOWN_NAME:
|
||||
return "Unkown named color";
|
||||
return "Invalid HSL format. Expected format: hsl(H,S%,L%)";
|
||||
case COLOR_OK:
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
static const char *color_mode_name(color_mode mode){
|
||||
switch(mode){
|
||||
case MODE_TRUECOLOR: return "Truecolor";
|
||||
case MODE_ANSI256: return "Ansi256";
|
||||
case MODE_ANSI16: return "Ansi16";
|
||||
case MODE_NONE:
|
||||
default: return "NoColor";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_env_debug(const char *name){
|
||||
const char *value = getenv(name);
|
||||
if(value)
|
||||
printf("%s: Some(\"%s\")\n", name, value);
|
||||
else
|
||||
printf("%s: None\n", name);
|
||||
}
|
||||
|
||||
void print_color_debug(const colorconfig *config, bool ansi_enabled){
|
||||
printf("Detected color mode: %s\n", color_mode_name(config->mode));
|
||||
printf("ANSI enabled: %s\n", ansi_enabled ? "true" : "false");
|
||||
print_env_debug("NO_COLOR");
|
||||
print_env_debug("COLORTERM");
|
||||
print_env_debug("TERM");
|
||||
#ifdef _WIN32
|
||||
{
|
||||
const char *shell = detect_windows_shell();
|
||||
if(shell)
|
||||
printf("Windows shell hint: Some(\"%s\")\n", shell);
|
||||
else
|
||||
printf("Windows shell hint: None\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+277
-123
@@ -16,28 +16,46 @@ static struct win term = {80, 24}; //if the function fails this is the defualt
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
//i have no way to test this because i don't have any windows pc in my house qwq
|
||||
//(i asked claude to write this for windows because i never looked at the windows api in my life)
|
||||
static void getterminalsize(void) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
int found = GetConsoleScreenBufferInfo(console, &info);
|
||||
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
|
||||
term.winwidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
term.winheight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
if(!found){
|
||||
HANDLE fallback = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if(fallback != INVALID_HANDLE_VALUE){
|
||||
found = GetConsoleScreenBufferInfo(fallback, &info);
|
||||
CloseHandle(fallback);
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
term.winwidth = info.srWindow.Right - info.srWindow.Left + 1;
|
||||
term.winheight = info.srWindow.Bottom - info.srWindow.Top + 1;
|
||||
}
|
||||
}
|
||||
#else //or this if they're not on windows which should be posixish like linux or macos
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static void getterminalsize(void) {
|
||||
struct winsize ws;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
|
||||
if(ws.ws_col > 0)
|
||||
term.winwidth = ws.ws_col;
|
||||
if(ws.ws_row > 0)
|
||||
term.winheight = ws.ws_row;
|
||||
struct winsize size_info;
|
||||
int tty_descriptor = -1;
|
||||
int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &size_info);
|
||||
|
||||
if(status != 0){
|
||||
tty_descriptor = open("/dev/tty", O_RDONLY);
|
||||
if(tty_descriptor >= 0)
|
||||
status = ioctl(tty_descriptor, TIOCGWINSZ, &size_info);
|
||||
}
|
||||
if(status == 0){
|
||||
if(size_info.ws_col > 0) term.winwidth = size_info.ws_col;
|
||||
if(size_info.ws_row > 0) term.winheight = size_info.ws_row;
|
||||
}
|
||||
if(tty_descriptor >= 0)
|
||||
close(tty_descriptor);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -154,11 +172,9 @@ char **wraptext(const char *text, int max, int *count){
|
||||
#define MIN_BUBBLE_WIDTH 20
|
||||
char **getbubblelines(char *text){
|
||||
char **templines = wraptext(text, MAX_BUBBLE_WIDTH - 4, &count);
|
||||
if(templines == NULL)
|
||||
return NULL;
|
||||
|
||||
size_t content_width = 0;
|
||||
for (int i = 0; templines[i] != NULL; i++) {
|
||||
for (int i = 0; templines != NULL && templines[i] != NULL; i++) {
|
||||
size_t current_len = displaywidth(templines[i]);
|
||||
if (current_len > content_width)
|
||||
content_width = current_len;
|
||||
@@ -185,7 +201,7 @@ char **getbubblelines(char *text){
|
||||
strcat(bubblelines[0], horizontal);
|
||||
strcat(bubblelines[0], top_right);
|
||||
|
||||
for (int i = 0; lines[i] != NULL; i++) {
|
||||
for (int i = 0; lines != NULL && lines[i] != NULL; i++) {
|
||||
int line_width = displaywidth(lines[i]);
|
||||
int padding_needed = (bubble_width - 2) - line_width;
|
||||
if (padding_needed < 0) padding_needed = 0;
|
||||
@@ -215,15 +231,17 @@ static void print_help(void){
|
||||
"Arguments:\n"
|
||||
" [TEXT] Text to display in the speech bubble\n\n"
|
||||
"Options:\n"
|
||||
" -s, --style <STYLE> Style of the Miku art. A random one is chosen if not specified\n"
|
||||
" -l, --list List all available art styles with their indices\n"
|
||||
" -h, --help Print help\n"
|
||||
" -V, --version Print version\n"
|
||||
" --rainbow Apply a smooth rainbow gradient across the art\n"
|
||||
" --saturation <0-100> Rainbow saturation (default: 100)\n"
|
||||
" --brightness <0-100> Rainbow brightness (default: 50)\n"
|
||||
" --color <COLOR> Set a single solid color\n"
|
||||
" --no-color Disable all coloring\n");
|
||||
" -s, --style <STYLE> Style of the Miku art. A random one is chosen if not specified\n"
|
||||
" -l, --list List all available art styles with their indices\n"
|
||||
" --rainbow Apply a smooth rainbow gradient across the ASCII art\n"
|
||||
" --saturation <SATURATION> Saturation level for rainbow gradient (0-100) [default: 100]\n"
|
||||
" --brightness <BRIGHTNESS> Brightness level for rainbow gradient (0-100) [default: 50]\n"
|
||||
" --color <COLOR> Override with a single solid color\n"
|
||||
" --gradient <START:END> Define a custom two-color gradient (e.g., --gradient red:blue)\n"
|
||||
" --no-color Disable all coloring (respects NO_COLOR environment variable)\n"
|
||||
" --color-debug Print color detection/debug info and exit\n"
|
||||
" -h, --help Print help\n"
|
||||
" -V, --version Print version\n");
|
||||
}
|
||||
|
||||
static void list_all_art_styles(void){
|
||||
@@ -238,11 +256,24 @@ static void list_all_art_styles(void){
|
||||
}
|
||||
|
||||
printf("\nUse --style <number> to select a specific style, or omit for random selection.\n");
|
||||
|
||||
printf("\nColor Options:\n"
|
||||
" --rainbow Apply a smooth rainbow gradient\n"
|
||||
" --saturation <0-100> Set rainbow saturation level (default: 100)\n"
|
||||
" --brightness <0-100> Set rainbow brightness level (default: 50)\n"
|
||||
" --color <COLOR> Set a single solid color (supports named colors, hex, rgb, hsl, ansi)\n"
|
||||
" --gradient <START:END> Define a custom two-color gradient\n"
|
||||
" --no-color Disable all coloring\n"
|
||||
"\nColor Format Examples:\n"
|
||||
" --color red Named color\n"
|
||||
" --color #FF0000 Hex color\n"
|
||||
" --color 255,0,128 RGB tuple\n"
|
||||
" --color hsl(180,100%%,50%%) HSL value\n"
|
||||
" --color 93 ANSI color code\n"
|
||||
" --gradient red:blue Custom gradient\n");
|
||||
}
|
||||
|
||||
static struct colorconfig colors;
|
||||
static int rainbow = 0, has_solid = 0;
|
||||
static rgbcolor solid;
|
||||
|
||||
static int is_art_space(unsigned long codepoint){
|
||||
return (codepoint >= 0x09 && codepoint <= 0x0D) || codepoint == 0x20 || codepoint == 0x85 || codepoint == 0xA0
|
||||
@@ -263,34 +294,42 @@ static size_t count_colored_chars(char **art){
|
||||
return total;
|
||||
}
|
||||
|
||||
static void print_art_line(const char *line, size_t *index, size_t total){
|
||||
char esc[32];
|
||||
const unsigned char *p = (const unsigned char *)line;
|
||||
static rgbcolor color_for_index(size_t index, size_t total){
|
||||
if(colors.has_gradient){
|
||||
float position = total <= 1 ? 0.0f : (float)index / (float)(total - 1);
|
||||
return lerp_color_hsv(colors.gradient_start, colors.gradient_end, position);
|
||||
}
|
||||
return hsl_to_rgb((unsigned short)((float)index / (float)total * 360.0f + 0.5f),
|
||||
(unsigned char)colors.saturation, (unsigned char)colors.brightness);
|
||||
}
|
||||
|
||||
if(colors.mode == MODE_NONE || (!rainbow && !has_solid)){
|
||||
static void print_art_line(const char *line, size_t *index, size_t total){
|
||||
char escape[32];
|
||||
const unsigned char *cursor = (const unsigned char *)line;
|
||||
int per_character = colors.rainbow_enabled || colors.has_gradient;
|
||||
|
||||
if(colors.mode == MODE_NONE || (!per_character && !colors.has_solid_color)){
|
||||
fputs(line, stdout);
|
||||
return;
|
||||
}
|
||||
if(!rainbow){//solid: one color for the whole line
|
||||
rgb_to_fg_escape(solid, colors.mode, esc, sizeof(esc));
|
||||
printf("%s%s\x1b[0m", esc, line);
|
||||
if(!per_character){
|
||||
rgb_to_fg_escape(colors.solid_color, colors.mode, escape, sizeof(escape));
|
||||
printf("%s%s\x1b[0m", escape, line);
|
||||
return;
|
||||
}
|
||||
while(*p){//rainbow: one color per non-space char
|
||||
while(*cursor){
|
||||
unsigned long codepoint;
|
||||
int len = utf8_decode(p, &codepoint);
|
||||
int byte_count = utf8_decode(cursor, &codepoint);
|
||||
if(is_art_space(codepoint)){
|
||||
fwrite(p, 1, (size_t)len, stdout);
|
||||
fwrite(cursor, 1, (size_t)byte_count, stdout);
|
||||
}else{
|
||||
unsigned short hue = (unsigned short)((float)*index / (float)total * 360.0f + 0.5f);
|
||||
rgbcolor c = hsl_to_rgb(hue, colors.saturation, colors.brightness);
|
||||
rgb_to_fg_escape(c, colors.mode, esc, sizeof(esc));
|
||||
printf("%s", esc);
|
||||
fwrite(p, 1, (size_t)len, stdout);
|
||||
rgb_to_fg_escape(color_for_index(*index, total), colors.mode, escape, sizeof(escape));
|
||||
fputs(escape, stdout);
|
||||
fwrite(cursor, 1, (size_t)byte_count, stdout);
|
||||
fputs("\x1b[0m", stdout);
|
||||
(*index)++;
|
||||
}
|
||||
p += len;
|
||||
cursor += byte_count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,109 +443,224 @@ void draw_miku_says(char *text, int style){
|
||||
freelines(speech_bubble_lines);
|
||||
free(all_content_lines);
|
||||
}
|
||||
|
||||
#define COLOR_FORMAT_HELP "expected a named color, #RRGGBB, #RGB, R,G,B, hsl(H,S%%,L%%), or ANSI value 0-255"
|
||||
|
||||
static int usage_error(const char *format, ...){
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
fputs("error: ", stderr);
|
||||
vfprintf(stderr, format, arguments);
|
||||
va_end(arguments);
|
||||
fputs("\n\nUsage: mikusays [OPTIONS] [TEXT]\n\nFor more information, try '--help'.\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
|
||||
//returns 1 if argv[*index] is this option; *value stays NULL when the value is missing
|
||||
static int match_value_option(int argc, char **argv, int *index, const char *long_name,
|
||||
char short_name, const char **value){
|
||||
const char *argument = argv[*index];
|
||||
size_t long_length = strlen(long_name);
|
||||
|
||||
*value = NULL;
|
||||
if(strcmp(argument, long_name) == 0
|
||||
|| (short_name != '\0' && argument[0] == '-' && argument[1] == short_name && argument[2] == '\0')){
|
||||
if(*index + 1 < argc){
|
||||
(*index)++;
|
||||
*value = argv[*index];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if(strncmp(argument, long_name, long_length) == 0 && argument[long_length] == '='){
|
||||
*value = argument + long_length + 1;
|
||||
return 1;
|
||||
}
|
||||
if(short_name != '\0' && argument[0] == '-' && argument[1] == short_name && argument[2] != '\0'){
|
||||
*value = argument + 2;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_percent_option(const char *flag, const char *value, int *destination){
|
||||
char *end;
|
||||
long parsed;
|
||||
|
||||
if(value == NULL)
|
||||
return usage_error("a value is required for '%s' but none was supplied", flag);
|
||||
parsed = strtol(value, &end, 10);
|
||||
if(*value == '\0' || *end != '\0' || parsed < 0 || parsed > 100)
|
||||
return usage_error("invalid value '%s' for '%s'", value, flag);
|
||||
*destination = (int)parsed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_gradient(const char *specification, rgbcolor *start, rgbcolor *end){
|
||||
const char *separator = strchr(specification, ':');
|
||||
char start_text[64];
|
||||
size_t start_length;
|
||||
ColorParseError error;
|
||||
|
||||
if(separator == NULL)
|
||||
return usage_error("invalid gradient: expected START:END (for example, red:blue)");
|
||||
start_length = (size_t)(separator - specification);
|
||||
if(start_length == 0 || separator[1] == '\0' || strchr(separator + 1, ':') != NULL)
|
||||
return usage_error("invalid gradient: expected exactly two colors as START:END");
|
||||
if(start_length >= sizeof(start_text))
|
||||
return usage_error("invalid gradient start: %s; " COLOR_FORMAT_HELP,
|
||||
color_parse_error_message(COLOR_ERR_FORMAT));
|
||||
|
||||
memcpy(start_text, specification, start_length);
|
||||
start_text[start_length] = '\0';
|
||||
error = parse_color(start_text, start);
|
||||
if(error != COLOR_OK)
|
||||
return usage_error("invalid gradient start '%s': %s; " COLOR_FORMAT_HELP,
|
||||
start_text, color_parse_error_message(error));
|
||||
error = parse_color(separator + 1, end);
|
||||
if(error != COLOR_OK)
|
||||
return usage_error("invalid gradient end '%s': %s; " COLOR_FORMAT_HELP,
|
||||
separator + 1, color_parse_error_message(error));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
long style = -1; //-1 means random
|
||||
int index, result = 0, list_requested = 0, color_debug = 0, no_color = 0;
|
||||
int has_text = 0, options_ended = 0, ansi_enabled = 0;
|
||||
const char *value, *color_text = NULL, *gradient_text = NULL;
|
||||
ColorParseError color_error;
|
||||
size_t length = 0;
|
||||
char *text;
|
||||
|
||||
srand(time(NULL));
|
||||
getterminalsize();
|
||||
colorconfig_init(&colors);
|
||||
|
||||
colorconfig_init(&colors);
|
||||
|
||||
long style = -1; //-1 means random
|
||||
int list = 0;
|
||||
int ret = 0;
|
||||
|
||||
size_t length = 0;
|
||||
for(int i = 1; i < argc; i++)
|
||||
length += strlen(argv[i]);
|
||||
char *text = malloc(length + argc);
|
||||
for(index = 1; index < argc; index++)
|
||||
length += strlen(argv[index]);
|
||||
text = malloc(length + (size_t)argc);
|
||||
if(!text) return 1;
|
||||
*text = '\0';
|
||||
|
||||
for(int i = 1; i < argc; i++){
|
||||
if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0){
|
||||
for(index = 1; index < argc; index++){
|
||||
const char *argument = argv[index];
|
||||
|
||||
if(!options_ended && strcmp(argument, "--") == 0){
|
||||
options_ended = 1;
|
||||
} else if(options_ended || argument[0] != '-' || argument[1] == '\0'){
|
||||
strcat(text, argument);
|
||||
strcat(text, " ");
|
||||
has_text = 1;
|
||||
} else if(strcmp(argument, "-h") == 0 || strcmp(argument, "--help") == 0){
|
||||
print_help();
|
||||
goto out;
|
||||
} else if(strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0){
|
||||
} else if(strcmp(argument, "-V") == 0 || strcmp(argument, "--version") == 0){
|
||||
printf("mikusays " VERSION "\n");
|
||||
goto out;
|
||||
} else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--list") == 0){
|
||||
list = 1;
|
||||
} else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--style") == 0){
|
||||
if(i + 1 >= argc){
|
||||
fprintf(stderr, "error: a value is required for '--style <STYLE>' but none was supplied\n");
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
i++;
|
||||
char *end;
|
||||
long v = strtol(argv[i], &end, 10);
|
||||
if(*argv[i] == '\0' || *end != '\0'){
|
||||
fprintf(stderr, "error: invalid value '%s' for '--style <STYLE>': not a number\n", argv[i]);
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
style = v < 0 ? -1 : v; //negative = random
|
||||
} else if(strcmp(argv[i], "--no-color") == 0){
|
||||
} else if(strcmp(argument, "-l") == 0 || strcmp(argument, "--list") == 0){
|
||||
list_requested = 1;
|
||||
} else if(strcmp(argument, "--no-color") == 0){
|
||||
no_color = 1;
|
||||
colors.mode = MODE_NONE;
|
||||
} else if(strcmp(argv[i], "--rainbow") == 0){
|
||||
rainbow = 1;
|
||||
} else if(strcmp(argv[i], "--color") == 0){
|
||||
if(i + 1 >= argc){
|
||||
fprintf(stderr, "error: a value is required for '--color <COLOR>' but none was supplied\n");
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
i++;
|
||||
ColorParseError err = parse_color(argv[i], &solid);
|
||||
if(err != COLOR_OK){
|
||||
fprintf(stderr, "error: invalid color '%s': %s; expected a named color, #RRGGBB, #RGB, R,G,B, hsl(H,S%%,L%%), or ANSI value 0-255\n",
|
||||
argv[i], color_parse_error_message(err));
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
has_solid = 1;
|
||||
} else if(strcmp(argv[i], "--saturation") == 0 || strcmp(argv[i], "--brightness") == 0){
|
||||
const char *flag = argv[i];
|
||||
if(i + 1 >= argc){
|
||||
fprintf(stderr, "error: a value is required for '%s <0-100>' but none was supplied\n", flag);
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
i++;
|
||||
}else if(strcmp(argument, "--rainbow") == 0){
|
||||
colors.rainbow_enabled = true;
|
||||
} else if(strcmp(argument, "--color-debug") == 0){
|
||||
color_debug = 1;
|
||||
} else if(match_value_option(argc, argv, &index, "--style", 's', &value)){
|
||||
char *end;
|
||||
long v = strtol(argv[i], &end, 10);
|
||||
if(*argv[i] == '\0' || *end != '\0' || v < 0 || v > 100){
|
||||
fprintf(stderr, "error: invalid value '%s' for '%s <0-100>'\n", argv[i], flag);
|
||||
ret = 2;
|
||||
long parsed;
|
||||
if(value == NULL){
|
||||
result = usage_error("a value is required for '--style <STYLE>' but none was supplied");
|
||||
goto out;
|
||||
}
|
||||
if(flag[2] == 's') colors.saturation = (unsigned char)v;
|
||||
else colors.brightness = (unsigned char)v;
|
||||
parsed = strtol(value, &end, 10);
|
||||
if(*value == '\0' || *end != '\0'){
|
||||
result = usage_error("invalid value '%s' for '--style <STYLE>': invalid digit found in string", value);
|
||||
goto out;
|
||||
}
|
||||
if(parsed < 0){
|
||||
result = usage_error("invalid value '%s' for '--style <STYLE>': %ld is not in 0..2147483647", value, parsed);
|
||||
goto out;
|
||||
}
|
||||
style = parsed;
|
||||
} else if(match_value_option(argc, argv, &index, "--color", '\0', &value)){
|
||||
if(value == NULL){
|
||||
result = usage_error("a value is required for '--color <COLOR>' but none was supplied");
|
||||
goto out;
|
||||
}
|
||||
color_text = value;
|
||||
} else if(match_value_option(argc, argv, &index, "--gradient", '\0', &value)){
|
||||
if(value == NULL){
|
||||
result = usage_error("a value is required for '--gradient <START:END>' but none was supplied");
|
||||
goto out;
|
||||
}
|
||||
gradient_text = value;
|
||||
} else if(match_value_option(argc, argv, &index, "--saturation", '\0', &value)){
|
||||
result = parse_percent_option("--saturation <SATURATION>", value, &colors.saturation);
|
||||
if(result) goto out;
|
||||
} else if(match_value_option(argc, argv, &index, "--brightness", '\0', &value)){
|
||||
result = parse_percent_option("--brightness <BRIGHTNESS>", value, &colors.brightness);
|
||||
if(result) goto out;
|
||||
} else {
|
||||
strcat(text, argv[i]);
|
||||
strcat(text, " ");
|
||||
result = usage_error("unexpected argument '%s' found", argument);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if(rainbow && has_solid){
|
||||
fprintf(stderr, "error: --rainbow cannot be used with --color\n");
|
||||
ret = 2;
|
||||
if(colors.rainbow_enabled && color_text){
|
||||
result = usage_error("the argument '--rainbow' cannot be used with '--color <COLOR>'");
|
||||
goto out;
|
||||
}
|
||||
if(colors.rainbow_enabled && gradient_text){
|
||||
result = usage_error("the argument '--rainbow' cannot be used with '--gradient <START:END>'");
|
||||
goto out;
|
||||
}
|
||||
if(color_text && gradient_text){
|
||||
result = usage_error("the argument '--color <COLOR>' cannot be used with '--gradient <START:END>'");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(list){
|
||||
if(list_requested){
|
||||
list_all_art_styles();
|
||||
} else if(text[0] == '\0'){
|
||||
fprintf(stderr, "Error: Text is required when not using --list\n");
|
||||
ret = 1;
|
||||
} else if(style >= (long)migucount){
|
||||
fprintf(stderr, "Style %ld does not exist! Choose one between 0 and %zu\n", style, migucount - 1);
|
||||
ret = 1;
|
||||
} else {
|
||||
draw_miku_says(text, (int)style);
|
||||
goto out;
|
||||
}
|
||||
//i didn't know you could do labels like in assembly (nasm) this is very useful knowledge
|
||||
if(!has_text){
|
||||
fprintf(stderr, "Error: Text is required when not using --list\n");
|
||||
result = 1;
|
||||
goto out;
|
||||
}
|
||||
if(style >= (long)migucount){
|
||||
result = usage_error("style %ld does not exist; choose a value between 0 and %lu",
|
||||
style, (unsigned long)(migucount - 1));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(color_text){
|
||||
color_error = parse_color(color_text, &colors.solid_color);
|
||||
if(color_error != COLOR_OK){
|
||||
result = usage_error("invalid color '%s': %s; " COLOR_FORMAT_HELP,
|
||||
color_text, color_parse_error_message(color_error));
|
||||
goto out;
|
||||
}
|
||||
colors.has_solid_color = true;
|
||||
}
|
||||
if(gradient_text){
|
||||
result = parse_gradient(gradient_text, &colors.gradient_start, &colors.gradient_end);
|
||||
if(result) goto out;
|
||||
colors.has_gradient = true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
ansi_enabled = !no_color && enable_windows_ansi();
|
||||
#endif
|
||||
if(color_debug){
|
||||
print_color_debug(&colors, ansi_enabled != 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
draw_miku_says(text, (int)style);
|
||||
//i didn't know you could do labels like in assembly (nasm) this is very useful knowledge
|
||||
out:
|
||||
free(text);
|
||||
return ret;
|
||||
}
|
||||
return result;
|
||||
} //ooooo 666 lines spookyyy evillll
|
||||
Reference in new issue
Block a user