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 | var cli = exports,
|
28 | argv, curr_opt, curr_val, full_opt, is_long,
|
29 | short_tags = [], opt_list, parsed = {},
|
30 | usage, argv_parsed, command_list, commands,
|
31 | daemon, daemon_arg, no_color, show_debug;
|
32 |
|
33 | cli.app = null;
|
34 | cli.version = null;
|
35 | cli.argv = [];
|
36 | cli.argc = 0;
|
37 |
|
38 | cli.options = {};
|
39 | cli.args = [];
|
40 | cli.command;
|
41 |
|
42 | cli.width = 70;
|
43 | cli.option_width = 25;
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | cli.native = {};
|
52 | var define_native = function (module) {
|
53 | Object.defineProperty(cli.native, module, {
|
54 | enumerable: true,
|
55 | configurable: true,
|
56 | get: function() {
|
57 | delete cli.native[module];
|
58 | return cli.native[module] = require(module);
|
59 | }
|
60 | });
|
61 | };
|
62 | var natives = process.binding('natives');
|
63 | for (var module in natives) {
|
64 | define_native(module);
|
65 | }
|
66 |
|
67 | cli.output = cli.native.util.print;
|
68 | cli.exit = require('exit');
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | var enable = {
|
81 | help: true,
|
82 | version: false,
|
83 | daemon: false,
|
84 | status: false,
|
85 | timeout: false,
|
86 | catchall: false,
|
87 | glob: false
|
88 | }
|
89 | cli.enable = function (/*plugins*/) {
|
90 | Array.prototype.slice.call(arguments).forEach(function (plugin) {
|
91 | switch (plugin) {
|
92 | case 'daemon':
|
93 | try {
|
94 | daemon = require('daemon');
|
95 | if (typeof daemon.daemonize !== 'function') {
|
96 | throw 'Invalid module';
|
97 | }
|
98 | } catch (e) {
|
99 | cli.fatal('daemon.node not installed. Please run `npm install daemon`');
|
100 | }
|
101 | break;
|
102 | case 'catchall':
|
103 | process.on('uncaughtException', function (err) {
|
104 | cli.error('Uncaught exception: ' + (err.msg || err));
|
105 | });
|
106 | break;
|
107 | case 'help': case 'version': case 'status':
|
108 | case 'autocomplete': case 'timeout':
|
109 |
|
110 | break;
|
111 | case 'glob':
|
112 | cli.glob = require('glob');
|
113 | break;
|
114 | default:
|
115 | cli.fatal('Unknown plugin "' + plugin + '"');
|
116 | break;
|
117 | }
|
118 | enable[plugin] = true;
|
119 | });
|
120 | return cli;
|
121 | }
|
122 | cli.disable = function (/*plugins*/) {
|
123 | Array.prototype.slice.call(arguments).forEach(function (plugin) {
|
124 | if (enable[plugin]) {
|
125 | enable[plugin] = false;
|
126 | }
|
127 | });
|
128 | return cli;
|
129 | }
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 | cli.setArgv = function (arr, keep_arg0) {
|
139 | if (typeof arr == 'string') {
|
140 | arr = arr.split(' ');
|
141 | } else {
|
142 | arr = arr.slice();
|
143 | }
|
144 | cli.app = arr.shift();
|
145 |
|
146 |
|
147 |
|
148 |
|
149 | if (!keep_arg0 && ('node' === cli.native.path.basename(cli.app)
|
150 | || cli.native.path.basename(process.execPath) === cli.app
|
151 | || process.execPath === cli.app)) {
|
152 | cli.app = arr.shift();
|
153 | }
|
154 | cli.app = cli.native.path.basename(cli.app);
|
155 | argv_parsed = false;
|
156 | cli.args = cli.argv = argv = arr;
|
157 | cli.argc = argv.length;
|
158 | cli.options = {};
|
159 | cli.command = null;
|
160 | };
|
161 | cli.setArgv(process.argv);
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 | cli.next = function () {
|
170 | if (!argv_parsed) {
|
171 | cli.args = [];
|
172 | argv_parsed = true;
|
173 | }
|
174 |
|
175 | curr_val = null;
|
176 |
|
177 |
|
178 | if (short_tags.length) {
|
179 | curr_opt = short_tags.shift();
|
180 | full_opt = '-' + curr_opt;
|
181 | return curr_opt;
|
182 | }
|
183 |
|
184 | if (!argv.length) {
|
185 | return false;
|
186 | }
|
187 |
|
188 | curr_opt = argv.shift();
|
189 |
|
190 |
|
191 | if (curr_opt === '-' || curr_opt === '--') {
|
192 | while (argv.length) {
|
193 | cli.args.push(argv.shift());
|
194 | }
|
195 | return false;
|
196 | }
|
197 |
|
198 |
|
199 | if (curr_opt[0] !== '-') {
|
200 | cli.args.push(curr_opt);
|
201 | return cli.next();
|
202 | } else {
|
203 |
|
204 | is_long = curr_opt[1] === '-';
|
205 | curr_opt = curr_opt.substr(is_long ? 2 : 1);
|
206 | }
|
207 |
|
208 |
|
209 | if (!is_long && curr_opt.length > 1) {
|
210 | short_tags = curr_opt.split('');
|
211 | return cli.next();
|
212 | }
|
213 |
|
214 | var eq, len;
|
215 |
|
216 |
|
217 | if (is_long && (eq = curr_opt.indexOf('=')) >= 0) {
|
218 | curr_val = curr_opt.substr(eq + 1);
|
219 | curr_opt = curr_opt.substr(0, eq);
|
220 | len = curr_val.length;
|
221 |
|
222 | if ((curr_val[0] === '"' && curr_val[len - 1] === '"') ||
|
223 | (curr_val[0] === "'" && curr_val[len - 1] === "'"))
|
224 | {
|
225 | curr_val = curr_val.substr(1, len-2);
|
226 | }
|
227 | if (curr_val.match(/^[0-9]+$/)) {
|
228 | curr_val = parseInt(curr_val, 10);
|
229 | }
|
230 | }
|
231 |
|
232 |
|
233 | full_opt = (is_long ? '--' : '-') + curr_opt;
|
234 |
|
235 | return curr_opt;
|
236 | };
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 | cli.parse = function (opts, command_def) {
|
257 | var default_val, i, parsed = cli.options, seen,
|
258 | catch_all = !opts;
|
259 | opt_list = opts || {};
|
260 | commands = command_def;
|
261 | command_list = commands || [];
|
262 | if (commands && !Array.isArray(commands)) {
|
263 | command_list = Object.keys(commands);
|
264 | }
|
265 | while (o = cli.next()) {
|
266 | seen = false;
|
267 | for (opt in opt_list) {
|
268 | if (!(opt_list[opt] instanceof Array)) {
|
269 | continue;
|
270 | }
|
271 | if (!opt_list[opt][0]) {
|
272 | opt_list[opt][0] = opt;
|
273 | }
|
274 | if (o === opt || o === opt_list[opt][0]) {
|
275 | seen = true;
|
276 | if (opt_list[opt].length === 2) {
|
277 | parsed[opt] = true;
|
278 | break;
|
279 | }
|
280 | default_val = null;
|
281 | if (opt_list[opt].length === 4) {
|
282 | default_val = opt_list[opt][3];
|
283 | }
|
284 | if (opt_list[opt][2] instanceof Array) {
|
285 | for (i = 0, l = opt_list[opt][2].length; i < l; i++) {
|
286 | if (typeof opt_list[opt][2][i] === 'number') {
|
287 | opt_list[opt][2][i] += '';
|
288 | }
|
289 | }
|
290 | parsed[opt] = cli.getArrayValue(opt_list[opt][2], is_long ? null : default_val);
|
291 | break;
|
292 | }
|
293 | if (opt_list[opt][2].toLowerCase) {
|
294 | opt_list[opt][2] = opt_list[opt][2].toLowerCase();
|
295 | }
|
296 | switch (opt_list[opt][2]) {
|
297 | case 'string': case 1: case true:
|
298 | parsed[opt] = cli.getValue(default_val);
|
299 | break;
|
300 | case 'int': case 'number': case 'num':
|
301 | case 'time': case 'seconds': case 'secs': case 'minutes': case 'mins':
|
302 | case 'x': case 'n':
|
303 | parsed[opt] = cli.getInt(default_val);
|
304 | break;
|
305 | case 'float': case 'decimal':
|
306 | parsed[opt] = cli.getFloat(default_val);
|
307 | break;
|
308 | case 'path': case 'file': case 'directory': case 'dir':
|
309 | parsed[opt] = cli.getPath(default_val, opt_list[opt][2]);
|
310 | break;
|
311 | case 'email':
|
312 | parsed[opt] = cli.getEmail(default_val);
|
313 | break;
|
314 | case 'url': case 'uri': case 'domain': case 'host':
|
315 | parsed[opt] = cli.getUrl(default_val, opt_list[opt][2]);
|
316 | break;
|
317 | case 'ip':
|
318 | parsed[opt] = cli.getIp(default_val);
|
319 | break;
|
320 | case 'bool': case 'boolean': case 'on':
|
321 | parsed[opt] = true;
|
322 | break;
|
323 | case 'false': case 'off': case false: case 0:
|
324 | parsed[opt] = false;
|
325 | break;
|
326 | default:
|
327 | cli.fatal('Unknown opt type "' + opt_list[opt][2] + '"');
|
328 | }
|
329 | break;
|
330 | }
|
331 | }
|
332 | if (process.env.NODE_DISABLE_COLORS) {
|
333 | no_color = true;
|
334 | }
|
335 | if (!seen) {
|
336 | if (enable.help && (o === 'h' || o === 'help')) {
|
337 | cli.getUsage();
|
338 | } else if (enable.version && (o === 'v' || o === 'version')) {
|
339 | if (cli.version == null) {
|
340 | cli.parsePackageJson();
|
341 | }
|
342 | console.error(cli.app + ' v' + cli.version);
|
343 | cli.exit();
|
344 | break;
|
345 | } else if (enable.daemon && (o === 'd' || o === 'daemon')) {
|
346 | daemon_arg = cli.getArrayValue(['start','stop','restart','pid','log'], is_long ? null : 'start');
|
347 | continue;
|
348 | } else if (enable.catchall && (o === 'c' || o === 'catch')) {
|
349 | continue;
|
350 | } else if (enable.status && (o === 'k' || o === 'no-color' || o === 'debug')) {
|
351 | no_color = (o === 'k' || o === 'no-color');
|
352 | show_debug = o === 'debug';
|
353 | continue;
|
354 | } else if (enable.timeout && (o === 't' || o === 'timeout')) {
|
355 | var secs = cli.getInt();
|
356 | setTimeout(function () {
|
357 | cli.fatal('Process timed out after ' + secs + 's');
|
358 | }, secs * 1000);
|
359 | continue;
|
360 | } else if (catch_all) {
|
361 | parsed[o] = curr_val || true;
|
362 | continue;
|
363 | }
|
364 | cli.fatal('Unknown option ' + full_opt);
|
365 | }
|
366 | }
|
367 |
|
368 | for (opt in opt_list) {
|
369 | default_val = opt_list[opt].length === 4 ? opt_list[opt][3] : null;
|
370 | if (!(opt_list[opt] instanceof Array)) {
|
371 | parsed[opt] = opt_list[opt];
|
372 | continue;
|
373 | } else if (typeof parsed[opt] === 'undefined') {
|
374 | parsed[opt] = default_val;
|
375 | }
|
376 | }
|
377 | if (command_list.length) {
|
378 | if (cli.args.length === 0) {
|
379 | if (enable.help) {
|
380 | cli.getUsage();
|
381 | } else {
|
382 | cli.fatal('A command is required (' + command_list.join(', ') + ').');
|
383 | }
|
384 | return cli.exit(1);
|
385 | } else {
|
386 | cli.command = cli.autocompleteCommand(cli.args.shift());
|
387 | }
|
388 | }
|
389 | cli.argc = cli.args.length;
|
390 | return parsed;
|
391 | };
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 | cli.autocompleteCommand = function (command) {
|
401 | var list;
|
402 | if (!(command_list instanceof Array)) {
|
403 | list = Object.keys(command_list);
|
404 | } else {
|
405 | list = command_list;
|
406 | }
|
407 | var i, j = 0, c = command.length, tmp_list;
|
408 | if (list.length === 0 || list.indexOf(command) !== -1) {
|
409 | return command;
|
410 | }
|
411 | for (i = 0; i < c; i++) {
|
412 | tmp_list = [];
|
413 | l = list.length;
|
414 | if (l <= 1) break;
|
415 | for (j = 0; j < l; j++)
|
416 | if (list[j].length >= i && list[j][i] === command[i])
|
417 | tmp_list.push(list[j]);
|
418 | list = tmp_list;
|
419 | }
|
420 | l = list.length;
|
421 | if (l === 1) {
|
422 | return list[0];
|
423 | } else if (l === 0) {
|
424 | cli.fatal('Unknown command "' + command + '"' + (enable.help ? '. Please see --help for more information' : ''));
|
425 | } else {
|
426 | list.sort();
|
427 | cli.fatal('The command "' + command + '" is ambiguous and could mean "' + list.join('", "') + '"');
|
428 | }
|
429 | };
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 |
|
444 | cli.status = function (msg, type) {
|
445 | var pre;
|
446 | switch (type) {
|
447 | case 'info':
|
448 | pre = no_color ? 'INFO:' : '\x1B[33mINFO\x1B[0m:';
|
449 | break;
|
450 | case 'debug':
|
451 | pre = no_color ? 'DEBUG:' : '\x1B[36mDEBUG\x1B[0m:';
|
452 | break;
|
453 | case 'error':
|
454 | case 'fatal':
|
455 | pre = no_color ? 'ERROR:' : '\x1B[31mERROR\x1B[0m:';
|
456 | break;
|
457 | case 'ok':
|
458 | pre = no_color ? 'OK:' : '\x1B[32mOK\x1B[0m:';
|
459 | break;
|
460 | }
|
461 | msg = pre + ' ' + msg;
|
462 | if (type === 'fatal') {
|
463 | console.error(msg);
|
464 | return cli.exit(1);
|
465 | }
|
466 | if (enable.status && !show_debug && type === 'debug') {
|
467 | return;
|
468 | }
|
469 | console.error(msg);
|
470 | };
|
471 | ['info','error','ok','debug','fatal'].forEach(function (type) {
|
472 | cli[type] = function (msg) {
|
473 | cli.status(msg, type);
|
474 | };
|
475 | });
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 | cli.setApp = function (name, version) {
|
489 | if (name.indexOf('package.json') !== -1) {
|
490 | cli.parsePackageJson(name);
|
491 | } else {
|
492 | cli.app = name;
|
493 | cli.version = version;
|
494 | }
|
495 | return cli;
|
496 | };
|
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 | cli.parsePackageJson = function (path) {
|
506 | var parse_packagejson = function (path) {
|
507 | var packagejson = JSON.parse(cli.native.fs.readFileSync(path, 'utf8'));
|
508 | cli.version = packagejson.version;
|
509 | cli.app = packagejson.name;
|
510 | };
|
511 | var try_all = function (arr, func, err) {
|
512 | for (var i = 0, l = arr.length; i < l; i++) {
|
513 | try {
|
514 | func(arr[i]);
|
515 | return;
|
516 | } catch (e) {
|
517 | if (i === l-1) {
|
518 | cli.fatal(err);
|
519 | }
|
520 | }
|
521 | }
|
522 | };
|
523 | try {
|
524 | if (path) {
|
525 | return parse_packagejson(path);
|
526 | }
|
527 | try_all([
|
528 | __dirname + '/package.json',
|
529 | __dirname + '/../package.json',
|
530 | __dirname + '/../../package.json'
|
531 | ], parse_packagejson);
|
532 | } catch (e) {
|
533 | cli.fatal('Could not detect ' + cli.app + ' version');
|
534 | }
|
535 | };
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 | cli.setUsage = function (u) {
|
545 | usage = u;
|
546 | return cli;
|
547 | };
|
548 |
|
549 | var pad = function (str, len) {
|
550 | if (typeof len === 'undefined') {
|
551 | len = str;
|
552 | str = '';
|
553 | }
|
554 | if (str.length < len) {
|
555 | len -= str.length;
|
556 | while (len--) str += ' ';
|
557 | }
|
558 | return str;
|
559 | };
|
560 |
|
561 |
|
562 |
|
563 |
|
564 |
|
565 |
|
566 |
|
567 | cli.getUsage = function (code) {
|
568 | var short, desc, optional, line, seen_opts = [],
|
569 | switch_pad = cli.option_width;
|
570 |
|
571 | var trunc_desc = function (pref, desc, len) {
|
572 | var pref_len = pref.length,
|
573 | desc_len = cli.width - pref_len,
|
574 | truncated = '';
|
575 | if (desc.length <= desc_len) {
|
576 | return desc;
|
577 | }
|
578 | var desc_words = (desc+'').split(' '), chars = 0, word;
|
579 | while (desc_words.length) {
|
580 | truncated += (word = desc_words.shift()) + ' ';
|
581 | chars += word.length;
|
582 | if (desc_words.length && chars + desc_words[0].length > desc_len) {
|
583 | truncated += '\n' + pad(pref_len);
|
584 | chars = 0;
|
585 | }
|
586 | }
|
587 | return truncated;
|
588 | };
|
589 |
|
590 | usage = usage || cli.app + ' [OPTIONS]' + (command_list.length ? ' <command>' : '') + ' [ARGS]';
|
591 | if (no_color) {
|
592 | console.error('Usage:\n ' + usage);
|
593 | console.error('Options: ');
|
594 | } else {
|
595 | console.error('\x1b[1mUsage\x1b[0m:\n ' + usage);
|
596 | console.error('\n\x1b[1mOptions\x1b[0m: ');
|
597 | }
|
598 | for (opt in opt_list) {
|
599 |
|
600 | if (opt.length === 1) {
|
601 | long = opt_list[opt][0];
|
602 | short = opt;
|
603 | } else {
|
604 | long = opt;
|
605 | short = opt_list[opt][0];
|
606 | }
|
607 |
|
608 |
|
609 | desc = opt_list[opt][1].trim();
|
610 | type = opt_list[opt].length >= 3 ? opt_list[opt][2] : null;
|
611 | optional = opt_list[opt].length === 4 ? opt_list[opt][3] : null;
|
612 |
|
613 |
|
614 | if (short === long) {
|
615 | if (short.length === 1) {
|
616 | line = ' -' + short;
|
617 | } else {
|
618 | line = ' --' + long;
|
619 | }
|
620 | } else if (short) {
|
621 | line = ' -' + short + ', --' + long;
|
622 | } else {
|
623 | line = ' --' + long;
|
624 | }
|
625 | line += ' ';
|
626 |
|
627 | if (type) {
|
628 | if (type instanceof Array) {
|
629 | desc += '. VALUE must be either [' + type.join('|') + ']';
|
630 | type = 'VALUE';
|
631 | }
|
632 | if (type === true || type === 1) {
|
633 | type = long.toUpperCase();
|
634 | }
|
635 | type = type.toUpperCase();
|
636 | if (type === 'FLOAT' || type === 'INT') {
|
637 | type = 'NUMBER';
|
638 | }
|
639 | line += optional ? '[' + type + ']' : type;
|
640 | }
|
641 | line = pad(line, switch_pad);
|
642 | line += trunc_desc(line, desc);
|
643 | line += optional ? ' (Default is ' + optional + ')' : '';
|
644 | console.error(line.replace('%s', '%\0s'));
|
645 |
|
646 | seen_opts.push(short);
|
647 | seen_opts.push(long);
|
648 | }
|
649 | if (enable.timeout && seen_opts.indexOf('t') === -1 && seen_opts.indexOf('timeout') === -1) {
|
650 | console.error(pad(' -t, --timeout N', switch_pad) + 'Exit if the process takes longer than N seconds');
|
651 | }
|
652 | if (enable.status) {
|
653 | if (seen_opts.indexOf('k') === -1 && seen_opts.indexOf('no-color') === -1) {
|
654 | console.error(pad(' -k, --no-color', switch_pad) + 'Omit color from output');
|
655 | }
|
656 | if (seen_opts.indexOf('debug') === -1) {
|
657 | console.error(pad(' --debug', switch_pad) + 'Show debug information');
|
658 | }
|
659 | }
|
660 | if (enable.catchall && seen_opts.indexOf('c') === -1 && seen_opts.indexOf('catch') === -1) {
|
661 | console.error(pad(' -c, --catch', switch_pad) + 'Catch unanticipated errors');
|
662 | }
|
663 | if (enable.daemon && seen_opts.indexOf('d') === -1 && seen_opts.indexOf('daemon') === -1) {
|
664 | console.error(pad(' -d, --daemon [ARG]', switch_pad) + 'Daemonize the process. Control the daemon using [start, stop, restart, log, pid]');
|
665 | }
|
666 | if (enable.version && seen_opts.indexOf('v') === -1 && seen_opts.indexOf('version') === -1) {
|
667 | console.error(pad(' -v, --version', switch_pad) + 'Display the current version');
|
668 | }
|
669 | if (enable.help && seen_opts.indexOf('h') === -1 && seen_opts.indexOf('help') === -1) {
|
670 | console.error(pad(' -h, --help', switch_pad) + 'Display help and usage details');
|
671 | }
|
672 | if (command_list.length) {
|
673 | console.error('\n\x1b[1mCommands\x1b[0m: ');
|
674 | if (!Array.isArray(commands)) {
|
675 | for (var c in commands) {
|
676 | line = ' ' + pad(c, switch_pad - 2);
|
677 | line += trunc_desc(line, commands[c]);
|
678 | console.error(line);
|
679 | }
|
680 | } else {
|
681 | command_list.sort();
|
682 | console.error(' ' + trunc_desc(' ', command_list.join(', ')));
|
683 | }
|
684 | }
|
685 | return cli.exit(code);
|
686 | };
|
687 |
|
688 |
|
689 |
|
690 |
|
691 |
|
692 |
|
693 |
|
694 |
|
695 | cli.getOptError = function (expects, type) {
|
696 | var err = full_opt + ' expects ' + expects
|
697 | + '. Use `' + cli.app + ' ' + full_opt + (is_long ? '=' : ' ') + type + '`';
|
698 | return err;
|
699 | };
|
700 |
|
701 |
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 |
|
708 |
|
709 |
|
710 |
|
711 | cli.getValue = function (default_val, validate_func, err_msg) {
|
712 | err_msg = err_msg || cli.getOptError('a value', 'VALUE');
|
713 |
|
714 | var value;
|
715 |
|
716 | try {
|
717 | if (curr_val) {
|
718 | if (validate_func) {
|
719 | curr_val = validate_func(curr_val);
|
720 | }
|
721 | return curr_val;
|
722 | }
|
723 |
|
724 |
|
725 | if (short_tags.length) {
|
726 | throw 'Short tags';
|
727 | }
|
728 |
|
729 |
|
730 |
|
731 | if (!argv.length || (argv[0].length === 1 && argv[0][0] === '-')) {
|
732 | throw 'No value';
|
733 | }
|
734 |
|
735 | value = argv.shift();
|
736 |
|
737 | if (value.match(/^[0-9]+$/)) {
|
738 | value = parseInt(value, 10);
|
739 | }
|
740 |
|
741 |
|
742 | if (validate_func) {
|
743 | value = validate_func(value);
|
744 | }
|
745 | } catch (e) {
|
746 |
|
747 |
|
748 |
|
749 | if (value) {
|
750 | argv.unshift(value);
|
751 | }
|
752 | return default_val != null ? default_val : cli.fatal(err_msg);
|
753 | }
|
754 | return value;
|
755 | };
|
756 |
|
757 | cli.getInt = function (default_val) {
|
758 | return cli.getValue(default_val, function (value) {
|
759 | if (typeof value === 'number') return value;
|
760 | if (!value.match(/^(?:-?(?:0|[1-9][0-9]*))$/)) {
|
761 | throw 'Invalid int';
|
762 | }
|
763 | return parseInt(value);
|
764 | }, cli.getOptError('a number', 'NUMBER'));
|
765 | }
|
766 |
|
767 | cli.getFloat = function (default_val) {
|
768 | return cli.getValue(default_val, function (value) {
|
769 | if (!value.match(/^(?:-?(?:0|[1-9][0-9]*))?(?:\.[0-9]*)?$/)) {
|
770 | throw 'Invalid float';
|
771 | }
|
772 | return parseFloat(value, 10);
|
773 | }, cli.getOptError('a number', 'NUMBER'));
|
774 | }
|
775 |
|
776 | cli.getUrl = function (default_val, identifier) {
|
777 | identifier = identifier || 'url';
|
778 | return cli.getValue(default_val, function (value) {
|
779 | if (!value.match(/^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2})?)|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/i)) {
|
780 | throw 'Invalid URL';
|
781 | }
|
782 | return value;
|
783 | }, cli.getOptError('a ' + identifier, identifier.toUpperCase()));
|
784 | }
|
785 |
|
786 | cli.getEmail = function (default_val) {
|
787 | return cli.getValue(default_val, function (value) {
|
788 | if (!value.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/)) {
|
789 | throw 'Invalid email';
|
790 | }
|
791 | return value;
|
792 | }, cli.getOptError('an email', 'EMAIL'));
|
793 | }
|
794 |
|
795 | cli.getIp = function (default_val) {
|
796 | return cli.getValue(default_val, function (value) {
|
797 | if (!value.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)) {
|
798 | throw 'Invalid IP';
|
799 | }
|
800 | return value;
|
801 | }, cli.getOptError('an IP', 'IP'));
|
802 | }
|
803 |
|
804 | cli.getPath = function (default_val, identifier) {
|
805 | identifier = identifier || 'path';
|
806 | return cli.getValue(default_val, function (value) {
|
807 | if (value.match(/[?*;{}]/)) {
|
808 | throw 'Invalid path';
|
809 | }
|
810 | return value;
|
811 | }, cli.getOptError('a ' + identifier, identifier.toUpperCase()));
|
812 | }
|
813 |
|
814 | cli.getArrayValue = function (arr, default_val) {
|
815 | return cli.getValue(default_val, function (value) {
|
816 | if (arr.indexOf(value) === -1) {
|
817 | throw 'Unexpected value';
|
818 | }
|
819 | return value;
|
820 | }, cli.getOptError('either [' + arr.join('|') + ']', 'VALUE'));
|
821 | }
|
822 |
|
823 |
|
824 |
|
825 |
|
826 |
|
827 |
|
828 |
|
829 |
|
830 | cli.withStdin = function (encoding, callback) {
|
831 | if (typeof encoding === 'function') {
|
832 | callback = encoding;
|
833 | encoding = 'utf8';
|
834 | }
|
835 | var stream = process.openStdin(), data = '';
|
836 | stream.setEncoding(encoding);
|
837 | stream.on('data', function (chunk) {
|
838 | data += chunk;
|
839 | });
|
840 | stream.on('end', function () {
|
841 | callback.apply(cli, [data]);
|
842 | });
|
843 | };
|
844 |
|
845 |
|
846 |
|
847 |
|
848 |
|
849 |
|
850 |
|
851 |
|
852 |
|
853 | cli.withStdinLines = function (callback) {
|
854 | cli.withStdin(function (data) {
|
855 | var sep = data.indexOf('\r\n') !== -1 ? '\r\n' : '\n';
|
856 | callback.apply(cli, [data.split(sep), sep]);
|
857 | });
|
858 | };
|
859 |
|
860 |
|
861 |
|
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 |
|
869 |
|
870 | cli.withInput = function (file, encoding, callback) {
|
871 | if (typeof encoding === 'function') {
|
872 | callback = encoding;
|
873 | encoding = 'utf8';
|
874 | } else if (typeof file === 'function') {
|
875 | callback = file;
|
876 | encoding = 'utf8';
|
877 | file = 'stdin';
|
878 | }
|
879 | if (file === 'stdin') {
|
880 | file = process.openStdin();
|
881 | } else {
|
882 | try {
|
883 | file = cli.native.fs.createReadStream(file);
|
884 | file.on('error', cli.fatal);
|
885 | } catch (e) {
|
886 | return cli.fatal(e);
|
887 | }
|
888 | }
|
889 | file.setEncoding(encoding);
|
890 | var lines = [], data = '', eof, sep;
|
891 | file.on('data', function (chunk) {
|
892 | if (eof) return;
|
893 | data += chunk;
|
894 | if (!sep) {
|
895 | if (data.indexOf('\r\n') !== -1) {
|
896 | sep = '\r\n';
|
897 | } else if (data.indexOf('\n') !== -1) {
|
898 | sep = '\n';
|
899 | } else {
|
900 | last_line = data;
|
901 | return;
|
902 | }
|
903 | }
|
904 | lines = data.split(sep);
|
905 | data = eof ? null : lines.pop();
|
906 | while (lines.length) {
|
907 | callback.apply(cli, [lines.shift(), sep, false]);
|
908 | }
|
909 | });
|
910 | file.on('end', function () {
|
911 | eof = true;
|
912 | if (data.length) {
|
913 | callback.apply(cli, [data, sep || '', false]);
|
914 | }
|
915 | callback.apply(cli, [null, null, true]);
|
916 | });
|
917 | };
|
918 |
|
919 |
|
920 |
|
921 |
|
922 |
|
923 |
|
924 |
|
925 |
|
926 |
|
927 |
|
928 |
|
929 |
|
930 |
|
931 |
|
932 |
|
933 | cli.daemon = function (arg, callback) {
|
934 | if (typeof daemon === 'undefined') {
|
935 | cli.fatal('Daemon is not initialized');
|
936 | }
|
937 |
|
938 | if (typeof arg === 'function') {
|
939 | callback = arg;
|
940 | arg = 'start';
|
941 | }
|
942 |
|
943 | var lock_file = '/tmp/' + cli.app + '.pid',
|
944 | log_file = '/tmp/' + cli.app + '.log';
|
945 |
|
946 | var start = function () {
|
947 | daemon.daemonize(log_file, lock_file, function (err) {
|
948 | if (err) return cli.error('Error starting daemon: ' + err);
|
949 | callback();
|
950 | });
|
951 | };
|
952 |
|
953 | var stop = function () {
|
954 | try {
|
955 | cli.native.fs.readFileSync(lock_file);
|
956 | } catch (e) {
|
957 | return cli.error('Daemon is not running');
|
958 | };
|
959 | daemon.kill(lock_file, function (err, pid) {
|
960 | if (err && err.errno === 3) {
|
961 | return cli.error('Daemon is not running');
|
962 | } else if (err) {
|
963 | return cli.error('Error stopping daemon: ' + err.errno);
|
964 | }
|
965 | cli.ok('Successfully stopped daemon with pid: ' + pid);
|
966 | });
|
967 | };
|
968 |
|
969 | switch(arg) {
|
970 | case 'stop':
|
971 | stop();
|
972 | break;
|
973 | case 'restart':
|
974 | daemon.stop(lock_file, function () {
|
975 | start();
|
976 | });
|
977 | break;
|
978 | case 'log':
|
979 | try {
|
980 | cli.native.fs.createReadStream(log_file, {encoding: 'utf8'}).pipe(process.stdout);
|
981 | } catch (e) {
|
982 | return cli.error('No daemon log file');
|
983 | };
|
984 | break;
|
985 | case 'pid':
|
986 | try {
|
987 | var pid = cli.native.fs.readFileSync(lock_file, 'utf8');
|
988 | cli.native.fs.statSync('/proc/' + pid);
|
989 | cli.info(pid);
|
990 | } catch (e) {
|
991 | return cli.error('Daemon is not running');
|
992 | };
|
993 | break;
|
994 | default:
|
995 | start();
|
996 | break;
|
997 | }
|
998 | }
|
999 |
|
1000 |
|
1001 |
|
1002 |
|
1003 |
|
1004 |
|
1005 |
|
1006 |
|
1007 | cli.main = function (callback) {
|
1008 | var after = function () {
|
1009 | callback.apply(cli, [cli.args, cli.options]);
|
1010 | };
|
1011 | if (enable.daemon && daemon_arg) {
|
1012 | cli.daemon(daemon_arg, after);
|
1013 | } else {
|
1014 | after();
|
1015 | }
|
1016 | }
|
1017 |
|
1018 |
|
1019 |
|
1020 |
|
1021 |
|
1022 |
|
1023 |
|
1024 |
|
1025 |
|
1026 |
|
1027 |
|
1028 | cli.createServer = function(/*layers*/) {
|
1029 | var defaultStackErrorHandler = function (req, res, err) {
|
1030 | if (err) {
|
1031 | console.error(err.stack);
|
1032 | res.writeHead(500, {"Content-Type": "text/plain"});
|
1033 | return res.end(err.stack + "\n");
|
1034 | }
|
1035 | res.writeHead(404, {"Content-Type": "text/plain"});
|
1036 | res.end("Not Found\n");
|
1037 | };
|
1038 | var handle = error = defaultStackErrorHandler,
|
1039 | layers = Array.prototype.slice.call(arguments);
|
1040 |
|
1041 |
|
1042 | if (layers.length && layers[0] instanceof Array) {
|
1043 | layers = layers[0];
|
1044 | }
|
1045 | layers.reverse().forEach(function (layer) {
|
1046 | var child = handle;
|
1047 | handle = function (req, res) {
|
1048 | try {
|
1049 | layer(req, res, function (err) {
|
1050 | if (err) return error(req, res, err);
|
1051 | child(req, res);
|
1052 | });
|
1053 | } catch (err) {
|
1054 | error(req, res, err);
|
1055 | }
|
1056 | };
|
1057 | });
|
1058 | return cli.native.http.createServer(handle);
|
1059 | };
|
1060 |
|
1061 |
|
1062 |
|
1063 |
|
1064 |
|
1065 |
|
1066 |
|
1067 |
|
1068 |
|
1069 |
|
1070 |
|
1071 |
|
1072 |
|
1073 | cli.exec = function (cmd, callback, errback) {
|
1074 | cli.native.child_process.exec(cmd, function (err, stdout, stderr) {
|
1075 | err = err || stderr;
|
1076 | if (err) {
|
1077 | if (errback) {
|
1078 | return errback(err, stdout);
|
1079 | }
|
1080 | return cli.fatal('exec() failed\n' + err);
|
1081 | }
|
1082 | if (callback) {
|
1083 | callback(stdout.split('\n'));
|
1084 | }
|
1085 | });
|
1086 | };
|
1087 |
|
1088 |
|
1089 |
|
1090 |
|
1091 |
|
1092 |
|
1093 |
|
1094 | var last_progress_call, progress_len = 74;
|
1095 | cli.progress = function (progress, decimals) {
|
1096 | if (progress < 0 || progress > 1 || isNaN(progress)) return;
|
1097 | if (!decimals) decimals = 0;
|
1098 | var now = (new Date()).getTime();
|
1099 | if (last_progress_call && (now - last_progress_call) < 100 && progress !== 1) {
|
1100 | return;
|
1101 | }
|
1102 | last_progress_call = now;
|
1103 |
|
1104 |
|
1105 | var barLength = Math.floor(progress_len * progress),
|
1106 | str = '';
|
1107 | if (barLength == 0 && progress > 0) {
|
1108 | barLength = 1;
|
1109 | }
|
1110 | for (var i = 1; i <= progress_len; i++) {
|
1111 | str += i <= barLength ? '#' : ' ';
|
1112 | }
|
1113 | var pwr = Math.pow(10, decimals);
|
1114 | var percentage = Math.floor(progress * 100 * pwr) / pwr + '%';
|
1115 | for (i = 0; i < decimals; i++) {
|
1116 | percentage += ' ';
|
1117 | }
|
1118 | cli.native.util.print('[' + str + '] ' + percentage + (progress === 1 ? '\n' : '\u000D'));
|
1119 | };
|
1120 |
|
1121 |
|
1122 |
|
1123 |
|
1124 |
|
1125 |
|
1126 |
|
1127 | var spinnerInterval;
|
1128 | cli.spinner = function (prefix, end) {
|
1129 | if (end) {
|
1130 | cli.native.util.print('\u000D' + prefix);
|
1131 | return clearInterval(spinnerInterval);
|
1132 | }
|
1133 | prefix = prefix + ' ' || '';
|
1134 | var spinner = ['-','\\','|','/'], i = 0, l = spinner.length;
|
1135 | spinnerInterval = setInterval(function () {
|
1136 | cli.native.util.print('\u000D' + prefix + spinner[i++]);
|
1137 | if (i == l) i = 0;
|
1138 | }, 200);
|
1139 | };
|