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

This commit is contained in:
david committed 2026-10-26 18:27:07 -03:00
1 parent 7aff48a428
commit b5e3328f64
8 files changed
+335 -160

No files matched your search

+3 -1
View File
@@ -6,7 +6,9 @@ mikusays_SOURCES = \
src/helper.c \ src/helper.c \
defs/all.h \ defs/all.h \
defs/mikuart.h \ defs/mikuart.h \
defs/helper.h defs/helper.h \
defs/colors.h \
defs/widechar.h
AM_CFLAGS = -Wall -Wextra AM_CFLAGS = -Wall -Wextra
Generated
+3 -1
View File
@@ -296,7 +296,9 @@ mikusays_SOURCES = \
src/helper.c \ src/helper.c \
defs/all.h \ defs/all.h \
defs/mikuart.h \ defs/mikuart.h \
defs/helper.h defs/helper.h \
defs/colors.h \
defs/widechar.h
AM_CFLAGS = -Wall -Wextra AM_CFLAGS = -Wall -Wextra
EXTRA_DIST = LICENSE README.md EXTRA_DIST = LICENSE README.md
Generated Vendored
+10 -10
View File
@@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # 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, # Copyright (C) 1992-1996, 1998-2017, 2020-2026 Free Software Foundation,
@@ -584,8 +584,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='mikusays' PACKAGE_NAME='mikusays'
PACKAGE_TARNAME='mikusays' PACKAGE_TARNAME='mikusays'
PACKAGE_VERSION='0.3.1' PACKAGE_VERSION='1.0.0'
PACKAGE_STRING='mikusays 0.3.1' PACKAGE_STRING='mikusays 1.0.0'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
PACKAGE_URL='https://git.squeakyrat.moe/mikusays-c' 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. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF 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]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1348,7 +1348,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of mikusays 0.3.1:";; short | recursive ) echo "Configuration of mikusays 1.0.0:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@@ -1443,7 +1443,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
mikusays configure 0.3.1 mikusays configure 1.0.0
generated by GNU Autoconf 2.73 generated by GNU Autoconf 2.73
Copyright (C) 2026 Free Software Foundation, Inc. Copyright (C) 2026 Free Software Foundation, Inc.
@@ -1555,7 +1555,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. 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 generated by GNU Autoconf 2.73. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@@ -3171,7 +3171,7 @@ fi
# Define the identity of the package. # Define the identity of the package.
PACKAGE='mikusays' PACKAGE='mikusays'
VERSION='0.3.1' VERSION='1.0.0'
printf '%s\n' "#define PACKAGE \"$PACKAGE\"" >>confdefs.h 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 # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" 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 generated by GNU Autoconf 2.73. Invocation command line was
CONFIG_FILES = $CONFIG_FILES 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 cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
mikusays config.status 0.3.1 mikusays config.status 1.0.0
configured by $0, generated by GNU Autoconf 2.73, configured by $0, generated by GNU Autoconf 2.73,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"
+1 -1
View File
@@ -1,5 +1,5 @@
AC_PREREQ([2.69]) 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_SRCDIR([src/main.c])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall]) AM_INIT_AUTOMAKE([foreign subdir-objects -Wall])
+1
View File
@@ -8,6 +8,7 @@
#include <locale.h> #include <locale.h>
#include <stdbool.h> #include <stdbool.h>
#include <ctype.h> #include <ctype.h>
#include <stdarg.h>
#include "helper.h" #include "helper.h"
+4 -20
View File
@@ -11,24 +11,6 @@ typedef enum color_mode {
MODE_TRUECOLOR MODE_TRUECOLOR
}color_mode; }color_mode;
typedef enum NamedColor {
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
BrightBlack,
BrightRed,
BrightGreen,
BrightYellow,
BrightBlue,
BrightMagenta,
BrightCyan,
BrightWhite,
}NamedColor;
typedef struct{ typedef struct{
unsigned char red; unsigned char red;
@@ -45,6 +27,9 @@ typedef struct colorconfig {
int brightness; //default 50 int brightness; //default 50
rgbcolor *rainbow_cache; //array of rgbcolor malloced rgbcolor *rainbow_cache; //array of rgbcolor malloced
size_t rainbow_count; size_t rainbow_count;
bool has_gradient;
rgbcolor gradient_start;
rgbcolor gradient_end;
}colorconfig; }colorconfig;
typedef enum ColorParseError { typedef enum ColorParseError {
@@ -53,13 +38,11 @@ typedef enum ColorParseError {
COLOR_ERR_HEX, COLOR_ERR_HEX,
COLOR_ERR_RGB, COLOR_ERR_RGB,
COLOR_ERR_HSL, COLOR_ERR_HSL,
COLOR_ERR_UNKNOWN_NAME
}ColorParseError; }ColorParseError;
void colorconfig_init(colorconfig *config); void colorconfig_init(colorconfig *config);
enum color_mode detect_color_mode(void); enum color_mode detect_color_mode(void);
bool enable_windows_ansi(void); bool enable_windows_ansi(void);
int parse_namedcolors(const char *name, rgbcolor *out_rgb);
ColorParseError parse_color(const char *s, rgbcolor *out); ColorParseError parse_color(const char *s, rgbcolor *out);
const char *color_parse_error_message(ColorParseError error); const char *color_parse_error_message(ColorParseError error);
unsigned char rgb_to_ansi16(rgbcolor rgb); 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 hsl_to_rgb(unsigned short h, unsigned char s, unsigned char l);
rgbcolor lerp_color_hsv(rgbcolor start, rgbcolor end, float t); 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 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 #endif
+38 -6
View File
@@ -560,17 +560,49 @@ ColorParseError parse_color(const char *s, rgbcolor *out){
const char *color_parse_error_message(ColorParseError error){ const char *color_parse_error_message(ColorParseError error){
switch(error){ switch(error){
case COLOR_ERR_FORMAT: case COLOR_ERR_FORMAT:
return "Invaliid color format"; return "Invalid color format";
case COLOR_ERR_HEX: case COLOR_ERR_HEX:
return "Invaliid hex color code"; return "Invalid hex color code";
case COLOR_ERR_RGB: 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: case COLOR_ERR_HSL:
return "Invaliid HSL format. Expected format: hsl(H,S%,L%)"; return "Invalid HSL format. Expected format: hsl(H,S%,L%)";
case COLOR_ERR_UNKNOWN_NAME:
return "Unkown named color";
case COLOR_OK: case COLOR_OK:
default: default:
return ""; 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
}
+275 -121
View File
@@ -16,28 +16,46 @@ static struct win term = {80, 24}; //if the function fails this is the defualt
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #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) { static void getterminalsize(void) {
CONSOLE_SCREEN_BUFFER_INFO info;
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
int found = GetConsoleScreenBufferInfo(console, &info);
SetConsoleOutputCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
CONSOLE_SCREEN_BUFFER_INFO csbi; if(!found){
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { HANDLE fallback = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
term.winwidth = csbi.srWindow.Right - csbi.srWindow.Left + 1; FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
term.winheight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; 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 <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
static void getterminalsize(void) { static void getterminalsize(void) {
struct winsize ws; struct winsize size_info;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { int tty_descriptor = -1;
if(ws.ws_col > 0) int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &size_info);
term.winwidth = ws.ws_col;
if(ws.ws_row > 0) if(status != 0){
term.winheight = ws.ws_row; 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 #endif
@@ -154,11 +172,9 @@ char **wraptext(const char *text, int max, int *count){
#define MIN_BUBBLE_WIDTH 20 #define MIN_BUBBLE_WIDTH 20
char **getbubblelines(char *text){ char **getbubblelines(char *text){
char **templines = wraptext(text, MAX_BUBBLE_WIDTH - 4, &count); char **templines = wraptext(text, MAX_BUBBLE_WIDTH - 4, &count);
if(templines == NULL)
return NULL;
size_t content_width = 0; 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]); size_t current_len = displaywidth(templines[i]);
if (current_len > content_width) if (current_len > content_width)
content_width = current_len; content_width = current_len;
@@ -185,7 +201,7 @@ char **getbubblelines(char *text){
strcat(bubblelines[0], horizontal); strcat(bubblelines[0], horizontal);
strcat(bubblelines[0], top_right); 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 line_width = displaywidth(lines[i]);
int padding_needed = (bubble_width - 2) - line_width; int padding_needed = (bubble_width - 2) - line_width;
if (padding_needed < 0) padding_needed = 0; if (padding_needed < 0) padding_needed = 0;
@@ -215,15 +231,17 @@ static void print_help(void){
"Arguments:\n" "Arguments:\n"
" [TEXT] Text to display in the speech bubble\n\n" " [TEXT] Text to display in the speech bubble\n\n"
"Options:\n" "Options:\n"
" -s, --style <STYLE> Style of the Miku art. A random one is chosen if not specified\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" " -l, --list List all available art styles with their indices\n"
" -h, --help Print help\n" " --rainbow Apply a smooth rainbow gradient across the ASCII art\n"
" -V, --version Print version\n" " --saturation <SATURATION> Saturation level for rainbow gradient (0-100) [default: 100]\n"
" --rainbow Apply a smooth rainbow gradient across the art\n" " --brightness <BRIGHTNESS> Brightness level for rainbow gradient (0-100) [default: 50]\n"
" --saturation <0-100> Rainbow saturation (default: 100)\n" " --color <COLOR> Override with a single solid color\n"
" --brightness <0-100> Rainbow brightness (default: 50)\n" " --gradient <START:END> Define a custom two-color gradient (e.g., --gradient red:blue)\n"
" --color <COLOR> Set a single solid color\n" " --no-color Disable all coloring (respects NO_COLOR environment variable)\n"
" --no-color Disable all coloring\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){ 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("\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 struct colorconfig colors;
static int rainbow = 0, has_solid = 0;
static rgbcolor solid;
static int is_art_space(unsigned long codepoint){ static int is_art_space(unsigned long codepoint){
return (codepoint >= 0x09 && codepoint <= 0x0D) || codepoint == 0x20 || codepoint == 0x85 || codepoint == 0xA0 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; return total;
} }
static void print_art_line(const char *line, size_t *index, size_t total){ static rgbcolor color_for_index(size_t index, size_t total){
char esc[32]; if(colors.has_gradient){
const unsigned char *p = (const unsigned char *)line; 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); fputs(line, stdout);
return; return;
} }
if(!rainbow){//solid: one color for the whole line if(!per_character){
rgb_to_fg_escape(solid, colors.mode, esc, sizeof(esc)); rgb_to_fg_escape(colors.solid_color, colors.mode, escape, sizeof(escape));
printf("%s%s\x1b[0m", esc, line); printf("%s%s\x1b[0m", escape, line);
return; return;
} }
while(*p){//rainbow: one color per non-space char while(*cursor){
unsigned long codepoint; unsigned long codepoint;
int len = utf8_decode(p, &codepoint); int byte_count = utf8_decode(cursor, &codepoint);
if(is_art_space(codepoint)){ if(is_art_space(codepoint)){
fwrite(p, 1, (size_t)len, stdout); fwrite(cursor, 1, (size_t)byte_count, stdout);
}else{ }else{
unsigned short hue = (unsigned short)((float)*index / (float)total * 360.0f + 0.5f); rgb_to_fg_escape(color_for_index(*index, total), colors.mode, escape, sizeof(escape));
rgbcolor c = hsl_to_rgb(hue, colors.saturation, colors.brightness); fputs(escape, stdout);
rgb_to_fg_escape(c, colors.mode, esc, sizeof(esc)); fwrite(cursor, 1, (size_t)byte_count, stdout);
printf("%s", esc);
fwrite(p, 1, (size_t)len, stdout);
fputs("\x1b[0m", stdout); fputs("\x1b[0m", stdout);
(*index)++; (*index)++;
} }
p += len; cursor += byte_count;
} }
} }
@@ -405,108 +444,223 @@ void draw_miku_says(char *text, int style){
free(all_content_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){ 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)); srand(time(NULL));
getterminalsize(); getterminalsize();
colorconfig_init(&colors); colorconfig_init(&colors);
long style = -1; //-1 means random for(index = 1; index < argc; index++)
int list = 0; length += strlen(argv[index]);
int ret = 0; text = malloc(length + (size_t)argc);
size_t length = 0;
for(int i = 1; i < argc; i++)
length += strlen(argv[i]);
char *text = malloc(length + argc);
if(!text) return 1; if(!text) return 1;
*text = '\0'; *text = '\0';
for(int i = 1; i < argc; i++){ for(index = 1; index < argc; index++){
if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0){ 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(); print_help();
goto out; 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"); printf("mikusays " VERSION "\n");
goto out; goto out;
} else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--list") == 0){ } else if(strcmp(argument, "-l") == 0 || strcmp(argument, "--list") == 0){
list = 1; list_requested = 1;
} else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--style") == 0){ } else if(strcmp(argument, "--no-color") == 0){
if(i + 1 >= argc){ no_color = 1;
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){
colors.mode = MODE_NONE; colors.mode = MODE_NONE;
} else if(strcmp(argv[i], "--rainbow") == 0){ }else if(strcmp(argument, "--rainbow") == 0){
rainbow = 1; colors.rainbow_enabled = true;
} else if(strcmp(argv[i], "--color") == 0){ } else if(strcmp(argument, "--color-debug") == 0){
if(i + 1 >= argc){ color_debug = 1;
fprintf(stderr, "error: a value is required for '--color <COLOR>' but none was supplied\n"); } else if(match_value_option(argc, argv, &index, "--style", 's', &value)){
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++;
char *end; char *end;
long v = strtol(argv[i], &end, 10); long parsed;
if(*argv[i] == '\0' || *end != '\0' || v < 0 || v > 100){ if(value == NULL){
fprintf(stderr, "error: invalid value '%s' for '%s <0-100>'\n", argv[i], flag); result = usage_error("a value is required for '--style <STYLE>' but none was supplied");
ret = 2;
goto out; goto out;
} }
if(flag[2] == 's') colors.saturation = (unsigned char)v; parsed = strtol(value, &end, 10);
else colors.brightness = (unsigned char)v; 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 { } else {
strcat(text, argv[i]); result = usage_error("unexpected argument '%s' found", argument);
strcat(text, " "); goto out;
} }
} }
if(rainbow && has_solid){ if(colors.rainbow_enabled && color_text){
fprintf(stderr, "error: --rainbow cannot be used with --color\n"); result = usage_error("the argument '--rainbow' cannot be used with '--color <COLOR>'");
ret = 2; 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; goto out;
} }
if(list){ if(list_requested){
list_all_art_styles(); list_all_art_styles();
} else if(text[0] == '\0'){ goto out;
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);
} }
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 //i didn't know you could do labels like in assembly (nasm) this is very useful knowledge
out: out:
free(text); free(text);
return ret; return result;
} } //ooooo 666 lines spookyyy evillll