forked from RubyMetric/chsrc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcore.c
More file actions
1812 lines (1568 loc) · 49.4 KB
/
Copy pathcore.c
File metadata and controls
1812 lines (1568 loc) · 49.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/** ------------------------------------------------------------
* SPDX-License-Identifier: GPL-3.0-or-later
* -------------------------------------------------------------
* File Name : core.c
* File Authors : Aoran Zeng <ccmywish@qq.com>
* | Heng Guo <2085471348@qq.com>
* Contributors : Peng Gao <gn3po4g@outlook.com>
* | Happy Game <happygame10124@gmail.com>
* | Yangmoooo <yangmoooo@outlook.com>
* |
* Created On : <2023-08-29>
* Last Modified : <2025-08-10>
*
* chsrc framework
* ------------------------------------------------------------*/
#if defined(__STDC__) && __STDC_VERSION__ < 201112L
# error "chsrc requires C11 or later, please use a new compiler which at least supports C11"
#endif
#if defined(__STDC__) && __STDC_VERSION__ < 201710L
# warning "chsrc recommends a C17 or later compiler"
#endif
#include "xy.h"
#include "struct.h"
#include "mirror.c"
#include "chef-helper.c"
#define App_Name "chsrc"
static int chsrc_get_cpucore ();
/* Global Program Mode */
struct
{
// 用户命令
bool MeasureMode;
bool ResetMode;
// 内部实现
bool TargetGroupMode;
// 用户命令选项
bool Ipv6Mode;
bool LocalMode;
bool EnglishMode;
bool DryRunMode;
bool NoColorMode;
}
ProgMode =
{
.MeasureMode = false,
.ResetMode = false,
.TargetGroupMode = false,
.Ipv6Mode = false,
.LocalMode = false,
.EnglishMode = false,
.DryRunMode = false,
.NoColorMode = false
};
/* recipe 相关 mode */
bool chsrc_in_target_group_mode() {return ProgMode.TargetGroupMode;}
// 并非作为 follower target,而是自身作为一个独立的 target 执行
bool chsrc_in_standalone_mode() {return !ProgMode.TargetGroupMode;}
void chsrc_set_target_group_mode(){ProgMode.TargetGroupMode = true;}
bool chsrc_in_reset_mode(){return ProgMode.ResetMode;}
bool chsrc_in_local_mode(){return ProgMode.LocalMode;}
bool chsrc_in_english_mode(){return ProgMode.EnglishMode;}
bool chsrc_in_no_color_mode(){return ProgMode.NoColorMode;}
/* Convenience */
#define ENGLISH chsrc_in_english_mode()
#define CHINESE !chsrc_in_english_mode()
/* 仅 framework 相关 mode */
static bool in_measure_mode(){return ProgMode.MeasureMode;}
static bool in_ipv6_mode(){return ProgMode.Ipv6Mode;}
static bool in_dry_run_mode(){return ProgMode.DryRunMode;}
static bool in_custom_user_agent_mode(){return false;}
/**
* Target Group mode (相反则称为 standalone mode)
*
* 1. 一个 target group 包含了多个 target,这些都被叫做 follower target
* 2. 触发该运行模式的 target 被称为 leader target,其往往只是一个virtual target,类似 APT 中的 virtual package
*
* 目前使用该模式的有两个: Python 和 Node.js,因为二者的包管理器存在多个
*/
/**
* -local 的含义是启用 *项目级* 换源
*
* 每个 target 对 [-local] 的支持情况可使用 | chsrc ls <target> | 来查看
*
*
* 1. 默认不使用该选项时,含义是 *全局* 换源,
*
* 全局分为 (1)系统级 (2)用户级
*
* 大多数第三方配置软件往往默认进行的是 *用户级* 的配置。所以 chsrc 首先将尝试使用 *用户级* 配置
*
* 2. 若不存在 *用户级* 的配置,chsrc 将采用 *系统级* 的配置
*
* 3. 最终效果本质由第三方软件决定,如 poetry 默认实现的就是项目级的换源
*/
typedef enum ChgType_t
{
ChgType_Auto,
ChgType_Reset,
ChgType_SemiAuto,
ChgType_Manual,
ChgType_Untested
} ChgType_t;
/* Global Program Status */
struct
{
int leader_selected_index; /* leader target 选中的索引 */
ChgType_t chgtype; /* 换源实现的类型 */
/* 此时 chsrc_run() 不再是recipe中指定要运行的一个外部命令,而是作为一个功能实现的支撑 */
bool chsrc_run_faas;
}
ProgStatus =
{
.leader_selected_index = -1,
.chgtype = ChgType_Auto,
.chsrc_run_faas = false
};
#define Exit_OK 0
#define Exit_Fatal 1
#define Exit_Unknown 2
#define Exit_Unsupported 3
#define Exit_UserCause 4
#define Exit_MaintainerCause 5
#define Exit_ExternalError 6
#define chsrc_log(str) xy_log(App_Name,str)
#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)
#ifdef XY_DEBUG
#define chsrc_debug(dom,str) xy_warn(App_Name "(DEBUG " dom ")",str)
#else
#define chsrc_debug(dom,str)
#endif
#define chsrc_verbose(str) xy_info(App_Name "(VERBOSE)",str)
#define faint(str) xy_str_to_faint(str)
#define red(str) xy_str_to_red(str)
#define blue(str) xy_str_to_blue(str)
#define green(str) xy_str_to_green(str)
#define yellow(str) xy_str_to_yellow(str)
#define purple(str) xy_str_to_purple(str)
#define bold(str) xy_str_to_bold(str)
#define bdred(str) xy_str_to_bold(xy_str_to_red(str))
#define bdblue(str) xy_str_to_bold(xy_str_to_blue(str))
#define bdgreen(str) xy_str_to_bold(xy_str_to_green(str))
#define bdyellow(str) xy_str_to_bold(xy_str_to_yellow(str))
#define bdpurple(str) xy_str_to_bold(xy_str_to_purple(str))
/* 2系列都是带有括号的 */
#define chsrc_succ2(str) xy_succ_brkt(App_Name,ENGLISH?"SUCCEED":"成功",str)
#define chsrc_log2(str) xy_info_brkt(App_Name,"LOG",str)
#define chsrc_warn2(str) xy_warn_brkt(App_Name,ENGLISH?"WARN":"警告",str)
#define chsrc_error2(str) xy_error_brkt(App_Name,ENGLISH?"ERROR":"错误",str)
#ifdef XY_DEBUG
#define chsrc_debug2(dom,str) xy_warn_brkt(App_Name,"DEBUG " dom,str)
#else
#define chsrc_debug2(dom,str)
#endif
#define chsrc_verbose2(str) xy_info_brkt(App_Name,"VERBOSE",str)
/**
* @note 输出在 stdout 中
*/
void
chsrc_note2 (const char *str)
{
char *msg = ENGLISH ? "NOTE" : "提示";
xy_log_brkt (blue(App_Name), bdblue(msg), blue(str));
}
/**
* @note 输出在 stdout 中
*/
void
chsrc_alert2 (const char *str)
{
char *msg = ENGLISH ? "ALERT" : "提醒";
xy_log_brkt (yellow(App_Name), bdyellow(msg), yellow(str));
}
void
chsrc_log_write (const char *filename)
{
char *msg = ENGLISH ? "WRITE" : "写入";
xy_log_brkt (blue(App_Name), bdblue(msg), blue(filename));
}
void
chsrc_log_backup (const char *filename)
{
char *msg = ENGLISH ? "BACKUP" : "备份";
char *bak = xy_2strjoin (filename, ".bak");
xy_log_brkt (blue(App_Name), bdblue(msg), xy_strjoin (3, bdyellow(filename), " -> ", bdgreen(bak)));
}
#define YesMark "✓"
#define NoMark "x"
#define HalfYesMark "⍻"
static void
log_check_result (const char *check_what, const char *check_type, bool exist)
{
char *chk_msg = NULL;
char *not_exist_msg = NULL;
char *exist_msg = NULL;
if (ENGLISH)
{
chk_msg = "CHECK";
not_exist_msg = " doesn't exist";
exist_msg = " exists";
}
else
{
chk_msg = "检查";
not_exist_msg = " 不存在";
exist_msg = " 存在";
}
if (!exist)
{
xy_log_brkt (App_Name, bdred (chk_msg), xy_strjoin (5,
red (NoMark " "), check_type, " ", red (check_what), not_exist_msg));
}
else
{
xy_log_brkt (App_Name, bdgreen (chk_msg), xy_strjoin (5,
green (YesMark " "), check_type, " ", green (check_what), exist_msg));
}
}
static void
log_cmd_result (bool result, int exit_status)
{
char *run_msg = NULL;
char *succ_msg = NULL;
char *fail_msg = NULL;
if (ENGLISH)
{
run_msg = "RUN";
succ_msg = YesMark " executed successfully";
fail_msg = NoMark " executed unsuccessfully, exit status: ";
}
else
{
run_msg = "运行";
succ_msg = YesMark " 命令执行成功";
fail_msg = NoMark " 命令执行失败,退出状态: ";
}
if (result)
xy_log_brkt (green (App_Name), bdgreen (run_msg), green (succ_msg));
else
{
char buf[8] = {0};
sprintf (buf, "%d", exit_status);
char *log = xy_2strjoin (red (fail_msg), bdred (buf));
xy_log_brkt (red (App_Name), bdred (run_msg), log);
}
}
#define Quiet_When_Exist 0x00
#define Noisy_When_Exist 0x01
#define Quiet_When_NonExist 0x00
#define Noisy_When_NonExist 0x10
/**
* 检测二进制程序是否存在
*
* @param check_cmd 检测 @param:prog_name 是否存在的一段命令,一般来说,填 @param:prog_name 本身即可,
* 但是某些情况下,需要使用其他命令绕过一些特殊情况,比如 python 这个命令在Windows上
* 会自动打开 Microsoft Store,需避免
*
* @param prog_name 要检测的二进制程序名
*
*/
bool
query_program_exist (char *check_cmd, char *prog_name, int mode)
{
char *which = check_cmd;
int status = system (which);
// char buf[32] = {0}; sprintf(buf, "错误码: %d", status);
char *msg = ENGLISH ? "command" : "命令";
if (0 != status)
{
if (mode & Noisy_When_NonExist)
{
// xy_warn (xy_strjoin(4, "× 命令 ", progname, " 不存在,", buf));
log_check_result (prog_name, msg, false);
}
return false;
}
else
{
if (mode & Noisy_When_Exist)
log_check_result (prog_name, msg, true);
return true;
}
}
/**
* @brief 生成用于 '检测一个程序是否存在' 的命令,该内部函数由 chsrc_check_program() 家族调用
*
* 检查一个程序是否存在时,我们曾经使用 "调用 程序名 --version" 的方式 (即 cmd_to_check_program2()),
* 但是该方式有三个问题:
*
* 1. 该程序得到直接执行,可能不太安全 (虽然基本不可能)
* 2. 有一些程序启动速度太慢,即使只调用 --version,也依旧会花费许多时间,比如 mvn
* 3. 有些程序并不支持 --version 选项 (虽然基本不可能)
*
* @note Unix 中,where 仅在 zsh 中可以使用,sh 和 Bash 中均无法使用,因为其并非二进制程序
* 所以在 Unix 中,只能使用 which 或 whereis
*/
static char *
cmd_to_check_program (char *prog_name)
{
char *check_tool = xy_on_windows ? "where " : "which ";
char *quiet_cmd = xy_str_to_quietcmd (xy_2strjoin (check_tool, prog_name));
return quiet_cmd;
}
XY_Deprecate_This("Use cmd_to_check_program() instead")
static char *
cmd_to_check_program2 (char *prog_name)
{
char *quiet_cmd = xy_str_to_quietcmd (xy_2strjoin (prog_name, " --version"));
return quiet_cmd;
}
/**
* @brief 检测程序是否存在
*
* @note
* 1. 一般只在 recipe 中使用,显式检测每一个需要用到的 program
* 2. 无论存在与否,*均输出检测信息*
*
*/
bool
chsrc_check_program (char *prog_name)
{
return query_program_exist (cmd_to_check_program(prog_name), prog_name, Noisy_When_Exist|Noisy_When_NonExist);
}
/**
* @brief 检测程序是否存在
*
* @note
* 1. 此函数没有强制性,只返回检查结果
* 2. 无论存在与否,*均不输出检测信息*
*/
bool
chsrc_check_program_quietly (char *prog_name)
{
return query_program_exist (cmd_to_check_program(prog_name), prog_name, Quiet_When_Exist|Quiet_When_NonExist);
}
/**
* @brief 检测程序是否存在
*
* @note 存在时不输出检测信息,不存在时才输出检测信息
*
*/
bool
chsrc_check_program_quietly_when_exist (char *prog_name)
{
return query_program_exist (cmd_to_check_program(prog_name), prog_name, Quiet_When_Exist|Noisy_When_NonExist);
}
/**
* @brief 确保程序一定存在
*
* @note
* 1. 此函数具有强制性,检测不到就直接退出
* 2. 存在时不输出检测信息,不存在时才输出检测信息
*
*/
void
chsrc_ensure_program (char *prog_name)
{
bool exist = query_program_exist (cmd_to_check_program(prog_name), prog_name, Quiet_When_Exist|Noisy_When_NonExist);
if (exist)
{
// OK, nothing should be done
}
else
{
char *msg1 = ENGLISH ? "not found " : "未找到 ";
char *msg2 = ENGLISH ? " command, please check for existence" : " 命令,请检查是否存在";
chsrc_error (xy_strjoin (3, msg1, prog_name, msg2));
exit (Exit_UserCause);
}
}
bool
chsrc_check_file (char *path)
{
char *msg = ENGLISH ? "file" : "文件";
if (xy_file_exist (path))
{
log_check_result (path, msg, true);
return true;
}
else
{
log_check_result (path, msg, false);
return false;
}
}
/**
* 用于 _setsrc 函数,检测用户输入的镜像站code,是否存在于该target可用源中
*
* @note 一个源Source必定来自于一个Provider,所以该函数名叫 query_mirror_exist
*
* @param target_name 目标名
* @param input 如果用户输入 default 或者 def,则选择第一个源
*/
int
query_mirror_exist (Source_t *sources, size_t size, char *target_name, char *input)
{
if (chef_is_url (input))
{
char *msg = ENGLISH ? "Using user-defined sources for this software is not supported at this time, please contact the developers to ask why or request support" : "暂不支持对该软件使用用户自定义源,请联系开发者询问原因或请求支持";
chsrc_error (msg);
exit (Exit_Unsupported);
}
if (0==size)
{
char *msg1 = ENGLISH ? "Currently " : "当前 ";
char *msg2 = ENGLISH ? " doesn't have any source available. Please contact the maintainers" : " 无任何可用源,请联系维护者";
chsrc_error (xy_strjoin (3, msg1, target_name, msg2));
exit (Exit_MaintainerCause);
}
if (1==size)
{
char *msg1 = ENGLISH ? "Currently " : "当前 ";
char *msg2 = ENGLISH ? " only the upstream source exists. Please contact the maintainers" : " 仅存在上游默认源,请联系维护者";
chsrc_error (xy_strjoin (3, msg1, target_name, msg2));
exit (Exit_MaintainerCause);
}
/* if (xy_streql ("reset", input)) 不再使用这种方式 */
if (chsrc_in_reset_mode())
{
char *msg = ENGLISH ? "Will reset to the upstream's default source" : "将重置为上游默认源";
say (msg);
return 0; /* 返回第1个,因为第1个是上游默认源 */
}
if (2==size)
{
char *msg1 = ENGLISH ? " is " : " 是 ";
char *msg2 = ENGLISH ? "'s ONLY mirror available currently, thanks for their generous support"
: " 目前唯一可用镜像站,感谢他们的慷慨支持";
const char *name = ENGLISH ? sources[1].mirror->abbr
: sources[1].mirror->name;
chsrc_succ (xy_strjoin (4, name, msg1, target_name, msg2));
}
if (xy_streql ("first", input))
{
char *msg = ENGLISH ? "Will use the first speedy source measured by maintainers" : "将使用维护团队测速第一的源";
say (msg);
return 1; /* 返回第2个,因为第1个是上游默认源 */
}
int idx = 0;
Source_t src = sources[0];
bool exist = false;
for (int i=0; i<size; i++)
{
src = sources[i];
if (xy_streql (src.mirror->code, input))
{
idx = i;
exist = true;
break;
}
}
if (!exist)
{
{
char *msg1 = ENGLISH ? "Mirror site " : "镜像站 ";
char *msg2 = ENGLISH ? " doesn't exist" : " 不存在";
chsrc_error (xy_strjoin (3, msg1, input, msg2));
}
char *msg = ENGLISH ? "To see available sources, use chsrc list " : "查看可使用源,请使用 chsrc list ";
chsrc_error (xy_2strjoin (msg, target_name));
exit (Exit_UserCause);
}
return idx;
}
/**
* 该函数来自 oh-my-mirrorz.py,由 @ccmywish 翻译为C语言,但功劳和版权属于原作者
*
* @param speed 单位为Byte/s
*/
char *
to_human_readable_speed (double speed)
{
char *scale[] = {"Byte/s", "KByte/s", "MByte/s", "GByte/s", "TByte/s"};
int i = 0;
while (speed > 1024.0)
{
i += 1;
speed /= 1024.0;
}
char *buf = xy_malloc0 (64);
sprintf (buf, "%.2f %s", speed, scale[i]);
char *new = NULL;
if (i <= 1 ) new = red (buf);
else
{
if (i == 2 && speed < 2.00) new = yellow (buf);
else new = green (buf);
}
return new;
}
/**
* 测速代码参考自 https://github.com/mirrorz-org/oh-my-mirrorz/blob/master/oh-my-mirrorz.py
* 功劳和版权属于原作者,由 @ccmywish 修改为C语言,并做了额外调整
*
* @return 返回测得的速度,若出错,返回-1
*
* 该函数实际原型为 char * (*)(const char*)
*/
void *
measure_speed_for_url (void *url)
{
char *time_sec = NULL;
time_sec = "8";
/**
* 现在我们切换至跳转后的链接来测速,不再使用下述判断
*
* if (xy_str_start_with(url, "https://registry.npmmirror"))
* {
* // 这里 npmmirror 跳转非常慢,需要1~3秒,所以我们给它留够至少8秒测速时间,否则非常不准
* time_sec = "10";
* }
*/
char *ipv6 = ""; // 默认不启用
if (in_ipv6_mode())
{
ipv6 = "--ipv6";
}
char *user_agent = NULL;
if (in_custom_user_agent_mode())
{
user_agent = strdup("maven/3.9.11");
}else{
user_agent = xy_2strjoin("chsrc/", Chsrc_Version);
}
char *os_devnull = xy_os_devnull;
/**
* @note 我们用 —L,因为部分链接会跳转到其他地方,比如: RubyChina, npmmirror
*/
char *curl_cmd = xy_strjoin (10, "curl -qsL ", ipv6,
" -o ", os_devnull,
" -w \"%{http_code} %{speed_download}\" -m", time_sec,
" -A ", user_agent, " ", url);
// chsrc_info (xy_2strjoin ("测速命令 ", curl_cmd));
char *curl_buf = xy_run (curl_cmd, 0);
free(user_agent);
return curl_buf;
}
/**
* @return 返回速度speed,单位为 Byte/s
*/
double
parse_and_say_curl_result (char *curl_buf)
{
// 分隔两部分数据
char *split = strchr (curl_buf, ' ');
if (split) *split = '\0';
// say(curl_buf); say(split+1);
int http_code = atoi (curl_buf);
double speed = atof (split+1);
char *speedstr = to_human_readable_speed (speed);
if (200!=http_code)
{
char *http_code_str = yellow (xy_2strjoin ("HTTP码 ", curl_buf));
say (xy_strjoin (3, speedstr, " | ", http_code_str));
}
else
{
say (speedstr);
}
return speed;
}
int
get_max_ele_idx_in_dbl_ary (double *array, int size)
{
double maxval = array[0];
int maxidx = 0;
for (int i=1; i<size; i++)
{
if (array[i]>maxval)
{
maxval = array[i];
maxidx = i;
}
}
return maxidx;
}
/**
* @param sources 所有待测源
* @param size 待测源的数量
* @param[out] speed_records 速度值记录,单位为Byte/s
*/
void
measure_speed_for_every_source (Source_t sources[], int size, double speed_records[])
{
// bool get_measured[size]; /* 是否真正执行了测速 */
int get_measured_n = 0; /* 测速了几个 */
char *measure_msgs[size];
double speed = 0.0;
for (int i=0; i<size; i++)
{
Source_t src = sources[i];
SourceProvider_t *provider = src.provider;
ProviderSpeedMeasureInfo_t psmi = provider->psmi;
bool provider_skip = psmi.skip;
bool has_dedicated_speed_url = false;
/**
* 存在两类测速链接
* 1. 有*专用测速链接*时,我们选专用,这是精准测速
* 2. 若无,我们用*镜像站整体测速链接*来进行代替,
* 若是专用镜像站,则是精准测速
* 若是通用镜像站,则是模糊测速
*/
const char *provider_speed_url = psmi.url;
const char *dedicated_speed_url = src.speed_measure_url;
/* 最终用来测速的 URL */
char *url = NULL;
if (!provider_skip && !provider_speed_url)
/* 没有声明跳过,但是却没有提供 URL,这是维护者维护时出了纰漏,我们软处理 */
{
char *msg1 = ENGLISH ? "Maintainers don't offer " : "维护者未提供 ";
char *msg2 = ENGLISH ? " mirror site's speed measure link, so skip it" : " 镜像站测速链接,跳过该站点(需修复)";
chsrc_warn (xy_strjoin (3, msg1, provider->code, msg2));
speed = 0;
speed_records[i] = speed;
// get_measured[i] = false;
measure_msgs[i] = NULL;
}
else if (!provider_skip && provider_speed_url)
{
if (chef_is_url (provider_speed_url))
{
url = xy_strdup (provider_speed_url);
chsrc_debug ("m", xy_2strjoin ("使用镜像站整体测速链接: ", url));
}
}
else if (provider_skip)
{
/* Provider 被声明为跳过测速,下方判断精准测速链接有无提供,若也没有提供,将会输出跳过原因 */
}
if (dedicated_speed_url)
{
if (chef_is_url (dedicated_speed_url))
{
url = xy_strdup (dedicated_speed_url);
has_dedicated_speed_url = true;
chsrc_debug ("m", xy_2strjoin ("使用专用测速链接: ", url));
}
else
{
/* 防止维护者没填,这里有一些脏数据,我们软处理:假装该链接URL不存在 */
has_dedicated_speed_url = false;
chsrc_debug ("m", xy_2strjoin ("专用测速链接为脏数据,请修复: ", provider->name));
}
}
if (provider_skip && !has_dedicated_speed_url)
{
if (xy_streql ("upstream", provider->code))
{
/* 上游源不测速,但不置0,因为要避免这么一种情况: 可能其他镜像站测速都为0,最后反而选择了该 upstream */
speed = -1024*1024*1024;
if (!src.url)
{
psmi.skip_reason_CN = "缺乏对上游默认源进行测速的URL,请帮助补充";
psmi.skip_reason_EN = "Lack of URL to measure upstream default source provider, please help to add";
}
}
else if (xy_streql ("user", provider->code))
{
/* 代码不会执行至此 */
speed = 1024*1024*1024;
}
else
{
/* 不测速的 Provider */
speed = 0;
}
// get_measured[i] = false;
speed_records[i] = speed;
const char *msg = ENGLISH ? provider->abbr : provider->name;
const char *skip_reason = ENGLISH ? psmi.skip_reason_EN : psmi.skip_reason_CN;
if (NULL==skip_reason)
{
skip_reason = ENGLISH ? "SKIP for no reason" : "无理由跳过";
}
measure_msgs[i] = xy_strjoin (4, faint(" x "), msg, " ", yellow(faint(skip_reason)));
println (measure_msgs[i]);
/* 下一位 */
continue;
}
/* 此时,一定获得了一个用于测速的链接 */
if (url)
{
const char *msg = ENGLISH ? provider->abbr : provider->name;
bool is_accurate = false;
if (has_dedicated_speed_url)
{
is_accurate = true;
}
else if (provider->psmi.accurate)
{
is_accurate = true;
}
char *accurate_msg = CHINESE ? (is_accurate ? bdblue(faint("[精准测速]")) : faint("[模糊测速]"))
: (is_accurate ? bdblue(faint("[accurate]")) : faint("[rough]"));
if (xy_streql ("upstream", provider->code))
{
measure_msgs[i] = xy_strjoin (7, faint(" ^ "), msg, " (", src.url, ") ", accurate_msg, faint(" ... "));
}
else
{
measure_msgs[i] = xy_strjoin (5, faint(" - "), msg, " ", accurate_msg, faint(" ... "));
}
print (measure_msgs[i]);
fflush (stdout);
char *curl_result = measure_speed_for_url (url);
double speed = parse_and_say_curl_result (curl_result);
speed_records[i] = speed;
}
else
{
xy_unreached();
}
}
}
/**
* 自动测速选择镜像站和源
*/
int
auto_select_mirror (Source_t *sources, size_t size, const char *target_name)
{
/* reset 时选择默认源 */
if (chsrc_in_reset_mode())
return 0;
if (!in_dry_run_mode())
{
char *msg = ENGLISH ? "Measuring speed in sequence" : "测速中";
xy_log_brkt (App_Name, bdpurple (ENGLISH ? "MEASURE" : "测速"), msg);
br();
}
if (0==size || 1==size)
{
char *msg1 = ENGLISH ? "Currently " : "当前 ";
char *msg2 = ENGLISH ? "No any source, please contact maintainers: chsrc issue" : " 无任何可用源,请联系维护者: chsrc issue";
chsrc_error (xy_strjoin (3, msg1, target_name, msg2));
exit (Exit_MaintainerCause);
}
if (in_dry_run_mode())
/* Dry Run 时,跳过测速 */
{
return 1; /* 原则第一个源 */
}
bool only_one = false;
if (2==size) only_one = true;
/** --------------------------------------------- */
bool exist_curl = chsrc_check_program_quietly_when_exist ("curl");
if (!exist_curl)
{
char *msg = ENGLISH ? "No curl, unable to measure speed" : "没有curl命令,无法测速";
chsrc_error (msg);
exit (Exit_UserCause);
}
if (xy_on_windows)
{
char *curl_version = xy_run ("curl --version", 1);
/**
* https://github.com/RubyMetric/chsrc/issues/144
*
* Cygwin上,curl 的版本信息为:
*
* curl 8.9.1 (x86_64-pc-cygwin)
*
*/
if (strstr (curl_version, "pc-cygwin"))
{
char *msg = ENGLISH ? "You're using curl built by Cygwin which has a bug! Please use another curl!" : "你使用的是Cygwin构建的curl,该版本的curl存在bug,请改用其他版本的curl";
chsrc_error (msg);
exit (Exit_UserCause);
}
}
/** --------------------------------------------- */
/* 总测速记录值 */
double speed_records[size];
measure_speed_for_every_source (sources, size, speed_records);
br();
/* DEBUG */
/*
for (int i=0; i<size; i++)
{
printf ("speed_records[%d] = %f\n", i, speed_records[i]);
}
*/
int fast_idx = get_max_ele_idx_in_dbl_ary (speed_records, size);
if (only_one)
{
char *msg1 = ENGLISH ? "NOTICE mirror site: " : "镜像站提示: ";
char *is = ENGLISH ? " is " : " 是 ";
char *msg2 = ENGLISH ? "'s ONLY mirror available currently, thanks for their generous support"
: " 目前唯一可用镜像站,感谢他们的慷慨支持";
const char *name = ENGLISH ? sources[fast_idx].mirror->abbr
: sources[fast_idx].mirror->name;
say (xy_strjoin (5, msg1, bdgreen(name), green(is), green(target_name), green(msg2)));
}
else
{
char *msg = ENGLISH ? "FASTEST mirror site: " : "最快镜像站: ";
const char *name = ENGLISH ? sources[fast_idx].mirror->abbr
: sources[fast_idx].mirror->name;
say (xy_2strjoin (msg, green(name)));
}
// https://github.com/RubyMetric/chsrc/pull/71
if (in_measure_mode())
{
char *msg = ENGLISH ? "URL of above source: " : "镜像源地址: ";
say (xy_2strjoin (msg, green(sources[fast_idx].url)));
}
return fast_idx;
}
int
use_specific_mirror_or_auto_select (char *input, Target_t *t)
{
if (input)
{
return query_mirror_exist (t->sources, t->sources_n, t->aliases, input);
}
else
{
return auto_select_mirror (t->sources, t->sources_n, t->aliases);
}
}
bool
source_is_upstream (Source_t *source)
{
return xy_streql (source->mirror->code, "upstream");
}
bool
source_is_userdefine (Source_t *source)
{
return xy_streql (source->mirror->code, "user");
}
bool
source_has_empty_url (Source_t *source)
{
return source->url == NULL;
}
/**
* @brief 为该 target 确定最终将使用的源
*
* 用户*只可能*通过下面5种方式来换源,无论哪一种都会返回一个 Source_t 出来
*
* 1. 用户指定了一个 Mirror Code,即 chsrc set <target> <code>
* 2. 用户指定了一个 URL, 即 chsrc set <target> https://ur
* 3. 用户什么都没指定, 即 chsrc set <target>
* 4. 用户正在重置源, 即 chsrc reset <target>
*
* 如果处于 Target Group 模式下,leader target 已经测速过了,follower target
* 不能再次测速,而是直接选择 leader 测过的结果
*
* 5. leader target 测速出来的某个源
*
*/
Source_t
chsrc_yield_source (Target_t *t, char *option)
{
Source_t source;
if (chsrc_in_target_group_mode() && ProgStatus.leader_selected_index==-1)
{