/************************************************************************/ /* 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 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 #include #include #include #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 */ /************************************************************************/