From 8d35bcd24d8038240334090eba9a1d613d735e30 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Thu, 6 Mar 2025 15:36:39 +0800 Subject: [PATCH] Move implementation out of xy.h --- lib/xy.c | 741 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/xy.h | 786 ++++++------------------------------------------------- 2 files changed, 828 insertions(+), 699 deletions(-) create mode 100644 lib/xy.c diff --git a/lib/xy.c b/lib/xy.c new file mode 100644 index 0000000..1aac210 --- /dev/null +++ b/lib/xy.c @@ -0,0 +1,741 @@ +/** ------------------------------------------------------------ + * Copyright © 2023-2025 Aoran Zeng, Heng Guo + * SPDX-License-Identifier: MIT + * ------------------------------------------------------------- + * File Authors : Aoran Zeng + * | Heng Guo <2085471348@qq.com> + * Contributors : juzeon + * | + * Created On : <2023-08-28> + * Major Revision : 2 + * Last Modified : <2025-03-06> + * ------------------------------------------------------------*/ + +#include "xy.h" + +/* Global default */ +bool xy_enable_color = 1; + + +void putf (double n) { printf ("%f\n", n); } +void puti (long long n) { printf ("%lld\n", n); } +void putb (bool n) { if (n) puts ("true"); else puts ("false"); } +void print (const char *s) { printf ("%s", s); } +void println (const char *s) { printf ("%s\n", s); } +void say (const char *s) { printf ("%s\n", s); } +void br () { puts (""); } +void p (const char *s) { printf ("%s\n", s); } + + + +void * +xy_malloc0 (size_t size) +{ + void *ptr = malloc (size); + memset (ptr, 0, size); + return ptr; +} + + +/****************************************************** + * String + ******************************************************/ + +/** + * 将str中所有的pat字符串替换成replace,返回一个全新的字符串 + */ +char * +xy_str_gsub (const char *str, const char *pat, const char *replace) +{ + size_t replace_len = strlen (replace); + size_t pat_len = strlen (pat); + + int unit = replace_len - pat_len; + if (unit <= 0) + unit = 0; + + size_t len = strlen (str); + + const char *cur = str; + int count = 0; + + while (cur < str + len) + { + char *fnd = strstr (cur, pat); + if (fnd) + { + count++; + cur = fnd + pat_len; + } + else + break; + } + // puti(count); DEBUG 匹配次数 + + char *ret = malloc (unit * count + len + 1); + char *retcur = ret; + + cur = str; + while (cur < str + len) + { + char *fnd = strstr (cur, pat); + if (fnd) + { + ptrdiff_t diff = fnd - cur; + strncpy (retcur, cur, diff); + cur = fnd + pat_len; + + retcur += diff; + strcpy (retcur, replace); + retcur += replace_len; + } + else + break; + } + strcpy (retcur, cur); + + return ret; +} + +char * +xy_2strjoin (const char *str1, const char *str2) +{ + size_t len = strlen (str1); + size_t size = len + strlen (str2) + 1; + char *ret = malloc (size); + strcpy (ret, str1); + strcpy (ret + len, str2); + return ret; +} + +char * +xy_strjoin (unsigned int count, ...) +{ + size_t al_fixed = 128; + char *ret = calloc (1, al_fixed); + // 已分配次数 + int al_times = 1; + // 当前已分配量 + size_t al_cur = al_fixed; + + const char *str = NULL; + // 需要分配的量 + size_t al_need = 0; + // 用于 strcpy() 到 ret 的哪个位置 + char *cur = ret + 0; + + va_list args; + va_start (args, count); + + for (int i = 0; i < count; i++) + { + // 是否需要重新分配 + bool need_realloc = false; + + str = va_arg (args, const char *); + al_need += strlen (str); + while (al_need > al_cur) + { + al_times += 1; + al_cur = al_times * al_fixed; + need_realloc = true; + } + // printf("al_times %d, al_need %zd, al_cur %zd\n", al_times, al_need, + // al_cur); + if (need_realloc) + { + ptrdiff_t diff = cur - ret; + ret = realloc (ret, al_cur); + cur = ret + diff; + } + if (NULL == ret) + { + fprintf (stderr, "xy.h: No availble memory!"); + return NULL; + } + strcpy (cur, str); + // puts(ret); + cur += strlen (str); + } + va_end (args); + + *cur = '\0'; + return ret; +} + +char * +xy_strdup (const char *str) +{ + size_t len = strlen (str); + char *new = xy_malloc0 (len + 1); + strcpy (new, str); + return new; +} + +#define _XY_Str_Bold 1 +#define _XY_Str_Faint 2 +#define _XY_Str_Italic 3 +#define _XY_Str_Underline 4 +#define _XY_Str_Blink 5 +#define _XY_Str_Cross 9 + +#define _XY_Str_Red 31 +#define _XY_Str_Green 32 +#define _XY_Str_Yellow 33 +#define _XY_Str_Blue 34 +#define _XY_Str_Magenta 35 +#define _XY_Str_Cyan 36 + +static char * +_xy_str_to_terminal_style (int style, const char *str) +{ + char *color_fmt_str = NULL; + + if (!xy_enable_color) + { + color_fmt_str = "%s"; + goto new_str; + } + + switch (style) + { + case _XY_Str_Red: + color_fmt_str = "\e[31m%s\e[0m"; break; + case _XY_Str_Green: + color_fmt_str = "\e[32m%s\e[0m"; break; + case _XY_Str_Yellow: + color_fmt_str = "\e[33m%s\e[0m"; break; + case _XY_Str_Blue: + color_fmt_str = "\e[34m%s\e[0m"; break; + case _XY_Str_Magenta: + color_fmt_str = "\e[35m%s\e[0m"; break; + case _XY_Str_Cyan: + color_fmt_str = "\e[36m%s\e[0m"; break; + case _XY_Str_Bold: + color_fmt_str = "\e[1m%s\e[0m"; break; + case _XY_Str_Faint: + color_fmt_str = "\e[2m%s\e[0m"; break; + case _XY_Str_Italic: + color_fmt_str = "\e[3m%s\e[0m"; break; + case _XY_Str_Underline: + color_fmt_str = "\e[4m%s\e[0m"; break; + case _XY_Str_Blink: + color_fmt_str = "\e[5m%s\e[0m"; break; + case _XY_Str_Cross: + color_fmt_str = "\e[9m%s\e[0m"; break; + } + + + size_t len = 0; +new_str: + // -2 把中间%s减掉 + len = strlen (color_fmt_str) - 2; + char *buf = malloc (strlen (str) + len + 1); + sprintf (buf, color_fmt_str, str); + return buf; +} + +char * xy_str_to_bold(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Bold, str); } +char * xy_str_to_faint(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Faint, str); } +char * xy_str_to_italic(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Italic, str);} +char * xy_str_to_underline(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Underline, str);} +char * xy_str_to_blink(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Blink, str); } +char * xy_str_to_cross(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Cross, str); } + +char * xy_str_to_red(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Red, str); } +char * xy_str_to_green(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Green, str); } +char * xy_str_to_yellow(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Yellow, str); } +char * xy_str_to_blue(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Blue, str); } +char * xy_str_to_magenta(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Magenta, str);} +char * xy_str_to_purple(const char *str) { return xy_str_to_magenta (str); } +char * xy_str_to_cyan(const char *str) { return _xy_str_to_terminal_style (_XY_Str_Cyan, str); } + + + +bool +xy_streql (const char *str1, const char *str2) +{ + if (NULL==str1 || NULL==str2) + { + return false; + } + return strcmp (str1, str2) == 0 ? true : false; +} + +char * +xy_str_to_quietcmd (const char *cmd) +{ + char *ret = NULL; +#ifdef _WIN32 + ret = xy_2strjoin (cmd, " >nul 2>nul "); +#else + ret = xy_2strjoin (cmd, " 1>/dev/null 2>&1 "); +#endif + return ret; +} + +bool +xy_str_end_with (const char *str, const char *suffix) +{ + size_t len1 = strlen (str); + size_t len2 = strlen (suffix); + + if (0 == len2) + return true; // 空字符串直接返回 + if (len1 < len2) + return false; + + const char *cur1 = str + len1 - 1; + const char *cur2 = suffix + len2 - 1; + + for (int i = 0; i < len2; i++) + { + if (*cur1 != *cur2) + return false; + cur1--; + cur2--; + } + return true; +} + +bool +xy_str_start_with (const char *str, const char *prefix) +{ + if (NULL==str || NULL==prefix) + { + return false; + } + + size_t len1 = strlen (str); + size_t len2 = strlen (prefix); + + if (0 == len2) + return true; // 空字符串直接返回 + if (len1 < len2) + return false; + + const char *cur1 = str; + const char *cur2 = prefix; + + for (int i = 0; i < len2; i++) + { + if (*cur1 != *cur2) + return false; + cur1++; + cur2++; + } + return true; +} + +char * +xy_str_delete_prefix (const char *str, const char *prefix) +{ + char *new = xy_strdup (str); + bool yes = xy_str_start_with (str, prefix); + if (!yes) + return new; + + size_t len = strlen (prefix); + char *cur = new + len; + return cur; +} + +char * +xy_str_delete_suffix (const char *str, const char *suffix) +{ + char *new = xy_strdup (str); + bool yes = xy_str_end_with (str, suffix); + if (!yes) + return new; + + size_t len1 = strlen (str); + size_t len2 = strlen (suffix); + char *cur = new + len1 - len2; + *cur = '\0'; + return new; +} + +char * +xy_str_strip (const char *str) +{ + char *new = xy_strdup (str); + + while (strchr ("\n\r\v\t\f ", new[0])) + { + new += 1; + } + + size_t len = strlen (new); + + char *last = new + len - 1; + + while (strchr ("\n\r\v\t\f ", *last)) + { + *last = '\0'; + last -= 1; + } + return new; +} + + +/****************************************************** + * Logging + ******************************************************/ + +#define _XY_Log_Plain 000000001 +#define _XY_Log_Success 000000001 << 1 +#define _XY_Log_Info 000000001 << 2 +#define _XY_Log_Warn 000000001 << 3 +#define _XY_Log_Error 000000001 << 4 + +static void +_xy_log (int level, const char *prompt, const char *content) +{ + char *str = NULL; + + bool to_stderr = false; + + /** + * 'app: content' + */ + if (level & _XY_Log_Plain) + { + str = xy_strjoin (3, prompt, ": ", content); + } + else if (level & _XY_Log_Success) + { + str = xy_strjoin (3, prompt, ": ", xy_str_to_green (content)); + } + else if (level & _XY_Log_Info) + { + str = xy_strjoin (3, prompt, ": ", xy_str_to_blue (content)); + } + else if (level & _XY_Log_Warn) + { + str = xy_strjoin (3, prompt, ": ", xy_str_to_yellow (content)); + to_stderr = true; + } + else if (level & _XY_Log_Error) + { + str = xy_strjoin (3, prompt, ": ", xy_str_to_red (content)); + to_stderr = true; + } + else + { + // xy_assert ("CAN'T REACH!"); + } + + if (to_stderr) + { + fprintf (stderr, "%s\n", str); + } + else + { + puts (str); + } + free (str); +} + +void xy_log (const char *prompt, const char *str) { _xy_log (_XY_Log_Plain, prompt, str); } +void xy_succ(const char *prompt, const char *str) { _xy_log (_XY_Log_Success, prompt, str); } +void xy_info(const char *prompt, const char *str) { _xy_log (_XY_Log_Info, prompt, str); } +void xy_warn(const char *prompt, const char *str) { _xy_log (_XY_Log_Warn, prompt, str); } +void xy_error(const char *prompt, const char *str) { _xy_log (_XY_Log_Error, prompt, str); } + + +/** + * brkt 系列输出受 pip 启发,为了输出方便,使用 xy.h 的程序应该 + * + * 1.若想完全自定义颜色和输出位置: + * + * 应基于下述 xy_log_brkt_to 定义自己的输出函数 + * + * 2.若想自定义颜色,而保持输出到stdout: + * + * 应基于下述 xy_log_brkt 定义 + * + * 3.若对log无细致要求,想要快速使用log功能: + * + * 应基于下述 xy__brkt 定义自己的 app__brkt(),或直接使用 xy__brkt + */ + +void +xy_log_brkt_to (const char *prompt, const char *content, FILE *stream) +{ + char *str = xy_strjoin (4, "[", prompt, "] ", content); + fprintf (stream, "%s\n", str); + free (str); +} + + + +static void +_xy_log_brkt (int level, const char *prompt1, const char *prompt2, const char *content) +{ + char *str = NULL; + + bool to_stderr = false; + + if (level & _XY_Log_Plain) + { + str = xy_strjoin (6, "[", prompt1, " ", prompt2, "] ", content); + } + else if (level & _XY_Log_Success) + { + /* [app 成功] [app success] */ + str = xy_strjoin (6, + "[", xy_str_to_green (prompt1), " ", xy_str_to_bold (xy_str_to_green (prompt2)), "] ", xy_str_to_green (content)); + } + else if (level & _XY_Log_Info) + { + /* [app 信息] [app info] + [app 提示] [app notice] + */ + str = xy_strjoin (6, + "[", xy_str_to_blue (prompt1), " ", xy_str_to_bold (xy_str_to_blue (prompt2)), "] ", xy_str_to_blue (content)); + } + else if (level & _XY_Log_Warn) + { + /* [app 警告] [app warn] */ + str = xy_strjoin (6, + "[", xy_str_to_yellow (prompt1), " ", xy_str_to_bold (xy_str_to_yellow (prompt2)), "] ", xy_str_to_yellow (content)); + to_stderr = true; + } + else if (level & _XY_Log_Error) + { + /* [app 错误] [app error] */ + str = xy_strjoin (6, + "[", xy_str_to_red (prompt1), " ", xy_str_to_bold (xy_str_to_red (prompt2)), "] ", xy_str_to_red (content)); + to_stderr = true; + } + else + { + // xy_assert ("CAN'T REACH!"); + } + + if (to_stderr) + { + fprintf (stderr, "%s\n", str); + } + else + { + puts (str); + } + free (str); +} + + +void xy_log_brkt (const char *prompt1, const char *prompt2, const char *content) { _xy_log_brkt(_XY_Log_Plain, prompt1,prompt2,content); } +void xy_succ_brkt(const char *prompt1, const char *prompt2, const char *content) { _xy_log_brkt(_XY_Log_Success,prompt1,prompt2,content); } +void xy_info_brkt(const char *prompt1, const char *prompt2, const char *content) { _xy_log_brkt(_XY_Log_Info, prompt1,prompt2,content); } +void xy_warn_brkt(const char *prompt1, const char *prompt2, const char *content) { _xy_log_brkt(_XY_Log_Warn, prompt1,prompt2,content); } +void xy_error_brkt(const char *prompt1,const char *prompt2, const char *content) { _xy_log_brkt(_XY_Log_Error, prompt1,prompt2,content); } + + + +/****************************************************** + * System + ******************************************************/ +/** + * 执行cmd,返回某行输出结果,并对已经遍历过的行执行iter_func + * + * @param cmd 要执行的命令 + * @param n 指定命令执行输出的结果行中的某一行,0 表示最后一行,n (n>0) 表示第n行 + * 该函数会返回这一行的内容 + * @param iter_func 对遍历时经过的行的内容,进行函数调用 + * + * @note 返回的字符串最后面一般有换行符号 + * + * 由于目标行会被返回出来,所以 iter_func() 并不执行目标行,只会执行遍历过的行 + */ +char * +xy_run_iter (const char *cmd, unsigned long n, void (*iter_func) (const char *)) +{ + const int size = 512; + char *buf = (char *) malloc (size); + + FILE *stream = popen (cmd, "r"); + if (stream == NULL) + { + fprintf (stderr, "xy: 命令执行失败\n"); + return NULL; + } + + char *ret = NULL; + unsigned long count = 0; + + while (true) + { + if (NULL == fgets (buf, size, stream)) + break; + /* 存在换行的总是会把换行符读出来,删掉 */ + ret = xy_str_delete_suffix (buf, "\n"); + count += 1; + if (n == count) + break; + if (iter_func) + { + iter_func (buf); + } + } + + pclose (stream); + return ret; +} + +char * +xy_run (const char *cmd, unsigned long n) +{ + return xy_run_iter (cmd, n, NULL); +} + + +char * +xy_os_home () +{ + char *home = NULL; + if (xy_on_windows) + home = getenv ("USERPROFILE"); + else + home = getenv ("HOME"); + return home; +} + +char * +xy_win_powershell_profile () +{ + return xy_2strjoin ( + xy_os_home(), "\\Documents\\PowerShell\\Microsoft.PowerShell_profile.ps1"); +} + +char * +xy_win_powershellv5_profile () +{ + return xy_2strjoin ( + xy_os_home(), + "\\Documents\\WindowsPowerShell\\Microsoft.PowerShell_profile.ps1"); +} + + + +/** + * @note Windows上,`path` 不要夹带变量名,因为最终 access() 不会帮你转换 + */ +bool +xy_file_exist (const char *path) +{ + const char *new_path = path; + if (xy_str_start_with (path, "~")) + { + new_path = xy_2strjoin (xy_os_home(), path + 1); + } + // 0 即 F_OK + return (0==access (new_path, 0)) ? true : false; +} + +/** + * @note xy_file_exist() 和 xy_dir_exist() 两个函数在所有平台默认都支持使用 '~', + * 但实现中都没有调用 xy_normalize_path(),以防万一,调用前可能需要用户手动调用它 + */ +bool +xy_dir_exist (const char *path) +{ + const char *dir = path; + if (xy_on_windows) + { + if (xy_str_start_with (path, "~")) + { + dir = xy_2strjoin (xy_os_home(), path + 1); + } + } + + if (xy_on_windows) + { +#ifdef XY_On_Windows + // 也可以用 opendir() #include + DWORD attr = GetFileAttributesA (dir); + + if (attr == INVALID_FILE_ATTRIBUTES) + { + // Q: 我们应该报错吗? + return false; + } + else if (attr & FILE_ATTRIBUTE_DIRECTORY) + { + return true; + } + else + { + return false; + } +#endif + } + else + { + int status = system (xy_2strjoin ("test -d ", dir)); + + if (0==status) + return true; + else + return false; + } +} + +/** + * 1. 删除路径左右两边多出来的空白符 + * 2. 将 ~/ 转换为绝对路径 + */ +char * +xy_normalize_path (const char *path) +{ + char *new = xy_str_strip (path); // 防止开发者多写了空白符 + + if (xy_on_windows) + { + if (xy_str_start_with (new, "~/")) + { + // 或 %USERPROFILE% + new = xy_strjoin (3, xy_os_home, "\\", + xy_str_delete_prefix (new, "~/")); + } + new = xy_str_gsub (new, "/", "\\"); + } + else + { + if (xy_str_start_with (new, "~/")) + { + new = xy_strjoin (3, xy_os_home, "/", + xy_str_delete_prefix (new, "~/")); + } + } + + return new; +} + +char * +xy_parent_dir (const char *path) +{ + char *dir = xy_normalize_path (path); + char *last = NULL; + if (xy_on_windows) + { + last = strrchr (dir, '\\'); + if (!last) + { + /* current dir */ + return "."; + } + *last = '\0'; + } + else + { + last = strrchr (dir, '/'); + if (!last) + { + /* current dir */ + return "."; + } + *last = '\0'; + } + return dir; +} diff --git a/lib/xy.h b/lib/xy.h index bb7df84..6f40ca2 100644 --- a/lib/xy.h +++ b/lib/xy.h @@ -2,23 +2,25 @@ * Copyright © 2023-2025 Aoran Zeng, Heng Guo * SPDX-License-Identifier: MIT * ------------------------------------------------------------- - * Lib Name : xy.h + * Lib Name : xy * Lib Authors : Aoran Zeng * | Heng Guo <2085471348@qq.com> * Contributors : juzeon * | * Created On : <2023-08-28> - * Last Modified : <2024-12-14> + * Last Modified : <2025-03-06> * * xy: 襄阳、咸阳 - * Corss-Platform C utilities for CLI applications in Ruby flavor + * Corss-Platform C lib for CLI applications in Ruby & Perl flavor * ------------------------------------------------------------*/ #ifndef XY_H #define XY_H -#define _XY_Version "v0.1.4.4-2024/12/14" -#define _XY_Maintain_URL "https://gitee.com/RubyMetric/chsrc/blob/main/include/xy.h" + +#define _XY_Version "v0.1.5-2025/03/06" +#define _XY_Maintain_URL "https://gitee.com/RubyMetric/chsrc/blob/main/lib/xy.h" + #include #include @@ -29,11 +31,14 @@ #include #include -/* Global */ -bool xy_enable_color = true; // #define NDEBUG + +/* Global */ +extern bool xy_enable_color; /* Default value defined in xy.c */ + + #ifdef _WIN32 #define XY_On_Windows 1 #define xy_on_windows true @@ -72,733 +77,116 @@ bool xy_enable_color = true; #define xy_useutf8() #endif -void putf (double n) { printf ("%f\n", n); } -void puti (long long n) { printf ("%lld\n", n); } -void putb (bool n) { if (n) puts ("true"); else puts ("false"); } -void print (const char *s) { printf ("%s", s); } -void println (const char *s) { printf ("%s\n", s); } -void say (const char *s) { printf ("%s\n", s); } -void br () { puts (""); } -void p (const char *s) { printf ("%s\n", s); } -#define assert_str(a, b) assert (xy_streql ((a), (b))) - -#define xy_unsupported() assert(!"Unsuppoted") -#define xy_unimplemented() assert(!"Unimplemented temporarily") -#define xy_unreached() assert(!"This code shouldn't be reached") +/****************************************************** + * Convenience + ******************************************************/ +void putf (double n); +void puti (long long n); +void putb (bool n); +void print (const char *s); +void println (const char *s); +void say (const char *s); +void br (); +void p (const char *s); #define xy_arylen(x) (sizeof (x) / sizeof (x[0])) -static inline void * -xy_malloc0 (size_t size) -{ - void *ptr = malloc (size); - memset (ptr, 0, size); - return ptr; -} + + +/****************************************************** + * Assertion + ******************************************************/ +#define assert_str(a, b) assert (xy_streql ((a), (b))) +#define xy_unsupported() assert(!"Unsuppoted") +#define xy_unimplemented() assert(!"Unimplemented temporarily") +#define xy_unreached() assert(!"This code shouldn't be reached") + + + +/****************************************************** + * Memory + ******************************************************/ +inline void * xy_malloc0 (size_t size); + /****************************************************** * String ******************************************************/ +char * xy_str_gsub (const char *str, const char *pat, const char *replace); +char * xy_2strjoin (const char *str1, const char *str2); +char * xy_strjoin (unsigned int count, ...); +char * xy_strdup (const char *str); +bool xy_streql (const char *str1, const char *str2); +char * xy_str_to_quietcmd (const char *cmd); -/** - * 将str中所有的pat字符串替换成replace,返回一个全新的字符串 - */ -static char * -xy_str_gsub (const char *str, const char *pat, const char *replace) -{ - size_t replace_len = strlen (replace); - size_t pat_len = strlen (pat); +bool xy_str_end_with (const char *str, const char *suffix); +bool xy_str_start_with (const char *str, const char *prefix); +char * xy_str_delete_prefix (const char *str, const char *prefix); +char * xy_str_delete_suffix (const char *str, const char *suffix); +char * xy_str_strip (const char *str); - int unit = replace_len - pat_len; - if (unit <= 0) - unit = 0; +char * xy_str_to_bold(const char *str); +char * xy_str_to_faint(const char *str); +char * xy_str_to_italic(const char *str); +char * xy_str_to_underline(const char *str); +char * xy_str_to_blink(const char *str); +char * xy_str_to_cross(const char *str); - size_t len = strlen (str); +char * xy_str_to_red(const char *str); +char * xy_str_to_green(const char *str); +char * xy_str_to_yellow(const char *str); +char * xy_str_to_blue(const char *str); +char * xy_str_to_magenta(const char *str); +char * xy_str_to_purple(const char *str); +char * xy_str_to_cyan(const char *str); - const char *cur = str; - int count = 0; - - while (cur < str + len) - { - char *fnd = strstr (cur, pat); - if (fnd) - { - count++; - cur = fnd + pat_len; - } - else - break; - } - // puti(count); DEBUG 匹配次数 - - char *ret = malloc (unit * count + len + 1); - char *retcur = ret; - - cur = str; - while (cur < str + len) - { - char *fnd = strstr (cur, pat); - if (fnd) - { - ptrdiff_t diff = fnd - cur; - strncpy (retcur, cur, diff); - cur = fnd + pat_len; - - retcur += diff; - strcpy (retcur, replace); - retcur += replace_len; - } - else - break; - } - strcpy (retcur, cur); - - return ret; -} - -static char * -xy_2strjoin (const char *str1, const char *str2) -{ - size_t len = strlen (str1); - size_t size = len + strlen (str2) + 1; - char *ret = malloc (size); - strcpy (ret, str1); - strcpy (ret + len, str2); - return ret; -} - -static char * -xy_strjoin (unsigned int count, ...) -{ - size_t al_fixed = 128; - char *ret = calloc (1, al_fixed); - // 已分配次数 - int al_times = 1; - // 当前已分配量 - size_t al_cur = al_fixed; - - const char *str = NULL; - // 需要分配的量 - size_t al_need = 0; - // 用于 strcpy() 到 ret 的哪个位置 - char *cur = ret + 0; - - va_list args; - va_start (args, count); - - for (int i = 0; i < count; i++) - { - // 是否需要重新分配 - bool need_realloc = false; - - str = va_arg (args, const char *); - al_need += strlen (str); - while (al_need > al_cur) - { - al_times += 1; - al_cur = al_times * al_fixed; - need_realloc = true; - } - // printf("al_times %d, al_need %zd, al_cur %zd\n", al_times, al_need, - // al_cur); - if (need_realloc) - { - ptrdiff_t diff = cur - ret; - ret = realloc (ret, al_cur); - cur = ret + diff; - } - if (NULL == ret) - { - fprintf (stderr, "xy.h: No availble memory!"); - return NULL; - } - strcpy (cur, str); - // puts(ret); - cur += strlen (str); - } - va_end (args); - - *cur = '\0'; - return ret; -} - -static char * -xy_strdup (const char *str) -{ - size_t len = strlen (str); - char *new = xy_malloc0 (len + 1); - strcpy (new, str); - return new; -} - -#define _XY_Str_Bold 1 -#define _XY_Str_Faint 2 -#define _XY_Str_Italic 3 -#define _XY_Str_Underline 4 -#define _XY_Str_Blink 5 -#define _XY_Str_Cross 9 - -#define xy_str_to_bold(str) _xy_str_to_terminal_style (_XY_Str_Bold, str) -#define xy_str_to_faint(str) _xy_str_to_terminal_style (_XY_Str_Faint, str) -#define xy_str_to_italic(str) _xy_str_to_terminal_style (_XY_Str_Italic, str) -#define xy_str_to_underline(str) _xy_str_to_terminal_style (_XY_Str_Underline, str) -#define xy_str_to_blink(str) _xy_str_to_terminal_style (_XY_Str_Blink, str) -#define xy_str_to_cross(str) _xy_str_to_terminal_style (_XY_Str_Cross, str) - -#define _XY_Str_Red 31 -#define _XY_Str_Green 32 -#define _XY_Str_Yellow 33 -#define _XY_Str_Blue 34 -#define _XY_Str_Magenta 35 -#define _XY_Str_Cyan 36 - -#define xy_str_to_red(str) _xy_str_to_terminal_style (_XY_Str_Red, str) -#define xy_str_to_green(str) _xy_str_to_terminal_style (_XY_Str_Green, str) -#define xy_str_to_yellow(str) _xy_str_to_terminal_style (_XY_Str_Yellow, str) -#define xy_str_to_blue(str) _xy_str_to_terminal_style (_XY_Str_Blue, str) -#define xy_str_to_magenta(str) _xy_str_to_terminal_style (_XY_Str_Magenta, str) -#define xy_str_to_purple xy_str_to_magenta -#define xy_str_to_cyan(str) _xy_str_to_terminal_style (_XY_Str_Cyan, str) - -static char * -_xy_str_to_terminal_style (int style, const char *str) -{ - char *color_fmt_str = NULL; - - if (!xy_enable_color) - { - color_fmt_str = "%s"; - goto new_str; - } - - switch (style) - { - case _XY_Str_Red: - color_fmt_str = "\e[31m%s\e[0m"; break; - case _XY_Str_Green: - color_fmt_str = "\e[32m%s\e[0m"; break; - case _XY_Str_Yellow: - color_fmt_str = "\e[33m%s\e[0m"; break; - case _XY_Str_Blue: - color_fmt_str = "\e[34m%s\e[0m"; break; - case _XY_Str_Magenta: - color_fmt_str = "\e[35m%s\e[0m"; break; - case _XY_Str_Cyan: - color_fmt_str = "\e[36m%s\e[0m"; break; - case _XY_Str_Bold: - color_fmt_str = "\e[1m%s\e[0m"; break; - case _XY_Str_Faint: - color_fmt_str = "\e[2m%s\e[0m"; break; - case _XY_Str_Italic: - color_fmt_str = "\e[3m%s\e[0m"; break; - case _XY_Str_Underline: - color_fmt_str = "\e[4m%s\e[0m"; break; - case _XY_Str_Blink: - color_fmt_str = "\e[5m%s\e[0m"; break; - case _XY_Str_Cross: - color_fmt_str = "\e[9m%s\e[0m"; break; - } - - - size_t len = 0; -new_str: - // -2 把中间%s减掉 - len = strlen (color_fmt_str) - 2; - char *buf = malloc (strlen (str) + len + 1); - sprintf (buf, color_fmt_str, str); - return buf; -} - -static bool -xy_streql (const char *str1, const char *str2) -{ - if (NULL==str1 || NULL==str2) - { - return false; - } - return strcmp (str1, str2) == 0 ? true : false; -} - -static char * -xy_str_to_quietcmd (const char *cmd) -{ - char *ret = NULL; -#ifdef _WIN32 - ret = xy_2strjoin (cmd, " >nul 2>nul "); -#else - ret = xy_2strjoin (cmd, " 1>/dev/null 2>&1 "); -#endif - return ret; -} - -static bool -xy_str_end_with (const char *str, const char *suffix) -{ - size_t len1 = strlen (str); - size_t len2 = strlen (suffix); - - if (0 == len2) - return true; // 空字符串直接返回 - if (len1 < len2) - return false; - - const char *cur1 = str + len1 - 1; - const char *cur2 = suffix + len2 - 1; - - for (int i = 0; i < len2; i++) - { - if (*cur1 != *cur2) - return false; - cur1--; - cur2--; - } - return true; -} - -static bool -xy_str_start_with (const char *str, const char *prefix) -{ - if (NULL==str || NULL==prefix) - { - return false; - } - - size_t len1 = strlen (str); - size_t len2 = strlen (prefix); - - if (0 == len2) - return true; // 空字符串直接返回 - if (len1 < len2) - return false; - - const char *cur1 = str; - const char *cur2 = prefix; - - for (int i = 0; i < len2; i++) - { - if (*cur1 != *cur2) - return false; - cur1++; - cur2++; - } - return true; -} - -static char * -xy_str_delete_prefix (const char *str, const char *prefix) -{ - char *new = xy_strdup (str); - bool yes = xy_str_start_with (str, prefix); - if (!yes) - return new; - - size_t len = strlen (prefix); - char *cur = new + len; - return cur; -} - -static char * -xy_str_delete_suffix (const char *str, const char *suffix) -{ - char *new = xy_strdup (str); - bool yes = xy_str_end_with (str, suffix); - if (!yes) - return new; - - size_t len1 = strlen (str); - size_t len2 = strlen (suffix); - char *cur = new + len1 - len2; - *cur = '\0'; - return new; -} - -static char * -xy_str_strip (const char *str) -{ - char *new = xy_strdup (str); - - while (strchr ("\n\r\v\t\f ", new[0])) - { - new += 1; - } - - size_t len = strlen (new); - - char *last = new + len - 1; - - while (strchr ("\n\r\v\t\f ", *last)) - { - *last = '\0'; - last -= 1; - } - return new; -} /****************************************************** * Logging ******************************************************/ +void xy_log (const char *prompt, const char *str); +void xy_succ(const char *prompt, const char *str); +void xy_info(const char *prompt, const char *str); +void xy_warn(const char *prompt, const char *str); +void xy_error(const char *prompt, const char *str); -#define _XY_Log_Plain 000000001 -#define _XY_Log_Success 000000001 << 1 -#define _XY_Log_Info 000000001 << 2 -#define _XY_Log_Warn 000000001 << 3 -#define _XY_Log_Error 000000001 << 4 +void xy_log_brkt (const char *prompt1, const char *prompt2, const char *content); +void xy_succ_brkt(const char *prompt1, const char *prompt2, const char *content); +void xy_info_brkt(const char *prompt1, const char *prompt2, const char *content); +void xy_warn_brkt(const char *prompt1, const char *prompt2, const char *content); +void xy_error_brkt(const char *prompt1,const char *prompt2, const char *content); -#define xy_log(prompt, str) _xy_log (_XY_Log_Plain, prompt, str) -#define xy_succ(prompt,str) _xy_log (_XY_Log_Success, prompt, str) -#define xy_info(prompt,str) _xy_log (_XY_Log_Info, prompt, str) -#define xy_warn(prompt,str) _xy_log (_XY_Log_Warn, prompt, str) -#define xy_error(prompt,str) _xy_log (_XY_Log_Error, prompt, str) - -static void -_xy_log (int level, const char *prompt, const char *content) -{ - char *str = NULL; - - bool to_stderr = false; - - /** - * 'app: content' - */ - if (level & _XY_Log_Plain) - { - str = xy_strjoin (3, prompt, ": ", content); - } - else if (level & _XY_Log_Success) - { - str = xy_strjoin (3, prompt, ": ", xy_str_to_green (content)); - } - else if (level & _XY_Log_Info) - { - str = xy_strjoin (3, prompt, ": ", xy_str_to_blue (content)); - } - else if (level & _XY_Log_Warn) - { - str = xy_strjoin (3, prompt, ": ", xy_str_to_yellow (content)); - to_stderr = true; - } - else if (level & _XY_Log_Error) - { - str = xy_strjoin (3, prompt, ": ", xy_str_to_red (content)); - to_stderr = true; - } - else - { - // xy_assert ("CAN'T REACH!"); - } - - if (to_stderr) - { - fprintf (stderr, "%s\n", str); - } - else - { - puts (str); - } - free (str); -} - - -/** - * brkt 系列输出受 pip 启发,为了输出方便,使用 xy.h 的程序应该 - * - * 1.若想完全自定义颜色和输出位置: - * - * 应基于下述 xy_log_brkt_to 定义自己的输出函数 - * - * 2.若想自定义颜色,而保持输出到stdout: - * - * 应基于下述 xy_log_brkt 定义 - * - * 3.若对log无细致要求,想要快速使用log功能: - * - * 应基于下述 xy__brkt 定义自己的 app__brkt(),或直接使用 xy__brkt - */ - -static void -xy_log_brkt_to (const char *prompt, const char *content, FILE *stream) -{ - char *str = xy_strjoin (4, "[", prompt, "] ", content); - fprintf (stream, "%s\n", str); - free (str); -} - -#define xy_log_brkt(prompt1,prompt2,content) _xy_log_brkt(_XY_Log_Plain, prompt1,prompt2,content) -#define xy_succ_brkt(prompt1,prompt2,content) _xy_log_brkt(_XY_Log_Success,prompt1,prompt2,content) -#define xy_info_brkt(prompt1,prompt2,content) _xy_log_brkt(_XY_Log_Info, prompt1,prompt2,content) -#define xy_warn_brkt(prompt1,prompt2,content) _xy_log_brkt(_XY_Log_Warn, prompt1,prompt2,content) -#define xy_error_brkt(prompt1,prompt2,content) _xy_log_brkt(_XY_Log_Error, prompt1,prompt2,content) - -static void -_xy_log_brkt (int level, const char *prompt1, const char *prompt2, const char *content) -{ - char *str = NULL; - - bool to_stderr = false; - - if (level & _XY_Log_Plain) - { - str = xy_strjoin (6, "[", prompt1, " ", prompt2, "] ", content); - } - else if (level & _XY_Log_Success) - { - /* [app 成功] [app success] */ - str = xy_strjoin (6, - "[", xy_str_to_green (prompt1), " ", xy_str_to_bold (xy_str_to_green (prompt2)), "] ", xy_str_to_green (content)); - } - else if (level & _XY_Log_Info) - { - /* [app 信息] [app info] - [app 提示] [app notice] - */ - str = xy_strjoin (6, - "[", xy_str_to_blue (prompt1), " ", xy_str_to_bold (xy_str_to_blue (prompt2)), "] ", xy_str_to_blue (content)); - } - else if (level & _XY_Log_Warn) - { - /* [app 警告] [app warn] */ - str = xy_strjoin (6, - "[", xy_str_to_yellow (prompt1), " ", xy_str_to_bold (xy_str_to_yellow (prompt2)), "] ", xy_str_to_yellow (content)); - to_stderr = true; - } - else if (level & _XY_Log_Error) - { - /* [app 错误] [app error] */ - str = xy_strjoin (6, - "[", xy_str_to_red (prompt1), " ", xy_str_to_bold (xy_str_to_red (prompt2)), "] ", xy_str_to_red (content)); - to_stderr = true; - } - else - { - // xy_assert ("CAN'T REACH!"); - } - - if (to_stderr) - { - fprintf (stderr, "%s\n", str); - } - else - { - puts (str); - } - free (str); -} +void xy_log_brkt_to (const char *prompt, const char *content, FILE *stream); /****************************************************** * System ******************************************************/ -/** - * 执行cmd,返回某行输出结果,并对已经遍历过的行执行iter_func - * - * @param cmd 要执行的命令 - * @param n 指定命令执行输出的结果行中的某一行,0 表示最后一行,n (n>0) 表示第n行 - * 该函数会返回这一行的内容 - * @param iter_func 对遍历时经过的行的内容,进行函数调用 - * - * @note 返回的字符串最后面一般有换行符号 - * - * 由于目标行会被返回出来,所以 iter_func() 并不执行目标行,只会执行遍历过的行 - */ -static char * -xy_run_iter (const char *cmd, unsigned long n, void (*iter_func) (const char *)) -{ - const int size = 512; - char *buf = (char *) malloc (size); +char * xy_run_iter (const char *cmd, unsigned long n, void (*iter_func) (const char *)); +char * xy_run (const char *cmd, unsigned long n); - FILE *stream = popen (cmd, "r"); - if (stream == NULL) - { - fprintf (stderr, "xy: 命令执行失败\n"); - return NULL; - } - - char *ret = NULL; - unsigned long count = 0; - - while (true) - { - if (NULL == fgets (buf, size, stream)) - break; - /* 存在换行的总是会把换行符读出来,删掉 */ - ret = xy_str_delete_suffix (buf, "\n"); - count += 1; - if (n == count) - break; - if (iter_func) - { - iter_func (buf); - } - } - - pclose (stream); - return ret; -} - -static char * -xy_run (const char *cmd, unsigned long n) -{ - return xy_run_iter (cmd, n, NULL); -} +bool xy_file_exist (const char *path); +bool xy_dir_exist (const char *path); +char * xy_normalize_path (const char *path); +char * xy_parent_dir (const char *path); -#define xy_os_home _xy_os_home () -static char * -_xy_os_home () -{ - char *home = NULL; - if (xy_on_windows) - home = getenv ("USERPROFILE"); - else - home = getenv ("HOME"); - return home; -} - -#define xy_win_powershell_profile _xy_win_powershell_profile () -#define xy_win_powershellv5_profile _xy_win_powershellv5_profile () -static char * -_xy_win_powershell_profile () -{ - return xy_2strjoin ( - xy_os_home, "\\Documents\\PowerShell\\Microsoft.PowerShell_profile.ps1"); -} - -char * -_xy_win_powershellv5_profile () -{ - return xy_2strjoin ( - xy_os_home, - "\\Documents\\WindowsPowerShell\\Microsoft.PowerShell_profile.ps1"); -} +/****************************************************** + * OS specific + ******************************************************/ #define xy_zshrc "~/.zshrc" #define xy_bashrc "~/.bashrc" #define xy_fishrc "~/.config/fish/config.fish" -/** - * @note Windows上,`path` 不要夹带变量名,因为最终 access() 不会帮你转换 - */ -static bool -xy_file_exist (const char *path) -{ - const char *new_path = path; - if (xy_str_start_with (path, "~")) - { - new_path = xy_2strjoin (xy_os_home, path + 1); - } - // 0 即 F_OK - return (0==access (new_path, 0)) ? true : false; -} +char * xy_os_home (); +char * xy_win_powershell_profile (); +char * xy_win_powershellv5_profile (); -/** - * @note xy_file_exist() 和 xy_dir_exist() 两个函数在所有平台默认都支持使用 '~', - * 但实现中都没有调用 xy_normalize_path(),以防万一,调用前可能需要用户手动调用它 - */ -static bool -xy_dir_exist (const char *path) -{ - const char *dir = path; - if (xy_on_windows) - { - if (xy_str_start_with (path, "~")) - { - dir = xy_2strjoin (xy_os_home, path + 1); - } - } - if (xy_on_windows) - { -#ifdef XY_On_Windows - // 也可以用 opendir() #include - DWORD attr = GetFileAttributesA (dir); - - if (attr == INVALID_FILE_ATTRIBUTES) - { - // Q: 我们应该报错吗? - return false; - } - else if (attr & FILE_ATTRIBUTE_DIRECTORY) - { - return true; - } - else - { - return false; - } -#endif - } - else - { - int status = system (xy_2strjoin ("test -d ", dir)); - - if (0==status) - return true; - else - return false; - } -} - -/** - * 1. 删除路径左右两边多出来的空白符 - * 2. 将 ~/ 转换为绝对路径 - */ -static char * -xy_normalize_path (const char *path) -{ - char *new = xy_str_strip (path); // 防止开发者多写了空白符 - - if (xy_on_windows) - { - if (xy_str_start_with (new, "~/")) - { - // 或 %USERPROFILE% - new = xy_strjoin (3, xy_os_home, "\\", - xy_str_delete_prefix (new, "~/")); - } - new = xy_str_gsub (new, "/", "\\"); - } - else - { - if (xy_str_start_with (new, "~/")) - { - new = xy_strjoin (3, xy_os_home, "/", - xy_str_delete_prefix (new, "~/")); - } - } - - return new; -} - -static char * -xy_parent_dir (const char *path) -{ - char *dir = xy_normalize_path (path); - char *last = NULL; - if (xy_on_windows) - { - last = strrchr (dir, '\\'); - if (!last) - { - /* current dir */ - return "."; - } - *last = '\0'; - } - else - { - last = strrchr (dir, '/'); - if (!last) - { - /* current dir */ - return "."; - } - *last = '\0'; - } - return dir; -} #endif