/************************************************************************/
/* genproto			by Nicolas Pomarède			*/
/*				pomarede@isty-info.uvsq.fr		*/
/*									*/
/* 13-15/12/1995	v1.0	Première version.			*/
/*									*/
/* 06/01/1996			Changement message d'aide.		*/
/*									*/
/* 07/01/1996		v1.1	Option -d (debug ON)			*/
/*				Ignore ligne si CurrentPos atteint	*/
/*				MAX_TOKEN_PER_PROTOTYPE.		*/
/*				RESET si token PARAMS non suivi d'un	*/
/*				token BLOC juste après.			*/
/*				Fonction MyAlloc avec sortie si erreur.	*/
/* 11/11/1996		v1.2	option -b (banner OFF)			*/
/*				Vérification ParamBufLen < PARAMLEN.	*/
/*				option -f gère code \x et FORMATLEN.	*/
/*				Réécriture fichier README.		*/
/*									*/
/* Détermine les lignes correspondant à une définition de fonction.	*/
/* Les fonctions repérées sont ensuite affichées grâce à un printf	*/
/* personnalisable (incluant n° de ligne et nom du fichier).		*/
/* On peut ainsi générer un index de toutes les fonctions contenues	*/
/* dans un ensemble de fichiers C ou C++.				*/
/* Le programme repère les définitions de fonctions et non pas les	*/
/* déclarations (= prototypes).						*/
/* ex:									*/
/* void		main ( void );	<= déclaration ignorée			*/
/* void		main ( void )	<= définition détectée			*/
/* { ... }								*/
/* Les fonctions obtenues peuvent être ensuite triées.			*/
/*									*/
/* Utilisation : voir fonction Usage()					*/
/*									*/
/* This file is part of genproto v1.2					*/
/* Copyright November 1996 by Nicolas Pomarede.				*/


/*
 Changed by: Freek <freequaos AT uni DOT de> on 15/06/2004
 v0.1
 -changed default format to suit a normal .h file
 -changed the banner to something less intrusive while
 -changed indentation to K&R, tab==8 using http://fte.sf.net


 19/06/2004
 v0.2
 -don't output usage on stderr, but stdout
 -reworked help output
 -parse options via getopt
 -added lots of { }


 25/06/2004
 v0.3
 -fixed a bug in -f command-line parsing (it was not really my fault ;)

 07/11/2004
 v0.4
 -fixed looping when bad input data
 -new TOKENID PARSE_ERROR(333) in proto.h

 */

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

#include "proto.h"

/****************************************/
/*   Structures et Variables globales	*/
/****************************************/

#define TRUE		1
#define FALSE		0

#define VERSION "0.4.1"
#define DEFAULT_FORMAT "%R %C%S%N%P;\n"

#define MAX_TOKEN_PER_PROTOTYPE		50	/* sûrement jamais atteint */
#define	FORMATLEN	1024	/* taille FormatString */


/* this is for getopt */
extern char *optarg;
extern int optind, opterr, optopt;


struct function
{
	struct function *next;	/* liste chaînée */

	char *ReturnParam;
	char *ClassName;
	char *FunctionName;
	char *Param;
	int Line;
	char *File;
};


struct function *pFirstF;	/* pour la liste chaînée */
struct function *pLastF;

struct function **TableFunctions;	/* tableaux de pointeurs */
int NbFunctions;

int LinePrototype;		/* n° de la ligne en cours */
char *CurrentFileName;

FILE *OutputFile;

int DebugMode = FALSE;		/* pas de debug par défaut */
int DisplayBanner = TRUE;	/* affiche BannerText */


/* Chaîne d'affichage par défaut */
char FormatString[FORMATLEN] = DEFAULT_FORMAT;

/* Ordre de tri par défaut : classe, nom, fichier, ligne */
char SortString[10] = "CNFL";

/* Chaîne vide, utilisée pour les fonctions sans ClassName */
char EmptyString[] = "\0";

/* Message d'en-tête */
char BannerText[] = "/* generated by genproto */\n\n";


/****************************************/
/*		Prototypes		*/
/****************************************/

void Usage(char *name);

char * CharCopy(char *buf, int len);
char * AddTokens(char **TokenList, int FirstToken, int LastToken);
void AddPrototype(char **TokenList, int ClassNamePos, int FunctionNamePos, int ParamPos);
void ScanOneFile(char *filename);

