├── .travis.yml ├── .zunit.yml ├── LICENSE ├── rules ├── escaped_commands ├── escaped_single_quotes ├── int_string_comparison └── unescaped_dollar_literals ├── tests ├── _output │ └── .gitkeep ├── _support │ └── .gitkeep └── rules │ ├── escaped_commands.zunit │ ├── int_string_comparison.zunit │ └── unescaped_dollar_literals.zunit └── zlint /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: linux 4 | dist: trusty 5 | sudo: required 6 | - os: osx 7 | env: ZSH_SRC=system 8 | - os: osx 9 | env: ZSH_SRC=homebrew 10 | before_script: 11 | - if [[ "$ZSH_SRC" == "homebrew" ]]; then brew install zsh; fi 12 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install zsh; fi 13 | - mkdir .bin 14 | - curl -L https://raw.githubusercontent.com/molovo/revolver/master/revolver > .bin/revolver 15 | - curl -L https://raw.githubusercontent.com/molovo/color/master/color.zsh > .bin/color 16 | - curl -L https://raw.githubusercontent.com/molovo/zunit/master/zunit > .bin/zunit 17 | - chmod u+x .bin/{color,revolver,zunit} 18 | - export PATH="$PWD/.bin:$PATH" 19 | script: zunit 20 | -------------------------------------------------------------------------------- /.zunit.yml: -------------------------------------------------------------------------------- 1 | tap: false 2 | directories: 3 | tests: tests 4 | output: tests/_output 5 | support: tests/_support 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 James Dinsdale 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /rules/escaped_commands: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | function _zlint_rule_escaped_commands() { 4 | local line="$@" 5 | 6 | pcre_compile '^[\\][a-zA-Z-_]+\s?' 7 | pcre_study 8 | if pcre_match "$line" 2>&1; then 9 | echo 'Prefer `command mycommand` instead of `\mycommand` to override aliases' 10 | return 1 11 | fi 12 | 13 | pcre_compile '^[^'\''"]+\\[^'\''"]+$' 14 | pcre_study 15 | if pcre_match "$line" 2>&1; then 16 | echo 'Commands containing backslashes should be quoted' 17 | return 1 18 | fi 19 | } 20 | -------------------------------------------------------------------------------- /rules/escaped_single_quotes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | function _zlint_rule_escaped_single_quotes() { 4 | local line="$@" 5 | 6 | pcre_compile ''\''[^'\'']+[\\]['\''][^'\'']+'\''' 7 | pcre_study 8 | if pcre_match "$line" 2>&1; then 9 | echo 'Single-quoted string must be closed to escape single quotes, like in '\''This is how it'\''\'\'''\''s done'\''' 10 | return 1 11 | fi 12 | } 13 | -------------------------------------------------------------------------------- /rules/int_string_comparison: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | function _zlint_rule_int_string_comparison() { 4 | local line="$@" 5 | 6 | local -A rules 7 | rules=( 8 | '= is for strings. Use -eq' '\[\[?\s(([0-9]\s=\s.*)|(.*\s=\s[0-9]))\s\]?\]' 9 | '!= is for strings. Use -ne' '\[\[?\s(([0-9]\s!=\s.*)|(.*\s!=\s[0-9]))\s\]?\]' 10 | '< is for strings. Use -lt' '\[\[?\s(([0-9]\s<\s.*)|(.*\s<\s[0-9]))\s\]?\]' 11 | '<= is for strings. Use -lte' '\[\[?\s(([0-9]\s<=\s.*)|(.*\s<=\s[0-9]))\s\]?\]' 12 | '> is for strings. Use -gt' '\[\[?\s(([0-9]\s>\s.*)|(.*\s>\s[0-9]))\s\]?\]' 13 | '>= is for strings. Use -gte' '\[\[?\s(([0-9]\s>=\s.*)|(.*\s>=\s[0-9]))\s\]?\]' 14 | ) 15 | 16 | for message rule in ${(@kv)rules[@]}; do 17 | pcre_compile "$rule" 18 | pcre_study 19 | if pcre_match "$line" 2>&1; then 20 | echo $message 21 | return 1 22 | fi 23 | done 24 | } 25 | -------------------------------------------------------------------------------- /rules/unescaped_dollar_literals: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | function _zlint_rule_unescaped_dollar_literals() { 4 | local line="$@" 5 | 6 | pcre_compile '"(?:.*[^\\])?(\$)(?:[^a-zA-Z0-9_\?\!\#\@\*\{\}\(\)].*)?"' 7 | pcre_study 8 | if pcre_match "$line" 2>&1; then 9 | echo 'Dollar literals in double-quoted strings must be escaped' 10 | return 1 11 | fi 12 | } 13 | -------------------------------------------------------------------------------- /tests/_output/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molovo/zlint/5e7bbe91d1be606f071ef057f19abfdbdd5f52a3/tests/_output/.gitkeep -------------------------------------------------------------------------------- /tests/_support/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molovo/zlint/5e7bbe91d1be606f071ef057f19abfdbdd5f52a3/tests/_support/.gitkeep -------------------------------------------------------------------------------- /tests/rules/escaped_commands.zunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zunit 2 | 3 | @setup { 4 | zmodload zsh/pcre 5 | load ../../rules/escaped_commands 6 | } 7 | 8 | @test 'escaped_commands rule finds attempted alias override' { 9 | run _zlint_rule_escaped_commands '\git status' 10 | 11 | assert $state equals 1 12 | assert $output same_as 'Prefer `command mycommand` instead of `\mycommand` to override aliases' 13 | } 14 | 15 | @test 'escaped_commands rule finds unquoted backslashes' { 16 | run _zlint_rule_escaped_commands 'Rainbows \o/' 17 | 18 | assert $state equals 1 19 | assert $output same_as 'Commands containing backslashes should be quoted' 20 | } 21 | 22 | @test 'escaped_commands rule does not match single-quoted backslashes' { 23 | run _zlint_rule_escaped_commands "'This is a \ within single quotes'" 24 | 25 | assert $state equals 0 26 | assert $output is_empty 27 | } 28 | 29 | @test 'escaped_commands rule does not match double-quoted backslashes' { 30 | run _zlint_rule_escaped_commands '"This is a \ within double quotes"' 31 | 32 | assert $state equals 0 33 | assert $output is_empty 34 | } 35 | -------------------------------------------------------------------------------- /tests/rules/int_string_comparison.zunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zunit 2 | 3 | @setup { 4 | zmodload zsh/pcre 5 | load ../../rules/int_string_comparison 6 | } 7 | 8 | @test 'int_string_comparison detects < used with int on right' { 9 | run _zlint_rule_int_string_comparison "[[ $var < 5 ]]" 10 | 11 | assert $state equals 1 12 | assert $output same_as '< is for strings. Use -lt' 13 | } 14 | 15 | @test 'int_string_comparison detects < used with int on left' { 16 | run _zlint_rule_int_string_comparison "[[ 5 < $var ]]" 17 | 18 | assert $state equals 1 19 | assert $output same_as '< is for strings. Use -lt' 20 | } 21 | 22 | @test 'int_string_comparison detects < used with quoted var and int on right' { 23 | run _zlint_rule_int_string_comparison "[[ \"$var\" < 5 ]]" 24 | 25 | assert $state equals 1 26 | assert $output same_as '< is for strings. Use -lt' 27 | 28 | @test 'int_string_comparison detects < used with quoted var and int on left' { 29 | run _zlint_rule_int_string_comparison "[[ 5 < \"$var\" ]]" 30 | 31 | assert $state equals 1 32 | assert $output same_as '< is for strings. Use -lt' 33 | } 34 | 35 | @test 'int_string_comparison detects > used with int on right' { 36 | run _zlint_rule_int_string_comparison "[[ $var > 5 ]]" 37 | 38 | assert $state equals 1 39 | assert $output same_as '> is for strings. Use -gt' 40 | } 41 | 42 | @test 'int_string_comparison detects > used with int on left' { 43 | run _zlint_rule_int_string_comparison "[[ 5 > $var ]]" 44 | 45 | assert $state equals 1 46 | assert $output same_as '> is for strings. Use -gt' 47 | } 48 | 49 | @test 'int_string_comparison detects > used with quoted var and int on right' { 50 | run _zlint_rule_int_string_comparison "[[ \"$var\" > 5 ]]" 51 | 52 | assert $state equals 1 53 | assert $output same_as '> is for strings. Use -gt' 54 | 55 | @test 'int_string_comparison detects > used with quoted var and int on left' { 56 | run _zlint_rule_int_string_comparison "[[ 5 > \"$var\" ]]" 57 | 58 | assert $state equals 1 59 | assert $output same_as '> is for strings. Use -gt' 60 | } 61 | 62 | @test 'int_string_comparison detects <= used with int on right' { 63 | run _zlint_rule_int_string_comparison "[[ $var <= 5 ]]" 64 | 65 | assert $state equals 1 66 | assert $output same_as '<= is for strings. Use -lte' 67 | } 68 | 69 | @test 'int_string_comparison detects <= used with int on left' { 70 | run _zlint_rule_int_string_comparison "[[ 5 <= $var ]]" 71 | 72 | assert $state equals 1 73 | assert $output same_as '<= is for strings. Use -lte' 74 | } 75 | 76 | @test 'int_string_comparison detects <= used with quoted var and int on right' { 77 | run _zlint_rule_int_string_comparison "[[ \"$var\" <= 5 ]]" 78 | 79 | assert $state equals 1 80 | assert $output same_as '<= is for strings. Use -lte' 81 | 82 | @test 'int_string_comparison detects <= used with quoted var and int on left' { 83 | run _zlint_rule_int_string_comparison "[[ 5 <= \"$var\" ]]" 84 | 85 | assert $state equals 1 86 | assert $output same_as '<= is for strings. Use -lte' 87 | } 88 | 89 | @test 'int_string_comparison detects >= used with int on right' { 90 | run _zlint_rule_int_string_comparison "[[ $var >= 5 ]]" 91 | 92 | assert $state equals 1 93 | assert $output same_as '>= is for strings. Use -gte' 94 | } 95 | 96 | @test 'int_string_comparison detects >= used with int on left' { 97 | run _zlint_rule_int_string_comparison "[[ 5 >= $var ]]" 98 | 99 | assert $state equals 1 100 | assert $output same_as '>= is for strings. Use -gte' 101 | } 102 | 103 | @test 'int_string_comparison detects >= used with quoted var and int on right' { 104 | run _zlint_rule_int_string_comparison "[[ \"$var\" >= 5 ]]" 105 | 106 | assert $state equals 1 107 | assert $output same_as '>= is for strings. Use -gte' 108 | 109 | @test 'int_string_comparison detects >= used with quoted var and int on left' { 110 | run _zlint_rule_int_string_comparison "[[ 5 >= \"$var\" ]]" 111 | 112 | assert $state equals 1 113 | assert $output same_as '>= is for strings. Use -gte' 114 | } 115 | 116 | @test 'int_string_comparison detects = used with int on right' { 117 | run _zlint_rule_int_string_comparison "[[ $var = 5 ]]" 118 | 119 | assert $state equals 1 120 | assert $output same_as '= is for strings. Use -eq' 121 | } 122 | 123 | @test 'int_string_comparison detects = used with int on left' { 124 | run _zlint_rule_int_string_comparison "[[ 5 = $var ]]" 125 | 126 | assert $state equals 1 127 | assert $output same_as '= is for strings. Use -eq' 128 | } 129 | 130 | @test 'int_string_comparison detects = used with quoted var and int on right' { 131 | run _zlint_rule_int_string_comparison "[[ \"$var\" = 5 ]]" 132 | 133 | assert $state equals 1 134 | assert $output same_as '= is for strings. Use -eq' 135 | 136 | @test 'int_string_comparison detects = used with quoted var and int on left' { 137 | run _zlint_rule_int_string_comparison "[[ 5 = \"$var\" ]]" 138 | 139 | assert $state equals 1 140 | assert $output same_as '= is for strings. Use -eq' 141 | } 142 | 143 | @test 'int_string_comparison detects != used with int on right' { 144 | run _zlint_rule_int_string_comparison "[[ $var != 5 ]]" 145 | 146 | assert $state equals 1 147 | assert $output same_as '!= is for strings. Use -ne' 148 | } 149 | 150 | @test 'int_string_comparison detects != used with int on left' { 151 | run _zlint_rule_int_string_comparison "[[ 5 != $var ]]" 152 | 153 | assert $state equals 1 154 | assert $output same_as '!= is for strings. Use -ne' 155 | } 156 | 157 | @test 'int_string_comparison detects != used with quoted var and int on right' { 158 | run _zlint_rule_int_string_comparison "[[ \"$var\" != 5 ]]" 159 | 160 | assert $state equals 1 161 | assert $output same_as '!= is for strings. Use -ne' 162 | 163 | @test 'int_string_comparison detects != used with quoted var and int on left' { 164 | run _zlint_rule_int_string_comparison "[[ 5 != \"$var\" ]]" 165 | 166 | assert $state equals 1 167 | assert $output same_as '!= is for strings. Use -ne' 168 | } 169 | -------------------------------------------------------------------------------- /tests/rules/unescaped_dollar_literals.zunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zunit 2 | 3 | @setup { 4 | zmodload zsh/pcre 5 | load ../../rules/unescaped_dollar_literals 6 | } 7 | 8 | @test 'unescaped_dollar_literals matches lone dollar in double quotes' { 9 | run _zlint_rule_unescaped_dollar_literals 'echo "$"' 10 | 11 | assert $state equals 1 12 | assert $output same_as 'Dollar literals in double-quoted strings must be escaped' 13 | } 14 | 15 | @test 'unescaped_dollar_literals matches dollar at end of string in double quotes' { 16 | run _zlint_rule_unescaped_dollar_literals 'echo "foo$"' 17 | 18 | assert $state equals 1 19 | assert $output same_as 'Dollar literals in double-quoted strings must be escaped' 20 | } 21 | 22 | @test 'unescaped_dollar_literals matches dollar in double quotes' { 23 | run _zlint_rule_unescaped_dollar_literals 'echo "There is a $ symbol here"' 24 | 25 | assert $state equals 1 26 | assert $output same_as 'Dollar literals in double-quoted strings must be escaped' 27 | } 28 | 29 | @test 'unescaped_dollar_literals does not match lone dollar in single quotes' { 30 | run _zlint_rule_unescaped_dollar_literals 'echo '\''$'\''' 31 | 32 | assert $state equals 0 33 | assert $output is_empty 34 | } 35 | 36 | @test 'unescaped_dollar_literals does not match dollar at end of string in single quotes' { 37 | run _zlint_rule_unescaped_dollar_literals 'echo '\''foo$'\''' 38 | 39 | assert $state equals 0 40 | assert $output is_empty 41 | } 42 | 43 | @test 'unescaped_dollar_literals does not match dollar in single quotes' { 44 | run _zlint_rule_unescaped_dollar_literals 'echo '\''There is a $ symbol here'\''' 45 | 46 | assert $state equals 0 47 | assert $output is_empty 48 | } 49 | 50 | @test 'unescaped_dollar_literals does not match lone dollar in unquoted string' { 51 | run _zlint_rule_unescaped_dollar_literals 'echo $' 52 | 53 | assert $state equals 0 54 | assert $output is_empty 55 | } 56 | 57 | 58 | @test 'unescaped_dollar_literals does not match dollar at end of unquoted string' { 59 | run _zlint_rule_unescaped_dollar_literals 'echo foo$' 60 | 61 | assert $state equals 0 62 | assert $output is_empty 63 | } 64 | 65 | @test 'unescaped_dollar_literals does not match dollar in unquoted string' { 66 | run _zlint_rule_unescaped_dollar_literals 'echo There is a $ symbol here' 67 | 68 | assert $state equals 0 69 | assert $output is_empty 70 | } 71 | 72 | @test 'unescaped_dollar_literals does not match variable in double quotes' { 73 | run _zlint_rule_unescaped_dollar_literals 'echo "There is a $var here"' 74 | 75 | assert $state equals 0 76 | assert $output is_empty 77 | } 78 | 79 | @test 'unescaped_dollar_literals does not match variable in unquoted string' { 80 | run _zlint_rule_unescaped_dollar_literals 'echo There is a $var here' 81 | 82 | assert $state equals 0 83 | assert $output is_empty 84 | } 85 | 86 | @test 'unescaped_dollar_literals does not match argument in double quotes' { 87 | run _zlint_rule_unescaped_dollar_literals 'echo "There is a $1 here"' 88 | 89 | assert $state equals 0 90 | assert $output is_empty 91 | } 92 | 93 | @test 'unescaped_dollar_literals does not match argument in unquoted string' { 94 | run _zlint_rule_unescaped_dollar_literals 'echo There is a $1 here' 95 | 96 | assert $state equals 0 97 | assert $output is_empty 98 | } 99 | 100 | @test 'unescaped_dollar_literals does not match escaped lone dollar in double quotes' { 101 | run _zlint_rule_unescaped_dollar_literals 'echo "\$"' 102 | 103 | assert $state equals 1 104 | assert $output same_as 'Dollar literals in double-quoted strings must be escaped' 105 | } 106 | 107 | @test 'unescaped_dollar_literals does not match escaped dollar at end of string in double quotes' { 108 | run _zlint_rule_unescaped_dollar_literals 'echo "foo\$"' 109 | 110 | assert $state equals 1 111 | assert $output same_as 'Dollar literals in double-quoted strings must be escaped' 112 | } 113 | 114 | @test 'unescaped_dollar_literals does not match escaped dollar in double quotes' { 115 | run _zlint_rule_unescaped_dollar_literals 'echo "There is a \$ symbol here"' 116 | 117 | assert $state equals 1 118 | assert $output same_as 'Dollar literals in double-quoted strings must be escaped' 119 | } 120 | -------------------------------------------------------------------------------- /zlint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | autoload -Uz crash && crash register 4 | zmodload zsh/pcre 5 | 6 | # Source the list of rules 7 | typeset -a _zlint_rules 8 | _zlint_rules=($(ls 'rules')) 9 | 10 | ### 11 | # Format a list of errors and output them to the screen 12 | ### 13 | function _zlint_report_errors() { 14 | local line="$1" 15 | integer width 16 | 17 | # Loop through each of the errors and add them to the list 18 | integer i=1 19 | for error in $errors; do 20 | # Set the width 21 | width=5 22 | 23 | # Format the line number and update the width 24 | formatted_line_no="\033[0;38;5;242m$line_no\033[0;m" 25 | width=$(( $width + ${#formatted_line_no} - ${#line_no} )) 26 | 27 | # Pad the left of the line number and print to screen 28 | printf '%*.*s' 0 ${#formatted_line_no} "$(printf '%0.1s' " "{1..$(( $width - ${#formatted_line_no} ))})" 29 | printf '%s' "$formatted_line_no" 30 | 31 | # Set the width 32 | width=60 33 | 34 | # Format the error message and update the width 35 | formatted_error=" \033[0;38;5;242m•\033[0;m $error" 36 | width=$(( $width + ${#formatted_error} - ${#error} )) 37 | 38 | # Truncate the error message to 50 characters 39 | if [[ ${#formatted_error} -gt ${width} ]]; then 40 | formatted_error="${formatted_error:0:$(( width - 5 ))}..." 41 | fi 42 | 43 | # Pad the right of the error message and print to screen 44 | printf '%s' "$formatted_error" 45 | printf '%*.*s' 0 ${#formatted_error} "$(printf '%0.1s' " "{1..$(( $width - ${#formatted_error} ))})" 46 | 47 | # Print the name of the rule which generated the message 48 | rule="${errored_rules[$i]}" 49 | printf '%s\n' " \033[0;38;5;242m$rule\033[0;m" 50 | i=$(( i + 1 )) 51 | done 52 | } 53 | 54 | ### 55 | # Run a string through the list of enabled linters 56 | ### 57 | function _zlint_process_string() { 58 | local line="$1" error 59 | typeset -ga errors 60 | typeset -ga errored_rules 61 | errors=() 62 | errored_rules=() 63 | 64 | line=$(echo $line | sed -e 's/^[ \t]*//') 65 | 66 | if [[ ${line:0:1} = "#" ]]; then 67 | return 68 | fi 69 | 70 | # Loop through the list of rules and run each one in turn, 71 | # appending any errors to the array 72 | for rule in $_zlint_rules; do 73 | output=$("_zlint_rule_$rule" "$line") 74 | if [[ $? -ne 0 ]]; then 75 | errors=($errors $output) 76 | errored_rules=($errored_rules $rule) 77 | fi 78 | done 79 | 80 | # If errors have been found 81 | if [[ ${#errors} -gt 0 ]]; then 82 | # Output the list of errors for the string/line 83 | _zlint_report_errors $line $errors 84 | return 1 85 | fi 86 | } 87 | 88 | ### 89 | # Run a file through the list of enabled linters line-by-line 90 | ### 91 | function _zlint_run_file() { 92 | local file="$1" width=$(tput cols) state heading 93 | local -a lines 94 | local -a errors 95 | errors=() 96 | 97 | # Load the file contents into an array 98 | IFS=$'\n' lines=($(cat -e $file)) 99 | line_no=1 100 | 101 | # Loop through each of the lines 102 | for line in "${(@f)lines[@]}"; do 103 | # Remove the trailing '$' character 104 | line=${line:0:(( ${#line} - 1 ))} 105 | 106 | # If the line is blank, skip ahead 107 | if [[ -z $line ]]; then 108 | line_no=$(( line_no + 1 )) 109 | continue 110 | fi 111 | 112 | # Run the line through the linters 113 | output=$(_zlint_process_string $line) 114 | state=$? 115 | 116 | # If errors have been found 117 | if [[ $state -ne 0 ]]; then 118 | # Increment the error count 119 | errors=($errors "$output") 120 | fi 121 | 122 | line_no=$(( line_no + 1 )) 123 | done 124 | 125 | # If errors have been found, return an error code 126 | if [[ ${#errors} -gt 0 ]]; then 127 | # Find the full filepath (if realpath is installed) 128 | if builtin type realpath >/dev/null 2>&1; then 129 | file=$(realpath $file) 130 | file=${file/"$PWD\/"/} 131 | fi 132 | 133 | heading="\033[1;33m$file\033[0;m" 134 | printf '%s' "$heading" 135 | printf '%*.*s\n' 0 ${#heading} "$(printf '%0.1s' " "{1..$(( width - ${#heading} ))})" 136 | 137 | # Output the formatted list of errors 138 | echo "$errors" 139 | echo 140 | return 1 141 | fi 142 | } 143 | 144 | ### 145 | # Run the linter against the files passed as arguments 146 | function _zlint_run() { 147 | local files=("$@") 148 | integer errors=0 149 | 150 | revolver start "Processing..." 151 | # Loop through each of the passed files, expanding globs 152 | for file in "${~files[@]}"; do 153 | revolver update "Processing file $file" 154 | 155 | # Run the file through the linters, and capture the output 156 | output=$(_zlint_run_file $file) 157 | state=$? 158 | 159 | # If errors are found 160 | if [[ $state -ne 0 ]]; then 161 | # Print the results to the screen 162 | echo "$output" 163 | echo 164 | 165 | # Incremement the error count 166 | errors=$(( errors + 1 )) 167 | fi 168 | 169 | done 170 | 171 | revolver stop 172 | 173 | if [[ $errors -gt 0 ]]; then 174 | return 1 175 | fi 176 | 177 | echo '\033[0;32mNo errors found\033[0;m' 178 | } 179 | 180 | function _zlint() { 181 | local help version string ctx="$1" files="$@" 182 | 183 | zparseopts -D \ 184 | h=help -help=help \ 185 | v=version -version=version \ 186 | s:=string -string:=string 187 | 188 | if [[ -n $help ]]; then 189 | _zlint_usage 190 | exit 191 | fi 192 | 193 | if [[ -n $version ]]; then 194 | echo '0.0.1' 195 | exit 196 | fi 197 | 198 | case $ctx in 199 | run ) 200 | shift 201 | ;; 202 | esac 203 | 204 | for rule in $_zlint_rules; do 205 | source "rules/$rule" 206 | done 207 | 208 | if [[ -n $string ]]; then 209 | shift string 210 | _zlint_process_string $string 211 | state=$? 212 | crash unload 213 | return $state 214 | fi 215 | 216 | _zlint_run "$@" 217 | state=$? 218 | crash unload 219 | return $state 220 | } 221 | 222 | _zlint "$@" 223 | --------------------------------------------------------------------------------