mirror of
https://github.com/RubyMetric/chsrc
synced 2026-10-26 01:48:31 +08:00
复用xy提供的解析函数,简化uv配置写入与换行处理
This commit is contained in:
1 parent
3be7fa8078
commit
e8acba12a2
2 files changed
+59
-190
No files matched your search
@@ -409,56 +409,11 @@ uvh_find_managed_index (const char *content, const char *index_header)
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测文件内容是否使用 CRLF 换行。
|
||||
*
|
||||
* 以第一个换行符前是否有 '\r' 判断; 混合换行风格时结果取决于首个换行。
|
||||
*
|
||||
* @param content 文件内容
|
||||
*
|
||||
* @return 使用 CRLF 时返回 true, 否则返回 false
|
||||
*/
|
||||
static bool
|
||||
uvh_detect_crlf (const char *content)
|
||||
{
|
||||
const char *nl = strchr (content, '\n');
|
||||
return nl && nl > content && nl[-1] == '\r';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按 content 的换行风格转换 str 中的 LF。
|
||||
*
|
||||
* content 为 CRLF 时返回 CRLF 版本, 否则原样返回。仅用于追加的纯 LF 文本,
|
||||
* 不要传入已含 CRLF 的 content 本身。
|
||||
*
|
||||
* @param content 用于判断换行风格的内容
|
||||
* @param str 纯 LF 的待转换文本
|
||||
*
|
||||
* @return malloc 的 caller-free 转换后字符串
|
||||
*
|
||||
* @memory SAFE
|
||||
* return caller-free
|
||||
*/
|
||||
static char *
|
||||
uvh_apply_eol (const char *content, const char *str)
|
||||
{
|
||||
bool crlf = uvh_detect_crlf (content);
|
||||
size_t n = strlen (str);
|
||||
char *ret = malloc (n + (crlf ? n : 0) + 1);
|
||||
size_t pos = 0;
|
||||
for (const char *s = str; *s; s++)
|
||||
{
|
||||
if (crlf && *s == '\n') ret[pos++] = '\r';
|
||||
ret[pos++] = *s;
|
||||
}
|
||||
ret[pos] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 用 new_line_text 替换 content 中从 line 开始的整行。
|
||||
*
|
||||
* 保留原行缩进、行尾风格 (CRLF/LF) 与后续内容; new_line_text 不含换行。
|
||||
* 保留原行缩进与后续内容; new_line_text 不含换行。中间改写统一使用 LF,
|
||||
* 行尾由调用方在最终写盘前按系统平台转换 (Windows 为 CRLF, 其余 LF)。
|
||||
*
|
||||
* @param content 原始内容
|
||||
* @param line content 中待替换行的行首指针
|
||||
@@ -475,9 +430,6 @@ uvh_replace_line (const char *content, const char *line, const char *new_line_te
|
||||
const char *line_end = strchr (line, '\n');
|
||||
if (!line_end) line_end = content + strlen (content);
|
||||
|
||||
/* 原行为 CRLF 时,新行同样使用 CRLF */
|
||||
bool crlf = (*line_end == '\n' && line_end > line && line_end[-1] == '\r');
|
||||
|
||||
/* 复用原行缩进 */
|
||||
const char *indent_end = uvh_skip_indent (line);
|
||||
size_t ind = indent_end - line;
|
||||
@@ -488,7 +440,6 @@ uvh_replace_line (const char *content, const char *line, const char *new_line_te
|
||||
pos += snprintf (ret + pos, len - pos, "%.*s", (int)(line - content), content);
|
||||
pos += snprintf (ret + pos, len - pos, "%.*s", (int)ind, line);
|
||||
pos += snprintf (ret + pos, len - pos, "%s", new_line_text);
|
||||
if (crlf) ret[pos++] = '\r';
|
||||
strcpy (ret + pos, line_end);
|
||||
return ret;
|
||||
}
|
||||
@@ -496,8 +447,8 @@ uvh_replace_line (const char *content, const char *line, const char *new_line_te
|
||||
/**
|
||||
* @brief 在 insert_at 位置之前插入 insert_text (以 '\n' 结尾)。
|
||||
*
|
||||
* 内容为 CRLF 时, 插入文本与分隔换行同样转换为 CRLF; insert_at 之前
|
||||
* 没有换行时会先补一个分隔换行。
|
||||
* 中间改写统一使用 LF, 行尾由调用方在最终写盘前按系统平台转换;
|
||||
* insert_at 之前没有换行时会先补一个分隔换行。
|
||||
*
|
||||
* @param content 原始内容
|
||||
* @param insert_at content 内的插入位置指针
|
||||
@@ -514,29 +465,33 @@ uvh_insert_before (const char *content, const char *insert_at, const char *inser
|
||||
size_t head = insert_at - content;
|
||||
|
||||
bool need_sep = (head > 0 && content[head - 1] != '\n');
|
||||
bool crlf = need_sep ? uvh_detect_crlf (content)
|
||||
: (head >= 2 && content[head - 1] == '\n' && content[head - 2] == '\r');
|
||||
|
||||
size_t text_len = strlen (insert_text);
|
||||
size_t extra = (crlf ? text_len : 0) + (need_sep ? 1 : 0);
|
||||
size_t len = strlen (content) + text_len + extra + 16;
|
||||
size_t len = strlen (content) + text_len + (need_sep ? 1 : 0) + 16;
|
||||
char *ret = calloc (len, 1);
|
||||
size_t pos = 0;
|
||||
pos += snprintf (ret + pos, len - pos, "%.*s", (int)head, content);
|
||||
if (need_sep)
|
||||
{
|
||||
if (crlf) ret[pos++] = '\r';
|
||||
ret[pos++] = '\n';
|
||||
}
|
||||
for (const char *t = insert_text; *t; t++)
|
||||
{
|
||||
if (crlf && *t == '\n') ret[pos++] = '\r';
|
||||
ret[pos++] = *t;
|
||||
}
|
||||
memcpy (ret + pos, insert_text, text_len);
|
||||
pos += text_len;
|
||||
strcpy (ret + pos, content + head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 以 LF 分隔,将完整 TOML 片段追加到文件末尾。
|
||||
*
|
||||
* @return malloc 的 caller-free 新内容
|
||||
*/
|
||||
static char *
|
||||
uvh_append_segment (const char *content, const char *segment)
|
||||
{
|
||||
return uvh_insert_before (content, content + strlen (content), segment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 替换/创建 index 数组表 (如 [[index]] 或 [[tool.uv.index]]) 的 url 值。
|
||||
@@ -567,23 +522,15 @@ replace_index_url (const char *content, const char *url, const char *index_heade
|
||||
if (!table)
|
||||
{
|
||||
/* 父表不存在: 在文件末尾创建父表并在其中创建 index 段,
|
||||
* 换行风格与 content 保持一致 */
|
||||
size_t clen = strlen (content);
|
||||
const char *sep = (clen > 0) ? "\n" : "";
|
||||
* 中间改写统一 LF, 行尾由最终写入前按系统平台转换 */
|
||||
char *escaped_url = uvh_escape_basic_string (url);
|
||||
size_t seg_len = strlen (parent_table) + strlen (index_header) + strlen (escaped_url) + 48;
|
||||
char *seg = calloc (seg_len, 1);
|
||||
snprintf (seg, seg_len, "%s\n%s\nurl = \"%s\"\ndefault = true\n",
|
||||
parent_table, index_header, escaped_url);
|
||||
free (escaped_url);
|
||||
char *seg_eol = uvh_apply_eol (content, seg);
|
||||
char *ret = uvh_append_segment (content, seg);
|
||||
free (seg);
|
||||
char *sep_eol = uvh_apply_eol (content, sep);
|
||||
size_t len = clen + strlen (sep_eol) + strlen (seg_eol) + 1;
|
||||
char *ret = calloc (len, 1);
|
||||
snprintf (ret, len, "%s%s%s", content, sep_eol, seg_eol);
|
||||
free (sep_eol);
|
||||
free (seg_eol);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -663,22 +610,14 @@ replace_key_value (const char *content, const char *key, const char *url, const
|
||||
if (!table)
|
||||
{
|
||||
/* 父表不存在: 在文件末尾创建父表并写入键,
|
||||
* 换行风格与 content 保持一致 */
|
||||
size_t clen = strlen (content);
|
||||
const char *sep = (clen > 0) ? "\n" : "";
|
||||
* 中间改写统一 LF, 行尾由最终写入前按系统平台转换 */
|
||||
char *escaped_url = uvh_escape_basic_string (url);
|
||||
size_t seg_len = strlen (parent_table) + strlen (key) + strlen (escaped_url) + 16;
|
||||
char *seg = calloc (seg_len, 1);
|
||||
snprintf (seg, seg_len, "%s\n%s = \"%s\"\n", parent_table, key, escaped_url);
|
||||
free (escaped_url);
|
||||
char *seg_eol = uvh_apply_eol (content, seg);
|
||||
char *ret = uvh_append_segment (content, seg);
|
||||
free (seg);
|
||||
char *sep_eol = uvh_apply_eol (content, sep);
|
||||
size_t len = clen + strlen (sep_eol) + strlen (seg_eol) + 1;
|
||||
char *ret = calloc (len, 1);
|
||||
snprintf (ret, len, "%s%s%s", content, sep_eol, seg_eol);
|
||||
free (sep_eol);
|
||||
free (seg_eol);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -776,7 +715,6 @@ uvh_get_value_in_table (const char *content, const char *key, const char *parent
|
||||
*
|
||||
* @param content 文件内容
|
||||
* @param index_header 段头文本, 如 "[[index]]" 或 "[[tool.uv.index]]"
|
||||
* @param parent_table 父表头, 如 NULL (顶层) 或 "[tool.uv]"
|
||||
*
|
||||
* @return malloc 的 caller-free 解码后 URL; 未找到返回 NULL
|
||||
*
|
||||
@@ -784,10 +722,9 @@ uvh_get_value_in_table (const char *content, const char *key, const char *parent
|
||||
* return caller-free
|
||||
*/
|
||||
static char *
|
||||
uvh_get_index_url (const char *content, const char *index_header, const char *parent_table)
|
||||
uvh_get_index_url (const char *content, const char *index_header)
|
||||
{
|
||||
/* fully-qualified [[tool.uv.index]] 会隐式创建父路径,不要求显式 [tool.uv]。 */
|
||||
(void)parent_table;
|
||||
const char *ih = uvh_find_managed_index (content, index_header);
|
||||
if (!ih) return NULL;
|
||||
|
||||
@@ -797,57 +734,3 @@ uvh_get_index_url (const char *content, const char *index_header, const char *pa
|
||||
if (!url_line) return NULL;
|
||||
return uvh_extract_string_value (url_line);
|
||||
}
|
||||
|
||||
|
||||
/* ============ uv.toml 便捷包装 (保持原函数名) ============ */
|
||||
|
||||
/**
|
||||
* @brief uv.toml 便捷包装: 替换/创建顶层 [[index]] 段的 url。
|
||||
*
|
||||
* @param content 原始内容
|
||||
* @param url 新的索引 URL
|
||||
*
|
||||
* @return malloc 的 caller-free 新内容
|
||||
*
|
||||
* @memory SAFE
|
||||
* return caller-free
|
||||
*/
|
||||
static char *
|
||||
replace_pypi_index_url (const char *content, const char *url)
|
||||
{
|
||||
return replace_index_url (content, url, "[[index]]", NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief uv.toml 便捷包装: 替换/创建顶层 python-install-mirror 键。
|
||||
*
|
||||
* @param content 原始内容
|
||||
* @param url 新的 Python 下载镜像基址
|
||||
*
|
||||
* @return malloc 的 caller-free 新内容
|
||||
*
|
||||
* @memory SAFE
|
||||
* return caller-free
|
||||
*/
|
||||
static char *
|
||||
replace_python_install_mirror (const char *content, const char *url)
|
||||
{
|
||||
return replace_key_value (content, "python-install-mirror", url, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief uv.toml 便捷包装: 提取顶层键的字符串值。
|
||||
*
|
||||
* @param content 文件内容
|
||||
* @param key 键名
|
||||
*
|
||||
* @return malloc 的 caller-free 解码后字符串; 未找到返回 NULL
|
||||
*
|
||||
* @memory SAFE
|
||||
* return caller-free
|
||||
*/
|
||||
static char *
|
||||
uvh_get_top_level_value (const char *content, const char *key)
|
||||
{
|
||||
return uvh_get_value_in_table (content, key, NULL);
|
||||
}
|
||||
@@ -84,38 +84,19 @@ pl_python_uv_read_config (const char *path)
|
||||
{
|
||||
if (!xy_file_exist (path)) return xy_strdup ("");
|
||||
|
||||
char *normalized = xy_normalize_path (path);
|
||||
FILE *fp = fopen (normalized, "rb");
|
||||
free (normalized);
|
||||
if (!fp || fseek (fp, 0, SEEK_END) != 0)
|
||||
char *file = xy_normalize_path (path);
|
||||
FILE *f = fopen (file, "rb");
|
||||
if (!f)
|
||||
{
|
||||
if (fp) fclose (fp);
|
||||
chsrc_error2 ("无法读取 uv 配置文件");
|
||||
free (file);
|
||||
return NULL;
|
||||
}
|
||||
fclose (f);
|
||||
|
||||
long size = ftell (fp);
|
||||
if (size < 0)
|
||||
{
|
||||
fclose (fp);
|
||||
chsrc_error2 ("无法读取 uv 配置文件");
|
||||
return NULL;
|
||||
}
|
||||
rewind (fp);
|
||||
|
||||
char *raw = xy_malloc0 ((size_t)size + 1);
|
||||
size_t nread = fread (raw, 1, (size_t)size, fp);
|
||||
bool failed = ferror (fp) != 0;
|
||||
fclose (fp);
|
||||
if (failed)
|
||||
{
|
||||
free (raw);
|
||||
chsrc_error2 ("无法读取 uv 配置文件");
|
||||
return NULL;
|
||||
}
|
||||
raw[nread] = '\0';
|
||||
|
||||
return raw;
|
||||
/* xy_file_read 会把 CRLF 与孤立 CR 统一为 LF。 */
|
||||
char *content = xy_file_read (file);
|
||||
free (file);
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +184,7 @@ pl_python_uv_getsrc (char *option)
|
||||
const char *index_header = pyproject ? "[[tool.uv.index]]" : "[[index]]";
|
||||
const char *parent_table = pyproject ? "[tool.uv]" : NULL;
|
||||
|
||||
char *url = uvh_get_index_url (content, index_header, parent_table);
|
||||
char *url = uvh_get_index_url (content, index_header);
|
||||
if (url)
|
||||
{
|
||||
say (url);
|
||||
@@ -219,9 +200,7 @@ pl_python_uv_getsrc (char *option)
|
||||
say (default_source.url);
|
||||
}
|
||||
|
||||
char *mirror = pyproject
|
||||
? uvh_get_value_in_table (content, "python-install-mirror", parent_table)
|
||||
: uvh_get_top_level_value (content, "python-install-mirror");
|
||||
char *mirror = uvh_get_value_in_table (content, "python-install-mirror", parent_table);
|
||||
if (mirror)
|
||||
{
|
||||
say (mirror);
|
||||
@@ -294,31 +273,30 @@ pl_uv_github_release_prelude (void)
|
||||
/**
|
||||
* 一次性完成uv配置文件的全部文件操作 (set 路径)
|
||||
*/
|
||||
static void
|
||||
static bool
|
||||
pl_python_uv_write_all (const char *uv_config, const char *pypi_url, const char *py_dl_url)
|
||||
{
|
||||
char *content = pl_python_uv_read_config (uv_config);
|
||||
if (!content) return;
|
||||
|
||||
char *final = NULL;
|
||||
if (xy_str_end_with (uv_config, PL_Python_uv_PyprojectConfigFile))
|
||||
if (!content)
|
||||
{
|
||||
// `pyproject.toml`: 配置位于 `[tool.uv]` 表内
|
||||
char *updated = replace_index_url (content, pypi_url, "[[tool.uv.index]]", "[tool.uv]");
|
||||
final = replace_key_value (updated, "python-install-mirror", py_dl_url, "[tool.uv]");
|
||||
free (updated);
|
||||
}
|
||||
else
|
||||
{
|
||||
// `uv.toml`: 配置位于顶层
|
||||
char *updated = replace_pypi_index_url (content, pypi_url);
|
||||
final = replace_python_install_mirror (updated, py_dl_url);
|
||||
free (updated);
|
||||
chsrc_error2 ("无法读取 uv 配置文件");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pyproject = xy_str_end_with (uv_config, PL_Python_uv_PyprojectConfigFile);
|
||||
const char *index_header = pyproject ? "[[tool.uv.index]]" : "[[index]]";
|
||||
const char *parent_table = pyproject ? "[tool.uv]" : NULL;
|
||||
|
||||
char *updated = replace_index_url (content, pypi_url, index_header, parent_table);
|
||||
char *final = replace_key_value (updated, "python-install-mirror", py_dl_url, parent_table);
|
||||
free (updated);
|
||||
|
||||
/* final 仍为纯 LF。框架最终以文本模式写入:Windows 写盘时转换为 CRLF,
|
||||
* 其他平台保持 LF。 */
|
||||
chsrc_overwrite_file (final, uv_config);
|
||||
free (final);
|
||||
free (content);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -360,7 +338,11 @@ pl_python_uv_setsrc (char *option)
|
||||
Source_t default_gh = pl_python_uv_yield_target_source (&pl_uv_github_release_target, "upstream");
|
||||
|
||||
chsrc_backup (uv_config);
|
||||
pl_python_uv_write_all (uv_config, default_pypi.url, default_gh.url);
|
||||
if (!pl_python_uv_write_all (uv_config, default_pypi.url, default_gh.url))
|
||||
{
|
||||
free (uv_config);
|
||||
return;
|
||||
}
|
||||
free (uv_config);
|
||||
|
||||
return;
|
||||
@@ -405,7 +387,11 @@ pl_python_uv_setsrc (char *option)
|
||||
chsrc_confirm_source (&source);
|
||||
|
||||
chsrc_backup (uv_config);
|
||||
pl_python_uv_write_all (uv_config, source.url, gh_source.url);
|
||||
if (!pl_python_uv_write_all (uv_config, source.url, gh_source.url))
|
||||
{
|
||||
free (uv_config);
|
||||
return;
|
||||
}
|
||||
free (uv_config);
|
||||
|
||||
if (chsrc_in_standalone_mode())
|
||||
|
||||
Reference in new issue
Block a user