main lint
  1#!/bin/sh
  2# a small clang wrapper that prints in the same style as the old sun linter. it needs some work,
  3# it should probably (definitely) use compile_commands.json, but it's still quite useful. 
  4# usage: 
  5#
  6#     lint [files/directory]
  7#     lint (recursively searches for .c files)
  8# 
  9# dependencies:
 10#     shell
 11#     clang
 12set -eu
 13
 14clang=${CLANG:-clang}
 15tmp=${TMPDIR:-/tmp}/lint.$$.out
 16trap 'rm -f "$tmp"' EXIT HUP INT TERM
 17
 18list() {
 19	if [ "$#" -eq 0 ]; then
 20		find . -type f -name '*.c'
 21		return
 22	fi
 23	for path do
 24		if [ -d "$path" ]; then
 25			find "$path" -type f -name '*.c'
 26		elif [ -f "$path" ]; then
 27			case $path in
 28			*.c)
 29				printf '%s\n' "$path"
 30				;;
 31			esac
 32		fi
 33	done
 34}
 35
 36if ! list "$@" | grep . > /dev/null; then
 37	printf '%s: no .c files found\n' "${0##*/}" >&2
 38	exit 1
 39fi
 40
 41set +e
 42list "$@" | sort -u |
 43xargs "$clang" -fsyntax-only \
 44	-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion \
 45	-Wshadow -Wformat=2 -Wimplicit-fallthrough -Wcast-qual \
 46	-Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes \
 47	-Wpointer-arith -Wundef -Wunreachable-code -Wnull-dereference \
 48	> /dev/null 2> "$tmp"
 49status=$?
 50set -e
 51
 52list "$@" | sort -u | awk -v diagfile="$tmp" '
 53BEGIN {
 54	nfiles = 0
 55}
 56
 57{
 58	files[++nfiles] = $0
 59}
 60
 61END {
 62	while ((getline line < diagfile) > 0) {
 63		sub(/\r$/, "", line)
 64		if (match(line, /^([^:]+):([0-9]+):([0-9]+): (warning|error|note): (.*)$/, m)) {
 65			file = m[1]
 66			lnum = m[2]
 67			kind = m[4]
 68			msg = m[5]
 69			if (kind == "note")
 70				continue
 71			gsub(/[[:space:]]*\[-W[^]]+\]$/, "", msg)
 72			msgs[file] = msgs[file] sprintf("(%s) %s: %s\n", lnum, kind, msg)
 73			continue
 74		}
 75		if (match(line, /^([^:]+): (warning|error|note): (.*)$/, m)) {
 76			file = m[1]
 77			kind = m[2]
 78			msg = m[3]
 79			if (kind == "note")
 80				continue
 81			gsub(/[[:space:]]*\[-W[^]]+\]$/, "", msg)
 82			msgs[file] = msgs[file] sprintf("%s: %s\n", kind, msg)
 83		}
 84	}
 85	close(diagfile)
 86	for (i = 1; i <= nfiles; ++i) {
 87		file = files[i]
 88		if (file in msgs) {
 89			print file ":"
 90			printf "%s", msgs[file]
 91		}
 92	}
 93	for (file in msgs) {
 94		found = 0
 95		for (i = 1; i <= nfiles; ++i) {
 96			if (files[i] == file) {
 97				found = 1
 98				break
 99			}
100		}
101		if (!found) {
102			print file ":"
103			printf "%s", msgs[file]
104		}
105	}
106}
107'
108
109exit "$status"