复用xy提供的解析函数,简化uv配置写入与换行处理

This commit is contained in:
Mikachu2333 authored and 曾奥然 committed 2026-08-05 15:35:13 +08:00
1 parent 3be7fa8078
commit e8acba12a2
2 files changed
+59 -190

No files matched your search

+23 -140
View File
@@ -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);
}
+36 -50
View File
@@ -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())