-
-
Notifications
You must be signed in to change notification settings - Fork 85
Expand file tree
/
Copy pathcat.js
More file actions
88 lines (77 loc) · 2.26 KB
/
cat.js
File metadata and controls
88 lines (77 loc) · 2.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env node
// cat.js — ESM: supports -n and -b (-b overrides -n)
import fs from "node:fs";
import readline from "node:readline";
const args = process.argv.slice(2);
let numberAll = false;
let numberNonblank = false;
const files = [];
// parse flags + files (flags can appear anywhere)
for (const a of args) {
if (a === "-n") numberAll = true;
else if (a === "-b") numberNonblank = true;
else files.push(a);
}
// precedence: -b wins over -n
if (numberNonblank) numberAll = false;
if (files.length === 0) {
console.error("Usage: node cat.js [-n|-b] <file...>");
process.exit(1);
}
let hadError = false;
let lineNo = 1;
for (const file of files) {
try {
const stat = await fs.promises.stat(file);
if (stat.isDirectory()) {
console.error(`cat: ${file}: Is a directory`);
hadError = true;
continue;
}
if (!numberAll && !numberNonblank) {
await pipeFile(file);
} else {
await numberFile(file, { nonblank: numberNonblank });
}
} catch (err) {
if (err?.code === "ENOENT" || err?.code === "ENOTDIR") {
console.error(`cat: ${file}: No such file or directory`);
} else {
console.error(`cat: ${file}: ${err?.message || "Error"}`);
}
hadError = true;
}
}
if (hadError) process.exitCode = 1;
function pipeFile(file) {
return new Promise((resolve, reject) => {
const rs = fs.createReadStream(file);
rs.on("error", reject);
rs.on("end", resolve);
rs.pipe(process.stdout, { end: false }); // keep stdout open between files
});
}
function numberFile(file, { nonblank }) {
return new Promise((resolve, reject) => {
const rs = fs.createReadStream(file);
const rl = readline.createInterface({ input: rs, crlfDelay: Infinity });
rl.on("line", (line) => {
if (nonblank) {
if (line.length === 0) {
process.stdout.write("\n"); // blank line, no number
} else {
process.stdout.write(formatNum(lineNo++) + line + "\n");
}
} else {
process.stdout.write(formatNum(lineNo++) + line + "\n");
}
});
rl.on("close", resolve);
rl.on("error", reject);
rs.on("error", reject);
});
}
function formatNum(n) {
// GNU cat style: width 6, right-aligned, then a tab
return String(n).padStart(6, " ") + "\t";
}