void SortPrototypes(struct function **T, int Gauche, int Droite);
void Swap(struct function **T, int i, int j);
int CompareFunctions(struct function *pF1, struct function *pF2);

void CopyListToTable(void);

void PrintOnePrototype(struct function *pF);
void PrintPrototypes(void);
void DeletePrototypes(void);

void *MyAlloc(size_t size);
void MyExit(void);


/************************************************************************/
/* Affiche un texte d'aide sur l'utilisation du programme "name".	*/
/************************************************************************/
void Usage(char *name)
{
	/*
	 ugly workaround to make the \n visible in the format
	 -Freek
	 */
	size_t len;
	char tmp_format[1024]=DEFAULT_FORMAT;

	len=strcspn(tmp_format, "\n");

        tmp_format[len]='\0';

       	printf("genproto " VERSION " based v1.2 (11/11/1996) (C) Nicolas Pomarède\n");
	printf("Usage: genproto [OPTIONS] [SOURCE FILES]\n" \
	       "\t-h          this help\n" \
	       "\t-d          turn on debugging\n" \
	       "\t-b          don't print generated by banner\n\n" \
	       "\t-f FORMAT   define custom format of output\n" \
	       "\t\t%%R : return parameters of the function\n" \
	       "\t\t%%C : ClassName of the function (if it exists)\n" \
	       "\t\t%%S : :: if the ClassName exists\n" \
	       "\t\t%%N : name of the function\n" \
	       "\t\t%%P : formal parameters of the function\n" \
	       "\t\t%%L : line number of the function\n" \
	       "\t\t%%F : file where the function is declared\n" \
	       "\t\tDefault: \"%s\\n\"\n" \
	       "\t-s [CNFL]    sort output, max. 4 criteria; default CNFL (see above)\n" \
	       "\t-o FILE      output to this file, not STDOUT\n",
               tmp_format
	      );
}




/************************************************************************/
/* Alloue len+1 octets et copie la chaîne buf dedans.			*/
/* Retourne un pointeur sur la mémoire allouée.				*/
/************************************************************************/
char *CharCopy(char *buf, int len)
{
	char *dest = (char *) MyAlloc(len + 1);

	strcpy(dest, buf);
	return dest;
}



/************************************************************************/
/* Crée une chaîne correspondant à la concaténation des tokens		*/
/* FirstToken à LastToken et retourne un pointeur sur la chaîne créée.	*/
/* Les tokens sont séparés par un caractère ' ', sauf les '*' qui ne	*/
/* sont pas suivis de ' ' (pour que "**" ne donne pas "* *").		*/
/* Il n'y a pas de ' ' à la fin de la chaîne résultat.			*/
/************************************************************************/
char *AddTokens(char **TokenList, int FirstToken, int LastToken)
{
	int i;
	int Len = 0;
	char *res, *s, *d;


	for (i = FirstToken; i <= LastToken; i++) {
		Len += strlen(TokenList[i]) + 1;	/* ajoute un ' ' après chaque token */
	}

	res = (char *) MyAlloc(Len + 1);	/* pour le '\0' */

	*res = '\0';		/* chaîne vide */
	d = res;

	/* Concatène tous les tokens dans res en les séparant par un ' ' */
	for (i = FirstToken; i <= LastToken; i++) {
		s = TokenList[i];
		while ((*d++ = *s++))	/* copie TokenList[ i ], '\0' inclu */
			;

		d--;		/* revient sur le '\0' */
		if (*(d - 1) != '*')	/* rajoute un ' ' après le token */
			*d++ = ' ';	/* s'il ne s'agit pas d'un '*' */
	}

	if (*(d - 1) == ' ') {	/* enlève le ' ' final */
		*(d - 1) = '\0';
	} else {
		*d = '\0';
	}

	return res;
}


