2019-10-09 21:35:13 +02:00
|
|
|
/* eslint no-useless-escape: 0*/
|
2019-07-30 09:35:42 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const { _ } = require('./locale');
|
2018-11-20 02:42:21 +02:00
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function dirname(path: string) {
|
2017-06-23 23:32:24 +02:00
|
|
|
if (!path) throw new Error('Path is empty');
|
2020-03-14 01:46:14 +02:00
|
|
|
const s = path.split(/\/|\\/);
|
2017-06-23 23:32:24 +02:00
|
|
|
s.pop();
|
|
|
|
return s.join('/');
|
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function basename(path: string) {
|
2017-06-23 20:51:02 +02:00
|
|
|
if (!path) throw new Error('Path is empty');
|
2020-03-14 01:46:14 +02:00
|
|
|
const s = path.split(/\/|\\/);
|
2017-06-23 20:51:02 +02:00
|
|
|
return s[s.length - 1];
|
|
|
|
}
|
|
|
|
|
2023-06-30 10:11:26 +02:00
|
|
|
export function filename(path: string, includeDir = false) {
|
2017-06-25 01:19:11 +02:00
|
|
|
if (!path) throw new Error('Path is empty');
|
2020-10-24 01:14:30 +02:00
|
|
|
const output = includeDir ? path : basename(path);
|
2017-06-25 01:19:11 +02:00
|
|
|
if (output.indexOf('.') < 0) return output;
|
|
|
|
|
2020-10-21 01:23:55 +02:00
|
|
|
const splitted = output.split('.');
|
|
|
|
splitted.pop();
|
|
|
|
return splitted.join('.');
|
2017-06-25 01:19:11 +02:00
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function fileExtension(path: string) {
|
2017-07-10 20:17:03 +02:00
|
|
|
if (!path) throw new Error('Path is empty');
|
|
|
|
|
2020-03-14 01:46:14 +02:00
|
|
|
const output = path.split('.');
|
2017-07-10 20:17:03 +02:00
|
|
|
if (output.length <= 1) return '';
|
|
|
|
return output[output.length - 1];
|
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function isHidden(path: string) {
|
2020-03-14 01:46:14 +02:00
|
|
|
const b = basename(path);
|
2019-09-19 23:51:18 +02:00
|
|
|
if (!b.length) throw new Error(`Path empty or not a valid path: ${path}`);
|
2017-06-23 20:51:02 +02:00
|
|
|
return b[0] === '.';
|
|
|
|
}
|
|
|
|
|
2023-12-24 13:24:48 +02:00
|
|
|
// Note that this function only sanitizes a file extension - it does NOT extract
|
|
|
|
// the file extension from a filename. So the way you'd normally call this is
|
|
|
|
// `safeFileExtension(fileExtension(filename))`
|
2020-11-12 21:13:28 +02:00
|
|
|
export function safeFileExtension(e: string, maxLength: number = null) {
|
2020-09-17 11:17:45 +02:00
|
|
|
// In theory the file extension can have any length but in practice Joplin
|
|
|
|
// expects a fixed length, so we limit it to 20 which should cover most cases.
|
|
|
|
// Note that it means that a file extension longer than 20 will break
|
|
|
|
// external editing (since the extension would be truncated).
|
|
|
|
// https://discourse.joplinapp.org/t/troubles-with-webarchive-files-on-ios/10447
|
|
|
|
if (maxLength === null) maxLength = 20;
|
2017-12-02 01:15:49 +02:00
|
|
|
if (!e || !e.replace) return '';
|
2019-06-12 10:45:31 +02:00
|
|
|
return e.replace(/[^a-zA-Z0-9]/g, '').substr(0, maxLength);
|
2017-12-02 01:15:49 +02:00
|
|
|
}
|
|
|
|
|
2023-06-30 10:11:26 +02:00
|
|
|
export function safeFilename(e: string, maxLength: number = null, allowSpaces = false) {
|
2018-09-04 12:59:09 +02:00
|
|
|
if (maxLength === null) maxLength = 32;
|
2018-05-23 15:25:59 +02:00
|
|
|
if (!e || !e.replace) return '';
|
2019-07-29 15:43:53 +02:00
|
|
|
const regex = allowSpaces ? /[^a-zA-Z0-9\-_\(\)\. ]/g : /[^a-zA-Z0-9\-_\(\)\.]/g;
|
2020-03-14 01:46:14 +02:00
|
|
|
const output = e.replace(regex, '_');
|
2018-05-23 15:25:59 +02:00
|
|
|
return output.substr(0, maxLength);
|
|
|
|
}
|
|
|
|
|
2021-08-10 20:13:16 +02:00
|
|
|
let friendlySafeFilename_blackListChars = '/\n\r<>:\'"\\|?*#';
|
2018-11-20 02:42:21 +02:00
|
|
|
for (let i = 0; i < 32; i++) {
|
|
|
|
friendlySafeFilename_blackListChars += String.fromCharCode(i);
|
|
|
|
}
|
|
|
|
|
2019-07-29 15:43:53 +02:00
|
|
|
const friendlySafeFilename_blackListNames = ['.', '..', 'CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'];
|
2018-11-20 02:42:21 +02:00
|
|
|
|
2021-08-18 00:29:46 +02:00
|
|
|
// The goal of this function is to provide a safe filename, that should work in
|
|
|
|
// any filesystem, but that's still user friendly, in particular because it
|
|
|
|
// supports any charset - Chinese, Russian, etc.
|
|
|
|
//
|
|
|
|
// "Safe" however doesn't mean it can be safely inserted in any content (HTML,
|
|
|
|
// Markdown, etc.) - it still needs to be encoded by the calling code according
|
|
|
|
// to the context.
|
|
|
|
|
2023-06-30 10:11:26 +02:00
|
|
|
export function friendlySafeFilename(e: string, maxLength: number = null, preserveExtension = false) {
|
2020-09-07 23:12:51 +02:00
|
|
|
// Although Windows supports paths up to 255 characters, but that includes the filename and its
|
|
|
|
// parent directory path. Also there's generally no good reason for dir or file names
|
|
|
|
// to be so long, so keep it at 50, which should prevent various errors.
|
|
|
|
if (maxLength === null) maxLength = 50;
|
2018-11-20 02:42:21 +02:00
|
|
|
if (!e || !e.replace) return _('Untitled');
|
2019-07-29 15:43:53 +02:00
|
|
|
|
2021-08-10 20:13:16 +02:00
|
|
|
let fileExt = '';
|
|
|
|
|
|
|
|
if (preserveExtension) {
|
2021-08-17 07:57:00 +02:00
|
|
|
const baseExt = fileExtension(e);
|
|
|
|
fileExt = baseExt ? `.${safeFileExtension(baseExt)}` : '';
|
2021-08-10 20:13:16 +02:00
|
|
|
e = filename(e);
|
|
|
|
}
|
|
|
|
|
2018-11-20 02:42:21 +02:00
|
|
|
let output = '';
|
|
|
|
for (let i = 0; i < e.length; i++) {
|
|
|
|
const c = e[i];
|
|
|
|
if (friendlySafeFilename_blackListChars.indexOf(c) >= 0) {
|
|
|
|
output += '_';
|
|
|
|
} else {
|
|
|
|
output += c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (output.length <= 4) {
|
|
|
|
if (friendlySafeFilename_blackListNames.indexOf(output.toUpperCase()) >= 0) {
|
|
|
|
output = '___';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (output.length) {
|
|
|
|
const c = output[output.length - 1];
|
|
|
|
if (c === ' ' || c === '.') {
|
|
|
|
output = output.substr(0, output.length - 1);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 02:36:23 +02:00
|
|
|
while (output.length) {
|
|
|
|
const c = output[0];
|
|
|
|
if (c === ' ') {
|
|
|
|
output = output.substr(1, output.length - 1);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-10 20:13:16 +02:00
|
|
|
if (!output) return _('Untitled') + fileExt;
|
2018-11-20 02:42:21 +02:00
|
|
|
|
2021-08-10 20:13:16 +02:00
|
|
|
return output.substr(0, maxLength) + fileExt;
|
2018-11-20 02:42:21 +02:00
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function toFileProtocolPath(filePathEncode: string, os: string = null) {
|
2019-07-29 12:16:47 +02:00
|
|
|
if (os === null) os = process.platform;
|
2019-07-29 15:43:53 +02:00
|
|
|
|
2019-07-29 12:16:47 +02:00
|
|
|
if (os === 'win32') {
|
|
|
|
filePathEncode = filePathEncode.replace(/\\/g, '/'); // replace backslash in windows pathname with slash e.g. c:\temp to c:/temp
|
2019-09-19 23:51:18 +02:00
|
|
|
filePathEncode = `/${filePathEncode}`; // put slash in front of path to comply with windows fileURL syntax
|
2019-07-29 12:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
filePathEncode = encodeURI(filePathEncode);
|
|
|
|
filePathEncode = filePathEncode.replace(/\+/g, '%2B'); // escape '+' with unicode
|
2019-09-19 23:51:18 +02:00
|
|
|
return `file://${filePathEncode.replace(/\'/g, '%27')}`; // escape '(single quote) with unicode, to prevent crashing the html view
|
2019-04-20 22:12:19 +02:00
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function toSystemSlashes(path: string, os: string = null) {
|
2018-05-14 19:46:04 +02:00
|
|
|
if (os === null) os = process.platform;
|
2019-07-29 15:43:53 +02:00
|
|
|
if (os === 'win32') return path.replace(/\//g, '\\');
|
|
|
|
return path.replace(/\\/g, '/');
|
2017-12-08 23:51:59 +02:00
|
|
|
}
|
|
|
|
|
2022-04-15 18:48:01 +02:00
|
|
|
export function toForwardSlashes(path: string) {
|
|
|
|
return toSystemSlashes(path, 'linux');
|
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function rtrimSlashes(path: string) {
|
2018-05-08 12:29:25 +02:00
|
|
|
return path.replace(/[\/\\]+$/, '');
|
2018-01-25 21:01:14 +02:00
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function ltrimSlashes(path: string) {
|
2018-01-25 21:01:14 +02:00
|
|
|
return path.replace(/^\/+/, '');
|
|
|
|
}
|
|
|
|
|
2021-02-01 12:48:37 +02:00
|
|
|
export function trimSlashes(path: string): string {
|
|
|
|
return ltrimSlashes(rtrimSlashes(path));
|
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function quotePath(path: string) {
|
2019-01-30 21:19:06 +02:00
|
|
|
if (!path) return '';
|
|
|
|
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
|
|
|
|
path = path.replace(/"/, '\\"');
|
2019-09-19 23:51:18 +02:00
|
|
|
return `"${path}"`;
|
2019-01-30 21:19:06 +02:00
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function unquotePath(path: string) {
|
2019-01-30 21:19:06 +02:00
|
|
|
if (!path.length) return '';
|
|
|
|
if (path.length && path[0] === '"') {
|
|
|
|
path = path.substr(1, path.length - 2);
|
|
|
|
}
|
|
|
|
path = path.replace(/\\"/, '"');
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export function extractExecutablePath(cmd: string) {
|
2019-01-30 21:19:06 +02:00
|
|
|
if (!cmd.length) return '';
|
|
|
|
|
2019-07-30 09:35:42 +02:00
|
|
|
const quoteType = ['"', '\''].indexOf(cmd[0]) >= 0 ? cmd[0] : '';
|
2019-01-30 21:19:06 +02:00
|
|
|
|
|
|
|
let output = '';
|
|
|
|
for (let i = 0; i < cmd.length; i++) {
|
|
|
|
const c = cmd[i];
|
|
|
|
if (quoteType) {
|
|
|
|
if (i > 0 && c === quoteType) {
|
|
|
|
output += c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (c === ' ') break;
|
|
|
|
}
|
|
|
|
|
|
|
|
output += c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|