1 /*
2  * $Id: mkdoc.i,v 1.1 2005-09-18 22:06:00 dhmunro Exp $
3  * Alphabetize DOCUMENT comments and prepare for line printer.
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
mkdoc(filename,outname,lpp)11 func mkdoc(filename, outname, lpp)
12 /* DOCUMENT mkdoc, filename
13          or mkdoc, filename, outname, lpp
14      alphabetizes and indexes the DOCUMENT comments in FILENAME, and
15      formats into "dictionary-like" pages for printing.  If OUTNAME
16      is not given or nil, the output file will be FILENAME with ".doc"
17      replacing the ".i".  If LPP is not given, it defaults to 58 --
18      the maximum number of available lines per page of output.
19      (Use 55 to be able to print with "lpr -p" style page headings.)
20      FILENAME can be an array of strings to combine several include
21      files into a single document.
22    SEE ALSO: help
23  */
24 {
25   extern mkdoc_lpp;
26 
27   if (is_void(lpp)) mkdoc_lpp= 58;
28   else mkdoc_lpp= lpp;
29   name_list= doc_list= oname= [];
30   inames= filename;
31 
32   for (ii=1 ; ii<=numberof(filename) ; ii++) {
33     f= open(filename(ii));
34 
35     /* strip off non-directory part of filename */
36     name= [string(0), filename(ii)];
37     do {
38       name= strtok(name(2), "/\:");
39     } while (name(2));
40     name= name(1);
41     inames(ii)= name;
42 
43     /* get output file name */
44     if (is_void(oname)) {
45       if (is_void(outname)) {
46         oname= name;
47         if (strpart(oname, -1:0)==".i") oname= strpart(oname, 1:-2);
48         oname+= ".doc";
49       } else {
50         oname= outname;
51       }
52     }
53 
54     /* scan the file to accumulate lists of function/variable/keyword names
55        and the corresponding document strings */
56     mkdoc_scan, f;
57     close, f;
58   }
59 
60   /* alphabetize the lists */
61   order= sort(name_list);
62   name_list= name_list(order);
63   doc_list= doc_list(order);
64 
65   /* make the title page */
66   f= open(oname, "w");
67   mkdoc_title, f, inames, name_list;
68 
69   n= numberof(name_list);
70   fragment= [];
71   /* loop on output pages */
72   while ((nlines= numberof(fragment)) || n) {
73     nleft= mkdoc_lpp-3;  /* leave 3 lines for heading */
74     if (nlines) {
75       /* part of the last entry has spilled onto the new page */
76       if (nlines < nleft) {
77         /* ...it fits on this page */
78         page= fragment;
79         fragment= [];
80         nleft-= nlines;
81       } else {
82         /* ...it fills this page completely, too --
83            be sure at least 6 lines on next page */
84         if (nlines < nleft+6) {
85           page= fragment(1:-6);
86           fragment= fragment(-5:0);
87         } else {
88           page= fragment(1:nleft);
89           fragment= fragment(nleft+1:0);
90         }
91         mkdoc_page, f, name, name, page;
92         continue;
93       }
94       fname= name;
95       not_top= 1;
96     } else {
97       page= [];
98       fname= name_list(1);
99       not_top= 0;
100     }
101 
102     /* loop on entries for this page */
103     while (n) {
104       oname= name;
105       name= name_list(1);
106       fragment= *doc_list(1);
107       nlines= 1+numberof(fragment);
108       if (nleft >= nlines+not_top) {
109         /* this entire entry fits on this page */
110         if (not_top) grow, page, "";
111         grow, page, swrite(format="%75s", name), fragment;
112         fragment= [];
113         nleft-= nlines+not_top;
114       } else if (nlines+not_top>7 && nleft>7 &&
115                  (nlines-nleft>=6 || nlines>12)) {
116         /* this entry runs over onto following pages */
117         if (not_top) grow, page, "";
118         nleft-= 1+not_top;
119         if (nlines-nleft<6) nlines-= 6;
120         else nlines= nleft;
121         grow, page, swrite(format="%75s", name), fragment(1:nlines);
122         fragment= fragment(nlines+1:);
123         nleft= 0;
124       } else {
125         /* this entire entry goes on next page */
126         name= oname;
127         fragment= [];
128         break;
129       }
130       if (--n) {
131         name_list= name_list(2:);
132         doc_list= doc_list(2:);
133       }
134       if (nleft<3 || numberof(fragment)) break;
135       not_top= 1;
136     }
137 
138     /* output this page with dictionary headings */
139     mkdoc_page, f, fname, name, page;
140   }
141 
142   close, f;
143 }
144 
mkdoc_scan(f)145 func mkdoc_scan(f)
146 {
147   /* Add extern, local, func, and struct declaration/definition names to
148      the name_list.  For extern and func, add the DOCUMENT comment to the
149      doc_list.  If no DOCUMENT comment appears within 10 non-blank lines,
150      skip to the next extern or func.  If subsequent extern lines precede
151      the DOCUMENT comment, generate a cross-reference SEE ... to the
152      first extern of the group.  For struct, the entire struct definition,
153      which is presumably commented, becomes the documentation text.  */
154   extern name_list, doc_list;
155 
156   while (line= rdline(f)) {
157 
158     split= strtok(line);
159     doctext= [];
160 
161     if (split(1)=="func" || split(1)=="extern" || split(1)=="local") {
162       name= strtok(split(2), " \t(,;");
163       if (split(1)!="local") crossref= [];
164       else crossref= mkdoc_cross(1, name(2), []);
165       name= name(1);
166       count= 10;        /* like help_worker function defined in std.i */
167       while ((line= rdline(f)) && count--) {
168         split= strtok(line);
169         if (!split(1)) break;
170         if (strmatch(line, "/* DOCUMENT")) {
171           do {
172             grow, doctext, [line];
173             if (strmatch(line, "*/")) break;
174           } while (line= rdline(f));
175         } else if (split(1)=="extern") {
176           crossref= mkdoc_cross(0, split(2), crossref);
177           if (count==9) count= 10;
178         } else if (split(1)=="local") {
179           crossref= mkdoc_cross(1, split(2), crossref);
180           if (count==9) count= 10;
181         }
182       }
183 
184     } else if (split(1)=="struct") {
185       name= strtok(split(2), " \t(,;")(1);
186       gotopen= 0;
187       do {
188         grow, doctext, [line];
189         if (!gotopen) gotopen= strmatch(line, "{");
190         if (gotopen && strmatch(line, "}")) break;
191       } while (line= rdline(f));
192       crossref= [];
193     }
194 
195     if (!is_void(doctext)) {
196       grow, name_list, [name];
197       grow, doc_list, [&doctext];
198       n= numberof(crossref);
199       for (i=1 ; i<=n ; i++) {
200         grow, name_list, crossref(i);
201         grow, doc_list, &["     /* SEE "+name+"     */"];
202       }
203     }
204   }
205 }
206 
mkdoc_cross(loc,names,crossref)207 func mkdoc_cross(loc, names, crossref)
208 {
209   split= strtok(names, " \t,;");
210   cross= crossref;
211   while (split(1) && !strmatch(split(1), "/*")) {
212     grow, cross, split(1:1);
213     if (!loc) break;
214     split= strtok(split(2), " \t,;");
215   }
216   return cross;
217 }
218 
mkdoc_title(f,inames,name_list)219 func mkdoc_title(f, inames, name_list)
220 {
221   extern mkdoc_lpp;
222 
223   write, f, format="\n\n\n"+
224     "                          Yorick Documentation\n"+
225     "                for functions, variables, and structures\n"+
226     "                         defined in file %s\n"+
227     "                   Printed: %s\n", inames(1), timestamp();
228 
229   write, f, format="\n   %s\n\n", "Contents:";
230 
231   nleft= mkdoc_lpp-10;
232 
233   n= numberof(name_list);
234   ncols= 1;
235   while (n/ncols > nleft-3) ncols++;
236   width= 80/ncols;
237   len= max(strlen(name_list));
238   if (len > width-3) {
239     /* Contents will overflow onto subsequent page(s).  Hopefully rare.  */
240     ncols= 80/(len+3);
241     width= 80/ncols;
242   }
243 
244   format= (width-len)/2 + 1;
245   width-= format;
246   format= swrite(format="%"+print(format)(1)+"s", "");  /* leading blanks */
247   format= format+"%-"+print(width)(1)+"s";           /* e.g.- "    %-12s" */
248 
249   len= (n-1)/ncols + 1;
250   for (i=1 ; i<=len ; i++) {
251     line= "";
252     for (j=i ; j<=n ; j+= len) line+= swrite(format=format, name_list(j));
253     j= strlen(line);
254     while (strpart(line, j:j)==" ") j--;
255     write, f, format="%s", strpart(line, 1:j)+"\n";
256   }
257 }
258 
mkdoc_page(f,fname,name,page)259 func mkdoc_page(f, fname, name, page)
260 {
261   extern mkdoc_lpp;
262 
263   write, f, format="\f\n%75s\n\n", swrite(format="FROM %s TO %s",
264                                           fname, name);
265   n= numberof(page);
266   for (i=1 ; i<=n ; i++) write, f, format="%s\n", page(i);
267 }
268