/************************************************************************/
/* Ajoute un prototype à la liste déjà existante.			*/
/* Un prototype est caractérisé par une liste de tokens, la position	*/
/* du nom de la classe, la position du nom de la fonction et la position*/
/* de la liste de paramètres.						*/
/* Les tokens 0 à ClassNamePos/FunctionNamePos constituent les types	*/
/* de retour.								*/
/************************************************************************/
void AddPrototype(char **TokenList, int ClassNamePos, int FunctionNamePos, int ParamPos)
{
	struct function *pNewF = MyAlloc(sizeof(struct function));


	/* Recrée les paramètres de retour */
	if (ClassNamePos >= 0) {
		pNewF->ReturnParam = AddTokens(TokenList, 0, ClassNamePos - 1);
	} else {
		pNewF->ReturnParam = AddTokens(TokenList, 0, FunctionNamePos - 1);
	}

	if (!pNewF->ReturnParam)	/* erreur AddToken ? */
		exit(1);


	/* Recopie le token ClassName s'il existe */
	if (ClassNamePos >= 0) {
		pNewF->ClassName = MyAlloc(strlen(TokenList[ClassNamePos]) + 1);
		pNewF->ClassName = strcpy(pNewF->ClassName, TokenList[ClassNamePos]);
	} else {
		pNewF->ClassName = EmptyString;
	}

	/* Recopie le token FunctionName */
	pNewF->FunctionName = MyAlloc(strlen(TokenList[FunctionNamePos]) + 1);
	pNewF->FunctionName = strcpy(pNewF->FunctionName, TokenList[FunctionNamePos]);


	/* Recopie le token Param */
	pNewF->Param = MyAlloc(strlen(TokenList[ParamPos]) + 1);
	pNewF->Param = strcpy(pNewF->Param, TokenList[ParamPos]);

	pNewF->next = NULL;
	pNewF->Line = LinePrototype;
	pNewF->File = CharCopy(CurrentFileName, strlen(CurrentFileName));


	if (pLastF) {
		pLastF->next = pNewF;	/* ajoute à la liste chaînée */
		pLastF = pNewF;
	} else {
		pFirstF = pLastF = pNewF;	/* crée la liste chaînée */
	}


	if (DebugMode) {
		fprintf(stderr, "\nAdding declaration: file %s line %d\n", CurrentFileName, LinePrototype);
		fprintf(stderr, "\treturn <%s>\n", pNewF->ReturnParam);
		fprintf(stderr, "\tclass  <%s>\n", pNewF->ClassName);
		fprintf(stderr, "\tname   <%s>\n", pNewF->FunctionName);
		fprintf(stderr, "\tparam  <%s>\n\n", pNewF->Param);
	}
}


/************************************************************************/
/* Récupère les tokens renvoyés par yylex() dans une pile.		*/
/* Lorsqu'on arrive au token BLOC, on crée un nouveau prototype ; sinon	*/
/* la pile est vidée à chaque token non satisfaisant.			*/
/************************************************************************/
void ScanOneFile(char *filename)
{
	char *TokenList[MAX_TOKEN_PER_PROTOTYPE];
	int ClassNamePos, FunctionNamePos, ParamPos;
	int CurrentPos;
	int Val;
	int i;
        FILE *dofile;

	CurrentPos = 0;		/* pointe début de la pile */
	ClassNamePos = -1234;
	FunctionNamePos = -1;
	ParamPos = -1;

	if ((dofile = fopen(filename, "r"))) {
                /* initialise yylex() */
		yyrestart(dofile);
		lineno = 1;
	} else {
		fprintf(stderr, "Error opening %s\n", filename);
                return;
	}



	while ((Val = yylex()) != 0) {	/* while !feof() */
		if (DebugMode) {
			fprintf(stderr, "tokenid %d, val <%s>, len %d\n", Val, yytext, yyleng);
			fprintf(stderr, " CurrentPos %d ClassNamePos %d", CurrentPos, ClassNamePos);
			fprintf(stderr, " FunctionNamePos %d ParamPos %d\n", FunctionNamePos, ParamPos);
		}

		/*
		 unfortunately we have to check it here; it doesn't make it
		 into the switch-case and I don't feel like debugging/rewriting.
		 -Freek
		 */
		if(Val==PARSE_ERROR) {
			fprintf(stderr, "Parse error in %s- aborting\n", filename);
			goto End;
		}

		/* Empêche dépassement de la pile des tokens lus */
		if (CurrentPos == MAX_TOKEN_PER_PROTOTYPE) {
			fprintf(stderr, "Too many tokens in file %s line %d !!\n", CurrentFileName, LinePrototype);
			Val = RESET;	/* force un abandon du prototype en cours */
		}


		/*
		 I hope I figured out the right if-else order here ...
		 Be the people damned who don't uses braces
                 -Freek
                 */
		if (ParamPos != -1) {
			/* liste des paramètres trouvée */
			/* gère cas du C++ */
			if (Val == ':')	{/* constructeur de classe de base */
				while (Val != BLOC) {
					Val = yylex();
					if (Val == 0) {	/* BLOC non trouvé */
						fprintf(stderr, "base constructor not found in file %s line %d !!\n",
							CurrentFileName, LinePrototype);
						goto End;
					}
				}
			} else if (Val != BLOC) {
				Val = RESET;	/* RESET si PARAMS non suivi par BLOC */
			}
		}

		switch (Val) {

		case KEYWORD:
		case ID:
			LinePrototype = lineno;
			TokenList[CurrentPos++] = CharCopy(yytext, yyleng);
			break;

		case DEUX_POINTS:
			ClassNamePos = CurrentPos - 1;
			break;

		case PARAMS:
			if (ClassNamePos == -1)	/* impossible */
				break;
			if (CurrentPos == 0)	/* impossible */
				break;

			FunctionNamePos = CurrentPos - 1;
			ParamPos = CurrentPos;
			TokenList[CurrentPos++] = CharCopy(ParamBuf, ParamBufLen);
			break;

		case BLOC:
			if (ParamPos != -1)
				AddPrototype(TokenList, ClassNamePos, FunctionNamePos, ParamPos);
			/* effectue un RESET après */

		default:	/* RESET */

		case RESET:
			if (CurrentPos)
				for (i = 0; i < CurrentPos; i++)
					free(TokenList[i]);

			CurrentPos = 0;
			ClassNamePos = -1234;	/* valeur < -1 */
			FunctionNamePos = -1;
			ParamPos = -1;
			break;
		}
	}


      End:

	if (CurrentPos) {		/* libère contenu de la pile */
		for (i = 0; i < CurrentPos; i++) {
			free(TokenList[i]);
		}
	}

	fclose(dofile);
}


