mirror of
https://github.com/cjdelisle/cjdns
synced 2025-10-06 00:32:50 +02:00
286 lines
10 KiB
JavaScript
286 lines
10 KiB
JavaScript
/* vim: set expandtab ts=4 sw=4: */
|
|
/*
|
|
* You may redistribute this program and/or modify it under the terms of
|
|
* the GNU General Public License as published by the Free Software Foundation,
|
|
* either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
'use strict';
|
|
var Fs = require('fs');
|
|
var nThen = require('nthen');
|
|
var Semaphore = require('saferphore');
|
|
var Child = require('child_process');
|
|
|
|
var headerLines = [
|
|
'/* vim: set expandtab ts=4 sw=4: */',
|
|
'/*',
|
|
' * You may redistribute this program and/or modify it under the terms of',
|
|
' * the GNU General Public License as published by the Free Software Foundation,',
|
|
' * either version 3 of the License, or (at your option) any later version.',
|
|
' *',
|
|
' * This program is distributed in the hope that it will be useful,',
|
|
' * but WITHOUT ANY WARRANTY; without even the implied warranty of',
|
|
' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the',
|
|
' * GNU General Public License for more details.',
|
|
' *',
|
|
' * You should have received a copy of the GNU General Public License',
|
|
' * along with this program. If not, see <https://www.gnu.org/licenses/>.',
|
|
' */'
|
|
];
|
|
|
|
var parseFile = function (fileName, fileContent) {
|
|
var output = '';
|
|
var parenthCount = 0;
|
|
var functionParenthCount = 0;
|
|
var expectBracket = 0;
|
|
var name = fileName.replace(/^.*\//, '').replace(/\..*$/,'');
|
|
|
|
var lines = fileContent.split('\n');
|
|
|
|
var lineInfo = '';
|
|
var ignore = false;
|
|
|
|
var error = function(msg) {
|
|
if (!ignore) {
|
|
output += lineInfo + ' ' + msg + '\n';
|
|
}
|
|
};
|
|
|
|
for (var lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
var line = lines[lineNum];
|
|
|
|
// switch to 1 indexing for human readability
|
|
lineInfo = fileName + ":" + (lineNum+1);
|
|
ignore = false;
|
|
|
|
if (lineNum < headerLines.length) {
|
|
var expectedLine = headerLines[lineNum];
|
|
if (line !== headerLines[lineNum]) {
|
|
error("missing header\n" + expectedLine + "\n" + line);
|
|
}
|
|
} else if (/\.h$/.test(fileName) && lineNum < headerLines.length + 1) {
|
|
if (line !== '#ifndef ' + name + "_H") {
|
|
error("expected #ifndef " + name + "_H found " + line);
|
|
}
|
|
} else if (/\.h$/.test(fileName) && lineNum < headerLines.length + 2) {
|
|
if (line !== '#define ' + name + "_H") {
|
|
error("expected #define " + name + "_H found " + line);
|
|
}
|
|
}
|
|
|
|
ignore = /CHECKFILES_IGNORE/.test(line);
|
|
|
|
if (expectBracket === 1) {
|
|
expectBracket = 0;
|
|
if (!(/^[\s]*{/.test(line))) {
|
|
error("expecting a { bracket " + line);
|
|
}
|
|
}
|
|
|
|
|
|
// implementations.. TUNConfigurator_Linux contains TUNConfigurator_doStuff...
|
|
var n = name.replace(/_.*/, '');
|
|
if ((/^\w+\s.*\(/).test(line)) {
|
|
if (!(/^int main\(/.test(line)
|
|
|| / CJDNS_/.test(line)
|
|
|| line.indexOf(' '+n) > -1
|
|
|| /^[ ]?static /.test(line)
|
|
|| /^typedef /.test(line)))
|
|
{
|
|
error("all globally visible functions must begin with the name of the file.");
|
|
}
|
|
}
|
|
|
|
var matches;
|
|
if (functionParenthCount === 0) {
|
|
matches = /^\w+\s.*(\(.*)$/.exec(line);
|
|
}
|
|
if (functionParenthCount > 0 || matches) {
|
|
var txt = (functionParenthCount > 0) ? line : matches[1];
|
|
functionParenthCount += (txt.match(/\(/g)||[]).length;
|
|
functionParenthCount -= (txt.match(/\)/g)||[]).length;
|
|
if (functionParenthCount === 0) {
|
|
txt = txt.substring(txt.lastIndexOf(')') + 1);
|
|
if (/{/.test(txt)) {
|
|
error("please put the opening bracket on the next line.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (/[\w]*int[\w]*\s+\*+\w/.test(line) || /[\w]*struct\s+[\w]+\s+\*+\w/.test(line)) {
|
|
error("int* blah; means int pointer named blah, int *blah; means int names splatblah");
|
|
}
|
|
|
|
if (line.length > 100) {
|
|
error("cjd's editor window is only 100 characters wide");
|
|
}
|
|
|
|
if (/\.h$/.test(fileName) && fileName.indexOf('util/platform/libc/') === -1) {
|
|
|
|
// If the name is CryptoAuth_pvt.h, it's ok to make a structure called CryptoAuth
|
|
var nameRe = name.replace(/_pvt$/, '').replace(/_impl$/, '');
|
|
|
|
if (/^struct /.test(line) && line.indexOf('struct ' + nameRe) !== 0 && !(/\(/.test(line))) {
|
|
error("all structures must begin with the name of the file.");
|
|
}
|
|
|
|
if (/#define /.test(line) && line.indexOf('#define ' + nameRe) === -1) {
|
|
error("all defines must begin with the name of the file.");
|
|
}
|
|
}
|
|
if (/\t/.test(line)) {
|
|
error("tabs are not allowed, use 4 spaces.");
|
|
}
|
|
|
|
if (/\s$/.test(line)) {
|
|
error("trailing whitespace.");
|
|
}
|
|
|
|
if (/[^A-Z](TODO|FIXME|XXX)[^A-Z]/.test(line)) {
|
|
if (/[^A-Z](TODO|FIXME|XXX)[^\(A-Z]/.test(line)) {
|
|
error("Please take responsibility for your TODO: eg: // TODO(cjd): make this work");
|
|
} else {
|
|
console.log(lineInfo + ' ' + line.replace(/[ \/]*/, ''));
|
|
}
|
|
}
|
|
|
|
if (/(if|for|while)\(/.test(line)) {
|
|
error("If/for/while statements must be followed by whitespace.");
|
|
}
|
|
|
|
matches = null;
|
|
if (parenthCount === 0) {
|
|
matches = /[^\w#](if|for|while) (\(.*$)/.exec(line);
|
|
}
|
|
if (parenthCount > 0 || matches) {
|
|
var txt1 = (parenthCount > 0) ? line : matches[2];
|
|
parenthCount += (txt1.match(/\(/g)||[]).length;
|
|
parenthCount -= (txt1.match(/\)/g)||[]).length;
|
|
if (parenthCount === 0) {
|
|
txt1 = txt1.substring(txt1.lastIndexOf(')') + 1);
|
|
// for (x; y; z) ; <-- ok
|
|
// for (x; y; z) { <-- ok
|
|
// for (x; y; z) { \ <-- ok (in preprocessor macro)
|
|
// for (x; y; z) <-- ok but you better put a bracket on the next line
|
|
// for (x; y; z) { j++; } <-- ok
|
|
// for (x; y; z) j++; <-- BZZZZZZZZZZT
|
|
if (!(/^[\s]*[;{].*$/.test(txt1)) && !(/^[\s]+{[\s]*\\$/).test(txt1)) {
|
|
if (/[\s]*$/.test(txt1)) {
|
|
expectBracket = 1;
|
|
} else {
|
|
error(parenthCount + ' ' + line);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
};
|
|
|
|
var checkFile = module.exports.checkFile = function (file, callback) {
|
|
Fs.readFile(file, function (err, ret) {
|
|
if (err) { throw err; }
|
|
callback(parseFile(file, ret.toString()));
|
|
});
|
|
};
|
|
|
|
var lint = module.exports.lint = function (fileName, fileContent, callback) {
|
|
var out = parseFile(fileName, fileContent);
|
|
callback(out, !!out);
|
|
};
|
|
|
|
var checkFiles = module.exports.checkFiles = function (files, callback) {
|
|
var sema = Semaphore.create(64);
|
|
var errors = '';
|
|
nThen(function (waitFor) {
|
|
files.forEach(function (file) {
|
|
sema.take(waitFor(function (returnAfter) {
|
|
checkFile(file, waitFor(returnAfter(function (err) {
|
|
if (err) {
|
|
errors += file + '\n' + err + '\n';
|
|
}
|
|
})));
|
|
}));
|
|
});
|
|
}).nThen(function (waitFor) {
|
|
callback(errors);
|
|
});
|
|
};
|
|
|
|
var checkDir = module.exports.checkDir = function (dir, runInFork, callback) {
|
|
|
|
var gitIgnoreLines;
|
|
|
|
if (runInFork) {
|
|
var err = '';
|
|
var out = '';
|
|
var proc = Child.spawn(process.execPath, [__filename]);
|
|
proc.stdout.on('data', function (data) { err += data.toString('utf8'); });
|
|
proc.stderr.on('data', function (data) { err += data.toString('utf8'); });
|
|
proc.on('close', function (ret) {
|
|
out += err;
|
|
var error;
|
|
if (ret !== 0) { error = new Error(out); }
|
|
callback(error, out);
|
|
});
|
|
return;
|
|
}
|
|
|
|
var output = '';
|
|
nThen(function (waitFor) {
|
|
|
|
Fs.readFile('.gitignore', waitFor(function (err, ret) {
|
|
if (err) { throw err; }
|
|
gitIgnoreLines = ret.toString('utf8').split('\n');
|
|
}));
|
|
|
|
}).nThen(function (waitFor) {
|
|
|
|
var addDir = function (dir) {
|
|
Fs.readdir(dir, waitFor(function (err, files) {
|
|
if (err) { throw err; }
|
|
files.forEach(function (file) {
|
|
Fs.stat(dir + '/' + file, waitFor(function (err, stat) {
|
|
if (err) { throw err; }
|
|
if (file === '.git') {
|
|
} else if (file === 'contrib') {
|
|
} else if (file === 'dependencies') {
|
|
} else if (gitIgnoreLines.indexOf(file) !== -1) {
|
|
} else {
|
|
if (stat.isDirectory()) {
|
|
addDir(dir + '/' + file);
|
|
} else if (/.*\.[ch]$/.test(file)) {
|
|
checkFile(dir + '/' + file, waitFor(function (ret) {
|
|
output += ret;
|
|
}));
|
|
}
|
|
}
|
|
}));
|
|
});
|
|
}));
|
|
};
|
|
addDir(dir);
|
|
|
|
}).nThen(function (waitFor) {
|
|
|
|
callback(output);
|
|
|
|
});
|
|
};
|
|
|
|
if (module.parent === null) {
|
|
checkDir('.', false, function(output) {
|
|
if (output !== '') {
|
|
console.log(output);
|
|
process.exit(1);
|
|
}
|
|
});
|
|
}
|