Skip to content

Commit da8b88e

Browse files
feat(myWc): complete custom wc implementation with full options and formatting
1 parent 2ab9eea commit da8b88e

File tree

1 file changed

+97
-0
lines changed
  • implement-shell-tools/wc/sample-files

1 file changed

+97
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env node
2+
const { program } = require("commander");
3+
const fs = require("fs");
4+
const path = require("path");
5+
6+
function expandWildcard(pattern) {
7+
const dir = path.dirname(pattern);
8+
const base = path.basename(pattern);
9+
10+
if (!base.includes("*")) return [pattern];
11+
12+
let files;
13+
try {
14+
files = fs.readdirSync(dir);
15+
} catch {
16+
console.error(`wc: ${pattern}: No such directory`);
17+
return [];
18+
}
19+
20+
const regex = new RegExp("^" + base.replace(/\*/g, ".*") + "$");
21+
return files
22+
.filter(f => regex.test(f))
23+
.map(f => path.join(dir, f));
24+
}
25+
26+
function countLines(text) {
27+
if (text === "") return 0;
28+
const matches = text.match(/\n/g) || [];
29+
return text.endsWith("\n") ? matches.length : matches.length + 1;
30+
}
31+
32+
function countWords(text) {
33+
return text.split(/\s+/).filter(Boolean).length;
34+
}
35+
36+
function countChars(text) {
37+
return Buffer.byteLength(text, "utf-8");
38+
}
39+
40+
function wcFile(filename, options) {
41+
let text;
42+
try {
43+
text = fs.readFileSync(filename, "utf-8");
44+
} catch {
45+
console.error(`wc: ${filename}: No such file`);
46+
return null;
47+
}
48+
49+
const lineCount = countLines(text);
50+
const wordCount = countWords(text);
51+
const charCount = countChars(text);
52+
53+
let output;
54+
const paddingSize = 7;
55+
if (options.lines && !options.words && !options.chars) output = `${lineCount} ${filename}`;
56+
else if (options.words && !options.lines && !options.chars) output = `${wordCount} ${filename}`;
57+
else if (options.chars && !options.lines && !options.words) output = `${charCount} ${filename}`;
58+
else output = `${String(lineCount).padStart(paddingSize)} ${String(wordCount).padStart(paddingSize)} ${String(charCount).padStart(paddingSize)} ${filename}`;
59+
console.log(output);
60+
61+
return { lines: lineCount, words: wordCount, chars: charCount };
62+
}
63+
64+
program
65+
.name("mywc")
66+
.description("Custom implementation of wc")
67+
.option("-l, --lines", "count lines")
68+
.option("-w, --words", "count words")
69+
.option("-c, --chars", "count characters")
70+
.argument("<files...>", "files or wildcard patterns")
71+
.action((patterns, options) => {
72+
let allFiles = [];
73+
patterns.forEach(p => allFiles = allFiles.concat(expandWildcard(p)));
74+
75+
let totalLines = 0, totalWords = 0, totalChars = 0;
76+
77+
allFiles.forEach(file => {
78+
const result = wcFile(file, options);
79+
if (result) {
80+
totalLines += result.lines;
81+
totalWords += result.words;
82+
totalChars += result.chars;
83+
}
84+
});
85+
const paddingSize = 7;
86+
if (allFiles.length > 1) {
87+
if (options.lines && !options.words && !options.chars) console.log(`${totalLines} total`);
88+
else if (options.words && !options.lines && !options.chars) console.log(`${totalWords} total`);
89+
else if (options.chars && !options.lines && !options.words) console.log(`${totalChars} total`);
90+
else console.log(
91+
`${String(totalLines).padStart(paddingSize)} ` +
92+
`${String(totalWords).padStart(paddingSize)} ` +
93+
`${String(totalChars).padStart(paddingSize)} total`);
94+
}
95+
});
96+
97+
program.parse();

0 commit comments

Comments
 (0)