/*
 * Scan a gmake/gcc compile log, counting the number of warnings from each
 * file.
 */
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>

typedef	struct _list {
	struct _list *link;
	int	count;
	char	*path;
} LIST;

static unsigned num_files;
static LIST *all_files;

static void failed(char *msg)
{
	perror(msg);
	exit(1);
}

static void count_file(char *dir, char *file)
{
	char path[PATH_MAX];
	char resolved[PATH_MAX];
	LIST *p;

	if (!strcmp(file, "bison.simple")) {
		strcpy(resolved, file);
	} else {
		if (*file == '/')
			strcpy(path, file);
		else
			sprintf(path, "%s/%s", dir, file);
		if (realpath(path, resolved)) {
			;
		} else {
			fprintf(stderr, "in\n\t%s\ncannot find\n\t%s\n", dir, file);
		}
	}

	for (p = all_files; p != 0; p = p->link) {
		if (!strcmp(p->path, resolved)) {
			p->count += 1;
			return;
		}
	}
	p = calloc(sizeof(LIST), 1);
	p->path = strdup(resolved);
	p->link = all_files;
	p->count = 1;
	all_files = p;
	num_files++;
}

static int compare(const void *a, const void *b)
{
	const LIST **p = (const LIST **)a;
	const LIST **q = (const LIST **)b;
	return ((*q)->count - (*p)->count);
}

static void show_files(void)
{
	LIST **p = calloc(sizeof(LIST *), num_files);
	LIST *q;
	unsigned n = 0;
	for (q = all_files; q != 0; q = q->link)
		p[n++] = q;

	qsort(p, num_files, sizeof(LIST *), compare);

	for (n = 0; n < num_files; n++) {
		printf("%d %s\n", p[n]->count, p[n]->path);
	}
}

#define BIGBUF (BUFSIZ*10)+PATH_MAX

static void c_warnings(FILE *fp)
{
	char buf[BIGBUF];
	char buf2[BIGBUF];
	char buf3[BIGBUF];
	unsigned line = 0;
	unsigned wide = 0;
	int level;
	char directory[PATH_MAX];
	int warnings = 0;
	int colons = 0;
	int c_dirs = 0;

	printf("Scanning...\n");
	fflush(stdout);

	strcpy(directory, ".");
	while (fgets(buf, sizeof(buf), fp)) {
		size_t len = strlen(buf);
		line++;
		if (len >= sizeof(buf)-1) {
			fprintf(stderr, "Line %d is too long\n", line);
			exit(1);
		}
		if (len > wide)
			wide = len;
		if (sscanf(buf, "make[%d]: Entering directory `%[^']'",
			&level, buf2) == 2) {
			strcpy(directory, buf2);
			c_dirs++;
		} else if (sscanf(buf, "make[%d]: %s", &level, buf2)) {
			;
		} else if (strstr(buf, "warning:")) {
			warnings++;
			if (sscanf(buf, "%[^:]:%d: warning: %s", buf2, &level, buf3) == 3) {
				count_file(directory, buf2);
			} else if (sscanf(buf, "%[^:]: warning: %s", buf2, buf3) == 2) {
				count_file(directory, buf2);
			} else {
				printf(">%s", buf);
			}
		} else if (strchr(buf, ':')) {
			colons++;
			//printf(">%s", buf);
		}
	}
	printf("%d lines processed, widest = %d\n", line, wide);
	printf("%d directories\n", c_dirs);
	printf("%d warnings\n", warnings);
	printf("%d other :-lines\n", colons);
	printf("%d files with warnings\n", num_files);
	if (num_files)
		show_files();
}

int main(int argc, char *argv[])
{
	int n;
	int found = 0;
	FILE *fp;

	for (n = 1; n < argc; n++) {
		char *name = argv[n];
		if (!strcmp(name, "-")) {
			c_warnings(stdin);
			found = 1;
		} else if ((fp = fopen(name, "r")) != 0) {
			c_warnings(fp);
			fclose(fp);
			found = 1;
		} else
			failed(name);
	}
	if (!found)
		c_warnings(stdin);
	return 0;
}
