Compare commits

..
2 Commits
Author SHA1 Message Date
owendswang 3696468611 Fixed display for available spacee of interal M.2 2026-07-18 09:58:05 +08:00
owendswang 777bb4abd4 Release v0.8 2026-07-14 18:49:24 +08:00
8 changed files with 534 additions and 81 deletions

No files matched your search

+2 -1
View File
@@ -3,4 +3,5 @@ gen/
web-file-mgr-linux
*.o
*.d
tools/
tools/
test/
+1 -1
View File
@@ -13,7 +13,7 @@ ifeq ($(MAKECMDGOALS),)
endif
endif
VERSION_TAG := v0.7
VERSION_TAG := v0.8b
TITLE_ID := FMGR88888
PYTHON ?= python3
STRIP ?= $(PS5_PAYLOAD_SDK)/bin/prospero-strip
+6 -2
View File
@@ -9,8 +9,12 @@
<body>
<main class="shell">
<header class="topbar">
<div id="path" class="path">/</div>
<div id="spaceInfo" class="space-info"></div>
<div class="top-left">
<div id="path" class="path">/</div>
</div>
<div class="top-right">
<div id="spaceInfo" class="space-info"></div>
</div>
<button id="exitBtn" class="icon-button power-button" type="button" aria-label="Exit"></button>
</header>
+2
View File
@@ -91,6 +91,8 @@ window.WFM_LANG = {
storageUsb: "USB",
storageM2: "M.2",
storageExtended: "Extended",
storageRoot: "Root",
storageCurrent: "Current",
err_target_dir_not_writable: "Target folder is not writable: {path}",
err_target_file_not_writable: "Target file is not writable: {path}",
err_target_parent_not_writable: "Current folder is not writable: {path}",
+2
View File
@@ -91,6 +91,8 @@ window.WFM_LANG = {
storageUsb: "USB存储",
storageM2: "M2扩充存储",
storageExtended: "扩展存储",
storageRoot: "根分区",
storageCurrent: "当前目录",
err_target_dir_not_writable: "目标目录不可写: {path}",
err_target_file_not_writable: "目标文件不可写: {path}",
err_target_parent_not_writable: "当前目录不可写: {path}",
+270 -35
View File
@@ -43,23 +43,48 @@ body {
}
.topbar {
position: relative;
flex: 0 0 auto;
height: 54px;
height: auto;
min-height: 54px;
display: flex;
align-items: center;
padding: 0 30px;
align-items: start;
flex-wrap: wrap;
justify-content: space-between;
padding: 0 18px;
background: #1b2026;
border-bottom: 1px solid #2d343d;
overflow: hidden;
overflow: visible;
}
.top-left,
.top-right {
height: auto;
display: flex;
align-items: center;
}
.top-left {
flex: 1 1 420px;
min-width: 0;
margin-top: 15px;
}
.top-right {
flex: 1 1 360px;
min-width: 0;
margin-left: auto;
justify-content: flex-end;
flex-wrap: wrap;
padding: 12px 40px 12px 0;
text-align: right;
}
.path {
height: 54px;
color: #aab4be;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 22px;
line-height: 54px;
line-height: 24px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -68,21 +93,25 @@ body {
}
.space-info {
height: 54px;
height: auto;
min-height: 0;
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
flex: 0 1 auto;
max-width: 58vw;
flex: 1 1 auto;
max-width: none;
color: #c9d3dc;
font-size: 18px;
line-height: 54px;
line-height: 30px;
white-space: nowrap;
overflow: hidden;
overflow: visible;
text-align: right;
}
.space-item {
margin-left: 14px;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
@@ -103,8 +132,6 @@ body {
width: 24px;
height: 24px;
padding: 0;
flex: 0 0 24px;
margin-left: 16px;
}
.power-button {
@@ -134,6 +161,13 @@ body {
transform: translateX(-50%);
}
#exitBtn {
position: absolute;
top: 15px;
right: 18px;
margin-left: 0;
}
.toolbar,
.tool-left,
.tool-right {
@@ -144,13 +178,13 @@ body {
.toolbar {
flex: 0 0 auto;
height: 84px;
height: auto;
min-height: 84px;
justify-content: space-between;
padding: 14px 30px;
padding: 15px 18px 0;
background: #15191e;
border-bottom: 1px solid #2d343d;
overflow: hidden;
overflow: visible;
}
.tool-left {
@@ -160,14 +194,13 @@ body {
.toolbar .tool-left,
.toolbar .tool-right {
height: 54px;
min-height: 54px;
flex-wrap: nowrap;
flex-wrap: wrap;
}
.tool-left > *,
.tool-right > * {
margin: 0 12px 0 0;
margin: 0 12px 15px 0;
}
.tool-right > * {
@@ -177,7 +210,7 @@ body {
.tool-right {
justify-content: flex-end;
flex: 0 0 auto;
flex: 1 1 auto;
}
button,
@@ -283,6 +316,7 @@ button.primary:hover {
table {
width: 100%;
min-width: 1034px;
border-collapse: separate;
border-spacing: 0;
table-layout: fixed;
@@ -297,6 +331,7 @@ td {
overflow: hidden;
text-overflow: ellipsis;
font-size: 20px;
line-height: 24px;
}
th {
@@ -314,7 +349,7 @@ th {
}
.name-col {
min-width: 360px;
min-width: 600px;
}
td.name-col {
@@ -351,6 +386,7 @@ tr {
}
thead tr {
height: 78px;
cursor: default;
}
@@ -368,6 +404,7 @@ tr[data-type="parent"] {
}
.select-hit {
height: 78px;
min-height: 78px;
width: 100%;
display: flex;
@@ -422,10 +459,11 @@ input[type="checkbox"] {
.status {
color: #aab4be;
padding: 10px 25px;
}
.status {
flex: 0 0 auto;
height: 46px;
min-height: 46px;
padding: 0 25px;
line-height: 46px;
border-top: 1px solid #2d343d;
background: #15191e;
font-size: 18px;
@@ -718,18 +756,215 @@ input[type="checkbox"] {
background: #8a929b;
}
@media (max-width: 900px) {
.topbar {
align-items: stretch;
flex-direction: column;
@media (max-width: 815px) {
.top-left {
padding: 0 40px 0 0;
}
th:nth-child(4),
td:nth-child(4),
th:nth-child(5),
td:nth-child(5),
th:nth-child(6),
td:nth-child(6) {
.top-right {
padding: 10px 0;
}
}
@media (max-width: 640px) {
body {
font-size: 16px;
}
.topbar {
min-height: 42px;
padding: 0 10px;
align-items: center;
flex-wrap: nowrap;
}
.top-left {
flex: 1 1 auto;
margin-top: 0;
padding-right: 34px;
}
.top-right {
display: none;
}
.path {
font-size: 16px;
line-height: 42px;
}
#exitBtn {
top: 9px;
right: 10px;
}
.toolbar {
min-height: 0;
padding: 8px 8px 0;
}
.toolbar .tool-left,
.toolbar .tool-right {
min-height: 0;
}
.tool-left,
.tool-right {
flex: 1 1 100%;
}
.tool-right {
justify-content: flex-start;
}
.tool-left > *,
.tool-right > * {
margin: 0 8px 8px 0;
}
.tool-right > * {
margin-left: 0;
}
button,
.button {
height: 40px;
min-height: 40px;
min-width: 76px;
padding: 0 12px;
border-radius: 6px;
font-size: 16px;
}
.paste-action {
max-width: 100%;
}
.paste-name {
max-width: 120px;
}
table {
min-width: 960px;
}
.name-col {
width: 600px;
}
th,
td {
padding: 10px 12px;
font-size: 16px;
line-height: 20px;
}
tr,
thead tr,
.select-hit,
.row-action {
height: 56px;
min-height: 56px;
}
.row-action {
padding: 10px 12px;
}
input[type="checkbox"] {
width: 28px;
height: 28px;
}
.icon {
width: 26px;
height: 26px;
flex-basis: 26px;
}
.status {
height: 34px;
min-height: 34px;
padding: 0 12px;
line-height: 34px;
font-size: 14px;
}
.task-overlay {
padding: 18px;
align-items: center;
}
.task-panel {
width: 100%;
max-width: none;
padding: 18px;
border-radius: 8px;
}
.task > * + * {
margin-top: 12px;
}
.task-head {
display: block;
font-size: 20px;
}
.task-name {
display: flex;
max-width: none;
white-space: nowrap;
}
.task-name > * {
margin: 0 8px 0 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-name-subject {
flex: 1 1 auto;
max-width: none;
min-width: 0;
}
.task-elapsed {
margin: 8px 0 0;
text-align: left;
font-size: 18px;
}
.task-current {
font-size: 18px;
white-space: normal;
word-break: break-all;
}
.task-meta {
display: block;
font-size: 18px;
}
.task-meta div,
.task-meta div:nth-child(2),
.task-meta div:nth-child(3) {
display: block;
margin: 0 0 8px;
text-align: left;
white-space: normal;
}
.task-meta div:last-child {
margin-bottom: 0;
}
.progress {
height: 32px;
}
.task .danger {
width: 100%;
}
}
+100 -19
View File
@@ -22,7 +22,7 @@ let textEditorOriginal = "";
let textEditorBusy = false;
let L = {};
const APP_VERSION = "v0.7";
const APP_VERSION = "v0.8b";
const LAST_PATH_KEY = "ps5-web-file-mgr:last-path";
const LOADING_DISPLAY_DELAY = 250;
@@ -129,6 +129,81 @@ async function api(path, params, options) {
return data;
}
function decodeFsText(text) {
text = String(text || "");
let needsDecode = false;
for (let i = 0; i < text.length; i++) {
const code = text.charCodeAt(i);
if (code > 0xff) return text;
if (code >= 0x80) needsDecode = true;
}
if (!needsDecode || typeof TextDecoder === "undefined" || typeof Uint8Array === "undefined") return text;
const bytes = new Uint8Array(text.length);
for (let i = 0; i < text.length; i++) bytes[i] = text.charCodeAt(i) & 0xff;
if (isUtf8Bytes(bytes)) {
try {
return new TextDecoder("utf-8").decode(bytes);
} catch (err) {
return text;
}
}
try {
return new TextDecoder("gbk").decode(bytes);
} catch (err) {
try {
return new TextDecoder("gb18030").decode(bytes);
} catch (err2) {
return text;
}
}
}
function isUtf8Bytes(bytes) {
for (let i = 0; i < bytes.length;) {
const c = bytes[i];
let n = 0;
let cp = 0;
if (c < 0x80) {
i++;
continue;
}
if ((c & 0xe0) === 0xc0) {
n = 2;
cp = c & 0x1f;
} else if ((c & 0xf0) === 0xe0) {
n = 3;
cp = c & 0x0f;
} else if ((c & 0xf8) === 0xf0) {
n = 4;
cp = c & 0x07;
} else {
return false;
}
if (i + n > bytes.length) return false;
for (let j = 1; j < n; j++) {
const t = bytes[i + j];
if ((t & 0xc0) !== 0x80) return false;
cp = (cp << 6) | (t & 0x3f);
}
if ((n === 2 && cp < 0x80) ||
(n === 3 && cp < 0x800) ||
(n === 4 && (cp < 0x10000 || cp > 0x10ffff)) ||
(cp >= 0xd800 && cp <= 0xdfff)) return false;
i += n;
}
return true;
}
function displayName(item) {
return item.displayName || decodeFsText(item.name);
}
function displayPath(path) {
return decodeFsText(path);
}
async function fetchText(path, params) {
const response = await request(path, params, { method: "GET" });
return {
@@ -356,8 +431,8 @@ function isPreviewableImage(item) {
function openImagePreview(item) {
if (busy) return;
imagePreviewNameEl.textContent = item.name;
imagePreviewNameEl.title = item.name;
imagePreviewNameEl.textContent = displayName(item);
imagePreviewNameEl.title = displayName(item);
imagePreviewEl.src = "/fs?path=" + encodeURIComponent(item.path);
imagePreviewOverlayEl.hidden = false;
imagePreviewCloseBtn.focus();
@@ -399,8 +474,8 @@ function requestCloseTextEditor() {
function showTextEditor(item, text, version) {
textEditorPath = item.path;
textEditorVersion = version;
textEditorPathEl.textContent = item.path;
textEditorPathEl.title = item.path;
textEditorPathEl.textContent = displayPath(item.path);
textEditorPathEl.title = displayPath(item.path);
textEditorEl.value = text;
textEditorOriginal = textEditorEl.value;
textEditorOverlayEl.hidden = false;
@@ -413,8 +488,8 @@ async function openTextEditor(item) {
textEditorPath = item.path;
textEditorVersion = null;
textEditorOriginal = "";
textEditorPathEl.textContent = item.path;
textEditorPathEl.title = item.path;
textEditorPathEl.textContent = displayPath(item.path);
textEditorPathEl.title = displayPath(item.path);
textEditorEl.value = "";
textEditorOverlayEl.hidden = false;
setTextEditorBusy(true, t("loadingText"));
@@ -498,13 +573,13 @@ function clipboardTitle() {
function itemTitle(items) {
if (!items.length) return "";
return items.length === 1 ? items[0].name : t("selectedItems", { name: items[0].name, count: items.length });
return items.length === 1 ? displayName(items[0]) : t("selectedItems", { name: displayName(items[0]), count: items.length });
}
function pathName(path) {
const clean = String(path || "").replace(/\/+$/, "");
const pos = clean.lastIndexOf("/");
return pos >= 0 ? clean.slice(pos + 1) || "/" : clean;
return displayPath(pos >= 0 ? clean.slice(pos + 1) || "/" : clean);
}
function taskSubject(task) {
@@ -675,8 +750,8 @@ function render() {
nameBtn.type = "button";
const nameText = document.createElement("span");
nameText.className = "name-text";
nameText.textContent = item.name;
nameBtn.title = item.path;
nameText.textContent = displayName(item);
nameBtn.title = displayPath(item.path);
const iconImg = document.createElement("img");
iconImg.className = "icon";
iconImg.src = isParent ? "/icon-up.png" : item.type === "d" ? "/icon-folder.png" :
@@ -736,13 +811,13 @@ async function load(path, scrollTop, force) {
setStatus(t("readDir"));
const data = await api("/api/list", { path });
cwd = data.path;
pathEl.textContent = cwd;
pathEl.textContent = displayPath(cwd);
savePath(cwd);
refreshSpaces();
entries = data.entries.sort((a, b) => {
if (a.type === "d" && b.type !== "d") return -1;
if (a.type !== "d" && b.type === "d") return 1;
return a.name.localeCompare(b.name);
return displayName(a).localeCompare(displayName(b));
});
selected.clear();
focusedPath = null;
@@ -853,7 +928,7 @@ function validatePasteTarget() {
function conflictText(names) {
if (!names.length) return "";
const shown = names.slice(0, 4).join(", ");
const shown = names.slice(0, 4).map(decodeFsText).join(", ");
return names.length > 4 ? t("selectedItems", { name: shown, count: names.length }) : shown;
}
@@ -906,6 +981,7 @@ function showPendingOverlay(text) {
}
function renderTaskPath(element, path) {
path = displayPath(path);
element.title = path;
element.textContent = path;
if (element.scrollWidth <= element.clientWidth) return;
@@ -1025,7 +1101,7 @@ function renderTasks(tasks) {
current.className += " task-current-plain";
current.textContent = task.error;
}
else current.textContent = task.current || task.src;
else current.textContent = displayPath(task.current || task.src);
const meta = document.createElement("div");
meta.className = "task-meta";
@@ -1129,8 +1205,8 @@ function actionRename() {
if (busy || loadingPath) return;
const item = singleSelected();
if (!item) return;
const name = (prompt(t("renamePrompt"), item.name) || "").trim();
if (!name || name === item.name) return;
const name = (prompt(t("renamePrompt"), displayName(item)) || "").trim();
if (!name || name === displayName(item)) return;
runImmediateAction(t("rename"), () => api("/api/rename", { path: item.path, name }));
}
@@ -1198,7 +1274,12 @@ filesEl.addEventListener("click", event => {
let target = event.target;
while (target && target !== row && !target.classList.contains("row-action")) target = target.parentNode;
if (target === row && row.dataset.type !== "parent") return;
const item = { path: row.dataset.path, name: row.dataset.name, type: row.dataset.type };
const item = {
path: row.dataset.path,
name: row.dataset.name,
type: row.dataset.type,
displayName: decodeFsText(row.dataset.name)
};
focusPath(item.path);
if (item.type === "parent" || item.type === "d") load(item.path);
else if (isPreviewableImage(item)) openImagePreview(item);
@@ -1228,7 +1309,7 @@ async function init() {
applyStaticText();
const savedPath = readSavedPath();
cwd = savedPath;
pathEl.textContent = cwd;
pathEl.textContent = displayPath(cwd);
taskRefreshPath = savedPath;
refreshSpaces();
await pollTasks();
+151 -23
View File
@@ -102,6 +102,7 @@ typedef struct task_completion {
time_t elapsed;
} task_completion_t;
static char *fs_path_value(char *path);
#if FILEMGR_PIPELINE_COPY
typedef struct copy_pipeline_slot {
char *data;
@@ -271,7 +272,7 @@ parse_paths(const char *raw, char ***out_paths, size_t *out_count) {
return -1;
}
paths = tmp;
if(!(paths[count] = strdup(line))) {
if(!(paths[count] = fs_path_value(strdup(line)))) {
free_paths(paths, count);
free(copy);
errno = ENOMEM;
@@ -576,27 +577,122 @@ task_finish_bytes(file_task_t *task, const char *current) {
pthread_mutex_unlock(&g_tasks_lock);
}
static int
utf8_decode_char(const char **ps, unsigned int *out) {
const unsigned char *s = (const unsigned char *)*ps;
unsigned char c = s[0];
unsigned int cp;
size_t n;
size_t i;
if(c < 0x80) {
*out = c;
*ps += 1;
return 0;
}
if((c & 0xe0) == 0xc0) {
cp = c & 0x1f;
n = 2;
} else if((c & 0xf0) == 0xe0) {
cp = c & 0x0f;
n = 3;
} else if((c & 0xf8) == 0xf0) {
cp = c & 0x07;
n = 4;
} else {
return -1;
}
for(i = 1; i < n; i++) {
unsigned char t = s[i];
if(!t || (t & 0xc0) != 0x80) {
return -1;
}
cp = (cp << 6) | (t & 0x3f);
}
if((n == 2 && cp < 0x80) ||
(n == 3 && cp < 0x800) ||
(n == 4 && (cp < 0x10000 || cp > 0x10ffff)) ||
(cp >= 0xd800 && cp <= 0xdfff)) {
return -1;
}
*out = cp;
*ps += n;
return 0;
}
static char *
fs_path_value(char *path) {
const char *p;
char *out;
size_t len;
size_t pos = 0;
int has_byte_token = 0;
if(!path) {
return path;
}
for(p = path; *p;) {
unsigned int cp;
const char *next = p;
if(utf8_decode_char(&next, &cp)) {
return path;
}
if(cp >= 0x80 && cp <= 0xff) {
has_byte_token = 1;
} else if(cp > 0xff) {
return path;
}
p = next;
}
if(!has_byte_token) {
return path;
}
len = strlen(path);
if(!(out = malloc(len + 1))) {
return path;
}
for(p = path; *p;) {
unsigned int cp;
const char *next = p;
if(utf8_decode_char(&next, &cp)) {
free(out);
return path;
}
out[pos++] = (char)(unsigned char)cp;
p = next;
}
out[pos] = 0;
free(path);
return out;
}
static int
json_escape(strbuf_t *b, const char *s) {
if(strbuf_append(b, "\"")) return -1;
for(; *s; s++) {
while(*s) {
unsigned char c = (unsigned char)*s;
switch(c) {
case '"': if(strbuf_append(b, "\\\"")) return -1; break;
case '\\': if(strbuf_append(b, "\\\\")) return -1; break;
case '\b': if(strbuf_append(b, "\\b")) return -1; break;
case '\f': if(strbuf_append(b, "\\f")) return -1; break;
case '\n': if(strbuf_append(b, "\\n")) return -1; break;
case '\r': if(strbuf_append(b, "\\r")) return -1; break;
case '\t': if(strbuf_append(b, "\\t")) return -1; break;
case '"': if(strbuf_append(b, "\\\"")) return -1; s++; break;
case '\\': if(strbuf_append(b, "\\\\")) return -1; s++; break;
case '\b': if(strbuf_append(b, "\\b")) return -1; s++; break;
case '\f': if(strbuf_append(b, "\\f")) return -1; s++; break;
case '\n': if(strbuf_append(b, "\\n")) return -1; s++; break;
case '\r': if(strbuf_append(b, "\\r")) return -1; s++; break;
case '\t': if(strbuf_append(b, "\\t")) return -1; s++; break;
default:
if(c < 0x20) {
if(c < 0x20 || c >= 0x80) {
if(strbuf_printf(b, "\\u%04x", c)) return -1;
} else {
if(strbuf_reserve(b, 1)) return -1;
b->data[b->len++] = (char)c;
b->data[b->len] = 0;
}
s++;
break;
}
}
@@ -2443,6 +2539,7 @@ vfs_bytes(fsblkcnt_t blocks, unsigned long block_size) {
return (unsigned long long)blocks * (unsigned long long)block_size;
}
#ifdef __SCE__
static int
path_is_mounted(const char *path, const struct stat *st) {
char parent[PATH_MAX];
@@ -2456,6 +2553,7 @@ path_is_mounted(const char *path, const struct stat *st) {
}
return st->st_dev != parent_st.st_dev;
}
#endif
static int
task_target_path(file_task_t *task, const char *src, char *out, size_t size) {
@@ -2737,7 +2835,7 @@ create_task_response(struct MHD_Connection *conn, task_op_t op,
static enum MHD_Result
api_list(struct MHD_Connection *conn) {
char *path = query_value(conn, "path");
char *path = fs_path_value(query_value(conn, "path"));
DIR *dir;
struct dirent *entry;
struct stat st;
@@ -2852,6 +2950,7 @@ api_tasks(struct MHD_Connection *conn) {
static enum MHD_Result
api_space(struct MHD_Connection *conn) {
#ifdef __SCE__
static const struct {
const char *label_key;
const char *path;
@@ -2865,14 +2964,16 @@ api_space(struct MHD_Connection *conn) {
{"storageUsb", "/mnt/usb5"},
{"storageUsb", "/mnt/usb6"},
{"storageUsb", "/mnt/usb7"},
{"storageM2", "/mnt/ex1"},
{"storageM2", "/mnt/ext1"},
{"storageExtended", "/mnt/ext0"},
};
char *current = query_value(conn, "path");
#endif
char *current = fs_path_value(query_value(conn, "path"));
strbuf_t b = {0};
int first = 1;
strbuf_append(&b, "{\"ok\":true,\"spaces\":[");
#ifdef __SCE__
for(size_t i = 0; i < sizeof(mounts) / sizeof(mounts[0]); i++) {
struct statvfs vfs;
struct stat st;
@@ -2909,6 +3010,33 @@ api_space(struct MHD_Connection *conn) {
strbuf_printf(&b, ",\"free\":%llu,\"total\":%llu,\"current\":%s}",
free_bytes, total_bytes, is_current ? "true" : "false");
}
#else
const char *paths[] = {"/", current && strcmp(current, "/") ? current : NULL};
const char *labels[] = {"storageRoot", "storageCurrent"};
for(size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
struct statvfs vfs;
unsigned long block_size;
unsigned long long free_bytes;
unsigned long long total_bytes;
if(!paths[i] || statvfs(paths[i], &vfs)) {
continue;
}
block_size = vfs.f_frsize ? vfs.f_frsize : vfs.f_bsize;
free_bytes = vfs_bytes(vfs.f_bavail, block_size);
total_bytes = vfs_bytes(vfs.f_blocks, block_size);
if(!first) {
strbuf_append(&b, ",");
}
first = 0;
strbuf_append(&b, "{\"label_key\":");
json_escape(&b, labels[i]);
strbuf_append(&b, ",\"path\":");
json_escape(&b, paths[i]);
strbuf_printf(&b, ",\"free\":%llu,\"total\":%llu,\"current\":%s}",
free_bytes, total_bytes, i ? "true" : "false");
}
#endif
free(current);
strbuf_append(&b, "]}");
return send_buffer(conn, MHD_HTTP_OK, b.data, "application/json");
@@ -2958,7 +3086,7 @@ api_exit(struct MHD_Connection *conn) {
static enum MHD_Result
api_copy(struct MHD_Connection *conn) {
char *paths_raw = query_value(conn, "paths");
char *dst = query_value(conn, "dst");
char *dst = fs_path_value(query_value(conn, "dst"));
char *overwrite = query_value(conn, "overwrite");
char **paths = NULL;
size_t count = 0;
@@ -2985,7 +3113,7 @@ api_copy(struct MHD_Connection *conn) {
static enum MHD_Result
api_move(struct MHD_Connection *conn) {
char *paths_raw = query_value(conn, "paths");
char *dst = query_value(conn, "dst");
char *dst = fs_path_value(query_value(conn, "dst"));
char *overwrite = query_value(conn, "overwrite");
char **paths = NULL;
size_t count = 0;
@@ -3025,8 +3153,8 @@ api_delete(struct MHD_Connection *conn) {
static enum MHD_Result
api_rename(struct MHD_Connection *conn) {
char *path = query_value(conn, "path");
char *name = query_value(conn, "name");
char *path = fs_path_value(query_value(conn, "path"));
char *name = fs_path_value(query_value(conn, "name"));
char parent[PATH_MAX];
char target[PATH_MAX];
int ret;
@@ -3053,8 +3181,8 @@ api_rename(struct MHD_Connection *conn) {
static enum MHD_Result
api_mkdir(struct MHD_Connection *conn) {
char *path = query_value(conn, "path");
char *name = query_value(conn, "name");
char *path = fs_path_value(query_value(conn, "path"));
char *name = fs_path_value(query_value(conn, "name"));
char target[PATH_MAX];
int ret;
@@ -3081,7 +3209,7 @@ api_mkdir(struct MHD_Connection *conn) {
static enum MHD_Result
api_text(struct MHD_Connection *conn) {
char *path = query_value(conn, "path");
char *path = fs_path_value(query_value(conn, "path"));
char *data;
size_t size;
struct stat st;
@@ -3118,8 +3246,8 @@ api_text(struct MHD_Connection *conn) {
static enum MHD_Result
api_text_create(struct MHD_Connection *conn) {
char *path = query_value(conn, "path");
char *name = query_value(conn, "name");
char *path = fs_path_value(query_value(conn, "path"));
char *name = fs_path_value(query_value(conn, "name"));
char target[PATH_MAX];
int fd = -1;
int ret = -1;
@@ -3213,7 +3341,7 @@ done:
static enum MHD_Result
api_text_save(struct MHD_Connection *conn, const char *body,
size_t body_size) {
char *path = query_value(conn, "path");
char *path = fs_path_value(query_value(conn, "path"));
char *expected = query_value(conn, "version");
char *current = NULL;
size_t current_size = 0;