// Name and filename sanitizing helpers. string normalize_name_whitespace(string name) { string result = ""; bool lastWasSpace = true; for (uint charIndex = 0; charIndex < name.length(); charIndex++) { string ch = name.substr(charIndex, 1); bool isSpace = (ch == " " || ch == "\t" || ch == "\r" || ch == "\n"); if (isSpace) { if (!lastWasSpace) { result += " "; lastWasSpace = true; } continue; } result += ch; lastWasSpace = false; } if (result.length() > 0 && result.substr(result.length() - 1) == " ") { result = result.substr(0, result.length() - 1); } return result; } bool is_windows_reserved_filename(const string&in upperName) { if (upperName == "CON" || upperName == "PRN" || upperName == "AUX" || upperName == "NUL") return true; if (upperName.length() == 4 && upperName.substr(0, 3) == "COM") { int num = parse_int(upperName.substr(3)); if (num >= 1 && num <= 9) return true; } if (upperName.length() == 4 && upperName.substr(0, 3) == "LPT") { int num = parse_int(upperName.substr(3)); if (num >= 1 && num <= 9) return true; } return false; } string sanitize_filename_base(string name, const int maxLength = 40, const string fallback = "item") { string normalized = normalize_name_whitespace(name); string result = ""; bool lastSeparator = false; for (uint charIndex = 0; charIndex < normalized.length(); charIndex++) { string ch = normalized.substr(charIndex, 1); bool isUpper = (ch >= "A" && ch <= "Z"); bool isLower = (ch >= "a" && ch <= "z"); bool isDigit = (ch >= "0" && ch <= "9"); if (isUpper || isLower || isDigit) { result += ch; lastSeparator = false; continue; } if (ch == " " || ch == "_" || ch == "-") { if (!lastSeparator && result.length() > 0) { result += "_"; lastSeparator = true; } } } while (result.length() > 0 && result.substr(result.length() - 1) == "_") { result = result.substr(0, result.length() - 1); } if (result.length() == 0) { result = fallback; } if (result.length() > maxLength) { result = result.substr(0, maxLength); } while (result.length() > 0 && result.substr(result.length() - 1) == "_") { result = result.substr(0, result.length() - 1); } string upperName = result.upper(); if (upperName == "." || upperName == "..") { result = fallback; upperName = result.upper(); } if (is_windows_reserved_filename(upperName)) { result = "file_" + result; } return result; } string build_filename_from_name(const string&in name, const string&in extension = ".dat", const int maxBaseLength = 40, const string fallback = "item") { return sanitize_filename_base(name, maxBaseLength, fallback) + extension; }