diff --git a/tools/toolkit-qa-check.sh b/tools/toolkit-qa-check.sh index cdc3228..83019fc 100755 --- a/tools/toolkit-qa-check.sh +++ b/tools/toolkit-qa-check.sh @@ -1084,6 +1084,7 @@ echo "" #============================================================================== # CHECK 31: local keyword outside functions (CRITICAL - script fails) #============================================================================== +# OPTIMIZED: Use AWK for 10-100x speedup on 50k+ lines echo "[31/42] Checking: 'local' outside functions..." { echo "## CHECK 31: 'local' keyword outside function context" @@ -1091,45 +1092,49 @@ echo "Severity: CRITICAL" echo "Issue: 'local' can only be used inside functions, causes runtime error" echo "" -while read -r file; do - # Read entire file and track function boundaries - in_function=0 - brace_depth=0 - line_num=0 +# AWK is much faster than bash loops for line-by-line processing +find "$TOOLKIT_PATH" -name "*.sh" -type f 2>/dev/null | while read -r file; do + awk -v file="$file" ' + BEGIN { + in_function = 0 + brace_depth = 0 + } - while IFS= read -r line_content; do - line_num=$((line_num + 1)) + # Skip comment lines + /^\s*#/ { next } - # Skip comments - if echo "$line_content" | grep -q '^\s*#'; then - continue - fi + # Detect function start (name followed by ()) + /^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*\(\)/ { + in_function = 1 + brace_depth = 0 + } - # Detect function start - if echo "$line_content" | grep -qE '^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*\(\)'; then - in_function=1 - brace_depth=0 - fi + # Track braces + { + # Count opening and closing braces + open_count = gsub(/{/, "{", $0) + close_count = gsub(/}/, "}", $0) + brace_depth += open_count - close_count - # Track braces - open_braces=$(echo "$line_content" | grep -o '{' | wc -l) - close_braces=$(echo "$line_content" | grep -o '}' | wc -l) - brace_depth=$((brace_depth + open_braces - close_braces)) + # Exit function when braces balance back to 0 + if (in_function && brace_depth <= 0) { + in_function = 0 + } + } - # If we were in a function and braces are now balanced back to 0, we've exited - if [ "$in_function" -eq 1 ] && [ "$brace_depth" -le 0 ]; then - in_function=0 - fi + # Check for local keyword outside functions + !in_function && /^\s*local\s+[a-zA-Z_]/ { + print "CRITICAL|" file "|" NR "|'\''local'\'' keyword outside function (runtime error)" + issues++ + } - # Check for 'local' keyword outside function - if [ "$in_function" -eq 0 ]; then - if echo "$line_content" | grep -qE '^\s*local\s+[a-zA-Z_]'; then - echo "CRITICAL|$file|$line_num|'local' keyword outside function (runtime error)" - count_issue "CRITICAL" - fi - fi - done < "$file" -done < <(find "$TOOLKIT_PATH" -name "*.sh" -type f 2>/dev/null) + END { + if (issues > 0) { + exit 1 + } + } + ' "$file" && true || count_issue "CRITICAL" +done echo "" } >> "$REPORT" @@ -1144,49 +1149,54 @@ echo "Severity: LOW" echo "Issue: Menus should follow standard format for consistent UX" echo "" -# Check for menus with inconsistent back buttons -while IFS=: read -r file line_num line_content; do - # Look for menu display functions (show_*_menu only, not handle_*_menu handlers) - if echo "$line_content" | grep -qE 'show_.*_menu\(\)'; then - menu_file="$file" +# Skip in quick mode (LOW severity) or summary mode (expensive nested loop) +if $QUICK_MODE || $SUMMARY_MODE; then + echo "Skipped in quick/summary mode (LOW severity, expensive check)" +else + # Check for menus with inconsistent back buttons + while IFS=: read -r file line_num line_content; do + # Look for menu display functions (show_*_menu only, not handle_*_menu handlers) + if echo "$line_content" | grep -qE 'show_.*_menu\(\)'; then + menu_file="$file" - # Check if this file has proper back button (within next 100 lines) - has_back=0 - line_count=0 - while IFS= read -r check_line && [ "$line_count" -lt 100 ]; do - if echo "$check_line" | grep -qE '\$\{RED\}0\)\$\{NC\}.*(Back|Exit)'; then - has_back=1 - break + # Check if this file has proper back button (within next 100 lines) + has_back=0 + line_count=0 + while IFS= read -r check_line && [ "$line_count" -lt 100 ]; do + if echo "$check_line" | grep -qE '\$\{RED\}0\)\$\{NC\}.*(Back|Exit)'; then + has_back=1 + break + fi + line_count=$((line_count + 1)) + done < <(tail -n +$line_num "$file") + + if [ "$has_back" -eq 0 ]; then + echo "LOW|$file|$line_num|Menu missing standard back button (RED 0)" + count_issue "LOW" fi - line_count=$((line_count + 1)) - done < <(tail -n +$line_num "$file") + fi + done < <(grep -rn 'show_.*_menu()' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) - if [ "$has_back" -eq 0 ]; then - echo "LOW|$file|$line_num|Menu missing standard back button (RED 0)" + # Check for inconsistent separators in menus + while IFS=: read -r file line_num line_content; do + # Non-standard separator (should use ─ or ═) + if echo "$line_content" | grep -qE 'echo.*"[-]{10,}"'; then + echo "LOW|$file|$line_num|Non-standard menu separator (use ─ or ═)" count_issue "LOW" fi - fi -done < <(grep -rn 'show_.*_menu()' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) + done < <(grep -rn 'echo.*"[-]\{10,\}"' "$TOOLKIT_PATH" --include="*.sh" --exclude="toolkit-qa-check.sh" 2>/dev/null) -# Check for inconsistent separators in menus -while IFS=: read -r file line_num line_content; do - # Non-standard separator (should use ─ or ═) - if echo "$line_content" | grep -qE 'echo.*"[-]{10,}"'; then - echo "LOW|$file|$line_num|Non-standard menu separator (use ─ or ═)" - count_issue "LOW" - fi -done < <(grep -rn 'echo.*"[-]\{10,\}"' "$TOOLKIT_PATH" --include="*.sh" --exclude="toolkit-qa-check.sh" 2>/dev/null) - -# Check for duplicate domain/user selection code (should use lib function) -while IFS=: read -r file line_num line_content; do - if echo "$line_content" | grep -qiE 'read.*-p.*"Enter domain|read -p.*domain.*:'; then - # Check if this file sources domain-selector.sh - if ! grep -q 'source.*domain-selector.sh' "$file" 2>/dev/null; then - echo "LOW|$file|$line_num|Duplicate domain selection (should use lib/domain-selector.sh)" - count_issue "LOW" + # Check for duplicate domain/user selection code (should use lib function) + while IFS=: read -r file line_num line_content; do + if echo "$line_content" | grep -qiE 'read.*-p.*"Enter domain|read -p.*domain.*:'; then + # Check if this file sources domain-selector.sh + if ! grep -q 'source.*domain-selector.sh' "$file" 2>/dev/null; then + echo "LOW|$file|$line_num|Duplicate domain selection (should use lib/domain-selector.sh)" + count_issue "LOW" + fi fi - fi -done < <(grep -rin 'read.*-p.*domain' "$TOOLKIT_PATH/modules" --include="*.sh" 2>/dev/null) + done < <(grep -rin 'read.*-p.*domain' "$TOOLKIT_PATH/modules" --include="*.sh" 2>/dev/null) +fi echo "" } >> "$REPORT"