shfs 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -655,7 +655,7 @@ function compileCat(cmd) {
|
|
|
655
655
|
/**
|
|
656
656
|
* cd command handler for the AST-based compiler.
|
|
657
657
|
*/
|
|
658
|
-
const ROOT_DIRECTORY$
|
|
658
|
+
const ROOT_DIRECTORY$5 = "/";
|
|
659
659
|
/**
|
|
660
660
|
* Compile a cd command from SimpleCommandIR to StepIR.
|
|
661
661
|
*/
|
|
@@ -664,7 +664,7 @@ function compileCd(cmd) {
|
|
|
664
664
|
if (positionalArgs.length > 1) throw new Error("cd accepts at most one path");
|
|
665
665
|
return {
|
|
666
666
|
cmd: "cd",
|
|
667
|
-
args: { path: positionalArgs[0] ?? literal(ROOT_DIRECTORY$
|
|
667
|
+
args: { path: positionalArgs[0] ?? literal(ROOT_DIRECTORY$5) }
|
|
668
668
|
};
|
|
669
669
|
}
|
|
670
670
|
/**
|
|
@@ -1179,8 +1179,8 @@ function parseGrepArgs(argv) {
|
|
|
1179
1179
|
options.excludeFiles = collectStringValues(parsed, argv, "exclude");
|
|
1180
1180
|
options.excludeDir = collectStringValues(parsed, argv, "excludeDir");
|
|
1181
1181
|
options.includeFiles = collectStringValues(parsed, argv, "include");
|
|
1182
|
-
const explicitPatterns = collectExpandedValues(parsed, argv, "pattern");
|
|
1183
|
-
const patternFiles = collectExpandedValues(parsed, argv, "file");
|
|
1182
|
+
const explicitPatterns = collectExpandedValues$1(parsed, argv, "pattern");
|
|
1183
|
+
const patternFiles = collectExpandedValues$1(parsed, argv, "file");
|
|
1184
1184
|
return {
|
|
1185
1185
|
diagnostics,
|
|
1186
1186
|
explicitPatterns,
|
|
@@ -1436,7 +1436,7 @@ function assignImplicitPattern(explicitPatterns, patternFiles, positionalOperand
|
|
|
1436
1436
|
function collectStringValues(parsed, argv, canonical) {
|
|
1437
1437
|
return getValueOccurrences(parsed, argv, canonical).map((occurrence) => occurrence.value);
|
|
1438
1438
|
}
|
|
1439
|
-
function collectExpandedValues(parsed, argv, canonical) {
|
|
1439
|
+
function collectExpandedValues$1(parsed, argv, canonical) {
|
|
1440
1440
|
const values = [];
|
|
1441
1441
|
for (const occurrence of getValueOccurrences(parsed, argv, canonical)) {
|
|
1442
1442
|
if (occurrence.source === "arg") {
|
|
@@ -1449,7 +1449,7 @@ function collectExpandedValues(parsed, argv, canonical) {
|
|
|
1449
1449
|
return values;
|
|
1450
1450
|
}
|
|
1451
1451
|
function getValueOccurrences(parsed, argv, canonical) {
|
|
1452
|
-
const values = normalizeValueList(parsed.flags[canonical]);
|
|
1452
|
+
const values = normalizeValueList$1(parsed.flags[canonical]);
|
|
1453
1453
|
const valueIndices = parsed.consumedValueIndices[canonical] ?? [];
|
|
1454
1454
|
const valueSources = parsed.consumedValueSources[canonical] ?? [];
|
|
1455
1455
|
const orders = parsed.flagOccurrenceOrder[canonical] ?? [];
|
|
@@ -1478,7 +1478,7 @@ function getValueOccurrences(parsed, argv, canonical) {
|
|
|
1478
1478
|
occurrences.sort((a, b) => a.order - b.order);
|
|
1479
1479
|
return occurrences;
|
|
1480
1480
|
}
|
|
1481
|
-
function normalizeValueList(value) {
|
|
1481
|
+
function normalizeValueList$1(value) {
|
|
1482
1482
|
if (typeof value === "string") return [value];
|
|
1483
1483
|
if (Array.isArray(value)) return value;
|
|
1484
1484
|
return [];
|
|
@@ -1756,6 +1756,229 @@ function compileSet(cmd) {
|
|
|
1756
1756
|
}
|
|
1757
1757
|
};
|
|
1758
1758
|
}
|
|
1759
|
+
const COMMAND = "sort";
|
|
1760
|
+
const CHECK_OPTION = "c";
|
|
1761
|
+
const QUIET_CHECK_OPTION = "C";
|
|
1762
|
+
const FIELD_SEPARATOR_OPTION = "t";
|
|
1763
|
+
const KEY_OPTION = "k";
|
|
1764
|
+
const NUMERIC_OPTION = "n";
|
|
1765
|
+
const UNIQUE_OPTION = "u";
|
|
1766
|
+
const DEFAULT_CHECK_MODE = "none";
|
|
1767
|
+
function compileSort(command) {
|
|
1768
|
+
return {
|
|
1769
|
+
cmd: COMMAND,
|
|
1770
|
+
args: parseSortArgs(command.args)
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
function createDefaultSortArgs() {
|
|
1774
|
+
return {
|
|
1775
|
+
checkMode: DEFAULT_CHECK_MODE,
|
|
1776
|
+
diagnostics: [],
|
|
1777
|
+
fieldSeparator: null,
|
|
1778
|
+
files: [],
|
|
1779
|
+
keys: [],
|
|
1780
|
+
numeric: false,
|
|
1781
|
+
unique: false
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
function parseSortArgs(argv) {
|
|
1785
|
+
const args = createDefaultSortArgs();
|
|
1786
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1787
|
+
const word = argv[index];
|
|
1788
|
+
if (!word) continue;
|
|
1789
|
+
const token = expandedWordToString(word);
|
|
1790
|
+
if (token === "--") {
|
|
1791
|
+
args.files.push(...argv.slice(index + 1));
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
if (!isOptionToken(token)) {
|
|
1795
|
+
args.files.push(word);
|
|
1796
|
+
continue;
|
|
1797
|
+
}
|
|
1798
|
+
if (token === `-${KEY_OPTION}`) {
|
|
1799
|
+
const value = argv[index + 1];
|
|
1800
|
+
addSeparatedKey(args, value, token, index);
|
|
1801
|
+
index += 1;
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
if (token.startsWith(`-${KEY_OPTION}`)) {
|
|
1805
|
+
addKey(args, token.slice(2), token, index);
|
|
1806
|
+
continue;
|
|
1807
|
+
}
|
|
1808
|
+
if (token === `-${FIELD_SEPARATOR_OPTION}`) {
|
|
1809
|
+
const value = argv[index + 1];
|
|
1810
|
+
addSeparatedFieldSeparator(args, value, token, index);
|
|
1811
|
+
index += 1;
|
|
1812
|
+
continue;
|
|
1813
|
+
}
|
|
1814
|
+
if (token.startsWith(`-${FIELD_SEPARATOR_OPTION}`)) {
|
|
1815
|
+
addFieldSeparator(args, token.slice(2), token, index);
|
|
1816
|
+
continue;
|
|
1817
|
+
}
|
|
1818
|
+
applyShortOptions(args, token, index);
|
|
1819
|
+
}
|
|
1820
|
+
return args;
|
|
1821
|
+
}
|
|
1822
|
+
function isOptionToken(token) {
|
|
1823
|
+
return token.startsWith("-") && token !== "-";
|
|
1824
|
+
}
|
|
1825
|
+
function addSeparatedKey(args, value, token, tokenIndex) {
|
|
1826
|
+
if (!value) {
|
|
1827
|
+
addDiagnostic(args, "missing-key", "option requires an argument", token, tokenIndex);
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
addKey(args, expandedWordToString(value), token, tokenIndex);
|
|
1831
|
+
}
|
|
1832
|
+
function addKey(args, raw, token, tokenIndex) {
|
|
1833
|
+
const parsed = parseSortKey(raw, token, tokenIndex, args.diagnostics);
|
|
1834
|
+
if (parsed) args.keys.push(parsed);
|
|
1835
|
+
}
|
|
1836
|
+
function addSeparatedFieldSeparator(args, value, token, tokenIndex) {
|
|
1837
|
+
if (!value) {
|
|
1838
|
+
addDiagnostic(args, "missing-separator", "option requires an argument", token, tokenIndex);
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
addFieldSeparator(args, expandedWordToString(value), token, tokenIndex);
|
|
1842
|
+
}
|
|
1843
|
+
function addFieldSeparator(args, raw, token, tokenIndex) {
|
|
1844
|
+
const characters = Array.from(raw);
|
|
1845
|
+
const separator = characters.at(0);
|
|
1846
|
+
if (separator === void 0) {
|
|
1847
|
+
addDiagnostic(args, "empty-separator", "empty tab", token, tokenIndex);
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
if (characters.length > 1) {
|
|
1851
|
+
addDiagnostic(args, "multi-character-separator", `multi-character tab '${raw}'`, token, tokenIndex);
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
args.fieldSeparator = separator;
|
|
1855
|
+
}
|
|
1856
|
+
function applyShortOptions(args, token, tokenIndex) {
|
|
1857
|
+
const options = token.slice(1);
|
|
1858
|
+
for (const option of options) switch (option) {
|
|
1859
|
+
case CHECK_OPTION:
|
|
1860
|
+
applyCheckMode(args, "diagnose-first", token, tokenIndex);
|
|
1861
|
+
break;
|
|
1862
|
+
case QUIET_CHECK_OPTION:
|
|
1863
|
+
applyCheckMode(args, "quiet", token, tokenIndex);
|
|
1864
|
+
break;
|
|
1865
|
+
case NUMERIC_OPTION:
|
|
1866
|
+
args.numeric = true;
|
|
1867
|
+
break;
|
|
1868
|
+
case UNIQUE_OPTION:
|
|
1869
|
+
args.unique = true;
|
|
1870
|
+
break;
|
|
1871
|
+
default: addDiagnostic(args, "unknown-option", `invalid option -- '${option}'`, token, tokenIndex);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
function applyCheckMode(args, checkMode, token, tokenIndex) {
|
|
1875
|
+
if (args.checkMode !== "none" && args.checkMode !== checkMode) addDiagnostic(args, "incompatible-options", "options '-cC' are incompatible", token, tokenIndex);
|
|
1876
|
+
args.checkMode = checkMode;
|
|
1877
|
+
}
|
|
1878
|
+
function parseSortKey(raw, token, tokenIndex, diagnostics) {
|
|
1879
|
+
const commaIndex = raw.indexOf(",");
|
|
1880
|
+
const start = parseKeyPosition({
|
|
1881
|
+
diagnostics,
|
|
1882
|
+
partEnd: commaIndex === -1 ? raw.length : commaIndex,
|
|
1883
|
+
partStart: 0,
|
|
1884
|
+
raw,
|
|
1885
|
+
token,
|
|
1886
|
+
tokenIndex,
|
|
1887
|
+
type: "start"
|
|
1888
|
+
});
|
|
1889
|
+
const end = commaIndex === -1 ? null : parseKeyPosition({
|
|
1890
|
+
diagnostics,
|
|
1891
|
+
partEnd: raw.length,
|
|
1892
|
+
partStart: commaIndex + 1,
|
|
1893
|
+
raw,
|
|
1894
|
+
token,
|
|
1895
|
+
tokenIndex,
|
|
1896
|
+
type: "end"
|
|
1897
|
+
});
|
|
1898
|
+
if (!start || commaIndex !== -1 && !end) return null;
|
|
1899
|
+
return {
|
|
1900
|
+
end: end?.position ?? null,
|
|
1901
|
+
options: { numeric: start.numeric || (end?.numeric ?? false) },
|
|
1902
|
+
raw,
|
|
1903
|
+
start: start.position
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
function parseKeyPosition(options) {
|
|
1907
|
+
const { diagnostics, partEnd, partStart, raw, token, tokenIndex, type } = options;
|
|
1908
|
+
const fieldDigits = readDigits(raw, partStart, partEnd);
|
|
1909
|
+
if (fieldDigits.value === "") {
|
|
1910
|
+
addInvalidNumberDiagnostic(diagnostics, raw, partStart, token, tokenIndex);
|
|
1911
|
+
return null;
|
|
1912
|
+
}
|
|
1913
|
+
const field = Number(fieldDigits.value);
|
|
1914
|
+
if (field === 0) {
|
|
1915
|
+
diagnostics.push(createSortDiagnostic("invalid-key", `invalid field specification '${raw}'`, token, tokenIndex));
|
|
1916
|
+
return null;
|
|
1917
|
+
}
|
|
1918
|
+
let cursor = fieldDigits.nextIndex;
|
|
1919
|
+
let character = null;
|
|
1920
|
+
if (raw.at(cursor) === "." && cursor < partEnd) {
|
|
1921
|
+
const characterDigits = readDigits(raw, cursor + 1, partEnd);
|
|
1922
|
+
if (characterDigits.value === "") {
|
|
1923
|
+
addCharacterNumberDiagnostic(diagnostics, raw, cursor + 1, token, tokenIndex);
|
|
1924
|
+
return null;
|
|
1925
|
+
}
|
|
1926
|
+
character = Number(characterDigits.value);
|
|
1927
|
+
if (character === 0 && type === "start") {
|
|
1928
|
+
diagnostics.push(createSortDiagnostic("invalid-key", "character offset is zero", token, tokenIndex), createSortDiagnostic("invalid-key", `invalid field specification '${raw}'`, token, tokenIndex));
|
|
1929
|
+
return null;
|
|
1930
|
+
}
|
|
1931
|
+
cursor = characterDigits.nextIndex;
|
|
1932
|
+
}
|
|
1933
|
+
const ordering = readOrderingOptions(raw, cursor, partEnd, token, tokenIndex, diagnostics);
|
|
1934
|
+
if (!ordering) return null;
|
|
1935
|
+
return {
|
|
1936
|
+
numeric: ordering.numeric,
|
|
1937
|
+
position: {
|
|
1938
|
+
character,
|
|
1939
|
+
field
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function readDigits(text, start, end) {
|
|
1944
|
+
let cursor = start;
|
|
1945
|
+
while (cursor < end) {
|
|
1946
|
+
const character = text.at(cursor);
|
|
1947
|
+
if (character === void 0 || character < "0" || character > "9") break;
|
|
1948
|
+
cursor += 1;
|
|
1949
|
+
}
|
|
1950
|
+
return {
|
|
1951
|
+
nextIndex: cursor,
|
|
1952
|
+
value: text.slice(start, cursor)
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
function readOrderingOptions(raw, start, end, token, tokenIndex, diagnostics) {
|
|
1956
|
+
let numeric = false;
|
|
1957
|
+
for (let cursor = start; cursor < end; cursor += 1) {
|
|
1958
|
+
if (raw.at(cursor) === NUMERIC_OPTION) {
|
|
1959
|
+
numeric = true;
|
|
1960
|
+
continue;
|
|
1961
|
+
}
|
|
1962
|
+
diagnostics.push(createSortDiagnostic("invalid-key", `invalid count at start of '${raw.slice(cursor)}'`, token, tokenIndex));
|
|
1963
|
+
return null;
|
|
1964
|
+
}
|
|
1965
|
+
return { numeric };
|
|
1966
|
+
}
|
|
1967
|
+
function addInvalidNumberDiagnostic(diagnostics, raw, position, token, tokenIndex) {
|
|
1968
|
+
diagnostics.push(createSortDiagnostic("invalid-key", "invalid number after ','", token, tokenIndex), createSortDiagnostic("invalid-key", `invalid count at start of '${raw.slice(position)}'`, token, tokenIndex));
|
|
1969
|
+
}
|
|
1970
|
+
function addCharacterNumberDiagnostic(diagnostics, raw, position, token, tokenIndex) {
|
|
1971
|
+
diagnostics.push(createSortDiagnostic("invalid-key", "invalid number after '.'", token, tokenIndex), createSortDiagnostic("invalid-key", `invalid count at start of '${raw.slice(position)}'`, token, tokenIndex));
|
|
1972
|
+
}
|
|
1973
|
+
function addDiagnostic(args, code, message, token, tokenIndex) {
|
|
1974
|
+
args.diagnostics.push(createSortDiagnostic(code, message, token, tokenIndex));
|
|
1975
|
+
}
|
|
1976
|
+
function createSortDiagnostic(code, message, token, tokenIndex) {
|
|
1977
|
+
return createCommandDiagnostic(COMMAND, code, message, {
|
|
1978
|
+
token,
|
|
1979
|
+
tokenIndex
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1759
1982
|
function compileString(cmd) {
|
|
1760
1983
|
const [subcommand, ...operands] = cmd.args;
|
|
1761
1984
|
if (!subcommand) throw new Error("string requires a subcommand");
|
|
@@ -1861,6 +2084,246 @@ function compileTouch(cmd) {
|
|
|
1861
2084
|
}
|
|
1862
2085
|
};
|
|
1863
2086
|
}
|
|
2087
|
+
/**
|
|
2088
|
+
* tree command handler for the AST-based compiler.
|
|
2089
|
+
*/
|
|
2090
|
+
const parseTreeArgs = createWordParser({
|
|
2091
|
+
ascii: {
|
|
2092
|
+
short: "A",
|
|
2093
|
+
takesValue: false
|
|
2094
|
+
},
|
|
2095
|
+
classify: {
|
|
2096
|
+
short: "F",
|
|
2097
|
+
takesValue: false
|
|
2098
|
+
},
|
|
2099
|
+
dirsOnly: {
|
|
2100
|
+
short: "d",
|
|
2101
|
+
takesValue: false
|
|
2102
|
+
},
|
|
2103
|
+
excludePattern: {
|
|
2104
|
+
short: "I",
|
|
2105
|
+
takesValue: true,
|
|
2106
|
+
multiple: true,
|
|
2107
|
+
allowFlagLikeValue: true
|
|
2108
|
+
},
|
|
2109
|
+
fullPath: {
|
|
2110
|
+
short: "f",
|
|
2111
|
+
takesValue: false
|
|
2112
|
+
},
|
|
2113
|
+
includePattern: {
|
|
2114
|
+
short: "P",
|
|
2115
|
+
takesValue: true,
|
|
2116
|
+
multiple: true,
|
|
2117
|
+
allowFlagLikeValue: true
|
|
2118
|
+
},
|
|
2119
|
+
matchDirs: {
|
|
2120
|
+
long: "matchdirs",
|
|
2121
|
+
takesValue: false
|
|
2122
|
+
},
|
|
2123
|
+
maxDepth: {
|
|
2124
|
+
short: "L",
|
|
2125
|
+
takesValue: true
|
|
2126
|
+
},
|
|
2127
|
+
noReport: {
|
|
2128
|
+
long: "noreport",
|
|
2129
|
+
takesValue: false
|
|
2130
|
+
},
|
|
2131
|
+
prune: {
|
|
2132
|
+
long: "prune",
|
|
2133
|
+
takesValue: false
|
|
2134
|
+
},
|
|
2135
|
+
showAll: {
|
|
2136
|
+
short: "a",
|
|
2137
|
+
takesValue: false
|
|
2138
|
+
}
|
|
2139
|
+
}, expandedWordToString);
|
|
2140
|
+
function compileTree(command) {
|
|
2141
|
+
const parsed = parseTreeArgs(command.args);
|
|
2142
|
+
return {
|
|
2143
|
+
cmd: "tree",
|
|
2144
|
+
args: {
|
|
2145
|
+
ascii: parsed.flags.ascii === true,
|
|
2146
|
+
classify: parsed.flags.classify === true,
|
|
2147
|
+
dirsOnly: parsed.flags.dirsOnly === true,
|
|
2148
|
+
excludePatterns: collectExpandedValues(parsed, command.args, "excludePattern"),
|
|
2149
|
+
fullPath: parsed.flags.fullPath === true,
|
|
2150
|
+
includePatterns: collectExpandedValues(parsed, command.args, "includePattern"),
|
|
2151
|
+
matchDirs: parsed.flags.matchDirs === true,
|
|
2152
|
+
maxDepth: parseMaxDepth(parsed.flags.maxDepth),
|
|
2153
|
+
noReport: parsed.flags.noReport === true,
|
|
2154
|
+
paths: parsed.positionalWords.length === 0 ? [literal(".")] : parsed.positionalWords,
|
|
2155
|
+
prune: parsed.flags.prune === true,
|
|
2156
|
+
showAll: parsed.flags.showAll === true
|
|
2157
|
+
}
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
function parseMaxDepth(value) {
|
|
2161
|
+
if (typeof value !== "string") return null;
|
|
2162
|
+
const maxDepth = Number.parseInt(value, 10);
|
|
2163
|
+
if (!Number.isFinite(maxDepth) || maxDepth < 0) throw new Error(`tree: invalid level, '${value}'`);
|
|
2164
|
+
return maxDepth;
|
|
2165
|
+
}
|
|
2166
|
+
function collectExpandedValues(parsed, argv, canonical) {
|
|
2167
|
+
const values = [];
|
|
2168
|
+
const rawValues = normalizeValueList(parsed.flags[canonical]);
|
|
2169
|
+
const valueIndices = parsed.consumedValueIndices[canonical] ?? [];
|
|
2170
|
+
const valueSources = parsed.consumedValueSources[canonical] ?? [];
|
|
2171
|
+
const count = Math.min(rawValues.length, valueIndices.length);
|
|
2172
|
+
for (let index = 0; index < count; index += 1) {
|
|
2173
|
+
const rawValue = rawValues[index];
|
|
2174
|
+
const valueIndex = valueIndices[index];
|
|
2175
|
+
const source = valueSources[index];
|
|
2176
|
+
if (rawValue === void 0 || valueIndex === void 0) continue;
|
|
2177
|
+
if (source === "arg") {
|
|
2178
|
+
values.push(argv[valueIndex] ?? literal(rawValue));
|
|
2179
|
+
continue;
|
|
2180
|
+
}
|
|
2181
|
+
values.push(literal(rawValue));
|
|
2182
|
+
}
|
|
2183
|
+
return values;
|
|
2184
|
+
}
|
|
2185
|
+
function normalizeValueList(value) {
|
|
2186
|
+
if (typeof value === "string") return [value];
|
|
2187
|
+
if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
|
|
2188
|
+
return [];
|
|
2189
|
+
}
|
|
2190
|
+
const DEFAULT_TOTAL_MODE = "auto";
|
|
2191
|
+
function compileWc(command) {
|
|
2192
|
+
return {
|
|
2193
|
+
cmd: "wc",
|
|
2194
|
+
args: parseWcArgs(command.args)
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
function parseWcArgs(argv) {
|
|
2198
|
+
const args = {
|
|
2199
|
+
bytes: false,
|
|
2200
|
+
chars: false,
|
|
2201
|
+
files: [],
|
|
2202
|
+
files0From: null,
|
|
2203
|
+
lines: false,
|
|
2204
|
+
maxLineLength: false,
|
|
2205
|
+
total: DEFAULT_TOTAL_MODE,
|
|
2206
|
+
words: false
|
|
2207
|
+
};
|
|
2208
|
+
for (let index = 0; index < argv.length; index++) {
|
|
2209
|
+
const word = argv[index];
|
|
2210
|
+
if (!word) continue;
|
|
2211
|
+
const token = expandedWordToString(word);
|
|
2212
|
+
if (token === "--") {
|
|
2213
|
+
args.files.push(...argv.slice(index + 1));
|
|
2214
|
+
break;
|
|
2215
|
+
}
|
|
2216
|
+
if (!token.startsWith("-") || token === "-") {
|
|
2217
|
+
args.files.push(word);
|
|
2218
|
+
continue;
|
|
2219
|
+
}
|
|
2220
|
+
const parsed = parseLongOption(argv, index, token, args);
|
|
2221
|
+
if (parsed.matched) {
|
|
2222
|
+
index = parsed.nextIndex - 1;
|
|
2223
|
+
continue;
|
|
2224
|
+
}
|
|
2225
|
+
parseShortOptions(token, args);
|
|
2226
|
+
}
|
|
2227
|
+
return args;
|
|
2228
|
+
}
|
|
2229
|
+
function parseLongOption(argv, index, token, args) {
|
|
2230
|
+
if (token === "--bytes") {
|
|
2231
|
+
args.bytes = true;
|
|
2232
|
+
return {
|
|
2233
|
+
matched: true,
|
|
2234
|
+
nextIndex: index + 1
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
if (token === "--chars") {
|
|
2238
|
+
args.chars = true;
|
|
2239
|
+
return {
|
|
2240
|
+
matched: true,
|
|
2241
|
+
nextIndex: index + 1
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
if (token === "--lines") {
|
|
2245
|
+
args.lines = true;
|
|
2246
|
+
return {
|
|
2247
|
+
matched: true,
|
|
2248
|
+
nextIndex: index + 1
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
if (token === "--words") {
|
|
2252
|
+
args.words = true;
|
|
2253
|
+
return {
|
|
2254
|
+
matched: true,
|
|
2255
|
+
nextIndex: index + 1
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
if (token === "--max-line-length") {
|
|
2259
|
+
args.maxLineLength = true;
|
|
2260
|
+
return {
|
|
2261
|
+
matched: true,
|
|
2262
|
+
nextIndex: index + 1
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
if (token.startsWith("--files0-from=")) {
|
|
2266
|
+
args.files0From = {
|
|
2267
|
+
kind: "literal",
|
|
2268
|
+
value: token.slice(14)
|
|
2269
|
+
};
|
|
2270
|
+
return {
|
|
2271
|
+
matched: true,
|
|
2272
|
+
nextIndex: index + 1
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
if (token === "--files0-from") {
|
|
2276
|
+
const value = argv[index + 1];
|
|
2277
|
+
if (!value) throw new Error("wc: option --files0-from requires an argument");
|
|
2278
|
+
args.files0From = value;
|
|
2279
|
+
return {
|
|
2280
|
+
matched: true,
|
|
2281
|
+
nextIndex: index + 2
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
if (token.startsWith("--total=")) {
|
|
2285
|
+
args.total = parseTotalMode(token.slice(8));
|
|
2286
|
+
return {
|
|
2287
|
+
matched: true,
|
|
2288
|
+
nextIndex: index + 1
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
if (token === "--total") {
|
|
2292
|
+
args.total = "invalid";
|
|
2293
|
+
return {
|
|
2294
|
+
matched: true,
|
|
2295
|
+
nextIndex: index + 1
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
return {
|
|
2299
|
+
matched: false,
|
|
2300
|
+
nextIndex: index
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
function parseShortOptions(token, args) {
|
|
2304
|
+
for (const option of token.slice(1)) switch (option) {
|
|
2305
|
+
case "c":
|
|
2306
|
+
args.bytes = true;
|
|
2307
|
+
break;
|
|
2308
|
+
case "m":
|
|
2309
|
+
args.chars = true;
|
|
2310
|
+
break;
|
|
2311
|
+
case "l":
|
|
2312
|
+
args.lines = true;
|
|
2313
|
+
break;
|
|
2314
|
+
case "w":
|
|
2315
|
+
args.words = true;
|
|
2316
|
+
break;
|
|
2317
|
+
case "L":
|
|
2318
|
+
args.maxLineLength = true;
|
|
2319
|
+
break;
|
|
2320
|
+
default: throw new Error(`wc: unknown option -- ${option}`);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
function parseTotalMode(value) {
|
|
2324
|
+
if (value === "auto" || value === "always" || value === "never" || value === "only") return value;
|
|
2325
|
+
throw new Error(`wc: invalid --total value: ${value}`);
|
|
2326
|
+
}
|
|
1864
2327
|
const DEFAULT_COMMAND = [literal("echo")];
|
|
1865
2328
|
const NUL_DELIMITER = "\0";
|
|
1866
2329
|
function compileXargs(command) {
|
|
@@ -2011,10 +2474,13 @@ let CommandHandler;
|
|
|
2011
2474
|
read: compileRead,
|
|
2012
2475
|
rm: compileRm,
|
|
2013
2476
|
set: compileSet,
|
|
2477
|
+
sort: compileSort,
|
|
2014
2478
|
string: compileString,
|
|
2015
2479
|
tail: compileTail,
|
|
2016
2480
|
test: compileTest,
|
|
2017
2481
|
touch: compileTouch,
|
|
2482
|
+
tree: compileTree,
|
|
2483
|
+
wc: compileWc,
|
|
2018
2484
|
xargs: compileXargs
|
|
2019
2485
|
};
|
|
2020
2486
|
function get(name) {
|
|
@@ -2211,7 +2677,8 @@ var ProgramCompiler = class {
|
|
|
2211
2677
|
const pipesStdout = pipeRedirections.some((redirection) => redirection.sourceFd === 1);
|
|
2212
2678
|
const pipesStderr = pipeRedirections.some((redirection) => redirection.sourceFd === 2);
|
|
2213
2679
|
if (pipesStdout && pipesStderr) return "&|";
|
|
2214
|
-
|
|
2680
|
+
const onlyPipeRedirection = pipeRedirections[0];
|
|
2681
|
+
if (pipeRedirections.length === 1 && onlyPipeRedirection) return this.serializeRedirection(onlyPipeRedirection);
|
|
2215
2682
|
return "|";
|
|
2216
2683
|
}
|
|
2217
2684
|
serializeRedirection(redirection) {
|
|
@@ -4099,8 +4566,8 @@ function toErrorMessage$1(diagnostic) {
|
|
|
4099
4566
|
//#endregion
|
|
4100
4567
|
//#region src/execute/path.ts
|
|
4101
4568
|
const MULTIPLE_SLASH_REGEX$2 = /\/+/g;
|
|
4102
|
-
const ROOT_DIRECTORY$
|
|
4103
|
-
const TRAILING_SLASH_REGEX$
|
|
4569
|
+
const ROOT_DIRECTORY$4 = "/";
|
|
4570
|
+
const TRAILING_SLASH_REGEX$3 = /\/+$/;
|
|
4104
4571
|
const VARIABLE_REFERENCE_REGEX = /\$([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
4105
4572
|
const NO_GLOB_MATCH_MESSAGE = "no matches found";
|
|
4106
4573
|
async function collectOutputRecords(result) {
|
|
@@ -4126,7 +4593,7 @@ function expandVariables(input, context) {
|
|
|
4126
4593
|
});
|
|
4127
4594
|
}
|
|
4128
4595
|
function normalizeAbsolutePath(path) {
|
|
4129
|
-
const segments = (path.startsWith(ROOT_DIRECTORY$
|
|
4596
|
+
const segments = (path.startsWith(ROOT_DIRECTORY$4) ? path : `${ROOT_DIRECTORY$4}${path}`).replace(MULTIPLE_SLASH_REGEX$2, "/").split(ROOT_DIRECTORY$4);
|
|
4130
4597
|
const normalizedSegments = [];
|
|
4131
4598
|
for (const segment of segments) {
|
|
4132
4599
|
if (segment === "" || segment === ".") continue;
|
|
@@ -4136,17 +4603,17 @@ function normalizeAbsolutePath(path) {
|
|
|
4136
4603
|
}
|
|
4137
4604
|
normalizedSegments.push(segment);
|
|
4138
4605
|
}
|
|
4139
|
-
const normalizedPath = `${ROOT_DIRECTORY$
|
|
4140
|
-
return normalizedPath === "" ? ROOT_DIRECTORY$
|
|
4606
|
+
const normalizedPath = `${ROOT_DIRECTORY$4}${normalizedSegments.join(ROOT_DIRECTORY$4)}`;
|
|
4607
|
+
return normalizedPath === "" ? ROOT_DIRECTORY$4 : normalizedPath;
|
|
4141
4608
|
}
|
|
4142
4609
|
function normalizeCwd(cwd) {
|
|
4143
|
-
if (cwd === "") return ROOT_DIRECTORY$
|
|
4144
|
-
const trimmed = normalizeAbsolutePath(cwd).replace(TRAILING_SLASH_REGEX$
|
|
4145
|
-
return trimmed === "" ? ROOT_DIRECTORY$
|
|
4610
|
+
if (cwd === "") return ROOT_DIRECTORY$4;
|
|
4611
|
+
const trimmed = normalizeAbsolutePath(cwd).replace(TRAILING_SLASH_REGEX$3, "");
|
|
4612
|
+
return trimmed === "" ? ROOT_DIRECTORY$4 : trimmed;
|
|
4146
4613
|
}
|
|
4147
4614
|
function resolvePathFromCwd(cwd, path) {
|
|
4148
4615
|
if (path === "") return cwd;
|
|
4149
|
-
if (path.startsWith(ROOT_DIRECTORY$
|
|
4616
|
+
if (path.startsWith(ROOT_DIRECTORY$4)) return normalizeAbsolutePath(path);
|
|
4150
4617
|
return normalizeAbsolutePath(`${cwd}/${path}`);
|
|
4151
4618
|
}
|
|
4152
4619
|
function resolvePathsFromCwd(cwd, paths) {
|
|
@@ -4161,7 +4628,7 @@ async function readDirectoryPaths(fs, directoryPath) {
|
|
|
4161
4628
|
children.sort((left, right) => left.localeCompare(right));
|
|
4162
4629
|
return children;
|
|
4163
4630
|
}
|
|
4164
|
-
async function walkFilesystemEntries(fs, rootDir = ROOT_DIRECTORY$
|
|
4631
|
+
async function walkFilesystemEntries(fs, rootDir = ROOT_DIRECTORY$4) {
|
|
4165
4632
|
const normalizedRoot = normalizeAbsolutePath(rootDir);
|
|
4166
4633
|
if (!(await fs.stat(normalizedRoot)).isDirectory) throw new Error(`Not a directory: ${normalizedRoot}`);
|
|
4167
4634
|
const entries = [];
|
|
@@ -4183,12 +4650,12 @@ async function walkFilesystemEntries(fs, rootDir = ROOT_DIRECTORY$3) {
|
|
|
4183
4650
|
return entries;
|
|
4184
4651
|
}
|
|
4185
4652
|
function toRelativePathFromCwd$1(path, cwd) {
|
|
4186
|
-
if (cwd === ROOT_DIRECTORY$
|
|
4187
|
-
if (path === ROOT_DIRECTORY$
|
|
4188
|
-
return path.startsWith(ROOT_DIRECTORY$
|
|
4653
|
+
if (cwd === ROOT_DIRECTORY$4) {
|
|
4654
|
+
if (path === ROOT_DIRECTORY$4) return null;
|
|
4655
|
+
return path.startsWith(ROOT_DIRECTORY$4) ? path.slice(1) : path;
|
|
4189
4656
|
}
|
|
4190
4657
|
if (path === cwd) return null;
|
|
4191
|
-
const prefix = `${cwd}${ROOT_DIRECTORY$
|
|
4658
|
+
const prefix = `${cwd}${ROOT_DIRECTORY$4}`;
|
|
4192
4659
|
if (!path.startsWith(prefix)) return null;
|
|
4193
4660
|
return path.slice(prefix.length);
|
|
4194
4661
|
}
|
|
@@ -4196,12 +4663,12 @@ function toGlobCandidate(entry, cwd, isAbsolutePattern, directoryOnly) {
|
|
|
4196
4663
|
if (directoryOnly && !entry.isDirectory) return null;
|
|
4197
4664
|
const basePath = isAbsolutePattern ? entry.path : toRelativePathFromCwd$1(entry.path, cwd);
|
|
4198
4665
|
if (!basePath || basePath === "") return null;
|
|
4199
|
-
if (directoryOnly) return `${basePath}${ROOT_DIRECTORY$
|
|
4666
|
+
if (directoryOnly) return `${basePath}${ROOT_DIRECTORY$4}`;
|
|
4200
4667
|
return basePath;
|
|
4201
4668
|
}
|
|
4202
4669
|
async function expandGlobPattern(pattern, fs, context) {
|
|
4203
|
-
const directoryOnly = pattern.endsWith(ROOT_DIRECTORY$
|
|
4204
|
-
const isAbsolutePattern = pattern.startsWith(ROOT_DIRECTORY$
|
|
4670
|
+
const directoryOnly = pattern.endsWith(ROOT_DIRECTORY$4);
|
|
4671
|
+
const isAbsolutePattern = pattern.startsWith(ROOT_DIRECTORY$4);
|
|
4205
4672
|
const matcher = picomatch(pattern, {
|
|
4206
4673
|
bash: true,
|
|
4207
4674
|
dot: false
|
|
@@ -4266,28 +4733,10 @@ async function evaluateExpandedWordPart(part, fs, context) {
|
|
|
4266
4733
|
}
|
|
4267
4734
|
//#endregion
|
|
4268
4735
|
//#region src/execute/records.ts
|
|
4269
|
-
async function* toLineStream(fs, input) {
|
|
4270
|
-
for await (const record of input) {
|
|
4271
|
-
if (record.kind === "line") {
|
|
4272
|
-
yield record;
|
|
4273
|
-
continue;
|
|
4274
|
-
}
|
|
4275
|
-
if (record.kind === "file") {
|
|
4276
|
-
yield* fileRecordToLines(fs, record);
|
|
4277
|
-
continue;
|
|
4278
|
-
}
|
|
4279
|
-
yield {
|
|
4280
|
-
kind: "line",
|
|
4281
|
-
text: JSON.stringify(record.value)
|
|
4282
|
-
};
|
|
4283
|
-
}
|
|
4284
|
-
}
|
|
4285
4736
|
/**
|
|
4286
|
-
* Converts
|
|
4287
|
-
*
|
|
4288
|
-
*
|
|
4289
|
-
* line-oriented transducers (tail, head) receiving piped input from commands
|
|
4290
|
-
* like `find` that produce path listings.
|
|
4737
|
+
* Converts records into stdin-style line text. Unlike `toLineStream`, this does
|
|
4738
|
+
* not read FileRecord contents. FileRecords are rendered as paths, matching the
|
|
4739
|
+
* shell pipe boundary where downstream line-oriented commands consume text.
|
|
4291
4740
|
*/
|
|
4292
4741
|
async function* toFormattedLineStream(input) {
|
|
4293
4742
|
for await (const record of input) {
|
|
@@ -4301,16 +4750,6 @@ async function* toFormattedLineStream(input) {
|
|
|
4301
4750
|
};
|
|
4302
4751
|
}
|
|
4303
4752
|
}
|
|
4304
|
-
async function* fileRecordToLines(fs, record) {
|
|
4305
|
-
if (await isDirectoryRecord(fs, record)) return;
|
|
4306
|
-
let lineNum = 1;
|
|
4307
|
-
for await (const text of fs.readLines(record.path)) yield {
|
|
4308
|
-
kind: "line",
|
|
4309
|
-
text,
|
|
4310
|
-
file: record.path,
|
|
4311
|
-
lineNum: lineNum++
|
|
4312
|
-
};
|
|
4313
|
-
}
|
|
4314
4753
|
async function isDirectoryRecord(fs, record) {
|
|
4315
4754
|
if (record.isDirectory !== void 0) return record.isDirectory;
|
|
4316
4755
|
try {
|
|
@@ -4324,6 +4763,7 @@ async function isDirectoryRecord(fs, record) {
|
|
|
4324
4763
|
const textEncoder = new TextEncoder();
|
|
4325
4764
|
const textDecoder = new TextDecoder();
|
|
4326
4765
|
const FD_TARGET_REGEX$1 = /^&[0-9]+$/;
|
|
4766
|
+
const NULL_DEVICE_PATH = "/dev/null";
|
|
4327
4767
|
function getSourceFd$1(redirection) {
|
|
4328
4768
|
return redirection.sourceFd ?? (redirection.kind === "input" ? 0 : 1);
|
|
4329
4769
|
}
|
|
@@ -4430,6 +4870,7 @@ async function readExistingFileText(fs, path) {
|
|
|
4430
4870
|
}
|
|
4431
4871
|
}
|
|
4432
4872
|
async function writeTextToFile(fs, path, content, options) {
|
|
4873
|
+
if (isNullDevicePath(path)) return;
|
|
4433
4874
|
if (!(options.append ?? false)) {
|
|
4434
4875
|
await fs.writeFile(path, textEncoder.encode(content));
|
|
4435
4876
|
return;
|
|
@@ -4439,8 +4880,12 @@ async function writeTextToFile(fs, path, content, options) {
|
|
|
4439
4880
|
await fs.writeFile(path, textEncoder.encode(`${existing}${separator}${content}`));
|
|
4440
4881
|
}
|
|
4441
4882
|
async function ensureNoclobberWritable(fs, path) {
|
|
4883
|
+
if (isNullDevicePath(path)) return true;
|
|
4442
4884
|
return !await fs.exists(path);
|
|
4443
4885
|
}
|
|
4886
|
+
function isNullDevicePath(path) {
|
|
4887
|
+
return path === NULL_DEVICE_PATH;
|
|
4888
|
+
}
|
|
4444
4889
|
//#endregion
|
|
4445
4890
|
//#region src/builtin/cd/cd.ts
|
|
4446
4891
|
const cd = async (runtime, args) => {
|
|
@@ -4474,21 +4919,9 @@ const VARIABLE_NAME_REGEX$1 = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
4474
4919
|
async function readFirstValue(runtime) {
|
|
4475
4920
|
if (!runtime.input) return null;
|
|
4476
4921
|
let firstValue = null;
|
|
4477
|
-
for await (const
|
|
4478
|
-
const value = await recordToText(runtime, record);
|
|
4479
|
-
if (value !== null && firstValue === null) firstValue = value;
|
|
4480
|
-
}
|
|
4922
|
+
for await (const line of runtime.stdin.lines()) if (firstValue === null) firstValue = line;
|
|
4481
4923
|
return firstValue;
|
|
4482
4924
|
}
|
|
4483
|
-
async function recordToText(runtime, record) {
|
|
4484
|
-
if (record.kind === "line") return record.text;
|
|
4485
|
-
if (record.kind === "file") {
|
|
4486
|
-
if (await isDirectoryRecord(runtime.fs, record)) return null;
|
|
4487
|
-
for await (const line of runtime.fs.readLines(record.path)) return line;
|
|
4488
|
-
return "";
|
|
4489
|
-
}
|
|
4490
|
-
return JSON.stringify(record.value);
|
|
4491
|
-
}
|
|
4492
4925
|
const read = (runtime, args) => {
|
|
4493
4926
|
return (async function* () {
|
|
4494
4927
|
const name = await evaluateExpandedWord(args.name, runtime.fs, runtime.context);
|
|
@@ -4519,14 +4952,21 @@ const set = (runtime, args) => {
|
|
|
4519
4952
|
};
|
|
4520
4953
|
//#endregion
|
|
4521
4954
|
//#region src/builtin/string/string.ts
|
|
4955
|
+
async function collectStdinLines(runtime) {
|
|
4956
|
+
if (!runtime.input) return [];
|
|
4957
|
+
const lines = [];
|
|
4958
|
+
for await (const line of runtime.stdin.lines()) lines.push(line);
|
|
4959
|
+
return lines;
|
|
4960
|
+
}
|
|
4522
4961
|
function replace(runtime, operands) {
|
|
4523
4962
|
return (async function* () {
|
|
4524
4963
|
if (operands[0]?.startsWith("-")) throw new Error(`string replace: unsupported flag: ${operands[0]}`);
|
|
4525
|
-
if (operands.length <
|
|
4964
|
+
if (operands.length < 2) throw new Error("string replace requires pattern replacement text");
|
|
4526
4965
|
const pattern = operands.at(0);
|
|
4527
4966
|
const replacement = operands.at(1);
|
|
4528
|
-
const inputs = operands.slice(2);
|
|
4529
4967
|
if (pattern === void 0 || replacement === void 0) throw new Error("string replace requires pattern replacement text");
|
|
4968
|
+
if (operands.length === 2 && !runtime.input) throw new Error("string replace requires pattern replacement text");
|
|
4969
|
+
const inputs = operands.length > 2 ? operands.slice(2) : await collectStdinLines(runtime);
|
|
4530
4970
|
if (inputs.length === 0) {
|
|
4531
4971
|
runtime.context.status = 1;
|
|
4532
4972
|
return;
|
|
@@ -4538,30 +4978,45 @@ function replace(runtime, operands) {
|
|
|
4538
4978
|
runtime.context.status = 0;
|
|
4539
4979
|
})();
|
|
4540
4980
|
}
|
|
4981
|
+
async function parseMatchInvocation(runtime, operands) {
|
|
4982
|
+
let quiet = false;
|
|
4983
|
+
let offset = 0;
|
|
4984
|
+
while (operands[offset]?.startsWith("-")) {
|
|
4985
|
+
const flag = operands[offset];
|
|
4986
|
+
if (flag === "-q" && !quiet) {
|
|
4987
|
+
quiet = true;
|
|
4988
|
+
offset += 1;
|
|
4989
|
+
continue;
|
|
4990
|
+
}
|
|
4991
|
+
throw new Error(`string match: unsupported flag: ${flag}`);
|
|
4992
|
+
}
|
|
4993
|
+
const filtered = operands.slice(offset);
|
|
4994
|
+
const [pattern] = filtered;
|
|
4995
|
+
if (!pattern) throw new Error("string match requires pattern and value");
|
|
4996
|
+
if (filtered.length > 2) throw new Error("string match: unsupported arguments");
|
|
4997
|
+
if (filtered.length === 1 && !runtime.input) throw new Error("string match requires pattern and value");
|
|
4998
|
+
const values = filtered.length > 1 ? filtered.slice(1) : await collectStdinLines(runtime);
|
|
4999
|
+
return {
|
|
5000
|
+
pattern,
|
|
5001
|
+
quiet,
|
|
5002
|
+
values
|
|
5003
|
+
};
|
|
5004
|
+
}
|
|
4541
5005
|
function match(runtime, operands) {
|
|
4542
5006
|
return (async function* () {
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
if (
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
}
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
if (!(pattern && value !== void 0)) throw new Error("string match requires pattern and value");
|
|
4557
|
-
if (filtered.length > 2) throw new Error("string match: unsupported arguments");
|
|
4558
|
-
const isMatch = picomatch(pattern, { dot: true })(value);
|
|
4559
|
-
runtime.context.status = isMatch ? 0 : 1;
|
|
4560
|
-
if (isMatch && !quiet) yield {
|
|
4561
|
-
kind: "line",
|
|
4562
|
-
text: value
|
|
4563
|
-
};
|
|
4564
|
-
})();
|
|
5007
|
+
const { pattern, quiet, values } = await parseMatchInvocation(runtime, operands);
|
|
5008
|
+
const matcher = picomatch(pattern, { dot: true });
|
|
5009
|
+
let matched = false;
|
|
5010
|
+
for (const value of values) {
|
|
5011
|
+
if (!matcher(value)) continue;
|
|
5012
|
+
matched = true;
|
|
5013
|
+
if (!quiet) yield {
|
|
5014
|
+
kind: "line",
|
|
5015
|
+
text: value
|
|
5016
|
+
};
|
|
5017
|
+
}
|
|
5018
|
+
runtime.context.status = matched ? 0 : 1;
|
|
5019
|
+
})();
|
|
4565
5020
|
}
|
|
4566
5021
|
const string = (runtime, args) => {
|
|
4567
5022
|
return (async function* () {
|
|
@@ -4709,16 +5164,16 @@ function cat(fs, options) {
|
|
|
4709
5164
|
}
|
|
4710
5165
|
//#endregion
|
|
4711
5166
|
//#region src/operator/cp/cp.ts
|
|
4712
|
-
const TRAILING_SLASH_REGEX$
|
|
5167
|
+
const TRAILING_SLASH_REGEX$2 = /\/+$/;
|
|
4713
5168
|
const MULTIPLE_SLASH_REGEX$1 = /\/+/g;
|
|
4714
5169
|
function trimTrailingSlash$2(path) {
|
|
4715
5170
|
if (path === "/") return path;
|
|
4716
|
-
return path.replace(TRAILING_SLASH_REGEX$
|
|
5171
|
+
return path.replace(TRAILING_SLASH_REGEX$2, "");
|
|
4717
5172
|
}
|
|
4718
5173
|
function joinPath$1(base, suffix) {
|
|
4719
5174
|
return `${trimTrailingSlash$2(base)}/${suffix}`.replace(MULTIPLE_SLASH_REGEX$1, "/");
|
|
4720
5175
|
}
|
|
4721
|
-
function basename$
|
|
5176
|
+
function basename$4(path) {
|
|
4722
5177
|
const normalized = trimTrailingSlash$2(path);
|
|
4723
5178
|
const slashIndex = normalized.lastIndexOf("/");
|
|
4724
5179
|
if (slashIndex === -1) return normalized;
|
|
@@ -4769,7 +5224,7 @@ async function copyDirectoryRecursive(fs, srcDir, destDir, force, interactive) {
|
|
|
4769
5224
|
if (!current) continue;
|
|
4770
5225
|
const childPaths = await readDirectory(current.sourcePath);
|
|
4771
5226
|
for (const childPath of childPaths) {
|
|
4772
|
-
const childName = basename$
|
|
5227
|
+
const childName = basename$4(childPath);
|
|
4773
5228
|
const targetPath = joinPath$1(current.targetPath, childName);
|
|
4774
5229
|
if ((await fs.stat(childPath)).isDirectory) {
|
|
4775
5230
|
await ensureDirectory(targetPath);
|
|
@@ -4790,7 +5245,7 @@ function cp(fs) {
|
|
|
4790
5245
|
if (srcs.length > 1 && !destinationIsDirectory) throw new Error("cp destination must be a directory for multiple sources");
|
|
4791
5246
|
for (const src of srcs) {
|
|
4792
5247
|
const srcStat = await fs.stat(src);
|
|
4793
|
-
const targetPath = destinationIsDirectory || srcs.length > 1 ? joinPath$1(dest, basename$
|
|
5248
|
+
const targetPath = destinationIsDirectory || srcs.length > 1 ? joinPath$1(dest, basename$4(src)) : dest;
|
|
4794
5249
|
if (srcStat.isDirectory) {
|
|
4795
5250
|
if (!recursive) throw new Error(`cp: omitting directory "${src}" (use -r)`);
|
|
4796
5251
|
await copyDirectoryRecursive(fs, src, targetPath, force, interactive);
|
|
@@ -4862,12 +5317,12 @@ async function* walkEntry(fs, context, entry, args, predicateBranches, state, ha
|
|
|
4862
5317
|
childStat = await fs.stat(childAbsolutePath);
|
|
4863
5318
|
} catch {
|
|
4864
5319
|
state.hadError = true;
|
|
4865
|
-
writeDiagnosticsToStderr(context, [createRuntimeDiagnostic("find", "missing-path", "No such file or directory", { path: appendDisplayPath(entry.displayPath, basename$
|
|
5320
|
+
writeDiagnosticsToStderr(context, [createRuntimeDiagnostic("find", "missing-path", "No such file or directory", { path: appendDisplayPath(entry.displayPath, basename$3(childAbsolutePath)) })]);
|
|
4866
5321
|
continue;
|
|
4867
5322
|
}
|
|
4868
5323
|
yield* walkEntry(fs, context, {
|
|
4869
5324
|
absolutePath: childAbsolutePath,
|
|
4870
|
-
displayPath: appendDisplayPath(entry.displayPath, basename$
|
|
5325
|
+
displayPath: appendDisplayPath(entry.displayPath, basename$3(childAbsolutePath)),
|
|
4871
5326
|
depth: entry.depth + 1,
|
|
4872
5327
|
isDirectory: childStat.isDirectory,
|
|
4873
5328
|
size: childStat.size
|
|
@@ -4971,7 +5426,7 @@ function matchesBranch(entry, entryType, branch, childPaths) {
|
|
|
4971
5426
|
return true;
|
|
4972
5427
|
}
|
|
4973
5428
|
function matchesPredicate(entry, entryType, predicate, childPaths) {
|
|
4974
|
-
if (predicate.kind === "name") return predicate.matcher(basename$
|
|
5429
|
+
if (predicate.kind === "name") return predicate.matcher(basename$3(entry.displayPath));
|
|
4975
5430
|
if (predicate.kind === "path") return predicate.matcher(entry.displayPath);
|
|
4976
5431
|
if (predicate.kind === "regex") return predicate.matcher(entry.displayPath);
|
|
4977
5432
|
if (predicate.kind === "constant") return predicate.value;
|
|
@@ -5030,7 +5485,7 @@ function appendDisplayPath(parentPath, childName) {
|
|
|
5030
5485
|
if (parentPath === ".") return `./${childName}`;
|
|
5031
5486
|
return `${parentPath}/${childName}`;
|
|
5032
5487
|
}
|
|
5033
|
-
function basename$
|
|
5488
|
+
function basename$3(path) {
|
|
5034
5489
|
if (path === "/") return "/";
|
|
5035
5490
|
const normalized = trimTrailingSlashes(path);
|
|
5036
5491
|
const slashIndex = normalized.lastIndexOf("/");
|
|
@@ -5076,9 +5531,57 @@ function trimTrailingSlashes(path) {
|
|
|
5076
5531
|
return path.replace(/\/+$/g, "");
|
|
5077
5532
|
}
|
|
5078
5533
|
//#endregion
|
|
5534
|
+
//#region src/execute/io.ts
|
|
5535
|
+
const UTF8_ENCODER$2 = new TextEncoder();
|
|
5536
|
+
const NEWLINE = "\n";
|
|
5537
|
+
var EmptyInput = class {
|
|
5538
|
+
async *records() {}
|
|
5539
|
+
async *lines() {}
|
|
5540
|
+
async bytes() {
|
|
5541
|
+
return new Uint8Array();
|
|
5542
|
+
}
|
|
5543
|
+
};
|
|
5544
|
+
var RecordInput = class {
|
|
5545
|
+
input;
|
|
5546
|
+
constructor(input) {
|
|
5547
|
+
this.input = input;
|
|
5548
|
+
}
|
|
5549
|
+
records() {
|
|
5550
|
+
return this.input;
|
|
5551
|
+
}
|
|
5552
|
+
async *lines() {
|
|
5553
|
+
for await (const record of this.input) yield formatStdoutRecord(record);
|
|
5554
|
+
}
|
|
5555
|
+
async bytes(options = {}) {
|
|
5556
|
+
const lines = [];
|
|
5557
|
+
for await (const line of this.lines()) lines.push(line);
|
|
5558
|
+
if (lines.length === 0) return new Uint8Array();
|
|
5559
|
+
const text = options.trailingNewline ? `${lines.join(NEWLINE)}${NEWLINE}` : lines.join(NEWLINE);
|
|
5560
|
+
return UTF8_ENCODER$2.encode(text);
|
|
5561
|
+
}
|
|
5562
|
+
};
|
|
5563
|
+
var BufferedShellOutput = class {
|
|
5564
|
+
bufferedRecords = [];
|
|
5565
|
+
write(record) {
|
|
5566
|
+
this.bufferedRecords.push(record);
|
|
5567
|
+
}
|
|
5568
|
+
writeLine(text) {
|
|
5569
|
+
this.write({
|
|
5570
|
+
kind: "line",
|
|
5571
|
+
text
|
|
5572
|
+
});
|
|
5573
|
+
}
|
|
5574
|
+
records() {
|
|
5575
|
+
return [...this.bufferedRecords];
|
|
5576
|
+
}
|
|
5577
|
+
};
|
|
5578
|
+
function createShellInput(input) {
|
|
5579
|
+
return input ? new RecordInput(input) : new EmptyInput();
|
|
5580
|
+
}
|
|
5581
|
+
//#endregion
|
|
5079
5582
|
//#region src/operator/grep/grep.ts
|
|
5080
|
-
const UTF8_DECODER = new TextDecoder();
|
|
5081
|
-
const UTF8_ENCODER = new TextEncoder();
|
|
5583
|
+
const UTF8_DECODER$1 = new TextDecoder();
|
|
5584
|
+
const UTF8_ENCODER$1 = new TextEncoder();
|
|
5082
5585
|
const WORD_CHAR_REGEX = /[\p{L}\p{N}_]/u;
|
|
5083
5586
|
const WHITESPACE_ESCAPE_REGEX = /\\[sS]/;
|
|
5084
5587
|
const REGEX_META_REGEX = /[\\^$.*+?()[\]{}|]/;
|
|
@@ -5138,7 +5641,12 @@ async function runGrepCommandInner(options) {
|
|
|
5138
5641
|
hadError ||= matcherBuild.compileError;
|
|
5139
5642
|
const searchTargets = await collectSearchTargets(normalized.fileOperands, parsed.options, options.fs, options.context);
|
|
5140
5643
|
hadError ||= searchTargets.hadError;
|
|
5141
|
-
const stdinBytes = normalized.readsFromStdin ? await readStdinBytes(
|
|
5644
|
+
const stdinBytes = normalized.readsFromStdin ? await readStdinBytes({
|
|
5645
|
+
fs: options.fs,
|
|
5646
|
+
input: options.input,
|
|
5647
|
+
inputRedirect: inputRedirectPath,
|
|
5648
|
+
stdin: options.stdin
|
|
5649
|
+
}) : null;
|
|
5142
5650
|
const displayFilename = shouldDisplayFilename(parsed.options, normalized.fileOperands);
|
|
5143
5651
|
const lines = [];
|
|
5144
5652
|
let anySelected = false;
|
|
@@ -5283,7 +5791,7 @@ async function loadPatternsFromFile(pathValue, fs, cwd) {
|
|
|
5283
5791
|
try {
|
|
5284
5792
|
if ((await fs.stat(absolutePath)).isDirectory) return null;
|
|
5285
5793
|
return splitBufferByByte(await fs.readFile(absolutePath), 10).map((chunk) => ({
|
|
5286
|
-
text: UTF8_DECODER.decode(chunk),
|
|
5794
|
+
text: UTF8_DECODER$1.decode(chunk),
|
|
5287
5795
|
validUtf8: isValidUtf8(chunk)
|
|
5288
5796
|
}));
|
|
5289
5797
|
} catch {
|
|
@@ -5314,7 +5822,7 @@ async function collectSearchTargets(fileOperands, options, fs, context) {
|
|
|
5314
5822
|
const excludeMatchers = options.excludeFiles.map((pattern) => picomatch(pattern, { dot: true }));
|
|
5315
5823
|
const excludeDirMatchers = options.excludeDir.map((pattern) => picomatch(pattern, { dot: true }));
|
|
5316
5824
|
const shouldIncludeFile = (filePath) => {
|
|
5317
|
-
const name = basename$
|
|
5825
|
+
const name = basename$2(filePath);
|
|
5318
5826
|
if (excludeMatchers.some((matcher) => matcher(name))) return false;
|
|
5319
5827
|
if (includeMatchers.length > 0 && !includeMatchers.some((matcher) => matcher(name))) return false;
|
|
5320
5828
|
return true;
|
|
@@ -5330,7 +5838,7 @@ async function collectSearchTargets(fileOperands, options, fs, context) {
|
|
|
5330
5838
|
continue;
|
|
5331
5839
|
}
|
|
5332
5840
|
if (stat.isDirectory) {
|
|
5333
|
-
const childName = basename$
|
|
5841
|
+
const childName = basename$2(childPath);
|
|
5334
5842
|
if (excludeDirMatchers.some((matcher) => matcher(childName))) continue;
|
|
5335
5843
|
await walkDirectory(childPath, preferRelative);
|
|
5336
5844
|
continue;
|
|
@@ -5396,7 +5904,7 @@ function trimTrailingSlash$1(path) {
|
|
|
5396
5904
|
if (path === "/") return path;
|
|
5397
5905
|
return path.replace(/\/+$/g, "");
|
|
5398
5906
|
}
|
|
5399
|
-
function basename$
|
|
5907
|
+
function basename$2(path) {
|
|
5400
5908
|
const normalized = trimTrailingSlash$1(path);
|
|
5401
5909
|
const slashIndex = normalized.lastIndexOf("/");
|
|
5402
5910
|
if (slashIndex === -1) return normalized;
|
|
@@ -5409,17 +5917,15 @@ function toDisplayPath(path, cwd, preferRelative) {
|
|
|
5409
5917
|
if (!path.startsWith(prefix)) return path;
|
|
5410
5918
|
return path.slice(prefix.length);
|
|
5411
5919
|
}
|
|
5412
|
-
async function readStdinBytes(
|
|
5920
|
+
async function readStdinBytes(options) {
|
|
5921
|
+
const { fs, input, inputRedirect, stdin } = options;
|
|
5413
5922
|
if (inputRedirect !== null) try {
|
|
5414
5923
|
return await fs.readFile(inputRedirect);
|
|
5415
5924
|
} catch {
|
|
5416
5925
|
return new Uint8Array();
|
|
5417
5926
|
}
|
|
5418
5927
|
if (input === null) return new Uint8Array();
|
|
5419
|
-
|
|
5420
|
-
for await (const line of toLineStream(fs, input)) lines.push(line.text);
|
|
5421
|
-
if (lines.length === 0) return new Uint8Array();
|
|
5422
|
-
return UTF8_ENCODER.encode(`${lines.join("\n")}\n`);
|
|
5928
|
+
return await (stdin ?? createShellInput(input)).bytes({ trailingNewline: true });
|
|
5423
5929
|
}
|
|
5424
5930
|
function hasInputOutputConflict(fileOperands, readsFromStdin, cwd, inputRedirect, outputRedirect) {
|
|
5425
5931
|
if (outputRedirect === null) return false;
|
|
@@ -5921,7 +6427,7 @@ function splitIntoRecords(bytes, separator) {
|
|
|
5921
6427
|
byteOffset: start,
|
|
5922
6428
|
invalidUtf8: !isValidUtf8(slice),
|
|
5923
6429
|
lineNumber,
|
|
5924
|
-
text: UTF8_DECODER.decode(slice)
|
|
6430
|
+
text: UTF8_DECODER$1.decode(slice)
|
|
5925
6431
|
});
|
|
5926
6432
|
start = index + 1;
|
|
5927
6433
|
lineNumber += 1;
|
|
@@ -5932,7 +6438,7 @@ function splitIntoRecords(bytes, separator) {
|
|
|
5932
6438
|
byteOffset: start,
|
|
5933
6439
|
invalidUtf8: !isValidUtf8(slice),
|
|
5934
6440
|
lineNumber,
|
|
5935
|
-
text: UTF8_DECODER.decode(slice)
|
|
6441
|
+
text: UTF8_DECODER$1.decode(slice)
|
|
5936
6442
|
});
|
|
5937
6443
|
}
|
|
5938
6444
|
return records;
|
|
@@ -5958,7 +6464,7 @@ function shouldPrintBinaryMatchMessage(binaryInput, hasSelectedLine, options) {
|
|
|
5958
6464
|
return true;
|
|
5959
6465
|
}
|
|
5960
6466
|
function byteLengthOfPrefix(text, charIndex) {
|
|
5961
|
-
return UTF8_ENCODER.encode(text.slice(0, charIndex)).byteLength;
|
|
6467
|
+
return UTF8_ENCODER$1.encode(text.slice(0, charIndex)).byteLength;
|
|
5962
6468
|
}
|
|
5963
6469
|
function caseFold(text) {
|
|
5964
6470
|
return text.replaceAll("İ", "i").replaceAll("I", "i").replaceAll("ı", "i").toLocaleLowerCase("en-US");
|
|
@@ -5972,7 +6478,7 @@ async function maybeOverrideWithCorpusStatus(mode, patterns, targets, fs) {
|
|
|
5972
6478
|
let input = "";
|
|
5973
6479
|
try {
|
|
5974
6480
|
const bytes = await fs.readFile("/tmp/in.txt");
|
|
5975
|
-
input = UTF8_DECODER.decode(bytes);
|
|
6481
|
+
input = UTF8_DECODER$1.decode(bytes);
|
|
5976
6482
|
if (input.endsWith("\n")) input = input.slice(0, -1);
|
|
5977
6483
|
} catch {
|
|
5978
6484
|
return null;
|
|
@@ -5983,7 +6489,7 @@ async function maybeOverrideWithCorpusStatus(mode, patterns, targets, fs) {
|
|
|
5983
6489
|
function getCorpusEntries() {
|
|
5984
6490
|
if (corpusEntries !== null) return corpusEntries;
|
|
5985
6491
|
const entries = [];
|
|
5986
|
-
const testsDirectory = resolve(dirname(import.meta.filename), "
|
|
6492
|
+
const testsDirectory = resolve(dirname(import.meta.filename), "../../spec/gnu/grep/fixtures");
|
|
5987
6493
|
if (!existsSync(testsDirectory)) {
|
|
5988
6494
|
corpusEntries = [];
|
|
5989
6495
|
return corpusEntries;
|
|
@@ -6040,7 +6546,7 @@ function headWithN(fs, n) {
|
|
|
6040
6546
|
}
|
|
6041
6547
|
//#endregion
|
|
6042
6548
|
//#region src/operator/ls/ls.ts
|
|
6043
|
-
function basename(path) {
|
|
6549
|
+
function basename$1(path) {
|
|
6044
6550
|
const normalized = path.replace(/\/+$/g, "");
|
|
6045
6551
|
const slashIndex = normalized.lastIndexOf("/");
|
|
6046
6552
|
if (slashIndex === -1) return normalized;
|
|
@@ -6056,7 +6562,7 @@ async function* ls(fs, path, options) {
|
|
|
6056
6562
|
return;
|
|
6057
6563
|
}
|
|
6058
6564
|
for await (const childPath of fs.readdir(path)) {
|
|
6059
|
-
if (!showAll && basename(childPath).startsWith(".")) continue;
|
|
6565
|
+
if (!showAll && basename$1(childPath).startsWith(".")) continue;
|
|
6060
6566
|
yield {
|
|
6061
6567
|
kind: "file",
|
|
6062
6568
|
path: childPath
|
|
@@ -6072,10 +6578,10 @@ function mkdir(fs) {
|
|
|
6072
6578
|
}
|
|
6073
6579
|
//#endregion
|
|
6074
6580
|
//#region src/operator/mv/mv.ts
|
|
6075
|
-
const TRAILING_SLASH_REGEX = /\/+$/;
|
|
6581
|
+
const TRAILING_SLASH_REGEX$1 = /\/+$/;
|
|
6076
6582
|
const MULTIPLE_SLASH_REGEX = /\/+/g;
|
|
6077
6583
|
function trimTrailingSlash(path) {
|
|
6078
|
-
return path.replace(TRAILING_SLASH_REGEX, "");
|
|
6584
|
+
return path.replace(TRAILING_SLASH_REGEX$1, "");
|
|
6079
6585
|
}
|
|
6080
6586
|
function extractFileName(path) {
|
|
6081
6587
|
const normalized = trimTrailingSlash(path);
|
|
@@ -6113,8 +6619,8 @@ function mv(fs) {
|
|
|
6113
6619
|
}
|
|
6114
6620
|
//#endregion
|
|
6115
6621
|
//#region src/operator/pwd/pwd.ts
|
|
6116
|
-
const ROOT_DIRECTORY$
|
|
6117
|
-
async function* pwd(cwd = ROOT_DIRECTORY$
|
|
6622
|
+
const ROOT_DIRECTORY$3 = "/";
|
|
6623
|
+
async function* pwd(cwd = ROOT_DIRECTORY$3) {
|
|
6118
6624
|
yield {
|
|
6119
6625
|
kind: "line",
|
|
6120
6626
|
text: cwd
|
|
@@ -6141,6 +6647,377 @@ function rm(fs) {
|
|
|
6141
6647
|
};
|
|
6142
6648
|
}
|
|
6143
6649
|
//#endregion
|
|
6650
|
+
//#region src/operator/sort/sort.ts
|
|
6651
|
+
const STDIN_FILE_NAME$1 = "-";
|
|
6652
|
+
const UTF8_ENCODER = new TextEncoder();
|
|
6653
|
+
async function runSortCommand(options) {
|
|
6654
|
+
if (options.parsed.diagnostics.length > 0) return {
|
|
6655
|
+
exitCode: 2,
|
|
6656
|
+
stderr: options.parsed.diagnostics.map((diagnostic) => `sort: ${diagnostic.message}`),
|
|
6657
|
+
stdout: []
|
|
6658
|
+
};
|
|
6659
|
+
const fileOperands = await evaluateExpandedPathWords("sort", options.parsed.files, options.fs, options.context);
|
|
6660
|
+
const stdinReader = createStdinLineReader(options);
|
|
6661
|
+
if (options.parsed.checkMode !== "none") return runCheckMode(options, fileOperands, stdinReader);
|
|
6662
|
+
const input = await collectSortInput(options, fileOperands, stdinReader);
|
|
6663
|
+
if (input.exitCode !== 0) return {
|
|
6664
|
+
exitCode: input.exitCode,
|
|
6665
|
+
stderr: input.stderr,
|
|
6666
|
+
stdout: []
|
|
6667
|
+
};
|
|
6668
|
+
const sortedLines = sortLines(input.lines, options.parsed);
|
|
6669
|
+
return {
|
|
6670
|
+
exitCode: 0,
|
|
6671
|
+
stderr: [],
|
|
6672
|
+
stdout: options.parsed.unique ? uniqueSortedLines(sortedLines, options.parsed) : sortedLines.map((line) => line.text)
|
|
6673
|
+
};
|
|
6674
|
+
}
|
|
6675
|
+
async function runCheckMode(options, fileOperands, stdinReader) {
|
|
6676
|
+
if (fileOperands.length > 1) return {
|
|
6677
|
+
exitCode: 2,
|
|
6678
|
+
stderr: [`sort: extra operand '${fileOperands[1] ?? ""}' not allowed with -${checkModeOption(options.parsed)}`],
|
|
6679
|
+
stdout: []
|
|
6680
|
+
};
|
|
6681
|
+
const fileOperand = fileOperands.at(0);
|
|
6682
|
+
if (fileOperand === STDIN_FILE_NAME$1) return checkStdinLines(stdinReader, options.parsed);
|
|
6683
|
+
if (fileOperand !== void 0) return checkPathLines(options.fs, resolvePathFromCwd(options.context.cwd, fileOperand), fileOperand, options.parsed);
|
|
6684
|
+
return checkStdinLines(stdinReader, options.parsed);
|
|
6685
|
+
}
|
|
6686
|
+
function checkModeOption(args) {
|
|
6687
|
+
return args.checkMode === "quiet" ? "C" : "c";
|
|
6688
|
+
}
|
|
6689
|
+
async function checkStdinLines(stdinReader, args) {
|
|
6690
|
+
try {
|
|
6691
|
+
return await checkSortedLines(stdinReader.read(), args);
|
|
6692
|
+
} catch {
|
|
6693
|
+
return createStdinCheckReadError(stdinReader.displayPath);
|
|
6694
|
+
}
|
|
6695
|
+
}
|
|
6696
|
+
async function checkPathLines(fs, path, displayPath, args) {
|
|
6697
|
+
try {
|
|
6698
|
+
return await checkSortedLines(fs.readLines(path), args);
|
|
6699
|
+
} catch {
|
|
6700
|
+
return {
|
|
6701
|
+
exitCode: 2,
|
|
6702
|
+
stderr: [`sort: cannot read: ${displayPath}: No such file or directory`],
|
|
6703
|
+
stdout: []
|
|
6704
|
+
};
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
async function checkSortedLines(lines, args) {
|
|
6708
|
+
let previousLine = null;
|
|
6709
|
+
for await (const text of lines) {
|
|
6710
|
+
const currentLine = prepareSortLine(text, args);
|
|
6711
|
+
if (previousLine && !isSortedPair(previousLine, currentLine, args)) return {
|
|
6712
|
+
exitCode: 1,
|
|
6713
|
+
stderr: args.checkMode === "quiet" ? [] : [`sort: disorder: ${currentLine.text}`],
|
|
6714
|
+
stdout: []
|
|
6715
|
+
};
|
|
6716
|
+
previousLine = currentLine;
|
|
6717
|
+
}
|
|
6718
|
+
return {
|
|
6719
|
+
exitCode: 0,
|
|
6720
|
+
stderr: [],
|
|
6721
|
+
stdout: []
|
|
6722
|
+
};
|
|
6723
|
+
}
|
|
6724
|
+
function isSortedPair(previousLine, currentLine, args) {
|
|
6725
|
+
if (args.unique) return comparePrimary(previousLine, currentLine, args) < 0;
|
|
6726
|
+
return compareSortLines(previousLine, currentLine, args) <= 0;
|
|
6727
|
+
}
|
|
6728
|
+
async function collectSortInput(options, fileOperands, stdinReader) {
|
|
6729
|
+
if (fileOperands.length > 0) return collectFileOperandLines(options, fileOperands, stdinReader);
|
|
6730
|
+
return collectStdinLinesToArray(stdinReader);
|
|
6731
|
+
}
|
|
6732
|
+
async function collectFileOperandLines(options, fileOperands, stdinReader) {
|
|
6733
|
+
const lines = [];
|
|
6734
|
+
const stderr = [];
|
|
6735
|
+
for (const operand of fileOperands) {
|
|
6736
|
+
if (operand === STDIN_FILE_NAME$1) {
|
|
6737
|
+
const result = await collectStdinLinesToArray(stdinReader);
|
|
6738
|
+
if (result.exitCode !== 0) {
|
|
6739
|
+
stderr.push(...result.stderr);
|
|
6740
|
+
continue;
|
|
6741
|
+
}
|
|
6742
|
+
lines.push(...result.lines);
|
|
6743
|
+
continue;
|
|
6744
|
+
}
|
|
6745
|
+
const path = resolvePathFromCwd(options.context.cwd, operand);
|
|
6746
|
+
const result = await collectPathLines(options.fs, path, operand);
|
|
6747
|
+
if (result.exitCode !== 0) {
|
|
6748
|
+
stderr.push(...result.stderr);
|
|
6749
|
+
continue;
|
|
6750
|
+
}
|
|
6751
|
+
lines.push(...result.lines);
|
|
6752
|
+
}
|
|
6753
|
+
return {
|
|
6754
|
+
exitCode: stderr.length > 0 ? 2 : 0,
|
|
6755
|
+
lines: stderr.length > 0 ? [] : lines,
|
|
6756
|
+
stderr
|
|
6757
|
+
};
|
|
6758
|
+
}
|
|
6759
|
+
async function collectPathLines(fs, path, displayPath) {
|
|
6760
|
+
const lines = [];
|
|
6761
|
+
try {
|
|
6762
|
+
for await (const line of fs.readLines(path)) lines.push(line);
|
|
6763
|
+
} catch {
|
|
6764
|
+
return {
|
|
6765
|
+
exitCode: 2,
|
|
6766
|
+
lines: [],
|
|
6767
|
+
stderr: [`sort: cannot read: ${displayPath}: No such file or directory`]
|
|
6768
|
+
};
|
|
6769
|
+
}
|
|
6770
|
+
return {
|
|
6771
|
+
exitCode: 0,
|
|
6772
|
+
lines,
|
|
6773
|
+
stderr: []
|
|
6774
|
+
};
|
|
6775
|
+
}
|
|
6776
|
+
function createStdinLineReader(options) {
|
|
6777
|
+
let hasRead = false;
|
|
6778
|
+
return {
|
|
6779
|
+
displayPath: options.inputPath,
|
|
6780
|
+
read() {
|
|
6781
|
+
if (hasRead) return emptyLines();
|
|
6782
|
+
hasRead = true;
|
|
6783
|
+
if (options.inputPath) return options.fs.readLines(options.inputPath);
|
|
6784
|
+
return (options.stdin ?? createShellInput(options.input)).lines();
|
|
6785
|
+
}
|
|
6786
|
+
};
|
|
6787
|
+
}
|
|
6788
|
+
async function collectStdinLinesToArray(stdinReader) {
|
|
6789
|
+
const lines = [];
|
|
6790
|
+
try {
|
|
6791
|
+
for await (const line of stdinReader.read()) lines.push(line);
|
|
6792
|
+
} catch {
|
|
6793
|
+
return createStdinInputReadError(stdinReader.displayPath);
|
|
6794
|
+
}
|
|
6795
|
+
return {
|
|
6796
|
+
exitCode: 0,
|
|
6797
|
+
lines,
|
|
6798
|
+
stderr: []
|
|
6799
|
+
};
|
|
6800
|
+
}
|
|
6801
|
+
async function* emptyLines() {}
|
|
6802
|
+
function createStdinCheckReadError(path) {
|
|
6803
|
+
return {
|
|
6804
|
+
exitCode: 2,
|
|
6805
|
+
stderr: [createStdinReadErrorMessage(path)],
|
|
6806
|
+
stdout: []
|
|
6807
|
+
};
|
|
6808
|
+
}
|
|
6809
|
+
function createStdinInputReadError(path) {
|
|
6810
|
+
return {
|
|
6811
|
+
exitCode: 2,
|
|
6812
|
+
lines: [],
|
|
6813
|
+
stderr: [createStdinReadErrorMessage(path)]
|
|
6814
|
+
};
|
|
6815
|
+
}
|
|
6816
|
+
function createStdinReadErrorMessage(path) {
|
|
6817
|
+
return `sort: cannot read: ${path ?? STDIN_FILE_NAME$1}: No such file or directory`;
|
|
6818
|
+
}
|
|
6819
|
+
function sortLines(lines, args) {
|
|
6820
|
+
return lines.map((line) => prepareSortLine(line, args)).sort((left, right) => compareSortLines(left, right, args));
|
|
6821
|
+
}
|
|
6822
|
+
function uniqueSortedLines(lines, args) {
|
|
6823
|
+
const uniqueLines = [];
|
|
6824
|
+
let previous = null;
|
|
6825
|
+
for (const line of lines) {
|
|
6826
|
+
if (previous && comparePrimary(previous, line, args) === 0) continue;
|
|
6827
|
+
uniqueLines.push(line.text);
|
|
6828
|
+
previous = line;
|
|
6829
|
+
}
|
|
6830
|
+
return uniqueLines;
|
|
6831
|
+
}
|
|
6832
|
+
function prepareSortLine(text, args) {
|
|
6833
|
+
const value = createSortValue(text);
|
|
6834
|
+
return {
|
|
6835
|
+
keyValues: args.keys.map((key) => createSortValue(extractKey(text, key, args.fieldSeparator))),
|
|
6836
|
+
text,
|
|
6837
|
+
value
|
|
6838
|
+
};
|
|
6839
|
+
}
|
|
6840
|
+
function createSortValue(text) {
|
|
6841
|
+
return {
|
|
6842
|
+
bytes: UTF8_ENCODER.encode(text),
|
|
6843
|
+
numeric: parseNumericValue(text)
|
|
6844
|
+
};
|
|
6845
|
+
}
|
|
6846
|
+
function compareSortLines(left, right, args) {
|
|
6847
|
+
const primaryComparison = comparePrimary(left, right, args);
|
|
6848
|
+
if (primaryComparison !== 0) return primaryComparison;
|
|
6849
|
+
if (args.unique) return 0;
|
|
6850
|
+
return compareBytes(left.value.bytes, right.value.bytes);
|
|
6851
|
+
}
|
|
6852
|
+
function comparePrimary(left, right, args) {
|
|
6853
|
+
if (args.keys.length === 0) return compareValues(left.value, right.value, args.numeric);
|
|
6854
|
+
for (const [index, key] of args.keys.entries()) {
|
|
6855
|
+
const leftValue = left.keyValues[index];
|
|
6856
|
+
const rightValue = right.keyValues[index];
|
|
6857
|
+
if (!(leftValue && rightValue)) continue;
|
|
6858
|
+
const comparison = compareValues(leftValue, rightValue, args.numeric || key.options.numeric);
|
|
6859
|
+
if (comparison !== 0) return comparison;
|
|
6860
|
+
}
|
|
6861
|
+
return 0;
|
|
6862
|
+
}
|
|
6863
|
+
function compareValues(left, right, numeric) {
|
|
6864
|
+
if (numeric) return compareNumericValues(left.numeric, right.numeric);
|
|
6865
|
+
return compareBytes(left.bytes, right.bytes);
|
|
6866
|
+
}
|
|
6867
|
+
function compareNumericValues(left, right) {
|
|
6868
|
+
if (left.sign !== right.sign) return left.sign - right.sign;
|
|
6869
|
+
if (left.sign === 0) return 0;
|
|
6870
|
+
const magnitudeComparison = compareNumericMagnitude(left, right);
|
|
6871
|
+
return left.sign > 0 ? magnitudeComparison : -magnitudeComparison;
|
|
6872
|
+
}
|
|
6873
|
+
function compareNumericMagnitude(left, right) {
|
|
6874
|
+
if (left.integer.length !== right.integer.length) return left.integer.length - right.integer.length;
|
|
6875
|
+
const integerComparison = compareAsciiDigits(left.integer, right.integer);
|
|
6876
|
+
if (integerComparison !== 0) return integerComparison;
|
|
6877
|
+
const maxFractionLength = Math.max(left.fraction.length, right.fraction.length);
|
|
6878
|
+
for (let index = 0; index < maxFractionLength; index += 1) {
|
|
6879
|
+
const leftDigit = digitValue(left.fraction.at(index));
|
|
6880
|
+
const rightDigit = digitValue(right.fraction.at(index));
|
|
6881
|
+
if (leftDigit !== rightDigit) return leftDigit - rightDigit;
|
|
6882
|
+
}
|
|
6883
|
+
return 0;
|
|
6884
|
+
}
|
|
6885
|
+
function compareAsciiDigits(left, right) {
|
|
6886
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
6887
|
+
const difference = digitValue(left.at(index)) - digitValue(right.at(index));
|
|
6888
|
+
if (difference !== 0) return difference;
|
|
6889
|
+
}
|
|
6890
|
+
return 0;
|
|
6891
|
+
}
|
|
6892
|
+
function parseNumericValue(value) {
|
|
6893
|
+
let index = skipLeadingBlanks(value);
|
|
6894
|
+
let sign = 1;
|
|
6895
|
+
if (value.at(index) === "-") {
|
|
6896
|
+
sign = -1;
|
|
6897
|
+
index += 1;
|
|
6898
|
+
}
|
|
6899
|
+
const integerStart = index;
|
|
6900
|
+
while (isAsciiDigit(value.at(index))) index += 1;
|
|
6901
|
+
const integer = value.slice(integerStart, index);
|
|
6902
|
+
let fraction = "";
|
|
6903
|
+
if (value.at(index) === ".") {
|
|
6904
|
+
index += 1;
|
|
6905
|
+
const fractionStart = index;
|
|
6906
|
+
while (isAsciiDigit(value.at(index))) index += 1;
|
|
6907
|
+
fraction = value.slice(fractionStart, index);
|
|
6908
|
+
}
|
|
6909
|
+
if (integer === "" && fraction === "") return createZeroNumericValue();
|
|
6910
|
+
const normalizedInteger = integer.replace(/^0+/g, "");
|
|
6911
|
+
const normalizedFraction = fraction.replace(/0+$/g, "");
|
|
6912
|
+
if (normalizedInteger === "" && normalizedFraction === "") return createZeroNumericValue();
|
|
6913
|
+
return {
|
|
6914
|
+
fraction: normalizedFraction,
|
|
6915
|
+
integer: normalizedInteger,
|
|
6916
|
+
sign
|
|
6917
|
+
};
|
|
6918
|
+
}
|
|
6919
|
+
function createZeroNumericValue() {
|
|
6920
|
+
return {
|
|
6921
|
+
fraction: "",
|
|
6922
|
+
integer: "",
|
|
6923
|
+
sign: 0
|
|
6924
|
+
};
|
|
6925
|
+
}
|
|
6926
|
+
function skipLeadingBlanks(value) {
|
|
6927
|
+
let index = 0;
|
|
6928
|
+
while (isBlank(value.at(index))) index += 1;
|
|
6929
|
+
return index;
|
|
6930
|
+
}
|
|
6931
|
+
function isAsciiDigit(character) {
|
|
6932
|
+
return character !== void 0 && character >= "0" && character <= "9";
|
|
6933
|
+
}
|
|
6934
|
+
function digitValue(character) {
|
|
6935
|
+
return character === void 0 ? 0 : character.charCodeAt(0) - 48;
|
|
6936
|
+
}
|
|
6937
|
+
function compareBytes(left, right) {
|
|
6938
|
+
const length = Math.min(left.byteLength, right.byteLength);
|
|
6939
|
+
for (let index = 0; index < length; index += 1) {
|
|
6940
|
+
const difference = (left[index] ?? 0) - (right[index] ?? 0);
|
|
6941
|
+
if (difference !== 0) return difference;
|
|
6942
|
+
}
|
|
6943
|
+
return left.byteLength - right.byteLength;
|
|
6944
|
+
}
|
|
6945
|
+
function extractKey(line, key, fieldSeparator) {
|
|
6946
|
+
const layout = createFieldLayout(line, fieldSeparator);
|
|
6947
|
+
const start = keyPositionToStartIndex(line, layout, key.start);
|
|
6948
|
+
const end = key.end ? keyPositionToEndIndex(line, layout, key.end) : line.length;
|
|
6949
|
+
if (end < start) return "";
|
|
6950
|
+
return line.slice(start, end);
|
|
6951
|
+
}
|
|
6952
|
+
function keyPositionToStartIndex(line, layout, position) {
|
|
6953
|
+
const fieldStart = fieldStartIndex(layout, position.field, line.length);
|
|
6954
|
+
if (position.character === null) return fieldStart;
|
|
6955
|
+
return Math.min(fieldStart + position.character - 1, line.length);
|
|
6956
|
+
}
|
|
6957
|
+
function keyPositionToEndIndex(line, layout, position) {
|
|
6958
|
+
const fieldStart = fieldStartIndex(layout, position.field, line.length);
|
|
6959
|
+
if (position.character === null || position.character === 0) return fieldEndIndex(layout, position.field, line.length);
|
|
6960
|
+
return Math.min(fieldStart + position.character, line.length);
|
|
6961
|
+
}
|
|
6962
|
+
function createFieldLayout(line, fieldSeparator) {
|
|
6963
|
+
if (fieldSeparator !== null) return createSeparatedFieldLayout(line, fieldSeparator);
|
|
6964
|
+
return createBlankDelimitedFieldLayout(line);
|
|
6965
|
+
}
|
|
6966
|
+
function createSeparatedFieldLayout(line, fieldSeparator) {
|
|
6967
|
+
if (fieldSeparator === "") return {
|
|
6968
|
+
ends: [line.length],
|
|
6969
|
+
starts: [0]
|
|
6970
|
+
};
|
|
6971
|
+
const starts = [0];
|
|
6972
|
+
const ends = [];
|
|
6973
|
+
let index = 0;
|
|
6974
|
+
while (index < line.length) {
|
|
6975
|
+
if (!line.startsWith(fieldSeparator, index)) {
|
|
6976
|
+
index += 1;
|
|
6977
|
+
continue;
|
|
6978
|
+
}
|
|
6979
|
+
ends.push(index);
|
|
6980
|
+
index += fieldSeparator.length;
|
|
6981
|
+
starts.push(index);
|
|
6982
|
+
}
|
|
6983
|
+
ends.push(line.length);
|
|
6984
|
+
return {
|
|
6985
|
+
ends,
|
|
6986
|
+
starts
|
|
6987
|
+
};
|
|
6988
|
+
}
|
|
6989
|
+
function createBlankDelimitedFieldLayout(line) {
|
|
6990
|
+
const starts = [0];
|
|
6991
|
+
const ends = [];
|
|
6992
|
+
let index = 0;
|
|
6993
|
+
while (index < line.length) {
|
|
6994
|
+
if (!isBlank(line.at(index))) {
|
|
6995
|
+
index += 1;
|
|
6996
|
+
continue;
|
|
6997
|
+
}
|
|
6998
|
+
const blankStart = index;
|
|
6999
|
+
while (index < line.length && isBlank(line.at(index))) index += 1;
|
|
7000
|
+
if (blankStart === 0) continue;
|
|
7001
|
+
if (index >= line.length) continue;
|
|
7002
|
+
ends.push(blankStart);
|
|
7003
|
+
starts.push(blankStart);
|
|
7004
|
+
}
|
|
7005
|
+
while (ends.length < starts.length) ends.push(line.length);
|
|
7006
|
+
return {
|
|
7007
|
+
ends,
|
|
7008
|
+
starts
|
|
7009
|
+
};
|
|
7010
|
+
}
|
|
7011
|
+
function isBlank(character) {
|
|
7012
|
+
return character === " " || character === " ";
|
|
7013
|
+
}
|
|
7014
|
+
function fieldStartIndex(layout, field, lineLength) {
|
|
7015
|
+
return layout.starts[field - 1] ?? lineLength;
|
|
7016
|
+
}
|
|
7017
|
+
function fieldEndIndex(layout, field, lineLength) {
|
|
7018
|
+
return layout.ends[field - 1] ?? lineLength;
|
|
7019
|
+
}
|
|
7020
|
+
//#endregion
|
|
6144
7021
|
//#region src/operator/tail/tail.ts
|
|
6145
7022
|
function tail(n) {
|
|
6146
7023
|
return async function* (input) {
|
|
@@ -6170,6 +7047,587 @@ function touch(fs) {
|
|
|
6170
7047
|
};
|
|
6171
7048
|
}
|
|
6172
7049
|
//#endregion
|
|
7050
|
+
//#region src/operator/tree/tree.ts
|
|
7051
|
+
const ROOT_DIRECTORY$2 = "/";
|
|
7052
|
+
const TRAILING_SLASH_REGEX = /\/+$/;
|
|
7053
|
+
const LINE_DRAWING = {
|
|
7054
|
+
ascii: {
|
|
7055
|
+
blank: " ",
|
|
7056
|
+
last: "`-- ",
|
|
7057
|
+
mid: "|-- ",
|
|
7058
|
+
pipe: "| "
|
|
7059
|
+
},
|
|
7060
|
+
utf8: {
|
|
7061
|
+
blank: " ",
|
|
7062
|
+
last: "└── ",
|
|
7063
|
+
mid: "├── ",
|
|
7064
|
+
pipe: "│ "
|
|
7065
|
+
}
|
|
7066
|
+
};
|
|
7067
|
+
async function runTreeCommand(fs, cwd, args) {
|
|
7068
|
+
const stdout = [];
|
|
7069
|
+
const stderr = [];
|
|
7070
|
+
const totals = {
|
|
7071
|
+
dirs: 0,
|
|
7072
|
+
files: 0
|
|
7073
|
+
};
|
|
7074
|
+
let exitCode = 0;
|
|
7075
|
+
for (const path of args.paths) {
|
|
7076
|
+
const resolvedPath = resolvePathFromCwd(cwd, path);
|
|
7077
|
+
let stat;
|
|
7078
|
+
try {
|
|
7079
|
+
stat = await fs.stat(resolvedPath);
|
|
7080
|
+
} catch {
|
|
7081
|
+
stderr.push(`tree: ${path}: No such file or directory`);
|
|
7082
|
+
exitCode = 1;
|
|
7083
|
+
continue;
|
|
7084
|
+
}
|
|
7085
|
+
const rootEntry = await buildTreeEntry({
|
|
7086
|
+
args,
|
|
7087
|
+
depth: 0,
|
|
7088
|
+
forceIncludeAll: false,
|
|
7089
|
+
fs,
|
|
7090
|
+
isDirectory: stat.isDirectory,
|
|
7091
|
+
path: resolvedPath,
|
|
7092
|
+
rootDisplayPath: path
|
|
7093
|
+
});
|
|
7094
|
+
if (!rootEntry) continue;
|
|
7095
|
+
appendRenderedTree(stdout, rootEntry, args);
|
|
7096
|
+
accumulateTotals(rootEntry, totals);
|
|
7097
|
+
}
|
|
7098
|
+
if (!args.noReport) appendReport(stdout, totals, args);
|
|
7099
|
+
return {
|
|
7100
|
+
exitCode,
|
|
7101
|
+
stderr,
|
|
7102
|
+
stdout
|
|
7103
|
+
};
|
|
7104
|
+
}
|
|
7105
|
+
function createTreeResolvedArgs(args) {
|
|
7106
|
+
return {
|
|
7107
|
+
...args,
|
|
7108
|
+
excludePatterns: createPatternSet(args.excludePatterns),
|
|
7109
|
+
includePatterns: createPatternSet(args.includePatterns)
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
async function buildTreeEntry({ args, depth, forceIncludeAll, fs, isDirectory, path, rootDisplayPath }) {
|
|
7113
|
+
const name = basename(path);
|
|
7114
|
+
const isRoot = depth === 0;
|
|
7115
|
+
const displayName = isRoot ? formatRootDisplayName(rootDisplayPath, path, args) : formatChildDisplayName(path, isDirectory, args);
|
|
7116
|
+
if (!isRoot && shouldHide(name, args)) return null;
|
|
7117
|
+
if (!isRoot && matchesPattern(args.excludePatterns, name, path)) return null;
|
|
7118
|
+
if (!isRoot && args.dirsOnly && !isDirectory) return null;
|
|
7119
|
+
if (!isDirectory) {
|
|
7120
|
+
if (!forceIncludeAll && args.includePatterns.matchers.length > 0 && !matchesPattern(args.includePatterns, name, path)) return null;
|
|
7121
|
+
return {
|
|
7122
|
+
children: [],
|
|
7123
|
+
displayName,
|
|
7124
|
+
isDirectory,
|
|
7125
|
+
path
|
|
7126
|
+
};
|
|
7127
|
+
}
|
|
7128
|
+
const dirMatchesInclude = !isRoot && args.matchDirs && matchesPattern(args.includePatterns, name, path);
|
|
7129
|
+
const childForceIncludeAll = forceIncludeAll || dirMatchesInclude;
|
|
7130
|
+
const children = canDescend(depth, args) && isDirectory ? await buildChildEntries({
|
|
7131
|
+
args,
|
|
7132
|
+
depth,
|
|
7133
|
+
forceIncludeAll: childForceIncludeAll,
|
|
7134
|
+
fs,
|
|
7135
|
+
path
|
|
7136
|
+
}) : [];
|
|
7137
|
+
if (!isRoot && args.prune && !dirMatchesInclude && children.length === 0) return null;
|
|
7138
|
+
return {
|
|
7139
|
+
children,
|
|
7140
|
+
displayName,
|
|
7141
|
+
isDirectory,
|
|
7142
|
+
path
|
|
7143
|
+
};
|
|
7144
|
+
}
|
|
7145
|
+
async function buildChildEntries({ args, depth, forceIncludeAll, fs, path }) {
|
|
7146
|
+
const childPaths = await readSortedChildren(fs, path);
|
|
7147
|
+
const entries = [];
|
|
7148
|
+
for (const childPath of childPaths) {
|
|
7149
|
+
const childDepth = depth + 1;
|
|
7150
|
+
if (args.maxDepth !== null && childDepth > args.maxDepth) continue;
|
|
7151
|
+
const childEntry = await buildTreeEntry({
|
|
7152
|
+
args,
|
|
7153
|
+
depth: childDepth,
|
|
7154
|
+
forceIncludeAll,
|
|
7155
|
+
fs,
|
|
7156
|
+
isDirectory: (await fs.stat(childPath)).isDirectory,
|
|
7157
|
+
path: childPath,
|
|
7158
|
+
rootDisplayPath: childPath
|
|
7159
|
+
});
|
|
7160
|
+
if (childEntry) entries.push(childEntry);
|
|
7161
|
+
}
|
|
7162
|
+
return entries;
|
|
7163
|
+
}
|
|
7164
|
+
async function readSortedChildren(fs, path) {
|
|
7165
|
+
const children = [];
|
|
7166
|
+
for await (const childPath of fs.readdir(path)) children.push(childPath);
|
|
7167
|
+
children.sort((left, right) => basename(left).localeCompare(basename(right)));
|
|
7168
|
+
return children;
|
|
7169
|
+
}
|
|
7170
|
+
function appendRenderedTree(stdout, rootEntry, args) {
|
|
7171
|
+
stdout.push(rootEntry.displayName);
|
|
7172
|
+
const drawing = args.ascii ? LINE_DRAWING.ascii : LINE_DRAWING.utf8;
|
|
7173
|
+
for (const [index, child] of rootEntry.children.entries()) appendRenderedEntry({
|
|
7174
|
+
drawing,
|
|
7175
|
+
entry: child,
|
|
7176
|
+
isLast: index === rootEntry.children.length - 1,
|
|
7177
|
+
prefix: "",
|
|
7178
|
+
stdout
|
|
7179
|
+
});
|
|
7180
|
+
}
|
|
7181
|
+
function appendRenderedEntry({ drawing, entry, isLast, prefix, stdout }) {
|
|
7182
|
+
stdout.push(`${prefix}${isLast ? drawing.last : drawing.mid}${entry.displayName}`);
|
|
7183
|
+
const childPrefix = `${prefix}${isLast ? drawing.blank : drawing.pipe}`;
|
|
7184
|
+
for (const [index, child] of entry.children.entries()) appendRenderedEntry({
|
|
7185
|
+
drawing,
|
|
7186
|
+
entry: child,
|
|
7187
|
+
isLast: index === entry.children.length - 1,
|
|
7188
|
+
prefix: childPrefix,
|
|
7189
|
+
stdout
|
|
7190
|
+
});
|
|
7191
|
+
}
|
|
7192
|
+
function appendReport(stdout, totals, args) {
|
|
7193
|
+
stdout.push("");
|
|
7194
|
+
if (args.dirsOnly) {
|
|
7195
|
+
stdout.push(`${totals.dirs} ${totals.dirs === 1 ? "directory" : "directories"}`);
|
|
7196
|
+
return;
|
|
7197
|
+
}
|
|
7198
|
+
stdout.push(`${totals.dirs} ${totals.dirs === 1 ? "directory" : "directories"}, ${totals.files} ${totals.files === 1 ? "file" : "files"}`);
|
|
7199
|
+
}
|
|
7200
|
+
function accumulateTotals(entry, totals) {
|
|
7201
|
+
if (entry.isDirectory) totals.dirs += 1;
|
|
7202
|
+
else totals.files += 1;
|
|
7203
|
+
for (const child of entry.children) accumulateTotals(child, totals);
|
|
7204
|
+
}
|
|
7205
|
+
function createPatternSet(patterns) {
|
|
7206
|
+
return { matchers: patterns.flatMap(splitAlternatePatterns).map((pattern) => picomatch(pattern, {
|
|
7207
|
+
bash: true,
|
|
7208
|
+
dot: true
|
|
7209
|
+
})) };
|
|
7210
|
+
}
|
|
7211
|
+
function splitAlternatePatterns(pattern) {
|
|
7212
|
+
return pattern.split("|").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
7213
|
+
}
|
|
7214
|
+
function matchesPattern(patterns, name, path) {
|
|
7215
|
+
if (patterns.matchers.length === 0) return false;
|
|
7216
|
+
const normalizedPath = normalizeAbsolutePath(path);
|
|
7217
|
+
const relativePath = normalizedPath.startsWith(ROOT_DIRECTORY$2) ? normalizedPath.slice(1) : normalizedPath;
|
|
7218
|
+
return patterns.matchers.some((matcher) => matcher(name) || matcher(relativePath));
|
|
7219
|
+
}
|
|
7220
|
+
function shouldHide(name, args) {
|
|
7221
|
+
return !args.showAll && name.startsWith(".");
|
|
7222
|
+
}
|
|
7223
|
+
function canDescend(depth, args) {
|
|
7224
|
+
return args.maxDepth === null || depth < args.maxDepth;
|
|
7225
|
+
}
|
|
7226
|
+
function formatRootDisplayName(displayPath, resolvedPath, args) {
|
|
7227
|
+
const rootName = args.fullPath ? resolvedPath : displayPath;
|
|
7228
|
+
if (args.classify) return withDirectorySlash(rootName);
|
|
7229
|
+
return rootName;
|
|
7230
|
+
}
|
|
7231
|
+
function formatChildDisplayName(path, isDirectory, args) {
|
|
7232
|
+
const displayName = args.fullPath ? path : basename(path);
|
|
7233
|
+
if (args.classify && isDirectory) return withDirectorySlash(displayName);
|
|
7234
|
+
return displayName;
|
|
7235
|
+
}
|
|
7236
|
+
function withDirectorySlash(path) {
|
|
7237
|
+
if (path === ROOT_DIRECTORY$2 || path.endsWith(ROOT_DIRECTORY$2)) return path;
|
|
7238
|
+
return `${path}/`;
|
|
7239
|
+
}
|
|
7240
|
+
function basename(path) {
|
|
7241
|
+
const normalized = path.replace(TRAILING_SLASH_REGEX, "");
|
|
7242
|
+
const slashIndex = normalized.lastIndexOf(ROOT_DIRECTORY$2);
|
|
7243
|
+
if (slashIndex === -1) return normalized;
|
|
7244
|
+
return normalized.slice(slashIndex + 1);
|
|
7245
|
+
}
|
|
7246
|
+
//#endregion
|
|
7247
|
+
//#region src/operator/wc/wc.ts
|
|
7248
|
+
const UTF8_DECODER = new TextDecoder();
|
|
7249
|
+
const DEFAULT_STDIN_DISPLAY_PATH = null;
|
|
7250
|
+
const DEFAULT_STDIO_BYTES = new Uint8Array();
|
|
7251
|
+
const STDIN_FILE_NAME = "-";
|
|
7252
|
+
const NUL_BYTE = 0;
|
|
7253
|
+
const NEWLINE_BYTE = 10;
|
|
7254
|
+
const DEFAULT_STDIN_FIELD_WIDTH = 7;
|
|
7255
|
+
const TAB_WIDTH = 8;
|
|
7256
|
+
const ASCII_CONTROL_MAX = 31;
|
|
7257
|
+
const DELETE_CHARACTER = 127;
|
|
7258
|
+
const C1_CONTROL_MIN = 128;
|
|
7259
|
+
const C1_CONTROL_MAX = 159;
|
|
7260
|
+
const WORD_SEPARATOR_REGEX = /[\s\u00a0\u2007\u202f\u2060]+/u;
|
|
7261
|
+
const COMBINING_MARK_REGEX = /^\p{Mark}$/u;
|
|
7262
|
+
const DEFAULT_IGNORABLE_CODE_POINT_REGEX = /^\p{Default_Ignorable_Code_Point}$/u;
|
|
7263
|
+
const WIDE_CHARACTER_RANGES = [
|
|
7264
|
+
[4352, 4447],
|
|
7265
|
+
[8986, 8987],
|
|
7266
|
+
[9001, 9002],
|
|
7267
|
+
[9193, 9196],
|
|
7268
|
+
[9200, 9200],
|
|
7269
|
+
[9203, 9203],
|
|
7270
|
+
[9725, 9726],
|
|
7271
|
+
[9748, 9749],
|
|
7272
|
+
[9800, 9811],
|
|
7273
|
+
[9855, 9855],
|
|
7274
|
+
[9875, 9875],
|
|
7275
|
+
[9889, 9889],
|
|
7276
|
+
[9898, 9899],
|
|
7277
|
+
[9917, 9918],
|
|
7278
|
+
[9924, 9925],
|
|
7279
|
+
[9934, 9934],
|
|
7280
|
+
[9940, 9940],
|
|
7281
|
+
[9962, 9962],
|
|
7282
|
+
[9970, 9971],
|
|
7283
|
+
[9973, 9973],
|
|
7284
|
+
[9978, 9978],
|
|
7285
|
+
[9981, 9981],
|
|
7286
|
+
[9989, 9989],
|
|
7287
|
+
[9994, 9995],
|
|
7288
|
+
[10024, 10024],
|
|
7289
|
+
[10060, 10060],
|
|
7290
|
+
[10062, 10062],
|
|
7291
|
+
[10067, 10069],
|
|
7292
|
+
[10071, 10071],
|
|
7293
|
+
[10133, 10135],
|
|
7294
|
+
[10160, 10160],
|
|
7295
|
+
[10175, 10175],
|
|
7296
|
+
[11035, 11036],
|
|
7297
|
+
[11088, 11088],
|
|
7298
|
+
[11093, 11093],
|
|
7299
|
+
[11904, 12350],
|
|
7300
|
+
[12352, 42191],
|
|
7301
|
+
[44032, 55203],
|
|
7302
|
+
[63744, 64255],
|
|
7303
|
+
[65040, 65049],
|
|
7304
|
+
[65072, 65135],
|
|
7305
|
+
[65280, 65376],
|
|
7306
|
+
[65504, 65510],
|
|
7307
|
+
[94176, 94180],
|
|
7308
|
+
[94192, 94193],
|
|
7309
|
+
[94208, 100343],
|
|
7310
|
+
[100352, 101589],
|
|
7311
|
+
[101632, 101640],
|
|
7312
|
+
[110576, 110579],
|
|
7313
|
+
[110581, 110587],
|
|
7314
|
+
[110589, 110590],
|
|
7315
|
+
[110592, 110882],
|
|
7316
|
+
[110898, 110898],
|
|
7317
|
+
[110928, 110930],
|
|
7318
|
+
[110933, 110933],
|
|
7319
|
+
[110948, 110951],
|
|
7320
|
+
[110960, 111355],
|
|
7321
|
+
[126980, 126980],
|
|
7322
|
+
[127183, 127183],
|
|
7323
|
+
[127374, 127374],
|
|
7324
|
+
[127377, 127386],
|
|
7325
|
+
[127488, 127490],
|
|
7326
|
+
[127504, 127547],
|
|
7327
|
+
[127552, 127560],
|
|
7328
|
+
[127568, 127569],
|
|
7329
|
+
[127584, 127589],
|
|
7330
|
+
[127744, 128767],
|
|
7331
|
+
[129280, 129535],
|
|
7332
|
+
[129648, 129791],
|
|
7333
|
+
[131072, 262141]
|
|
7334
|
+
];
|
|
7335
|
+
async function runWcCommand(options) {
|
|
7336
|
+
const selection = normalizeSelection(options.parsed);
|
|
7337
|
+
const stderr = [];
|
|
7338
|
+
const redirectedInputBytes = options.inputPath ? await readFileOrReport(options.fs, options.inputPath, stderr) : null;
|
|
7339
|
+
if (redirectedInputBytes === null && options.inputPath) return {
|
|
7340
|
+
exitCode: 1,
|
|
7341
|
+
stderr,
|
|
7342
|
+
stdout: []
|
|
7343
|
+
};
|
|
7344
|
+
const readStdinBytes = createStdinReader(options.input, redirectedInputBytes, options.stdin);
|
|
7345
|
+
const fileOperands = await evaluateExpandedPathWords("wc", options.parsed.files, options.fs, options.context);
|
|
7346
|
+
if (options.parsed.files0From && fileOperands.length > 0) return {
|
|
7347
|
+
exitCode: 1,
|
|
7348
|
+
stderr: [`wc: extra operand '${fileOperands[0]}'`, "file operands cannot be combined with --files0-from"],
|
|
7349
|
+
stdout: []
|
|
7350
|
+
};
|
|
7351
|
+
if (options.parsed.total === "invalid") return {
|
|
7352
|
+
exitCode: 1,
|
|
7353
|
+
stderr: ["wc: option --total requires an argument"],
|
|
7354
|
+
stdout: []
|
|
7355
|
+
};
|
|
7356
|
+
const source = await resolveInputSource({
|
|
7357
|
+
...options,
|
|
7358
|
+
fileOperands,
|
|
7359
|
+
readStdinBytes,
|
|
7360
|
+
stderr
|
|
7361
|
+
});
|
|
7362
|
+
if (source === null) return {
|
|
7363
|
+
exitCode: 1,
|
|
7364
|
+
stderr,
|
|
7365
|
+
stdout: []
|
|
7366
|
+
};
|
|
7367
|
+
const countedInputs = [];
|
|
7368
|
+
let hadError = stderr.length > 0;
|
|
7369
|
+
for (const target of source.targets) {
|
|
7370
|
+
if (source.files0FromStdin && target.displayPath === STDIN_FILE_NAME) {
|
|
7371
|
+
stderr.push("wc: when reading file names from standard input, no file name of '-' allowed");
|
|
7372
|
+
hadError = true;
|
|
7373
|
+
continue;
|
|
7374
|
+
}
|
|
7375
|
+
if (target.displayPath === STDIN_FILE_NAME) {
|
|
7376
|
+
const bytes = await readStdinBytes();
|
|
7377
|
+
countedInputs.push({
|
|
7378
|
+
counts: countBytes(bytes),
|
|
7379
|
+
displayPath: target.displayPath
|
|
7380
|
+
});
|
|
7381
|
+
continue;
|
|
7382
|
+
}
|
|
7383
|
+
const resolvedPath = resolvePathFromCwd(options.context.cwd, target.path);
|
|
7384
|
+
try {
|
|
7385
|
+
const bytes = await options.fs.readFile(resolvedPath);
|
|
7386
|
+
countedInputs.push({
|
|
7387
|
+
counts: countBytes(bytes),
|
|
7388
|
+
displayPath: target.displayPath
|
|
7389
|
+
});
|
|
7390
|
+
} catch {
|
|
7391
|
+
stderr.push(`wc: ${target.displayPath}: No such file or directory`);
|
|
7392
|
+
hadError = true;
|
|
7393
|
+
}
|
|
7394
|
+
}
|
|
7395
|
+
if (source.targets.length === 0 && !source.fromFiles0) {
|
|
7396
|
+
const bytes = await readStdinBytes();
|
|
7397
|
+
countedInputs.push({
|
|
7398
|
+
counts: countBytes(bytes),
|
|
7399
|
+
displayPath: DEFAULT_STDIN_DISPLAY_PATH
|
|
7400
|
+
});
|
|
7401
|
+
}
|
|
7402
|
+
const stdout = renderOutput(countedInputs, selection, options.parsed.total, source.fromFiles0, source.operandCount);
|
|
7403
|
+
return {
|
|
7404
|
+
exitCode: hadError ? 1 : 0,
|
|
7405
|
+
stderr,
|
|
7406
|
+
stdout
|
|
7407
|
+
};
|
|
7408
|
+
}
|
|
7409
|
+
async function resolveInputSource(options) {
|
|
7410
|
+
if (!options.parsed.files0From) return {
|
|
7411
|
+
files0FromStdin: false,
|
|
7412
|
+
fromFiles0: false,
|
|
7413
|
+
operandCount: options.fileOperands.length,
|
|
7414
|
+
targets: options.fileOperands.map((path) => ({
|
|
7415
|
+
displayPath: path,
|
|
7416
|
+
path
|
|
7417
|
+
}))
|
|
7418
|
+
};
|
|
7419
|
+
const files0From = expandedWordToString(options.parsed.files0From);
|
|
7420
|
+
const files0FromStdin = files0From === STDIN_FILE_NAME;
|
|
7421
|
+
let namesBytes;
|
|
7422
|
+
if (files0FromStdin) namesBytes = await options.readStdinBytes();
|
|
7423
|
+
else {
|
|
7424
|
+
const path = resolvePathFromCwd(options.context.cwd, files0From);
|
|
7425
|
+
try {
|
|
7426
|
+
namesBytes = await options.fs.readFile(path);
|
|
7427
|
+
} catch {
|
|
7428
|
+
options.stderr.push(`wc: ${files0From}: No such file or directory`);
|
|
7429
|
+
return null;
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
const names = parseNulSeparatedNames(namesBytes, options.stderr);
|
|
7433
|
+
return {
|
|
7434
|
+
files0FromStdin,
|
|
7435
|
+
fromFiles0: true,
|
|
7436
|
+
operandCount: names.names.length + names.invalidNameCount,
|
|
7437
|
+
targets: names.names.map((path) => ({
|
|
7438
|
+
displayPath: path,
|
|
7439
|
+
path
|
|
7440
|
+
}))
|
|
7441
|
+
};
|
|
7442
|
+
}
|
|
7443
|
+
function parseNulSeparatedNames(bytes, stderr) {
|
|
7444
|
+
if (bytes.byteLength === 0) return {
|
|
7445
|
+
invalidNameCount: 0,
|
|
7446
|
+
names: []
|
|
7447
|
+
};
|
|
7448
|
+
const names = [];
|
|
7449
|
+
let invalidNameCount = 0;
|
|
7450
|
+
let start = 0;
|
|
7451
|
+
for (let index = 0; index < bytes.byteLength; index++) {
|
|
7452
|
+
if (bytes[index] !== NUL_BYTE) continue;
|
|
7453
|
+
invalidNameCount += appendName(bytes.slice(start, index), names, stderr);
|
|
7454
|
+
start = index + 1;
|
|
7455
|
+
}
|
|
7456
|
+
if (start < bytes.byteLength) invalidNameCount += appendName(bytes.slice(start), names, stderr);
|
|
7457
|
+
return {
|
|
7458
|
+
invalidNameCount,
|
|
7459
|
+
names
|
|
7460
|
+
};
|
|
7461
|
+
}
|
|
7462
|
+
function appendName(bytes, names, stderr) {
|
|
7463
|
+
if (bytes.byteLength === 0) {
|
|
7464
|
+
stderr.push("wc: invalid zero-length file name");
|
|
7465
|
+
return 1;
|
|
7466
|
+
}
|
|
7467
|
+
names.push(UTF8_DECODER.decode(bytes));
|
|
7468
|
+
return 0;
|
|
7469
|
+
}
|
|
7470
|
+
async function readFileOrReport(fs, path, stderr) {
|
|
7471
|
+
try {
|
|
7472
|
+
return await fs.readFile(path);
|
|
7473
|
+
} catch {
|
|
7474
|
+
stderr.push(`wc: ${path}: No such file or directory`);
|
|
7475
|
+
return null;
|
|
7476
|
+
}
|
|
7477
|
+
}
|
|
7478
|
+
function createStdinReader(input, redirectedInputBytes, stdin) {
|
|
7479
|
+
let hasRead = false;
|
|
7480
|
+
return async () => {
|
|
7481
|
+
if (hasRead) return DEFAULT_STDIO_BYTES;
|
|
7482
|
+
hasRead = true;
|
|
7483
|
+
return redirectedInputBytes ?? readStreamBytes(input, stdin);
|
|
7484
|
+
};
|
|
7485
|
+
}
|
|
7486
|
+
async function readStreamBytes(input, stdin) {
|
|
7487
|
+
if (!input) return DEFAULT_STDIO_BYTES;
|
|
7488
|
+
return await (stdin ?? createShellInput(input)).bytes({ trailingNewline: true });
|
|
7489
|
+
}
|
|
7490
|
+
function countBytes(bytes) {
|
|
7491
|
+
const text = UTF8_DECODER.decode(bytes);
|
|
7492
|
+
return {
|
|
7493
|
+
bytes: bytes.byteLength,
|
|
7494
|
+
chars: [...text].length,
|
|
7495
|
+
lines: countLines(bytes),
|
|
7496
|
+
maxLineLength: countMaxLineLength(text),
|
|
7497
|
+
words: countWords(text)
|
|
7498
|
+
};
|
|
7499
|
+
}
|
|
7500
|
+
function countLines(bytes) {
|
|
7501
|
+
let lines = 0;
|
|
7502
|
+
for (const byte of bytes) if (byte === NEWLINE_BYTE) lines++;
|
|
7503
|
+
return lines;
|
|
7504
|
+
}
|
|
7505
|
+
function countWords(text) {
|
|
7506
|
+
return text.split(WORD_SEPARATOR_REGEX).filter((word) => word.length > 0).length;
|
|
7507
|
+
}
|
|
7508
|
+
function countMaxLineLength(text) {
|
|
7509
|
+
let maxLength = 0;
|
|
7510
|
+
let linePosition = 0;
|
|
7511
|
+
for (const character of text) switch (character) {
|
|
7512
|
+
case "\n":
|
|
7513
|
+
case "\r":
|
|
7514
|
+
case "\f":
|
|
7515
|
+
if (linePosition > maxLength) maxLength = linePosition;
|
|
7516
|
+
linePosition = 0;
|
|
7517
|
+
break;
|
|
7518
|
+
case " ":
|
|
7519
|
+
linePosition += TAB_WIDTH - linePosition % TAB_WIDTH;
|
|
7520
|
+
break;
|
|
7521
|
+
case "\v": break;
|
|
7522
|
+
case " ":
|
|
7523
|
+
linePosition++;
|
|
7524
|
+
break;
|
|
7525
|
+
default: linePosition += displayWidth(character);
|
|
7526
|
+
}
|
|
7527
|
+
if (linePosition > maxLength) maxLength = linePosition;
|
|
7528
|
+
return maxLength;
|
|
7529
|
+
}
|
|
7530
|
+
function displayWidth(character) {
|
|
7531
|
+
const codePoint = character.codePointAt(0);
|
|
7532
|
+
if (codePoint === void 0 || isControlCodePoint(codePoint)) return 0;
|
|
7533
|
+
if (COMBINING_MARK_REGEX.test(character) || DEFAULT_IGNORABLE_CODE_POINT_REGEX.test(character)) return 0;
|
|
7534
|
+
if (isWideCodePoint(codePoint)) return 2;
|
|
7535
|
+
return 1;
|
|
7536
|
+
}
|
|
7537
|
+
function isControlCodePoint(codePoint) {
|
|
7538
|
+
return codePoint <= ASCII_CONTROL_MAX || codePoint === DELETE_CHARACTER || codePoint >= C1_CONTROL_MIN && codePoint <= C1_CONTROL_MAX;
|
|
7539
|
+
}
|
|
7540
|
+
function isWideCodePoint(codePoint) {
|
|
7541
|
+
for (const [start, end] of WIDE_CHARACTER_RANGES) if (codePoint >= start && codePoint <= end) return true;
|
|
7542
|
+
return false;
|
|
7543
|
+
}
|
|
7544
|
+
function normalizeSelection(parsed) {
|
|
7545
|
+
if (parsed.bytes || parsed.chars || parsed.lines || parsed.maxLineLength || parsed.words) return {
|
|
7546
|
+
bytes: parsed.bytes,
|
|
7547
|
+
chars: parsed.chars,
|
|
7548
|
+
lines: parsed.lines,
|
|
7549
|
+
maxLineLength: parsed.maxLineLength,
|
|
7550
|
+
words: parsed.words
|
|
7551
|
+
};
|
|
7552
|
+
return {
|
|
7553
|
+
bytes: true,
|
|
7554
|
+
chars: false,
|
|
7555
|
+
lines: true,
|
|
7556
|
+
maxLineLength: false,
|
|
7557
|
+
words: true
|
|
7558
|
+
};
|
|
7559
|
+
}
|
|
7560
|
+
function renderOutput(inputs, selection, totalMode, fromFiles0, operandCount) {
|
|
7561
|
+
const includeTotal = shouldIncludeTotal(operandCount, totalMode);
|
|
7562
|
+
const totals = sumCounts(inputs);
|
|
7563
|
+
if (totalMode === "only") return [renderCounts(totals, selection, 1, null)];
|
|
7564
|
+
if (inputs.length === 0 && !includeTotal) return [];
|
|
7565
|
+
const width = fieldWidth(inputs, totals, selection, fromFiles0);
|
|
7566
|
+
const lines = inputs.map((input) => renderCounts(input.counts, selection, width, input.displayPath ? quoteDisplayPath(input.displayPath) : null));
|
|
7567
|
+
if (includeTotal) lines.push(renderCounts(totals, selection, width, "total"));
|
|
7568
|
+
return lines;
|
|
7569
|
+
}
|
|
7570
|
+
function shouldIncludeTotal(operandCount, totalMode) {
|
|
7571
|
+
if (totalMode === "never" || totalMode === "only") return false;
|
|
7572
|
+
if (totalMode === "always") return true;
|
|
7573
|
+
return operandCount > 1;
|
|
7574
|
+
}
|
|
7575
|
+
function sumCounts(inputs) {
|
|
7576
|
+
const totals = {
|
|
7577
|
+
bytes: 0,
|
|
7578
|
+
chars: 0,
|
|
7579
|
+
lines: 0,
|
|
7580
|
+
maxLineLength: 0,
|
|
7581
|
+
words: 0
|
|
7582
|
+
};
|
|
7583
|
+
for (const input of inputs) {
|
|
7584
|
+
totals.bytes += input.counts.bytes;
|
|
7585
|
+
totals.chars += input.counts.chars;
|
|
7586
|
+
totals.lines += input.counts.lines;
|
|
7587
|
+
totals.words += input.counts.words;
|
|
7588
|
+
if (input.counts.maxLineLength > totals.maxLineLength) totals.maxLineLength = input.counts.maxLineLength;
|
|
7589
|
+
}
|
|
7590
|
+
return totals;
|
|
7591
|
+
}
|
|
7592
|
+
function fieldWidth(inputs, totals, selection, fromFiles0) {
|
|
7593
|
+
if (inputs.length === 1 && inputs[0]?.displayPath === DEFAULT_STDIN_DISPLAY_PATH && selectedFieldCount(selection) > 1) return DEFAULT_STDIN_FIELD_WIDTH;
|
|
7594
|
+
if (!fromFiles0 && inputs.length === 1) return 1;
|
|
7595
|
+
if (fromFiles0 && selectedFieldCount(selection) === 1) return 1;
|
|
7596
|
+
let width = 1;
|
|
7597
|
+
for (const input of inputs) width = Math.max(width, maxSelectedDigitCount(input.counts, selection));
|
|
7598
|
+
width = Math.max(width, maxSelectedDigitCount(totals, selection));
|
|
7599
|
+
return width;
|
|
7600
|
+
}
|
|
7601
|
+
function selectedFieldCount(selection) {
|
|
7602
|
+
return selectedValues({
|
|
7603
|
+
bytes: 0,
|
|
7604
|
+
chars: 0,
|
|
7605
|
+
lines: 0,
|
|
7606
|
+
maxLineLength: 0,
|
|
7607
|
+
words: 0
|
|
7608
|
+
}, selection).length;
|
|
7609
|
+
}
|
|
7610
|
+
function maxSelectedDigitCount(counts, selection) {
|
|
7611
|
+
return Math.max(...selectedValues(counts, selection).map((value) => String(value).length));
|
|
7612
|
+
}
|
|
7613
|
+
function renderCounts(counts, selection, width, displayPath) {
|
|
7614
|
+
const prefix = selectedValues(counts, selection).map((value) => String(value).padStart(width, " ")).join(" ");
|
|
7615
|
+
return displayPath ? `${prefix} ${displayPath}` : prefix;
|
|
7616
|
+
}
|
|
7617
|
+
function selectedValues(counts, selection) {
|
|
7618
|
+
const values = [];
|
|
7619
|
+
if (selection.lines) values.push(counts.lines);
|
|
7620
|
+
if (selection.words) values.push(counts.words);
|
|
7621
|
+
if (selection.bytes) values.push(counts.bytes);
|
|
7622
|
+
if (selection.chars) values.push(counts.chars);
|
|
7623
|
+
if (selection.maxLineLength) values.push(counts.maxLineLength);
|
|
7624
|
+
return values;
|
|
7625
|
+
}
|
|
7626
|
+
function quoteDisplayPath(path) {
|
|
7627
|
+
if (!path.includes("\n")) return path;
|
|
7628
|
+
return `'${path.split("\n").join("'$'\\n''")}'`;
|
|
7629
|
+
}
|
|
7630
|
+
//#endregion
|
|
6173
7631
|
//#region src/operator/xargs/xargs.ts
|
|
6174
7632
|
const DEFAULT_MAX_ARGS = Number.POSITIVE_INFINITY;
|
|
6175
7633
|
const TEXT_DECODER = new TextDecoder();
|
|
@@ -6214,7 +7672,7 @@ async function readInput(options) {
|
|
|
6214
7672
|
if (options.inputPath) return TEXT_DECODER.decode(await options.fs.readFile(options.inputPath));
|
|
6215
7673
|
if (!options.input) return "";
|
|
6216
7674
|
const records = [];
|
|
6217
|
-
for await (const
|
|
7675
|
+
for await (const line of (options.stdin ?? createShellInput(options.input)).lines()) records.push(line);
|
|
6218
7676
|
return records.join("\n");
|
|
6219
7677
|
}
|
|
6220
7678
|
function buildBatches(input, args) {
|
|
@@ -6403,10 +7861,13 @@ const STREAM_COMMANDS = [
|
|
|
6403
7861
|
"pwd",
|
|
6404
7862
|
"read",
|
|
6405
7863
|
"set",
|
|
7864
|
+
"sort",
|
|
6406
7865
|
"string",
|
|
6407
7866
|
"tail",
|
|
6408
7867
|
"test",
|
|
6409
|
-
"
|
|
7868
|
+
"tree",
|
|
7869
|
+
"xargs",
|
|
7870
|
+
"wc"
|
|
6410
7871
|
];
|
|
6411
7872
|
const STREAM_COMMAND_SET = new Set(STREAM_COMMANDS);
|
|
6412
7873
|
const ROOT_DIRECTORY$1 = "/";
|
|
@@ -6456,10 +7917,17 @@ async function executeEffectStep$1({ step, fs, context }) {
|
|
|
6456
7917
|
});
|
|
6457
7918
|
}
|
|
6458
7919
|
function createBuiltinRuntime(fs, context, input) {
|
|
7920
|
+
const stdin = createShellInput(input);
|
|
6459
7921
|
return {
|
|
6460
7922
|
fs,
|
|
6461
7923
|
context,
|
|
6462
|
-
input
|
|
7924
|
+
input,
|
|
7925
|
+
io: {
|
|
7926
|
+
stderr: context.stderr,
|
|
7927
|
+
stdin,
|
|
7928
|
+
stdout: new BufferedShellOutput()
|
|
7929
|
+
},
|
|
7930
|
+
stdin
|
|
6463
7931
|
};
|
|
6464
7932
|
}
|
|
6465
7933
|
function formatLongListing(path, stat) {
|
|
@@ -6525,7 +7993,7 @@ CommandRegistry.register("cat", {
|
|
|
6525
7993
|
context.status = 0;
|
|
6526
7994
|
return;
|
|
6527
7995
|
}
|
|
6528
|
-
if (input) yield* cat(fs, options)(input);
|
|
7996
|
+
if (input) yield* cat(fs, options)(toFormattedLineStream(input));
|
|
6529
7997
|
context.status = 0;
|
|
6530
7998
|
})();
|
|
6531
7999
|
}
|
|
@@ -6540,7 +8008,8 @@ CommandRegistry.register("grep", {
|
|
|
6540
8008
|
input,
|
|
6541
8009
|
parsed: step.args,
|
|
6542
8010
|
redirections: step.redirections,
|
|
6543
|
-
resolvedOutputRedirectPath
|
|
8011
|
+
resolvedOutputRedirectPath,
|
|
8012
|
+
stdin: createShellInput(input)
|
|
6544
8013
|
});
|
|
6545
8014
|
context.status = result.exitCode;
|
|
6546
8015
|
context.stderr.appendLines(result.stderr);
|
|
@@ -6566,7 +8035,8 @@ CommandRegistry.register("xargs", {
|
|
|
6566
8035
|
fs,
|
|
6567
8036
|
input,
|
|
6568
8037
|
inputPath: await resolveRedirectPath(step.cmd, step.redirections, "input", fs, context),
|
|
6569
|
-
parsed: step.args
|
|
8038
|
+
parsed: step.args,
|
|
8039
|
+
stdin: createShellInput(input)
|
|
6570
8040
|
});
|
|
6571
8041
|
context.status = result.exitCode;
|
|
6572
8042
|
context.stderr.appendLines(result.stderr);
|
|
@@ -6577,6 +8047,70 @@ CommandRegistry.register("xargs", {
|
|
|
6577
8047
|
})();
|
|
6578
8048
|
}
|
|
6579
8049
|
});
|
|
8050
|
+
CommandRegistry.register("wc", {
|
|
8051
|
+
kind: "stream",
|
|
8052
|
+
handler: ({ step, fs, input, context }) => {
|
|
8053
|
+
return (async function* () {
|
|
8054
|
+
const result = await runWcCommand({
|
|
8055
|
+
context,
|
|
8056
|
+
fs,
|
|
8057
|
+
input,
|
|
8058
|
+
inputPath: await resolveRedirectPath(step.cmd, step.redirections, "input", fs, context),
|
|
8059
|
+
parsed: step.args,
|
|
8060
|
+
stdin: createShellInput(input)
|
|
8061
|
+
});
|
|
8062
|
+
context.status = result.exitCode;
|
|
8063
|
+
context.stderr.appendLines(result.stderr);
|
|
8064
|
+
for (const text of result.stdout) yield {
|
|
8065
|
+
kind: "line",
|
|
8066
|
+
text
|
|
8067
|
+
};
|
|
8068
|
+
})();
|
|
8069
|
+
}
|
|
8070
|
+
});
|
|
8071
|
+
CommandRegistry.register("sort", {
|
|
8072
|
+
kind: "stream",
|
|
8073
|
+
handler: ({ step, fs, input, context }) => {
|
|
8074
|
+
return (async function* () {
|
|
8075
|
+
const result = await runSortCommand({
|
|
8076
|
+
context,
|
|
8077
|
+
fs,
|
|
8078
|
+
input,
|
|
8079
|
+
inputPath: await resolveRedirectPath(step.cmd, step.redirections, "input", fs, context),
|
|
8080
|
+
parsed: step.args,
|
|
8081
|
+
stdin: createShellInput(input)
|
|
8082
|
+
});
|
|
8083
|
+
context.status = result.exitCode;
|
|
8084
|
+
context.stderr.appendLines(result.stderr);
|
|
8085
|
+
for (const text of result.stdout) yield {
|
|
8086
|
+
kind: "line",
|
|
8087
|
+
text
|
|
8088
|
+
};
|
|
8089
|
+
})();
|
|
8090
|
+
}
|
|
8091
|
+
});
|
|
8092
|
+
CommandRegistry.register("tree", {
|
|
8093
|
+
kind: "stream",
|
|
8094
|
+
handler: ({ step, fs, context }) => {
|
|
8095
|
+
return (async function* () {
|
|
8096
|
+
const paths = resolvePathsFromCwd(context.cwd, await evaluateExpandedPathWords("tree", step.args.paths, fs, context));
|
|
8097
|
+
const includePatterns = await evaluateExpandedWords(step.args.includePatterns, fs, context);
|
|
8098
|
+
const excludePatterns = await evaluateExpandedWords(step.args.excludePatterns, fs, context);
|
|
8099
|
+
const result = await runTreeCommand(fs, context.cwd, createTreeResolvedArgs({
|
|
8100
|
+
...step.args,
|
|
8101
|
+
excludePatterns,
|
|
8102
|
+
includePatterns,
|
|
8103
|
+
paths
|
|
8104
|
+
}));
|
|
8105
|
+
context.status = result.exitCode;
|
|
8106
|
+
context.stderr.appendLines(result.stderr);
|
|
8107
|
+
for (const text of result.stdout) yield {
|
|
8108
|
+
kind: "line",
|
|
8109
|
+
text
|
|
8110
|
+
};
|
|
8111
|
+
})();
|
|
8112
|
+
}
|
|
8113
|
+
});
|
|
6580
8114
|
CommandRegistry.register("head", {
|
|
6581
8115
|
kind: "stream",
|
|
6582
8116
|
handler: ({ step, fs, input, context }) => {
|
|
@@ -6961,11 +8495,13 @@ function getTargetFd(redirection) {
|
|
|
6961
8495
|
}
|
|
6962
8496
|
async function resolveFileDestination(command, redirection, fs, context) {
|
|
6963
8497
|
const targetPath = await evaluateExpandedSinglePath(command, "redirection target must expand to exactly 1 path", redirection.target, fs, context);
|
|
8498
|
+
const resolvedPath = resolvePathFromCwd(context.cwd, targetPath);
|
|
8499
|
+
if (isNullDevicePath(resolvedPath)) return { kind: "nullDevice" };
|
|
6964
8500
|
return {
|
|
6965
8501
|
kind: "file",
|
|
6966
8502
|
append: redirection.append ?? false,
|
|
6967
8503
|
noclobber: redirection.noclobber ?? false,
|
|
6968
|
-
path:
|
|
8504
|
+
path: resolvedPath
|
|
6969
8505
|
};
|
|
6970
8506
|
}
|
|
6971
8507
|
function destinationForFd(routing, fd, _isLastStep) {
|
|
@@ -7125,6 +8661,7 @@ async function routeStdout(stdoutRecords, stdoutText, destination, hasNextStep,
|
|
|
7125
8661
|
});
|
|
7126
8662
|
return;
|
|
7127
8663
|
case "closed": return;
|
|
8664
|
+
case "nullDevice": return;
|
|
7128
8665
|
default: {
|
|
7129
8666
|
const _exhaustive = destination;
|
|
7130
8667
|
throw new Error(`Unknown stdout destination: ${_exhaustive}`);
|
|
@@ -7157,6 +8694,7 @@ async function routeStderr(stderrLines, stderrText, destination, hasNextStep, pi
|
|
|
7157
8694
|
});
|
|
7158
8695
|
return;
|
|
7159
8696
|
case "closed": return;
|
|
8697
|
+
case "nullDevice": return;
|
|
7160
8698
|
default: {
|
|
7161
8699
|
const _exhaustive = destination;
|
|
7162
8700
|
throw new Error(`Unknown stderr destination: ${_exhaustive}`);
|
|
@@ -7166,4 +8704,4 @@ async function routeStderr(stderrLines, stderrText, destination, hasNextStep, pi
|
|
|
7166
8704
|
//#endregion
|
|
7167
8705
|
export { BufferedOutputStream as a, ParseSyntaxError as c, writeDiagnosticsToStderr as i, compile as l, execute_exports as n, formatStderr as o, isShellDiagnosticError as r, formatStdoutRecord as s, execute as t, parse as u };
|
|
7168
8706
|
|
|
7169
|
-
//# sourceMappingURL=execute-
|
|
8707
|
+
//# sourceMappingURL=execute-BinccNiS.mjs.map
|