2023-02-28 03:19:40 +00:00
|
|
|
// AUTOGENERATED COPYRIGHT HEADER START
|
|
|
|
// Copyright (C) 2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
|
|
// AUTOGENERATED COPYRIGHT HEADER END
|
2023-02-28 00:27:31 +00:00
|
|
|
const CHILD_PROCESS = require("node:child_process");
|
|
|
|
const PROCESS = require("node:process");
|
|
|
|
const PATH = require("node:path");
|
|
|
|
const FS = require("node:fs");
|
|
|
|
const FSPROMISES = require("node:fs/promises");
|
|
|
|
const OS = require("os");
|
|
|
|
|
2023-02-28 03:19:40 +00:00
|
|
|
const SECTION_START = "AUTOGENERATED COPYRIGHT HEADER START";
|
|
|
|
const SECTION_END = "AUTOGENERATED COPYRIGHT HEADER END";
|
|
|
|
|
2023-02-28 00:27:31 +00:00
|
|
|
async function git_isIgnored(path) {
|
|
|
|
await new Promise((resolve, reject) => {
|
|
|
|
try {
|
|
|
|
let proc = CHILD_PROCESS.spawn("git", [
|
|
|
|
"check-ignore",
|
|
|
|
path
|
|
|
|
], {
|
|
|
|
"cwd": PROCESS.cwd(),
|
|
|
|
"encoding": "utf8",
|
|
|
|
});
|
|
|
|
proc.stdout.on('data', (data) => {
|
|
|
|
})
|
|
|
|
proc.on('close', (code) => {
|
|
|
|
resolve(code == 0);
|
|
|
|
});
|
|
|
|
proc.on('exit', (code) => {
|
|
|
|
resolve(code == 0);
|
|
|
|
});
|
|
|
|
} catch (ex) {
|
|
|
|
reject(ex);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
/* Sync alternative
|
|
|
|
try {
|
|
|
|
return CHILD_PROCESS.spawnSync("git", [
|
|
|
|
"check-ignore",
|
|
|
|
path
|
|
|
|
], {
|
|
|
|
"cwd": PROCESS.cwd(),
|
|
|
|
"encoding": "utf8"
|
|
|
|
}).status == 0;
|
|
|
|
} catch (ex) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
async function git_retrieveAuthors(file) {
|
|
|
|
// git --no-pager log --date-order --reverse "--format=format:%aI|%aN <%aE>" -- file
|
|
|
|
let lines = await new Promise((resolve, reject) => {
|
|
|
|
try {
|
|
|
|
let lines = "";
|
|
|
|
let proc = CHILD_PROCESS.spawn("git", [
|
|
|
|
"--no-pager",
|
|
|
|
"log",
|
|
|
|
"--date-order",
|
|
|
|
"--reverse",
|
|
|
|
"--format=format:%aI|%aN <%aE>",
|
|
|
|
"--",
|
|
|
|
file
|
|
|
|
], {
|
|
|
|
"cwd": PROCESS.cwd(),
|
|
|
|
"encoding": "utf8",
|
|
|
|
});
|
|
|
|
proc.stdout.on('data', (data) => {
|
|
|
|
lines += data.toString();
|
|
|
|
})
|
|
|
|
proc.on('close', (code) => {
|
|
|
|
resolve(lines);
|
|
|
|
});
|
|
|
|
proc.on('exit', (code) => {
|
|
|
|
resolve(lines);
|
|
|
|
});
|
|
|
|
} catch (ex) {
|
|
|
|
reject(ex);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
lines = lines.split(lines.indexOf("\r\n") >= 0 ? "\r\n" : "\n");
|
|
|
|
let authors = new Map();
|
|
|
|
for (let line of lines) {
|
|
|
|
let [date, name] = line.split("|");
|
|
|
|
|
|
|
|
let author = authors.get(name);
|
|
|
|
if (author) {
|
|
|
|
author.to = new Date(date)
|
|
|
|
} else {
|
|
|
|
authors.set(name, {
|
|
|
|
from: new Date(date),
|
|
|
|
to: new Date(date),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return authors;
|
|
|
|
|
|
|
|
/* Sync Variant
|
|
|
|
try {
|
|
|
|
let data = await CHILD_PROCESS
|
|
|
|
let lines = data.stdout.toString().split("\n");
|
|
|
|
let authors = new Map();
|
|
|
|
for (let line of lines) {
|
|
|
|
let [date, name] = line.split("|");
|
|
|
|
|
|
|
|
let author = authors.get(name);
|
|
|
|
if (author) {
|
|
|
|
author.to = new Date(date)
|
|
|
|
} else {
|
|
|
|
authors.set(name, {
|
|
|
|
from: new Date(date),
|
|
|
|
to: new Date(date),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return authors;
|
|
|
|
} catch (ex) {
|
|
|
|
console.error(ex);
|
|
|
|
throw ex;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
async function generateCopyright(file) {
|
|
|
|
let authors = await git_retrieveAuthors(file)
|
|
|
|
let lines = [];
|
|
|
|
for (let entry of authors) {
|
|
|
|
let from = entry[1].from.getUTCFullYear();
|
|
|
|
let to = entry[1].to.getUTCFullYear();
|
|
|
|
lines.push(`Copyright (C) ${from != to ? `${from}-${to}` : to} ${entry[0]}`);
|
|
|
|
}
|
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeHeader(file, copyright) {
|
|
|
|
let file_name = PATH.basename(file).toLocaleLowerCase();
|
|
|
|
let file_exts = file_name.substring(file_name.indexOf("."));
|
|
|
|
|
|
|
|
let styles = {
|
|
|
|
"#": {
|
|
|
|
files: [
|
|
|
|
"cmakelists.txt"
|
|
|
|
], exts: [
|
|
|
|
".clang-tidy",
|
|
|
|
".clang-format",
|
|
|
|
".cmake",
|
|
|
|
".editorconfig",
|
|
|
|
".gitignore",
|
|
|
|
".gitmodules",
|
|
|
|
".yml",
|
|
|
|
],
|
|
|
|
prepend: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`# ${SECTION_START}`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
|
|
|
append: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`# ${SECTION_END}`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
|
|
|
prefix: "# ",
|
|
|
|
suffix: "",
|
|
|
|
},
|
|
|
|
";": {
|
|
|
|
files: [
|
|
|
|
""
|
|
|
|
], exts: [
|
|
|
|
".iss",
|
|
|
|
".iss.in",
|
|
|
|
],
|
|
|
|
prepend: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`; ${SECTION_START}`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
|
|
|
append: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`; ${SECTION_END}`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
|
|
|
prefix: "; ",
|
|
|
|
suffix: "",
|
|
|
|
},
|
2023-02-28 03:19:40 +00:00
|
|
|
"//": {
|
2023-02-28 00:27:31 +00:00
|
|
|
files: [
|
|
|
|
], exts: [
|
|
|
|
".c",
|
|
|
|
".c.in",
|
|
|
|
".cpp",
|
|
|
|
".cpp.in",
|
|
|
|
".h",
|
|
|
|
".h.in",
|
|
|
|
".hpp",
|
|
|
|
".hpp.in",
|
|
|
|
".js",
|
|
|
|
".rc",
|
|
|
|
".rc.in",
|
|
|
|
".effect"
|
|
|
|
],
|
|
|
|
prepend: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`// ${SECTION_START}`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
|
|
|
append: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`// ${SECTION_END}`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
2023-02-28 03:19:40 +00:00
|
|
|
prefix: "// ",
|
2023-02-28 00:27:31 +00:00
|
|
|
suffix: "",
|
|
|
|
},
|
|
|
|
"<!---->": {
|
|
|
|
files: [
|
|
|
|
], exts: [
|
|
|
|
".htm",
|
|
|
|
".htm.in",
|
|
|
|
".html",
|
|
|
|
".html.in",
|
|
|
|
".xml",
|
|
|
|
".xml.in",
|
|
|
|
".plist",
|
|
|
|
".plist.in",
|
|
|
|
".pkgproj",
|
|
|
|
".pkgproj.in",
|
|
|
|
],
|
|
|
|
prepend: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`<!-- ${SECTION_START} -->`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
|
|
|
append: [
|
2023-02-28 03:19:40 +00:00
|
|
|
`<!-- ${SECTION_END} -->`,
|
2023-02-28 00:27:31 +00:00
|
|
|
],
|
2023-02-28 03:19:40 +00:00
|
|
|
prefix: "<!-- ",
|
|
|
|
suffix: " -->",
|
2023-02-28 00:27:31 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (let key in styles) {
|
|
|
|
let style = [key, styles[key]];
|
|
|
|
if (style[1].files.includes(file_name)
|
|
|
|
|| style[1].files.includes(file)
|
|
|
|
|| style[1].exts.includes(file_exts)) {
|
|
|
|
let header = [];
|
|
|
|
header.push(...style[1].prepend);
|
|
|
|
for (let line of copyright) {
|
|
|
|
header.push(`${style[1].prefix}${line}${style[1].suffix}`);
|
|
|
|
}
|
|
|
|
header.push(...style[1].append);
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error("Unrecognized file format.")
|
|
|
|
}
|
|
|
|
|
|
|
|
async function addCopyright(file) {
|
|
|
|
try {
|
|
|
|
|
|
|
|
// Async/Promises
|
|
|
|
let content = await FSPROMISES.readFile(file);
|
|
|
|
let eol = (content.indexOf("\r\n") != -1 ? OS.EOL : "\n");
|
|
|
|
|
|
|
|
let copyright = await generateCopyright(file);
|
|
|
|
let header = makeHeader(file, copyright);
|
|
|
|
let insert = Buffer.from(header.join(eol) + eol);
|
|
|
|
|
2023-02-28 03:19:40 +00:00
|
|
|
// Find the starting point.
|
|
|
|
let startHeader = content.indexOf(SECTION_START);
|
|
|
|
startHeader = content.lastIndexOf(eol, startHeader);
|
|
|
|
startHeader += Buffer.from(eol).byteLength;
|
|
|
|
|
|
|
|
// Find the ending point.
|
|
|
|
let endHeader = content.indexOf(SECTION_END);
|
|
|
|
endHeader = content.indexOf(eol, endHeader);
|
|
|
|
endHeader += Buffer.from(eol).byteLength;
|
2023-02-28 00:27:31 +00:00
|
|
|
|
|
|
|
let fd = await FSPROMISES.open(file, "w+");
|
|
|
|
let fp = [];
|
|
|
|
if ((startHeader >= 0) && (endHeader >= 0)) {
|
|
|
|
let pos = 0;
|
|
|
|
if (startHeader > 0) {
|
|
|
|
fd.write(content, 0, startHeader, 0);
|
|
|
|
pos += startHeader;
|
|
|
|
}
|
|
|
|
fd.write(insert, 0, undefined, pos);
|
|
|
|
pos += insert.byteLength;
|
|
|
|
fd.write(content, endHeader, undefined, pos);
|
|
|
|
} else {
|
|
|
|
fd.write(insert, 0, undefined, 0);
|
|
|
|
fd.write(content, 0, undefined, insert.byteLength);
|
|
|
|
}
|
|
|
|
await fd.close();
|
|
|
|
|
|
|
|
/* Sync variant (slow!)
|
|
|
|
let content = FS.readFileSync(file);
|
|
|
|
let eol = (content.indexOf("\r\n") != -1 ? OS.EOL : "\n");
|
|
|
|
|
|
|
|
let copyright = await generateCopyright(file);
|
|
|
|
let header = makeHeader(file, copyright);
|
|
|
|
let insert = Buffer.from(header.join(eol) + eol);
|
|
|
|
|
|
|
|
let startHeader = content.indexOf(header[0]);
|
|
|
|
let endHeader = content.indexOf(header[header.length - 1], startHeader + 1);
|
|
|
|
endHeader += header[header.length - 1].length + eol.length;
|
|
|
|
|
|
|
|
let fd = FS.openSync(file, "w+");
|
|
|
|
if ((startHeader >= 0) && (endHeader >= 0)) {
|
|
|
|
let pos = 0;
|
|
|
|
if (startHeader > 0) {
|
|
|
|
FS.writeSync(fd, content, 0, startHeader, 0);
|
|
|
|
pos += startHeader;
|
|
|
|
}
|
|
|
|
FS.writeSync(fd, insert, 0, undefined, pos);
|
|
|
|
pos += insert.byteLength;
|
|
|
|
FS.writeSync(fd, content, endHeader, undefined, pos);
|
|
|
|
} else {
|
|
|
|
FS.writeSync(fd, insert, 0, undefined, 0);
|
|
|
|
FS.writeSync(fd, content, 0, undefined, insert.byteLength);
|
|
|
|
}
|
|
|
|
FS.close(fd, (err) => {
|
|
|
|
if (err)
|
|
|
|
throw err;
|
|
|
|
})*/
|
|
|
|
} catch (ex) {
|
|
|
|
console.error(`Error processing '${file}'!: ${ex}`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function addCopyrights(path) {
|
|
|
|
if (await git_isIgnored(path)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let promises = [];
|
|
|
|
|
|
|
|
let files = await FSPROMISES.readdir(path, { "withFileTypes": true });
|
|
|
|
for (let file of files) {
|
|
|
|
let fullname = PATH.join(path, file.name);
|
|
|
|
if (await git_isIgnored(fullname)) {
|
|
|
|
console.log(`Ignoring path '${fullname}'...`);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (file.isDirectory()) {
|
|
|
|
console.log(`Scanning path '${fullname}'...`);
|
|
|
|
promises.push(addCopyrights(fullname));
|
|
|
|
} else {
|
|
|
|
console.log(`Updating file '${fullname}'...`);
|
|
|
|
promises.push(addCopyright(fullname));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
|
|
|
(async function () {
|
|
|
|
let file = PROCESS.argv[2];
|
|
|
|
let pathStat = await FSPROMISES.stat(file);
|
|
|
|
if (pathStat.isDirectory()) {
|
|
|
|
await addCopyrights(PATH.resolve(file));
|
|
|
|
} else {
|
|
|
|
await addCopyright(PATH.resolve(file));
|
|
|
|
}
|
|
|
|
console.log("Done");
|
|
|
|
})();
|