#include #include #include #include #include #include #include #include #include "pagin.h" #include "permut.h" #include "nadoc.m" #define NADOC_VERSION 2 #define NADOC_RELEASE 8 static void showver() { fprintf (stderr,"nadoc %d.%d (linuxconf-tools %d.%d)\n" ,NADOC_VERSION,NADOC_RELEASE ,TOOLS_VERSION,TOOLS_RELEASE); } static void near usage (const char *argv0) { showver(); const char *msg = MSG_B(I_USAGE, "Usage : nadoc Outline_file Prototypes_file [result]\n" " Format a manual from to file\n" " Prototypes_file is built using the extractor (proto).\n" " Type a '-' if omitted.\n" "\n" " -a : Author of the document\n" " -c : Number of char per line.\n" " Default value: 60\n" "\n" " -f : Generate a TLMPDOC document.\n" " Optionnally -f may specify a relative path.\n" " It will be used to build hypertext links.\n" "\n" " -g : Generate a SGML (linux HOWTO style) document.\n" "\n" " -h : Generate a HTML document.\n" " Optionnally -h may specify a relative path.\n" " It will be used to build hypertext links.\n" "\n" " -i : Generate an integrity report\n" " -i without argument output on stderr\n" " -ixxx output in file xxx.\n" "\n" " -l : Number of line per page.\n" " Default value: 58\n" "\n" " -m : Left margin (character).\n" " Default value: 4\n" "\n" " -p : Extract specification from the source files of the\n" " current directory.\n" " -pP : Extract the specification from source for the given\n" " directory P. Option -p may be used several times.\n" "\n" " -t : Title of the document\n" " -xF : Cross reference file.\n" " Option -x may be repeated, F is a file path.\n" " file F was produced by the crossref utility.\n" "\n" " Outline_file hold the general organisation of\n" " software system to describe.\n" " It holds standard ascii text plus special command\n" " to control formatting, and to merge function\n" " specifications.\n" "\n" " Normally the manual is printed on the standard output.\n" " A third, optionnal argument, indicates the path of\n" " a file which will hold the manual.\n" "\n" " Special commands begin with a pound sign '#' and ends\n" " with a colon ':'. They are always located at the\n" " beginning of a line. They may be followed by some\n" " parameters. Here is a list of supported commands.\n" "\n" "#endsub:\n" " It directs Nadoc to resume section numbering at\n" " the current level, before the last #subsection\n" " command.\n" "\n" "#function: function ...\n" " It tells Nadoc to merge the specifications of those\n" " functions from the prototypes_file.\n" "\n" "#ignore: function ...\n" " Normally Nadoc warns when a function of a system\n" " is not referenced. #ignore marks those functions\n" " as if they were referenced.\n" "\n" "#page:\n" " Insert a page break.\n" "\n" "#ref: label\n" " Record a label for cross-referencing\n" "\n" "#spec: key [ / key ... ]\n" " Merge all the specifications matching exactly this key\n" "#spec: key [ / key ... ] / *\n" " Merge all the specifications with a key beginning like\n" " this key\n" "#spec: clé [ / clé ... ] / ...\n" " Merge all the specifications with a key beginning like\n" " this key and which has not been merged so far in the\n" " document.\n" "\n" "#section: any title\n" " Nadoc will automaticly assign a section number.\n" " The title will be added to the table of content.\n" "\n" "#subsection:\n" " It tell Nadoc that following #section commands\n" " are sub-section of the current section of the\n" " manual.\n" "#\n" " Activate/Desactivate paragraph formatting\n" " Formatting is active initially.\n" , "Usage : nadoc [options] fichier_guide fichier-prototypes [résultat]\n" " Formatte un manuel en fusionnant un guide\n" " et une série de prototypes.\n" " Le fichier-prototypes est produit par l'extracteur\n" " de prototypes (proto). Il est optionnel. Inscrire\n" " un '-' si omis.\n" "\n" " -c : Nombre de caractères par ligne.\n" " Valeur par défaut: 60\n" "\n" " -f : Produit un document TLMPDOC.\n" " Optionnellement -f peut spécifier un path relatif\n" " qui sera utilisé pour les liens hypertextes\n" "\n" " -g : Produit un document SGML (style Linux HOWTO).\n" "\n" " -h : Produit un document HTML.\n" " Optionnellement -h peut spécifier un path relatif\n" " qui sera utilisé pour les liens hypertextes\n" "\n" " -i : Commande une copie du rapport d'intégrité\n" " -i sans argument produit le rapport sur le canal\n" " d'erreur.\n" " -ixxx produit le résultat dans le fichier xxx.\n" "\n" " -l : Nombre de lignes par page.\n" " Valeur par défaut: 58\n" " -m : Nombre de caractère réservés à la marge de gauche.\n" " Valeur par défaut: 4\n" "\n" " -p : Commande l'extraction des spécifications en\n" " balayant les fichiers sources. Le format des\n" " commentaires est le suivant:\n" " -pP : Commande l'extraction pour les sources du répertoire P.\n" " L'option -p peut être répêté plusieurs fois.\n" " +pP : Comme -pP, mais balaye recursivement les sous-répertoires.\n" "\n" " -xF : Fichier références croisées.\n" " L'option -x peut être répétée, F est un fichier.\n" " Le (les) fichier F a été produit par l'utilitaire crossref.\n" "\n" " /* #Spécification: clé [ / clé ... ]\n" " ...\n" " */\n" " ou,\n" " // #Spécification: clé [ / clé ... ]\n" " ...\n" " // #specend\n" "\n" " Fichier_présentation contient le squelette du manuel\n" " à produire. Il contient du texte ascii ainsi que des\n" " commandes. Il y a des commandes pour controler le\n" " formattage et des commandes pour fusionner les\n" " spécifications de fonctions extraites des sources.\n" "\n" " Par défaut, le manuel est produit à l'écran. Un\n" " troisième argument optionnel indique le nom du\n" " qui contiendra le manuel.\n" "\n" " Les commandes commencent toujours au début d'une ligne.\n" " Elle débutent par le caractère '#' et terminent par le\n" " caractère ':'. Voici la liste des commandes supportées.\n" "\n" "#finsous:\n" " Ceci indique la fin d'une série de sous-sections.\n" " Le numérotage des sections continuera au niveau\n" " précédant, immédiatement avant la dernière commande\n" " #soussection.\n" "\n" "#fonction: fonction ...\n" " Les spécifications des fonctions énumérées seront\n" " incluses dans le texte, à la position courante.\n" "\n" "#ignore: fonction ...\n" " Normallement Nadoc génère un avertissement pour\n" " chaque fonctions du système qui n'est pas référencée\n" " par le manuel. La commande ignore désactive cet action\n" " pour les fonctions énumérées\n" "\n" "#page:\n" " Insère un saut de page.\n" "\n" "#ref: label\n" " Enregistre un point d'entrée (cross-reference)\n" "\n" "#spec: clé [ / clé ... ]\n" " Formatte les spécifications correspondant exactement\n" " à cette clé.\n" "#spec: clé [ / clé ... ] / *\n" " Formatte les spécifications dont le début de la clé\n" " correspond à cette clé\n" "#spec: clé [ / clé ... ] / ...\n" " Formatte les spécifications dont le début de la clé\n" " correspond à cette clé et qui n'ont encore pas été\n" " mentionnées.\n" "\n" "#section: titre ...\n" " Nadoc numérote automatiquement cette section et ajoute\n" " le titre à la table des matières.\n" "\n" "#soussection:\n" " Suite à cette commande, Nadoc sous-numérotera toutes\n" " les sections suivantes, jusqu'à la prochaine commande\n" " #finsous.\n" "#\n" " Active/Désactive le formattage des paragraphes\n" " Par défaut, le formattage est actif\n"); fputs (msg,stderr); } /* Formatte l'entête d'une section */ static void near nadoc_section ( int section[], int niveau, char *buf, /* Le buffer est modifié */ PAGIN_V *pout, PAGIN_V *mat) /* Output table des matières */ { char *ctl = "%d"; char line[40]; char *ptl = line; section[niveau]++; for (int i=0; i<=niveau; i++){ ptl += sprintf (ptl,ctl,section[i]); ctl = ".%d"; } str_strip (buf,buf); buf = str_skip (buf); { pout->format_titre (niveau,line,buf); mat->format_titre (niveau,line,buf); } } /* Formatte conditionnellement un paragraphe */ static void near nadoc_flush ( PAGIN_V *pout, char *accum, char *ptacc) { if (ptacc > accum){ pout->nl (); strip_end (accum); pout->format_paragraphe(accum); } } /* Sépare un texte en lignes et affiche avec une marge. Le texte est un commentaire de fonction. Par défaut, essai de formatter des paragraphes. Supporte l'opérateur # pour stopper ou activer le formattage. Préserve l'état du formattage. */ static void near nadoc_indent ( PAGIN_V *pout, const char *txt) { /* Trouve le premier bloc de commentaire avant la fonction */ int format = 1; int first = 1; const char *pt = txt; char line[500]; char *ptl = line; line [0] = '\0'; while (1){ pt = str_skip (pt); if (strncmp(pt,"/*",2)==0){ pt += 2; while (1){ if (*pt == '\0'){ break; }else if (*pt == '\n'){ if (ptl > line){ pout->printf ("%s\n",line); if (first){ first = 0; pout->nl(); pout->set_format (PAG_FORMAT_PARA); } }else if (!first){ pout->nl(); } pt++; ptl = line; line[0] = '\0'; }else if (strncmp(pt,"#",2)==0){ format = !format; pout->set_format(format ? PAG_FORMAT_PARA : PAG_FORMAT_NONE); pt += 2; }else if (*pt == '*' && pt[1] == '/'){ pt += 2; break; }else{ *ptl++ = *pt++; *ptl = '\0'; } } pout->printf ("%s\n",line); format = 1; pout->nl(); }else if (strncmp(pt,"//",2)==0){ pt +=2; while (*pt != '\n') *ptl++ = *pt++; *ptl = '\0'; pt++; pout->printf ("%s\n",line); ptl = line; }else{ break; } } txt = pt; pout->nl (); pout->set_format (PAG_FORMAT_QUOT); while (1){ char *pt = (char*)strchr (txt,'\n'); if (pt == NULL){ if (*txt != '\0'){ pout->printf("%s\n",txt); } break; }else{ *pt = '\0'; pout->printf("%s\n",txt); *pt = '\n'; txt = pt+1; } } pout->set_format(format); } /* Affiche le nom de toutes les fonctions qui ne sont pas traitées par le fichier de contrôle Retourne != 0 si au moins un fonction pas traitée */ static int near nadoc_integre ( NAP_LIST &lst, PAGIN_V *pout, PAGIN_V *pmat, FILE *fout, const char *msg) { int nbfct = lst.getnb(); NAP_FCT **tbnot = (NAP_FCT**)malloc_err (nbfct*sizeof(NAP_FCT*),1); int nbnot = 0; for (int i=0; iflag) tbnot[nbnot++] = elm; } nap_sort (tbnot,nbnot); const char *lastfct = NULL; for (int i=0; inl(); const char *fct = elm->nom; if (lastfct == NULL || strcmp(fct,lastfct)!=0){ pout->format_fct (fct,1); pmat->format_fct (fct,1); nadoc_indent (pout,elm->desc); lastfct = fct; } } if (fout != NULL) fprintf (fout,msg,elm->nom); } free (tbnot); return nbnot; } static void near nadoc_copy ( const char *f1, const char *f2, const char *out) { FILE *fout = out == NULL ? stdout : fopen_err (out,"w",1); FILE *fin = fopen_err (f1,"r",1); char buf[300]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ fputs (buf,fout); } fclose (fin); fputs ("\f",fout); fin = fopen_err (f2,"r",1); while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ fputs (buf,fout); } fclose (fin); if (out != NULL) fclose (fout); } static void near nadoc_formatspec ( COM_INDEX &comi, PAGIN_V &pout, PAGIN_V &pmat, char *buf, int noline) { char cle[300]; char *tbcle[30]; int nbcle; strcpy (cle,buf); nbcle = nacom_parse (buf,tbcle); if (nbcle <= 0){ fprintf(stderr,MSG_U(E_IVLDSPEC,"Invalid spec statement, line %d\n") ,noline); }else{ int nbmatch = 0; int newpos; COM_SPC *spc; char *sel = "specification"; for (int i=0; i<2; i++, sel = "specbeg"){ int pos = 0; /* #Spécification: présentation / specbeg La commande #spec: ... recherche en premier les spécification selon le format normal .. #Spécification: ... et ensuite, selon le format alternatif .. #Specbeg: ... Cela fait que les spécification alternative apparaissent à la suite des autres dans le document. */ while ((newpos=comi.locate(pos,sel,tbcle,nbcle,&spc))!=-1){ char buf[300]; char filespec[MAXSIZ_PATH]; pos = newpos; spc->usage++; nacom_format (spc,filespec,buf); pout.format_spec (filespec,buf); pmat.format_spec (filespec,buf); nacom_print (spc,&pout,1); nbmatch++; } } if (nbmatch==0 && strcmp(tbcle[nbcle-1],"...")!=0){ fprintf (stderr ,MSG_U(E_NOMATCHSPEC ,"No specification matching the key, line %d\n\t%s\n") ,noline,cle); } } } /* Lit un nap et un fichier de controle et fusionne en un document */ static int near nadoc_format ( COM_INDEX &comi, const char *squel, const char *nap, const char *intgout, PAGIN_V & pout, // Gènération du document PAGIN_V & pmat) // Génération de la table des matières { int ret = 0; FILE *fin = vfopen_err (squel,"r",1); char *accum = (char*) malloc_err (10000,1); char *ptacc = accum; int section[10]; int niveau = 0; NAP_LIST lst; char rbuf[400]; int noline = 0; int separe = 1; if (strcmp(nap,"-")!=0) lst.parse (nap); section[0] = 0; while (fgets(rbuf,sizeof(rbuf)-1,fin)!=NULL){ strip_end (rbuf); char *buf = rbuf; while (isspace(buf[0])) buf++; noline++; if (buf[0] == '#' && buf[1] != '#'){ nadoc_flush (&pout,accum,ptacc); ptacc = accum; *ptacc = '\0'; if (strncmp(buf,"#ignore:",8)==0 || strncmp(buf,"#fonction:",10)==0 || strncmp(buf,"#function:",10)==0){ int ignore = buf[1] == 'i'; char *pt = ignore ? buf+8 : buf+10; char fct[100]; while ((pt=str_copymot (fct,pt))!=NULL){ NAP_FCT *info = lst.locate (fct); if (info != NULL){ int first = 1; while (1){ info->flag = 1; if (!ignore){ if (!separe) pout.nl(); pout.format_fct (fct,first); pmat.format_fct (fct,first); nadoc_indent (&pout,info->desc); separe = 0; } info = lst.locatenext (info); if (info == NULL) break; first = 0; }; }else{ fprintf (stderr,MSG_U(E_UNKNOWNFUNC ,"Unknown %s function\n"),fct); ret = -1; } } }else if (strncasecmp(buf,"#spec:",6)==0){ /* #Spécification: nadoc / sélection des spécifications On sélectionne le formattage d'une spécification à partir de la commande #spec:. La commande doit donnner la liste des expressions de classement. exemple: #spec: nadoc / sélection des spécifications La commande #specification: est équivalente. */ nadoc_formatspec(comi,pout,pmat,buf+6,noline); }else if (strncasecmp(buf,"#ref:",5)==0){ /* #Spécification: nadoc / définition de références La commande #ref: permet de définir une référence (point d'entrée) dans le document. On choisit généralement un mot identifiant la portion de document et partout où ce mot sera vue dans le documents (ou d'autres documents) un hyper-lien sera établi. exemple: #ref: une_classe_c++ */ pout.format_ref (str_skip(buf+5)); }else if (strncasecmp(buf,"#exec:",6)==0){ FILE * execin = popen (buf+6,"r"); if (execin != NULL){ char bufin[1000]; while (fgets(bufin,sizeof(bufin)-1,execin)!=NULL){ pout.printf ("%s",bufin); } pclose (execin); } }else if (str_nicmpfranc(buf,"#specification:",15)==0){ nadoc_formatspec(comi,pout,pmat,buf+15,noline); }else if (strncmp(buf,"#soussection:",13)==0 || strncmp(buf,"#subsection:",12)==0){ niveau++; section[niveau] = 0; pout.soussection(); pmat.soussection(); }else if (strncmp(buf,"#finsous:",9)==0 || strncmp(buf,"#endsub:",8)==0){ niveau--; if (niveau < 0){ fprintf (stderr,MSG_U(E_ENDSUB ,"endsub statement out of sequence on line %d\n") ,noline); niveau = 0; ret = -1; }else{ pout.finsous(); pmat.finsous(); } }else if (strncmp(buf,"#section:",9)==0){ if (!separe) pout.nl (); nadoc_section (section,niveau,buf+9,&pout,&pmat); separe = 0; }else if (strncmp(buf,"#page:",6)==0){ pout.form (); }else if (!isgraph(buf[1])){ pout.set_format(!pout.get_format()); }else{ fprintf (stderr,MSG_U(E_IVLDCMD,"Invalid command, line %d\n%s"),noline,buf); ret = -1; } }else{ if (buf[0] == '#') buf[0] = ' '; strip_end (buf); if (rbuf[0] != '\0'){ ptacc = stpcpy (ptacc,rbuf); *ptacc++ = '\n'; *ptacc = '\0'; }else{ /* Changement de paragraphe détecté */ nadoc_flush (&pout,accum,ptacc); ptacc = accum; separe = 0; } } } nadoc_flush (&pout,accum,ptacc); fclose (fin); { int integre_fct = nadoc_integre (lst,NULL,NULL,NULL,""); int integre_com = comi.chknotusedp (NULL,NULL); if (integre_com){ char msg[100]; strcpy (msg,MSG_U(E_SPECNOTCOVERED ,"List of specification not dispatched")); nadoc_section (section,0,msg,&pout,&pmat); comi.chknotusedp (&pout,&pmat); } if (integre_fct){ char msg[100]; strcpy (msg,MSG_U(E_FUNCNOTCOVERED ,"List of function not covered")); nadoc_section (section,0,msg,&pout,&pmat); nadoc_integre (lst,&pout,&pmat,NULL,"\t%s\n"); } if (intgout != NULL){ /* #Spécification: nadoc / rapport d'intégrité Si un rapport d'intégrité est demandée, il est produit sur stderr sauf si un fichier est spécifié. Le rapport d'intégrité est toujours ajouté au résultat formatté. */ if (intgout[0] != '\0'){ FILE *intg = fopen_err (intgout,"w",1); nadoc_integre (lst,NULL,NULL,intg,"#ignore: %s\n"); fprintf (intg,"\n"); comi.chknotused (intg); fclose (intg); }else{ nadoc_integre (lst,NULL,NULL,stderr,"\t%s\n"); comi.chknotused (stderr); } } } free (accum); return ret; } /* Atoi avec valeur par défaut si str == NULL. Si str contient valeur invalide, retourne -1 et affiche un message */ static int near nadoc_atoi( const char *str, int defval, const char *msg) { int ret = defval; if (str != NULL){ int err = 1; if (isdigit(str[0])){ const char *pt = str+1; while (isdigit(pt[0])) pt++; err = *pt != '\0'; } if (err){ fprintf (stderr,MSG_U(E_IVLDVALUE ,"Invalid value :%s: for %s (default %d)\n") ,str,msg,defval); ret = -1; }else{ ret = atoi(str); } } return ret; } /* Lit un nap et un fichier de controle et fusionne en un document */ static int near nadoc_format ( COM_INDEX &comi, const char *squel, const char *nap, const char *out, const char *intgout, int marge, // Marge de gauche int char_line, // Nombre de caractères par ligne int line_page) // Nombre de ligne par page { PAGIN pout; PAGIN_IDX pmat(&pout); char tmp1[MAXSIZ_PATH]; char tmp2[MAXSIZ_PATH]; #ifdef UNIX int pid = getpid(); sprintf (tmp1,"/tmp/nadoc.1.%d",pid); sprintf (tmp2,"/tmp/nadoc.2.%d",pid); #else strcpy (tmp1,"tmp1.$$$"); strcpy (tmp2,"tmp2.$$$"); #endif pout.openerr (tmp1,1,marge,char_line,line_page); pmat.openerr (tmp2,1,marge,char_line,line_page); int ret = nadoc_format (comi,squel,nap,intgout,pout,pmat); pout.close (); pmat.close (); nadoc_copy (tmp2,tmp1,out); unlink (tmp2); unlink (tmp1); return ret; } /* Lit un nap et un fichier de controle et fusionne en un document HTML */ static int near nadoc_format_html ( COM_INDEX &comi, const char *squel, const char *nap, const char *rel_path, // Path relatif du document html // pour établir les liens hypertexte // Toutes les composantes produites // sont stocké dans le même sous-répertoire. const char *out, const char *intgout) { PAGIN_HTML pout; PAGIN_HTML_IDX pmat; pout.openerr (out,1); pmat.openerr (out,rel_path,1); int ret = nadoc_format (comi,squel,nap,intgout,pout,pmat); pout.close(); pmat.close(); return ret; } /* Lit un nap et un fichier de controle et fusionne en un document TLMPDOC */ static int near nadoc_format_tlmp ( COM_INDEX &comi, const char *squel, const char *nap, const char *rel_path, // Path relatif du document html // pour établir les liens hypertexte // Toutes les composantes produites // sont stocké dans le même sous-répertoire. const char *out, const char *intgout) { PAGIN_TLMP pout; PAGIN_TLMP_IDX pmat; pout.openerr (out,1); pmat.openerr (out,1); int ret = nadoc_format (comi,squel,nap,intgout,pout,pmat); pout.close(); pmat.close(); return ret; } /* Lit un nap et un fichier de controle et fusionne en un document SGML */ static int near nadoc_format_sgml ( COM_INDEX &comi, const char *squel, const char *nap, const char *out, const char *intgout, const char *title, const char *author) { PAGIN_SGML pout; PAGIN_SGML_IDX pmat; pout.openerr (out,title,author,1); pmat.openerr (out,1); int ret = nadoc_format (comi,squel,nap,intgout,pout,pmat); pout.close(); pmat.close(); return ret; } int main (int argc, char *argv[]) { etc_loadmsg(); int ret = -1; ARGP_MULTI argp[26]; argc = anlparm_multi(argc,argv,argp,"acfghilmp*tx*v"); if (argp['v'-'a'].ptr != NULL){ showver(); }else if (argc != 3 && argc != 4){ usage(argv[0]); }else{ const char *title = argp['t'-'a'].ptr; const char *author = argp['a'-'a'].ptr; char *squel = argv[1]; char *nap = argv[2]; char *out = argc == 4 ? argv[3] : (char*)NULL; bool sgmlmode = argp['g'-'a'].ptr != NULL; char *html_path = argp['h'-'a'].ptr; char *tlmp_path = argp['f'-'a'].ptr; char *intgout = argp['i'-'a'].ptr; COM_INDEX comi; int line_page = nadoc_atoi(argp['l'-'a'].ptr,58 ,MSG_U(F_LINEPERPAGE,"line per page")); int char_line = nadoc_atoi(argp['c'-'a'].ptr,60 ,MSG_U(F_CHARPERLINE,"char per line")); int marge = nadoc_atoi(argp['m'-'a'].ptr,4 ,MSG_U(F_LEFTMARGIN,"left margin")); if (marge != -1 && char_line != -1 && line_page != -1 && comi.ok()){ ret = 0; ARGP_MULTI *arg = &argp['p'-'a']; if (arg->ptr != NULL){ /* Extraction des commentaires */ for (int i=0; inbptr; i++){ static char *tb[]={ "*.c","*.cc","*.tlcc","*.h", "*.h0" }; char *ptr = arg->ptrs[i]; if (ptr[0] == '\0') ptr = "."; ret = comi.scanargv(5,tb,ptr,arg->signes[i]); } comi.sort(); } arg = &argp['x'-'a']; if (arg->ptr != NULL){ // Chargement des références croisées for (int i=0; inbptr; i++){ char *ptr = arg->ptrs[i]; crosstbl_load (ptr); } } if (ret == 0){ if (sgmlmode){ ret = nadoc_format_sgml (comi,squel,nap ,out,intgout,title,author); }else if (html_path != NULL){ ret = nadoc_format_html (comi,squel,nap,html_path ,out,intgout); }else if (tlmp_path != NULL){ ret = nadoc_format_tlmp (comi,squel,nap,tlmp_path ,out,intgout); }else{ ret = nadoc_format (comi,squel,nap,out,intgout ,marge,char_line,line_page); } } } } return ret; }