/************************************************************************/
/* Tri le tableau T entre Gauche et Droite inclus avec un quicksort.	*/
/* Le tableau T contient des pointeurs sur toutes les fonctions trouvées*/
/* Le tri est effectué dans l'ordre croissant.				*/
/************************************************************************/
void SortPrototypes(struct function **T, int Gauche, int Droite)
{
	int i, Last;

	if (Gauche >= Droite)
		return;		/* moins de 2 éléments */

	Swap(T, Gauche, (Gauche + Droite) / 2);
	Last = Gauche;

	for (i = Gauche + 1; i <= Droite; i++) {
		if (CompareFunctions(T[i], T[Gauche]) < 0)
			Swap(T, ++Last, i);
	}

	Swap(T, Gauche, Last);
	SortPrototypes(T, Gauche, Last - 1);
	SortPrototypes(T, Last + 1, Droite);
}


/************************************************************************/
/* Echange T[ i ] et T[ j ]						*/
/************************************************************************/
void Swap(struct function **T, int i, int j)
{
	struct function *pF;

	pF = T[i];
	T[i] = T[j];
	T[j] = pF;
}


/************************************************************************/
/* Compare les 2 fonctions pF1 et pF2 suivant les critères de SortString*/
/* pF1 et pF2 peuvent être comparées suivant 4 critères : C, N, F et L	*/
/* Dès que l'un des critères permet de différencier pF1 et pF2, on	*/
/* retourne un nombre >0 ou <0. Sinon, si les champs comparés étaient	*/
/* égaux, on lit un nouvel élément de SortString et on recompare pF1 et	*/
/* pF2 en fonction de cet élément.					*/
/* Par défaut, on compare ClassName, puis FunctionName, puis File et	*/
/* enfin Line.								*/
/*									*/
/* Retourne	<0	si pF1 < pF2					*/
/*		>0	si pF1 > pF2					*/
/*		0	si pF1 = pF2					*/
/************************************************************************/
int CompareFunctions(struct function *pF1, struct function *pF2)
{
	char *p = SortString;
	char c;
	int res=0;

	while ((c = *p++))
		switch (c) {

		case 'C':
			res = strcmp(pF1->ClassName, pF2->ClassName);
			if (res)
				return res;
			break;

		case 'N':
			res = strcmp(pF1->FunctionName, pF2->FunctionName);
			if (res)
				return res;
			break;

		case 'F':
			res = strcmp(pF1->File, pF2->File);
			if (res)
				return res;
			break;

		case 'L':
			res = pF1->Line - pF2->Line;
			if (res)
				return res;
			break;

		default:
			fprintf(stderr, "Unknown sort parameter '%c'\n", c);
			exit(1);
		}

	return res;
}


