#!/usr/bin/perl -w
use strict;
# checkincludes.pl by Adrien "Krunch" Kunysz <a_kunysz@yahoo.com>
# 
# This will check a C source file for useless #includes.
# You first need to comment your #includes by telling why you need
# each #included file. When this program will be run it will
# just look through the source to see if you really use the functions,
# types and variables you documented.
#
# Example:
# Say you have a .c file that looks like this:
# 	#include <string.h>      /* memset(3), strlen(3), NULL      */
# 	#include <stdlib.h>      /* malloc(3), free(3)              */
# 	#include <sys/socket.h>  /* socket(2), AF_INET, send(2),... */
# 	#include "foobar.h"      // baz()
# 	...
# If you don't really use memset(3) in your code,
# this script will warn you about that.
#
# Of course it's not foolproof, this is just a hack that works fine for me
# most of the time. If you want something more reliable you should probably
# write a real parser with yacc/lex or something (maybe it should be a
# feature of lint if it isn't yet).
#
# TODO:
# 	- Find functions, types and variables that are used but not
# 	  documented in #includes (better to do this with a real parser).
# BUGS:
# 	- You may see weird behaviour if you have #includes commented
# 	  from another line. Like this:
# 	  	/*
# 	  	#include "bla.h"
# 	  	*/

die "Usage: $0 file.c" if @ARGV != 1;

my %symbols;
my $in_comment = 0;

open(INPUT, '<', $ARGV[0]) or die "Can't open file: $ARGV[0].";
while (<INPUT>) {
	if (/^\s*#\s*include/) {
		s/^.*((\/\*)|(\/\/))\s*//;	# remove beginning junk
		s/\s*(\*\/)?\s*\n$//;       # remove ending junk
		for (split /,\s*/) {
			s/\(.*\)$/\\\(/;
			s/,\.\.\.$//;
			$symbols{$_} = 0;
		}
		next;
	}

	# strip comments
	s/^.*\*\/// || next if $in_comment;
	$in_comment = 0;
	s/\/\/.*\n$//;
	s/\/\*.*\*\// /g;
	s/\/\*.*\n$// and $in_comment = 1;
	next if $in_comment || /^\s*$/;

	# look for documented symbols
	for my $symbol (keys %symbols) {
		$symbols{$symbol}++ if /$symbol/;
	}
}
close INPUT;

while (my ($symbol, $used) = each %symbols) {
	$symbol =~ s/\\\(/\(\)/;
	print "unused symbol: $symbol\n" if ! $used;
}
