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