/************************************************************************/
/* Recopie les adresses de toutes les fonctions trouvées dans un tableau*/
/* de pointeurs.							*/
/* En sortie, on met à jour NbFunctions et TableFunctions.		*/
/************************************************************************/
void CopyListToTable(void)
{
	struct function *pF;
	int i;

	NbFunctions = 0;

	pF = pFirstF;
	if (!pF)
		return;

	while (pF) {
		NbFunctions++;
		pF = pF->next;
	}

	/* alloue un tableau de NbFunctions pointeurs */
	TableFunctions = (struct function **) MyAlloc(NbFunctions * sizeof(struct function *));

	if (!TableFunctions)
		exit(1);

	pF = pFirstF;

	for (i = 0; i < NbFunctions; i++) {
		TableFunctions[i] = pF;	/* remplit le tableau de pointeurs */
		pF = pF->next;
	}
}


/************************************************************************/
/* Affiche un prototype en fonction de la chaîne de formatage.		*/
/* Les caractères autorisés sont les suivants:				*/
/*	%R : paramètre de retour					*/
/*	%C : nom de la classe (s'il existe)				*/
/*	%S : affiche séparateur "::" s'il y a un nom de classe		*/
/*	%N : nom de la fonction						*/
/*	%P : paramètres de la fonction					*/
/*	%L : ligne de début de la déclaration				*/
/*	%F : fichier							*/
/* On balaie FormatString jusqu'à trouver un '%'; à ce moment,on affiche*/
/* tous les caractères précédents (depuis StartPtr) avec un printf et on*/
/* affiche un texte en fonction du caractère suivant '%'.		*/
/* On continue jusqu'à la fin de FormatString.				*/
/************************************************************************/
void PrintOnePrototype(struct function *pF)
{
	char *StartPtr, *Ptr;
	char c;

	StartPtr = Ptr = FormatString;

	while ((c = *Ptr))
		if (c == '%') {	/* caractère d'affichage d'un champ */
			*Ptr = '\0';	/* remplace '%' par '\0' */
			printf(StartPtr);	/* affiche jusqu'à '%' */

			*Ptr++ = '%';	/* restaure '%' pour les prochains appels */

			switch (*Ptr) {
			case 'R':
                                if(pF->ReturnParam[0])
					printf("%s", pF->ReturnParam);
				break;
			case 'C':
				if (pF->ClassName[0])	/* chaîne non vide */
					printf("%s", pF->ClassName);
				break;
			case 'S':
				if (pF->ClassName[0])	/* chaîne non vide */
					printf("::");
				break;
			case 'N':
				printf("%s", pF->FunctionName);
				break;
			case 'P':
				printf("%s", pF->Param);
				break;
			case 'L':
				printf("%d", pF->Line);
				break;
			case 'F':
				printf("%s", pF->File);
				break;
			default:
				fprintf(stderr, "Unknown format %%%c, aborting\n", *Ptr);
				exit(1);
			}

			Ptr++;	/* saute le caractère d'affichage */
			StartPtr = Ptr;

		} else {  /* caractère != '%' */
			Ptr++;
		}

	printf(StartPtr);	/* affiche la fin de Format String */
}


/************************************************************************/
/* Affiche tous les prototypes créés.					*/
/************************************************************************/
void PrintPrototypes(void)
{
	int i;


	if (DisplayBanner)
		printf("%s", BannerText);	/* texte d'info sur genproto */

	if (NbFunctions == 0)
		return;

	for (i = 0; i < NbFunctions; i++) {
		PrintOnePrototype(TableFunctions[i]);
	}
}


