summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Gordon <assafgordon@gmail.com>2014-06-21 03:04:38 (GMT)
committerA. Gordon <assafgordon@gmail.com>2014-06-21 03:04:38 (GMT)
commit6bb109bf01ed769c34a3c501e5428bb1962ba044 (patch)
treeb253f2ed692c182cbd3e186a385575e844c76113
parent7e1d4559ac5500073450d9dc69f63722d2175c32 (diff)
downloadagnostic-6bb109bf01ed769c34a3c501e5428bb1962ba044.zip
agnostic-6bb109bf01ed769c34a3c501e5428bb1962ba044.tar.gz
agnostic-6bb109bf01ed769c34a3c501e5428bb1962ba044.tar.bz2
New program: grep (alpha stage)
-rw-r--r--src/node_modules/programs/grep.js327
1 files changed, 327 insertions, 0 deletions
diff --git a/src/node_modules/programs/grep.js b/src/node_modules/programs/grep.js
new file mode 100644
index 0000000..b262b7e
--- /dev/null
+++ b/src/node_modules/programs/grep.js
@@ -0,0 +1,327 @@
+/*
+ This file is part of UNIX Guide for the Perplexed project.
+ Copyright (C) 2014 by Assaf Gordon <assafgordon@gmail.com>
+ Released under GPLv3 or later, with the following addition:
+
+ As additional permission under GNU GPL version 3 section 7, you
+ may distribute non-source (e.g., minimized or compacted) forms of
+ that code without the copy of the GNU GPL normally required by
+ section 4, provided you include this license notice and a URL
+ through which recipients can access the Corresponding Source.
+
+ See: https://www.gnu.org/philosophy/javascript-trap.html
+
+ POSIX grep:
+ http://pubs.opengroup.org/onlinepubs/009695399/utilities/grep.html
+
+
+ POSIX Regular Expressions (BRE & ERE):
+ http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html
+*/
+
+/*
+A Naive implementation of grep(1)
+*/
+"use strict";
+
+var ob_utils = require('utils/object_utils');
+var str_utils = require('utils/string_utils');
+var sprintf = require('utils/sprintf');
+var PosixGetOpt = require('utils/posix-getopt');
+var ProgramBase = require('programs/program_base');
+
+module.exports = ProgramGrep;
+
+function ProgramGrep() {
+ this.name = "ProgramGrep";
+ this.program_name = "grep";
+ this.files = [] ; //input file names
+ this.regex_type = "basic"; // or fixed / extended / perl
+ this.patterns = []; //list of regex patterns
+ this.patterns_file = null ; // -f "pattern_file"
+ this.ignore_case = false; // -i
+ this.show_filenames = false; // -l
+ this.show_line_number = false; // -n
+ this.show_counts = false; //-c
+ this.quiet = false ; // -q
+ this.ignore_non_readable_files = false ; // -s
+ this.invert_match = false ; // -v
+ this.match_entire_line = false ; // -x
+ this.match_words = false ; // -w
+ this.regex_type = "BRE";
+
+ this.match_found_exit_code = 1; //default - no match found
+}
+ProgramGrep.prototype = new ProgramBase();
+ProgramGrep.prototype.constructor = ProgramGrep;
+
+ProgramGrep.prototype.parse_command_line=function() {
+ //Options copied from GNU grep, though some will trigger a "not implemented" error.
+ var parser = new PosixGetOpt(':E(extended-regexp)' +
+ 'F(fixed-strings)' +
+ 'G(basic-regexp)' +
+ 'P(perl-regexp)' +
+ 'e:(regexp)' +
+ 'f:(file)' +
+ 'i(ignore-case)' +
+ 'w(word-regexp)' +
+ 'x(line-regexp)' +
+ 'z(null-data)' +
+ 's(no-messages)' +
+ 'v(invert-match)' +
+ 'V(version)' +
+ '\u1000(help)' +
+ 'm:(max-count)' +
+ 'b(byte-offset)' +
+ 'n(line-number)' +
+ '\u1001(line-buffered)' +
+ 'H(with-filename)' +
+ 'h(no-filename)' +
+ '\u1002:(label)' +
+ 'o(only-matching)' +
+ 'q(quiet)' +
+ '\u1003(silent)' +
+ '\u1004(binary-files)' +
+ 'a(text)' +
+ 'I' +
+ 'd:(directories)' +
+ 'D:(devices)' +
+ 'r(recursive)' +
+ 'R(dereference-recursive)' +
+ '\u1010:(include)' +
+ '\u1011:(exclude)' +
+ '\u1012:(exclude-from)' +
+ '\u1013:(exclude-dir)' +
+ 'L(files-without-match)' +
+ 'l(files-with-matches)' +
+ 'c(count)' +
+ 'T(initial-tab)' +
+ 'Z(null)' +
+ 'B:(before-context)' +
+ 'A:(after-context)' +
+ 'C:(context)' +
+ '\u1020:(color)' +
+ 'U(binary)' +
+ 'u(unix-byte-offets)', this.argv);
+
+ var option;
+ while ((option = parser.getopt()) !== undefined) {
+ switch (option.option) {
+ case 'E':
+ this.regex_type = "ERE";
+ break;
+
+ case 'F':
+ this.regex_type = "FIXED";
+ break;
+
+ case 'G':
+ this.regex_type = 'BRE';
+ break;
+
+ case 'P':
+ //TODO: Javascript's RegExp is not fully Perl-compatible.
+ this.regex_type = 'PERL';
+ break;
+
+ case 'e':
+ this.patterns.push(option.optarg);
+ break;
+
+ case 'f':
+ this.pattern_file = option.optarg;
+ break;
+
+ case 'w':
+ this.match_words = true;
+ break;
+
+ case 'x':
+ this.match_entire_line = true;
+ break;
+
+ case 's':
+ this.ignore_non_readable_files = true;
+ break;
+
+ case 'v':
+ this.invert_match = true;
+ break;
+
+ case 'i':
+ this.ignore_case = true;
+ break;
+
+ case 'l':
+ this.show_filenames = true;
+ break;
+
+ case 'n':
+ this.show_line_number = true;
+ break;
+
+ case 'c':
+ this.show_counts = true;
+ break;
+
+ case 'q':
+ case '\u1003': // --silent
+ this.quiet = true;
+ break;
+
+ case '\u1000':
+ this.show_help();
+ this.exit(0);
+
+ case 'V':
+ this.show_version();
+ this.exit(0);
+
+//Unsupported GNU Grep options follow, notify the user
+ case 'z':
+ case 'm':
+ case 'b':
+ case '\u1001': // --line-buffered
+ case 'H':
+ case 'h':
+ case '\u1002': // --label
+ case 'o':
+ case '\u1004': // --binary-files
+ case 'a':
+ case 'I':
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ case '\u1010': //--include
+ case '\u1011': //--exclude
+ case '\u1012': //--exclude-from
+ case '\u1013': //--exclude-dir
+ case 'L':
+ case 'T':
+ case 'Z':
+ case 'B':
+ case 'A':
+ case 'C':
+ case '\u1020': //--color
+ case 'u':
+ case 'U':
+ this.error_not_implemented_feature("Advanged GREP options","not supported by this Emulation");
+
+ default:
+ this.error_invalid_argument(option.errmsg);
+ }
+ }
+
+ // Get first/increment/last parameters
+ var args = this.argv.slice(parser.optind());
+
+ // Without -e or -f ,
+ // assume the first non-option parameter is the pattern
+ if ( this.patterns.length === 0 && ! this.pattern_file ) {
+ if (args.length===0)
+ this.error_invalid_argument("Usage: grep [OPTION]... PATTERN [FILE]...");
+
+ this.patterns.push(args.shift());
+ }
+
+ // If a pattern file is given, load patterns from the file.
+ // TODO: implement this
+ if ( this.pattern_file )
+ this.error_not_implemented_feature("Loading PATTERNs from file (-f/--file=FILE) is not implemented yet","");
+
+ // Convert the Regex patterns to javascript
+ if (this.regex_type === "BRE")
+ this.patterns = this.patterns.map ( str_utils.regex_BRE_to_JS );
+ else if (this.regex_type === "ERE")
+ this.patterns = this.patterns.map ( str_utils.regex_ERE_to_JS );
+ else if (this.regex_type !== "FIXED")
+ //TODO: Perl regex should be easy (or so...) in Javascript RegExp
+ this.error_not_implemented_feature("Perl Regex Patterns","");
+
+ // Build RegExp object for each pattern
+ this.patterns = this.patterns.map( function(x) { return new RegExp(x); });
+
+ // If no files are given, process "stdin"
+ // TODO: in GNU Grep, if using "-r" and no file is given, process "." instead of STDIN.
+ if (args.length === 0)
+ args.push("-");
+ this.files = args ;
+}
+
+ProgramGrep.prototype.grep_content=function(filename,content) {
+
+ if (filename === '-')
+ filename = "(standard input)";
+
+ // Match already found with "-q" - don't check other files.
+ if (this.quiet && (this.match_found_exit_code==0))
+ return;
+
+ var match_count = 0;
+
+ next_line: for (var i in content) {
+ var line = content[i];
+
+ for (var p in this.patterns) {
+ var pattern = this.patterns[p];
+ var line_match;
+
+ if (this.regex_type === "FIXED")
+ line_match = (line.indexOf(pattern) != -1);
+ else
+ line_match = pattern.test(line);
+
+ if (!line_match)
+ continue;
+
+ // Line matches - what to do?
+
+ // Match found with "-q" mode -
+ // we just need to return a SUCCESS exit code,
+ // not to test the rest of the file or any other file.
+ if (this.quiet) {
+ this.match_found_exit_code = 0 ;
+ return;
+ }
+
+
+ // Match found with "-l" mode (show filenames)
+ // don't check the result of the file, just print its name
+ if (this.show_filenames) {
+ this.match_found_exit_code = 0 ;
+ this.runtime.stdout.put_line(filename);
+ return;
+ }
+
+ // Match found with '-c' mode (show counts)
+ // stop testing this line
+ if (this.show_counts) {
+ match_count++;
+ this.match_found_exit_code = 0 ;
+ continue next_line;
+ }
+
+ }
+ }
+
+ //At the end of the file
+ if (this.show_counts) {
+ if (this.files.length===1) {
+ // one file => show the count
+ this.runtime.stdout.put_line(match_count.toString());
+ } else {
+ //multiple files => show the file name:count
+ this.runtime.stdout.put_line(filename + ":" + match_count.toString());
+ }
+
+ }
+}
+
+ProgramGrep.prototype.internal_run=function() {
+ this.parse_command_line();
+
+ this.process_files (this.files, this.grep_content );
+
+ return this.match_found_exit_code;
+}