/*
 *	An implementation of the MHEARD command for Linux. This needs AX25.021 or higher with the G4KLX
 *	changes for /proc/net/ax25_route.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

#define	PROC_PATH	"/proc/net/ax25_route"


struct PortRecord {
	char	Callsign[16];
	char	PortName[16];
	unsigned long Frames;
	time_t	Time;
	struct  PortRecord *Next;
};

static struct PortRecord *PortList = NULL;

static FILE *OpenPortFile(void)
{
	FILE *f = fopen(PROC_PATH, "r");
	char discard[256];

	if (f == NULL) {
		perror(PROC_PATH);
		exit(1);
	}

	fgets(discard, 255, f);	/* Throw away titles */

	return f;
}

static struct PortRecord *ReadPortFile(FILE *f)
{
	static struct PortRecord pr;
	char buf[256];

	if (fgets(buf, 256, f) == NULL)
		return NULL;

	sscanf(buf, "%s %s %ld %ld",
		pr.Callsign, pr.PortName, &pr.Frames, &pr.Time);

	return &pr;
}

static void PrintPortEntry(struct PortRecord *pr)
{
	char *ct = ctime(&pr->Time);

	ct[19] = 0;

	printf("%-10s %-5s %5ld   %s\n",
		pr->Callsign, pr->PortName, pr->Frames, ct);
}

static void ListAllPorts(void)
{
	struct PortRecord *pr;

	for (pr = PortList; pr != NULL; pr = pr->Next)
		PrintPortEntry(pr);
}

static void ListOnlyPort(char *name)
{
	struct PortRecord *pr;

	for (pr = PortList; pr != NULL; pr = pr->Next)
		if (strcmp(pr->PortName, name) == 0)
			PrintPortEntry(pr);
}

static void LoadPortData(void)
{
	FILE *f = OpenPortFile();
	struct PortRecord *pr;
	struct PortRecord *n;

	while ((pr = ReadPortFile(f)) != NULL) {
		n  = (struct PortRecord *)malloc(sizeof(struct PortRecord));
		*n = *pr;
		n->Next  = PortList;
		PortList = n;
	}

	fclose(f);
}

static void SortByTime(void)
{
	struct PortRecord *p = PortList;
	struct PortRecord *n;
	PortList = NULL;

	while (p != NULL) {
		struct PortRecord *w = PortList;

		n = p->Next;

		if (w == NULL || p->Time > w->Time) {
			p->Next  = w;
			PortList = p;
			p = n;
			continue;
		}

		while (w->Next!=NULL && p->Time <= w->Next->Time)
			w = w->Next;

		p->Next = w->Next;
		w->Next = p;
		p = n;
	}
}


static void SortByPort(void)
{
	struct PortRecord *p = PortList;
	struct PortRecord *n;
	PortList = NULL;

	while (p != NULL) {
		struct PortRecord *w = PortList;

		n = p->Next;

		if (w == NULL || strcmp(p->PortName,w->PortName) < 0) {
			p->Next  = w;
			PortList = p;
			p = n;
			continue;
		}

		while (w->Next != NULL && strcmp(p->PortName, w->Next->PortName) >= 0)
			w = w->Next;

		p->Next = w->Next;
		w->Next = p;
		p = n;
	}
}

static void SortByCall(void)
{
	struct PortRecord *p = PortList;
	struct PortRecord *n;
	PortList = NULL;

	while (p != NULL) {
		struct PortRecord *w = PortList;

		n = p->Next;

		if (w == NULL || strcmp(p->Callsign, w->Callsign) < 0) {
			p->Next  = w;
			PortList = p;
			p = n;
			continue;
		}

		while (w->Next != NULL && strcmp(p->Callsign, w->Next->Callsign) >= 0)
			w = w->Next;

		p->Next = w->Next;
		w->Next = p;
		p = n;
	}
}

static void SortByFrame(void)
{
	struct PortRecord *p = PortList;
	struct PortRecord *n;
	PortList = NULL;

	while (p != NULL) {
		struct PortRecord *w = PortList;

		n = p->Next;

		if (w == NULL || p->Frames > w->Frames) {
			p->Next  = w;
			PortList = p;
			p = n;
			continue;
		}

		while (w->Next != NULL && p->Frames <= w->Next->Frames)
			w = w->Next;

		p->Next = w->Next;
		w->Next = p;
		p = n;
	}
}
		
int main(int argc, char *argv[])
{
	int mode = 0;
	int c;

	LoadPortData();

	while ((c = getopt(argc, argv, "cfpt")) != -1) {
		switch (c) {
			case 'c':
				mode = 2;
				break;
			case 'f':
				mode = 3;
				break;
			case 'p':
				mode = 1;
				break;
			case 't':
				mode = 0;
				break;
			case '?':
			case ':':
				fprintf(stderr, "Usage: %s [-c] [-f] [-p] [-t] [port ...]\n", argv[0]);
				return 1;
		}
	}
	
	switch (mode) {
		case 0: SortByTime();  break;
		case 1: SortByPort();  break;
		case 2: SortByCall();  break;
		case 3: SortByFrame(); break;
	}

	if (argc == optind) {
		ListAllPorts();
	} else {
		while (argv[optind] != NULL) {
			printf("Port %s:\n", argv[optind]);
			ListOnlyPort(argv[optind]);
			optind++;
		}
	}

	return 0;
}