/************************************************************************/
/* Efface tous les prototypes déjà créés.				*/
/************************************************************************/
void DeletePrototypes(void)
{
	struct function *pF;
	struct function *pFnext;

	pF = pFirstF;
	while (pF) {
		free(pF->ReturnParam);
		if(pF->ClassName) {
			free(pF->ClassName);
		}
		free(pF->FunctionName);
		free(pF->Param);
		free(pF->File);

		pFnext = pF->next;
		free(pF);
		pF = pFnext;
	}

	pFirstF = pLastF = NULL;


	if (TableFunctions)
		free(TableFunctions);
	TableFunctions = NULL;
}


/************************************************************************/
/* Fonction appelée en cas d'appel à exit() (suite à une erreur).	*/
/* Libère la mémoire allouée avant de sortir.				*/
/************************************************************************/
void MyExit(void)
{
	if (pFirstF)
		DeletePrototypes();
}


/************************************************************************/
/* Fonction d'allocation mémoire avec récupération des erreurs.		*/
/* On appelle malloc(), et en cas d'erreurs, on sort par exit( 1 ).	*/
/* Cette fonction retourne toujours un résultat != NULL.		*/
/************************************************************************/
void *MyAlloc(size_t size)
{
	void *res;

	res = malloc(size);

	if (res)
		return res;

	fprintf(stderr, "Malloc Error for %u bytes\n", size);
	exit(1);
}


/************************************************************************/
/* Fonction principale ; les options possibles sont -h, -o, -s et -f.	*/
/* On reparcourt ensuite tous les argv[] et on scanne tous les fichiers	*/
/* dont le nom ne commence pas par '-'.					*/
/* La liste chaînée ainsi créée est recopiée dans un tableau afin d'être*/
/* éventuellemnt triée, puis affichée et détruite.			*/
/************************************************************************/
int main(int argc, char **argv)
{
	char *s;
	char *d;
	char c;
	int i, len;
	int SortFlag = FALSE;

	atexit(MyExit);		/* intercepte exit() */
	OutputFile = stdout;	/* affichage par défaut sur stdout */


	/* Analyse des options de la ligne de commande */
        /* simply use getopt */

	while((c=getopt(argc, argv, "bdf:hs:o:")) != -1) {

		switch(c) {


		case 'b':
			DisplayBanner=FALSE;
			break;

		case 'd':
			DebugMode=TRUE;
                        break;

		case 'f':
			/*
			 Freek:
			 this resembles the original code.
			 */
			s = optarg;
			d = FormatString;

			if (!*s)	/* s'il n'y a rien après "-f" */
				break;
			len = 1;

			while ((c = *s++)) {	/* traite la suite de "-f" */
				if (c == '\\') {
					switch (c = *s++) {
					case 'n':
						*d++ = '\n';
						break;
					case 't':
						*d++ = '\t';
						break;
					case 'v':
						*d++ = '\v';
						break;
					case 'b':
						*d++ = '\b';
						break;
					case 'r':
						*d++ = '\r';
						break;
					case 'f':
						*d++ = '\f';
						break;
					case 'a':
						*d++ = '\a';
						break;
					default:
						*d++ = c;
						break;	/* \c devient c */
					}

				} else {
					*d++ = c;
				}

				len++;

				if (len == FORMATLEN - 1) {
					fprintf(stderr, "Format String too long\n");
					exit(1);
				}
			}

                        /* terminate string */
			*d = '\0';
			break;

		case 's':
			/* copy the arg into the SortString */
			strncpy(SortString, optarg, sizeof(SortString));
			SortFlag=TRUE;
                        break;

		case 'o':
			s=optarg;

			if (!freopen(s, "w", stdout)) {
				fprintf(stderr, "Can't open %s for output\n", s);
				exit(1);
			}

			break;

		case 'h':
                        /* fall thru */
		default:
			Usage(0);
                        exit(1);


		}

	}


	/* Tous les argv[ i ] ne commençant pas par '-' sont interprétés comme */
	/* des noms de fichiers à scanner. */

	/*
	 Start looking for files after all options! Not at i=1; argv[i] !
	 -Freek
	 */
	i = optind;

	while (i < argc) {
                /* this is legacy -Freek */
		CurrentFileName=argv[i];
		ScanOneFile(argv[i]);
		i++;
	}


	CopyListToTable();	/* recopie la liste chaînée */

	if (SortFlag)
		SortPrototypes(TableFunctions, 0, NbFunctions - 1);

	PrintPrototypes();
	DeletePrototypes();

        return(0);
}


/************************************************************************/
/*				END main.c				*/
/************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1