chsrc/include/chsrc.h

578 lines
15 KiB
C
Raw Normal View History

/** ------------------------------------------------------------
* File : chsrc.h
* License : GPLv3
* Authors : Aoran Zeng <ccmywish@qq.com>
* Created on : <2023-08-29>
2024-06-11 18:03:02 +08:00
* Last modified : <2024-06-11>
*
* chsrc:
*
* chsrc.c
* ------------------------------------------------------------*/
2023-08-29 23:04:54 +08:00
2023-09-03 14:56:49 +08:00
#include "xy.h"
2024-05-24 20:43:48 +08:00
#include "source.h"
2023-09-03 14:48:53 +08:00
2024-06-08 08:34:53 +08:00
#define App_Name "chsrc"
2023-09-15 12:50:56 +08:00
#define chsrc_log(str) xy_log(App_Name,str)
2024-06-08 08:34:53 +08:00
#define chsrc_succ(str) xy_succ(App_Name,str)
#define chsrc_info(str) xy_info(App_Name,str)
#define chsrc_warn(str) xy_warn(App_Name,str)
#define chsrc_error(str) xy_error(App_Name,str)
#define chsrc_succ_remarkably(str) xy_succ_remarkably(App_Name,"成功",str);
#define chsrc_infolog_remarkably(str) xy_info_remarkably(App_Name,"LOG",str);
#define chsrc_info_remarkably(str) xy_info_remarkably(App_Name,"提示",str);
#define chsrc_note_remarkably(str) xy_warn_remarkably(App_Name,"提示",str);
#define chsrc_warn_remarkably(str) xy_warn_remarkably(App_Name,"警告",str);
#define chsrc_error_remarkably(str) xy_error_remarkably(App_Name,"错误",str);
2023-09-27 15:20:44 +08:00
2024-06-11 18:03:02 +08:00
void
chsrc_check_remarkably (const char *check_what, const char *check_type, bool exist)
{
if (!exist)
{
xy_log_remarkably (App_Name, xy_str_to_bold (xy_str_to_red ("检查")),
xy_strjoin (5, xy_str_to_red ("x "), check_type, " ", xy_str_to_red (check_what), " 不存在"));
}
else
{
xy_log_remarkably (App_Name, xy_str_to_bold (xy_str_to_green ("检查")),
xy_strjoin (5, xy_str_to_green (""), check_type, " ", xy_str_to_green (check_what), " 存在"));
}
}
2023-09-15 12:50:56 +08:00
bool Cli_Option_IPv6 = false;
bool Cli_Option_Locally = false;
bool Cli_Option_InEnglish = false;
2023-09-15 12:50:56 +08:00
/**
*
*
* @param check_cmd `prog_name` `prog_name`
2023-09-15 12:50:56 +08:00
* 使 python Windows上
* Microsoft Store
*
* @param prog_name
2023-09-15 12:50:56 +08:00
*/
bool
query_program_exist (char *check_cmd, char *prog_name)
2023-09-15 12:50:56 +08:00
{
char *which = check_cmd;
2023-09-15 12:50:56 +08:00
int ret = system(which);
// char buf[32] = {0}; sprintf(buf, "错误码: %d", ret);
if (0 != ret)
{
// xy_warn (xy_strjoin(4, "× 命令 ", progname, " 不存在,", buf));
2024-06-11 18:03:02 +08:00
chsrc_check_remarkably (prog_name, "命令", false);
return false;
}
else
{
2024-06-11 18:03:02 +08:00
chsrc_check_remarkably (prog_name, "命令", true);
return true;
}
2023-09-15 12:50:56 +08:00
}
/**
* _setsrc codetarget可用源中
*
* @param target
* @param input default def
*/
2023-10-05 09:40:10 +08:00
#define find_mirror(s, input) query_mirror_exist(s##_sources, s##_sources_n, (char*)#s+3, input)
2023-09-15 12:50:56 +08:00
int
2024-05-25 00:49:13 +08:00
query_mirror_exist (SourceInfo *sources, size_t size, char *target, char *input)
2023-09-15 12:50:56 +08:00
{
2024-06-07 23:51:11 +08:00
if (0==size || 1==size)
2024-05-25 00:49:13 +08:00
{
2024-06-08 08:34:53 +08:00
chsrc_error (xy_strjoin (3, "当前 ", target, " 无任何可用源,请联系维护者"));
2024-05-25 00:49:13 +08:00
exit (1);
}
2023-09-15 12:50:56 +08:00
2024-06-07 23:51:11 +08:00
if (2==size)
2024-05-25 00:49:13 +08:00
{
2024-06-08 08:34:53 +08:00
chsrc_succ (xy_strjoin (4, sources[1].mirror->name, "", target, " 目前唯一可用镜像站,感谢他们的慷慨支持"));
2024-06-07 23:51:11 +08:00
}
if (xy_streql ("reset", input))
{
puts ("将重置为上游默认源");
2024-06-07 23:51:11 +08:00
return 0; // 返回第1个因为第1个是上游默认源
2024-05-25 00:49:13 +08:00
}
2023-09-15 12:50:56 +08:00
if (xy_streql ("first", input))
2024-05-25 00:49:13 +08:00
{
puts ("将使用维护团队测速第一的源");
2024-06-07 23:51:11 +08:00
return 1; // 返回第2个因为第1个是上游默认源
2024-05-25 00:49:13 +08:00
}
2023-09-15 12:50:56 +08:00
int idx = 0;
2023-10-05 09:28:34 +08:00
SourceInfo source = sources[0];
2023-09-15 12:50:56 +08:00
bool exist = false;
for (int i=0; i<size; i++)
2024-05-25 00:49:13 +08:00
{
source = sources[i];
if (xy_streql (source.mirror->code, input))
2024-05-25 00:49:13 +08:00
{
idx = i;
exist = true;
break;
}
}
if (!exist)
{
2024-06-08 08:34:53 +08:00
chsrc_error (xy_strjoin (3, "镜像站 ", input, " 不存在"));
chsrc_error (xy_2strjoin ("查看可使用源,请使用 chsrc list ", target));
2024-05-25 00:49:13 +08:00
exit (1);
2023-09-15 12:50:56 +08:00
}
return idx;
}
/**
2023-09-27 19:31:23 +08:00
* oh-my-mirrorz.py @ccmywish C语言
2023-09-15 12:50:56 +08:00
*/
2024-05-25 00:49:13 +08:00
char *
2023-09-15 12:50:56 +08:00
to_human_readable_speed (double speed)
{
2024-05-25 00:49:13 +08:00
char *scale[] = {"Byte/s", "KByte/s", "MByte/s", "GByte/s", "TByte/s"};
2023-09-15 12:50:56 +08:00
int i = 0;
while (speed > 1024.0)
{
i += 1;
speed /= 1024.0;
}
2024-05-25 00:49:13 +08:00
char *buf = xy_malloc0 (64);
sprintf (buf, "%.2f %s", speed, scale[i]);
2023-09-15 12:50:56 +08:00
2024-05-25 00:49:13 +08:00
char *new = NULL;
if (i <= 1 ) new = xy_str_to_red (buf);
2023-09-15 12:50:56 +08:00
else
2024-05-25 00:49:13 +08:00
{
if (i == 2 && speed < 2.00) new = xy_str_to_yellow (buf);
else new = xy_str_to_green (buf);
}
2023-09-15 12:50:56 +08:00
return new;
}
/**
* https://github.com/mirrorz-org/oh-my-mirrorz/blob/master/oh-my-mirrorz.py
2023-09-27 19:31:23 +08:00
* @ccmywish C语言
2023-09-15 12:50:56 +08:00
*
* @return -1
*/
double
2024-05-25 00:49:13 +08:00
test_speed_url (const char *url)
2023-09-15 12:50:56 +08:00
{
2024-05-25 00:49:13 +08:00
char *time_sec = "6";
2023-09-15 12:50:56 +08:00
/* 现在我们切换至跳转后的链接来测速,不再使用下述判断
2023-09-15 12:50:56 +08:00
if (xy_str_start_with(url, "https://registry.npmmirror"))
{
// 这里 npmmirror 跳转非常慢需要1~3秒所以我们给它留够至少8秒测速时间否则非常不准
time_sec = "10";
}
*/
char *ipv6 = ""; // 默认不启用
if (Cli_Option_IPv6==true) {
ipv6 = "--ipv6";
2023-09-15 12:50:56 +08:00
}
// 我们用 —L因为Ruby China源会跳转到其他地方
// npmmirror 也会跳转
char *curl_cmd = xy_strjoin (7, "curl -qsL ", ipv6,
" -o " xy_os_devnull,
2024-05-25 00:49:13 +08:00
" -w \"%{http_code} %{speed_download}\" -m", time_sec ,
" -A chsrc/" Chsrc_Version " ", url);
2023-09-15 12:50:56 +08:00
2024-06-08 08:34:53 +08:00
// chsrc_info (xy_2strjoin ("测速命令 ", curl_cmd));
2023-09-15 12:50:56 +08:00
char *buf = xy_run (curl_cmd, 0, NULL);
2023-09-15 12:50:56 +08:00
// 如果尾部有换行,删除
2023-09-27 19:31:23 +08:00
buf = xy_str_strip (buf);
2023-09-15 12:50:56 +08:00
2023-09-27 19:31:23 +08:00
// 分隔两部分数据
2024-05-25 00:49:13 +08:00
char *split = strchr (buf, ' ');
2023-09-15 12:50:56 +08:00
if (split) *split = '\0';
// puts(buf); puts(split+1);
2024-05-25 00:49:13 +08:00
int http_code = atoi (buf);
double speed = atof (split+1);
char *speedstr = to_human_readable_speed (speed);
if (200!=http_code)
{
char* httpcodestr = xy_str_to_yellow (xy_2strjoin ("HTTP码 ", buf));
puts (xy_strjoin (3, speedstr, " | ", httpcodestr));
}
else
{
puts (speedstr);
}
2023-09-15 12:50:56 +08:00
return speed;
}
2023-09-03 14:48:53 +08:00
int
2024-05-25 00:49:13 +08:00
get_max_ele_idx_in_dbl_ary (double *array, int size)
2023-09-03 14:48:53 +08:00
{
double maxval = array[0];
2023-09-06 19:45:37 +08:00
int maxidx = 0;
2023-09-03 14:48:53 +08:00
2024-05-25 00:49:13 +08:00
for (int i=1; i<size; i++)
{
if (array[i]>maxval)
{
maxval = array[i];
maxidx = i;
}
2023-09-03 14:48:53 +08:00
}
return maxidx;
}
2023-09-27 09:40:31 +08:00
/**
*
*/
#define auto_select(s) auto_select_(s##_sources, s##_sources_n, (char*)#s+3)
2023-09-15 12:50:56 +08:00
int
2024-05-25 00:49:13 +08:00
auto_select_ (SourceInfo *sources, size_t size, const char *target)
2023-09-15 12:50:56 +08:00
{
2024-06-07 23:51:11 +08:00
if (0==size || 1==size)
2024-05-25 00:49:13 +08:00
{
2024-06-08 08:34:53 +08:00
chsrc_error (xy_strjoin (3, "当前 ", target, " 无任何可用源,请联系维护者"));
2024-05-25 00:49:13 +08:00
exit (1);
}
2023-09-15 12:50:56 +08:00
bool onlyone = false;
2024-06-07 23:51:11 +08:00
if (2==size) onlyone = true;
2023-09-15 12:50:56 +08:00
double speeds[size];
double speed = 0.0;
for (int i=0;i<size;i++)
{
2023-10-05 09:28:34 +08:00
SourceInfo src = sources[i];
2023-09-15 12:50:56 +08:00
const char* url = src.mirror->__bigfile_url;
2024-05-25 00:49:13 +08:00
if (NULL==url)
{
2024-06-05 15:47:15 +08:00
if (xy_streql ("upstream", src.mirror->code))
{
continue; // 上游默认源不测速
}
else
{
chsrc_warn (xy_strjoin (3, "开发者未提供 ", src.mirror->code, " 镜像站测速链接,跳过该站点"));
speed = 0;
}
2024-05-25 00:49:13 +08:00
}
else
{
printf ("%s", xy_strjoin (3, "测速 ", src.mirror->site , " ... "));
2024-05-25 00:49:13 +08:00
fflush (stdout);
speed = test_speed_url (url);
}
2023-09-15 12:50:56 +08:00
speeds[i] = speed;
}
2023-10-05 09:40:10 +08:00
int fastidx = get_max_ele_idx_in_dbl_ary (speeds, size);
2023-09-15 12:50:56 +08:00
if (onlyone)
2024-06-08 08:34:53 +08:00
chsrc_succ (xy_strjoin (4, sources[fastidx].mirror->name, "", target, " 目前唯一可用镜像站,感谢他们的慷慨支持"));
2023-09-15 12:50:56 +08:00
else
2024-05-25 00:49:13 +08:00
puts (xy_2strjoin ("最快镜像站: ", xy_str_to_green (sources[fastidx].mirror->name)));
2023-09-15 12:50:56 +08:00
return fastidx;
}
2023-09-27 09:40:31 +08:00
#define use_specific_mirror_or_auto_select(input, s) \
(NULL!=(input)) ? find_mirror(s, input) : auto_select(s)
2024-06-07 23:51:11 +08:00
bool
is_upstream (SourceInfo *source)
{
return xy_streql (source->mirror->code, "upstream");
}
bool
source_has_empty_url (SourceInfo *source)
2024-06-07 23:51:11 +08:00
{
return source->url == NULL;
}
#define split_between_source_changing_process puts ("--------------------------------")
2023-09-03 17:57:45 +08:00
/**
* _setsrc
2024-06-07 23:51:11 +08:00
*
* 1.
* 2.
2023-09-03 17:57:45 +08:00
*/
void
chsrc_confirm_selection (SourceInfo *source)
2023-09-03 17:57:45 +08:00
{
2024-06-07 23:51:11 +08:00
// 由于实现问题,我们把本应该独立出去的默认上游源,也放在了可以换源的数组中,而且放在第一个
// chsrc 已经规避用户使用未实现的 `chsrc reset`
// 但是某些用户可能摸索着强行使用 chsrc set target upstream从而执行起该禁用的功能
// 之所以禁用,是因为有的 reset 我们并没有实现,我们在这里阻止这些邪恶的用户
if (is_upstream (source) && source_has_empty_url (source))
2024-06-07 23:51:11 +08:00
{
chsrc_error ("暂未对该软件实现重置");
exit (2);
}
else if (source_has_empty_url (source))
2024-06-07 23:51:11 +08:00
{
chsrc_error ("该源URL不存在请向开发团队提交bug");
exit (2);
}
else
{
puts (xy_strjoin (5, "选中镜像站: ", xy_str_to_green (source->mirror->abbr), " (", xy_str_to_green (source->mirror->code), ")"));
}
split_between_source_changing_process;
2023-09-03 17:57:45 +08:00
}
2023-09-15 12:50:56 +08:00
#define ChsrcTypeAuto "auto"
#define ChsrcTypeReset "reset"
#define ChsrcTypeSemiAuto "semiauto"
#define ChsrcTypeManual "manual"
#define ChsrcTypeUntested "untested"
/**
* @param source NULL
* @param last_word 5ChsrcTypeAuto | ChsrcTypeReset | ChsrcTypeSemiAuto | ChsrcTypeManual | ChsrcTypeUntested
*/
2023-09-04 15:39:49 +08:00
void
chsrc_say_lastly (SourceInfo *source, const char *last_word)
2023-09-04 15:39:49 +08:00
{
split_between_source_changing_process;
if (xy_streql (ChsrcTypeAuto, last_word))
{
if (source)
{
chsrc_log (xy_2strjoin ("全自动换源完成,感谢镜像提供方: ", xy_str_to_purple (source->mirror->name)));
}
else
{
chsrc_log ("全自动换源完成");
}
}
else if (xy_streql (ChsrcTypeReset, last_word))
{
// is_upstream (source)
chsrc_log (xy_str_to_purple ("已重置为上游默认源"));
}
else if (xy_streql (ChsrcTypeSemiAuto, last_word))
{
if (source)
{
chsrc_log (xy_2strjoin ("半自动换源完成,仍需按上述提示手工操作,感谢镜像提供方: ", xy_str_to_purple (source->mirror->name)));
}
else
{
chsrc_log ("半自动换源完成,仍需按上述提示手工操作");
}
2024-06-08 13:26:52 +08:00
chsrc_warn ("若您有完全自动化的换源方案,邀您帮助: chsrc issue");
}
else if (xy_streql (ChsrcTypeManual, last_word))
{
if (source)
{
chsrc_log (xy_2strjoin ("因实现约束需按上述提示手工操作,感谢镜像提供方: ", xy_str_to_purple (source->mirror->name)));
}
else
{
chsrc_log ("因实现约束需按上述提示手工操作");
}
2024-06-08 13:26:52 +08:00
chsrc_warn ("若您有完全自动化的换源方案,邀您帮助: chsrc issue");
}
else if (xy_streql (ChsrcTypeUntested, last_word))
2024-06-07 23:51:11 +08:00
{
if (source)
{
chsrc_log (xy_2strjoin ("感谢镜像提供方: ", xy_str_to_purple (source->mirror->name)));
}
else
{
chsrc_log ("自动换源完成");
}
2024-06-08 13:26:52 +08:00
chsrc_warn ("该换源步骤已实现但未经测试或存在任何反馈,请报告使用情况: chsrc issue");
2024-06-07 23:51:11 +08:00
}
else
{
puts (last_word);
2024-06-07 23:51:11 +08:00
}
2023-09-04 15:39:49 +08:00
}
2023-09-03 14:48:53 +08:00
2023-09-22 13:07:49 +08:00
void
2023-09-29 21:28:02 +08:00
chsrc_ensure_root ()
2023-09-22 13:07:49 +08:00
{
2024-05-25 00:49:13 +08:00
char *euid = getenv ("$EUID");
if (NULL==euid)
{
char *buf = xy_run ("id -u", 0, NULL);
2024-05-25 00:49:13 +08:00
if (0!=atoi(buf)) goto not_root;
else return;
}
else
{
if (0!=atoi(euid)) goto not_root;
else return;
}
2023-09-22 13:07:49 +08:00
not_root:
2024-06-08 08:34:53 +08:00
chsrc_error ("请在命令前使用 sudo 或切换为root用户来保证必要的权限");
2024-05-25 00:49:13 +08:00
exit (1);
2023-09-22 13:07:49 +08:00
}
2023-09-26 21:41:47 +08:00
static void
2024-05-25 00:49:13 +08:00
chsrc_run (const char *cmd)
2023-09-26 21:41:47 +08:00
{
xy_info_remarkably (App_Name, "运行", cmd);
int status = system (cmd);
if (0==status)
{
xy_succ_remarkably (App_Name, "运行", "命令执行成功");
}
else
{
char buf[8] = {0};
2024-06-08 15:50:46 +08:00
sprintf (buf, "%d", status);
char *str = xy_2strjoin ("命令执行失败,返回码 ", buf);
xy_error_remarkably (App_Name, "运行", str);
}
puts ("");
2023-09-26 21:41:47 +08:00
}
2023-09-26 22:24:48 +08:00
static void
2024-05-25 00:49:13 +08:00
chsrc_check_file (const char *path)
2023-09-26 22:24:48 +08:00
{
2024-05-25 00:49:13 +08:00
char *cmd = NULL;
2023-09-27 18:31:45 +08:00
path = xy_uniform_path (path);
2024-05-25 00:49:13 +08:00
if(xy_on_windows)
{
cmd = xy_2strjoin ("type ", path);
}
else
{
cmd = xy_2strjoin ("cat ", path);
}
2023-09-26 22:24:48 +08:00
chsrc_run (cmd);
}
2023-09-29 21:23:58 +08:00
static void
2024-05-25 00:49:13 +08:00
chsrc_ensure_dir (const char *dir)
2023-09-29 21:23:58 +08:00
{
2023-09-29 22:15:58 +08:00
dir = xy_uniform_path (dir);
2024-05-25 00:49:13 +08:00
char *mkdir_cmd = NULL;
if (xy_on_windows)
{
mkdir_cmd = "md ";
}
else
{
mkdir_cmd = "mkdir -p ";
}
char *cmd = xy_2strjoin (mkdir_cmd, dir);
2023-09-29 21:23:58 +08:00
cmd = xy_str_to_quietcmd (cmd);
chsrc_run (cmd);
}
2023-09-26 21:41:47 +08:00
static void
2024-05-25 00:49:13 +08:00
chsrc_append_to_file (const char *str, const char *file)
2023-09-26 21:41:47 +08:00
{
2023-09-27 18:31:45 +08:00
file = xy_uniform_path (file);
2024-05-25 00:49:13 +08:00
char *dir = xy_parent_dir (file);
2023-09-29 21:23:58 +08:00
chsrc_ensure_dir (dir);
2024-05-25 00:49:13 +08:00
char *cmd = NULL;
if (xy_on_windows)
{
cmd = xy_strjoin (4, "echo ", str, " >> ", file);
}
else
{
cmd = xy_strjoin (4, "echo '", str, "' >> ", file);
}
chsrc_run (cmd);
2023-09-26 21:41:47 +08:00
}
static void
2024-05-25 00:49:13 +08:00
chsrc_overwrite_file (const char *str, const char *file)
2023-09-26 21:41:47 +08:00
{
2023-09-27 18:31:45 +08:00
file = xy_uniform_path (file);
2024-05-25 00:49:13 +08:00
char *dir = xy_parent_dir (file);
2023-09-29 21:23:58 +08:00
chsrc_ensure_dir (dir);
2024-05-25 00:49:13 +08:00
char *cmd = NULL;
if (xy_on_windows)
{
cmd = xy_strjoin (4, "echo ", str, " > ", file);
}
else
{
cmd = xy_strjoin (4, "echo '", str, "' > ", file);
}
chsrc_run (cmd);
2023-09-26 21:41:47 +08:00
}
2023-09-26 23:02:12 +08:00
static void
2024-05-25 00:49:13 +08:00
chsrc_backup (const char *path)
2023-09-26 23:02:12 +08:00
{
2024-05-25 00:49:13 +08:00
char *cmd = NULL;
2023-10-05 17:32:02 +08:00
if (xy_on_bsd)
2024-05-25 00:49:13 +08:00
{
// 似乎BSD的cp并没有 --backup='t' 选项
cmd = xy_strjoin (5, "cp -f ", path, " ", path, ".bak");
}
else if (xy_on_windows)
{
// /Y 表示覆盖
cmd = xy_strjoin (5, "copy /Y ", path, " ", path, ".bak" );
}
else
{
cmd = xy_strjoin (5, "cp ", path, " ", path, ".bak --backup='t'");
}
2023-10-05 17:32:02 +08:00
2023-09-26 23:02:12 +08:00
chsrc_run (cmd);
2024-06-08 15:59:59 +08:00
chsrc_info_remarkably (xy_strjoin (3, "备份文件名 ", path, ".bak"));
2023-09-26 23:02:12 +08:00
}
2023-09-26 21:41:47 +08:00
2023-09-02 19:07:30 +08:00
/* Target Info */
typedef struct TargetInfo_t {
void (*getfn) (char *option);
void (*setfn) (char *option);
void (*resetfn) (char *option);
2024-05-25 00:49:13 +08:00
SourceInfo *sources;
2023-10-05 09:28:34 +08:00
size_t sources_n;
} TargetInfo;
2023-09-02 19:07:30 +08:00
// 大部分target还不支持reset所以暂时先默认设置为NULL来过渡
#define def_target(t) TargetInfo t##_target = {t##_getsrc, t##_setsrc, NULL, t##_sources, t##_sources_n}
#define def_target_full(t) TargetInfo t##_target = {t##_getsrc, t##_setsrc, t##_resetsrc, t##_sources, t##_sources_n}
#define def_target_noget(t) TargetInfo t##_target = {NULL, t##_setsrc, NULL, t##_sources, t##_sources_n}