/* MKDOC.I Alphabetize DOCUMENT comments and prepare for line printer. $Id: mkdoc.i,v 1.1 1993/08/27 18:50:06 munro Exp $ */ /* Copyright (c) 1994. The Regents of the University of California. All rights reserved. */ func mkdoc (filename, outname, lpp) /* DOCUMENT mkdoc, filename or mkdoc, filename, outname, lpp alphabetizes and indexes the DOCUMENT comments in FILENAME, and formats into "dictionary-like" pages for printing. If OUTNAME is not given or nil, the output file will be FILENAME with ".doc" replacing the ".i". If LPP is not given, it defaults to 58 -- the maximum number of available lines per page of output. (Use 55 to be able to print with "lpr -p" style page headings.) FILENAME can be an array of strings to combine several include files into a single document. SEE ALSO: help */ { extern mkdoc_lpp; if (is_void(lpp)) mkdoc_lpp= 58; else mkdoc_lpp= lpp; name_list= doc_list= oname= []; inames= filename; for (ii=1 ; ii<=numberof(filename) ; ii++) { f= open(filename(ii)); /* strip off non-directory part of filename */ name= [string(0), filename(ii)]; do { name= strtok(name(2), "/\:"); } while (name(2)); name= name(1); inames(ii)= name; /* get output file name */ if (is_void(oname)) { if (is_void(outname)) { oname= name; if (strpart(oname, -1:0)==".i") oname= strpart(oname, 1:-2); oname+= ".doc"; } else { oname= outname; } } /* scan the file to accumulate lists of function/variable/keyword names and the corresponding document strings */ mkdoc_scan, f; close, f; } /* alphabetize the lists */ order= sort(name_list); name_list= name_list(order); doc_list= doc_list(order); /* make the title page */ f= open(oname, "w"); mkdoc_title, f, inames, name_list; n= numberof(name_list); fragment= []; /* loop on output pages */ while ((nlines= numberof(fragment)) || n) { nleft= mkdoc_lpp-3; /* leave 3 lines for heading */ if (nlines) { /* part of the last entry has spilled onto the new page */ if (nlines < nleft) { /* ...it fits on this page */ page= fragment; fragment= []; nleft-= nlines; } else { /* ...it fills this page completely, too -- be sure at least 6 lines on next page */ if (nlines < nleft+6) { page= fragment(1:-6); fragment= fragment(-5:0); } else { page= fragment(1:nleft); fragment= fragment(nleft+1:0); } mkdoc_page, f, name, name, page; continue; } fname= name; not_top= 1; } else { page= []; fname= name_list(1); not_top= 0; } /* loop on entries for this page */ while (n) { oname= name; name= name_list(1); fragment= *doc_list(1); nlines= 1+numberof(fragment); if (nleft >= nlines+not_top) { /* this entire entry fits on this page */ if (not_top) grow, page, ""; grow, page, swrite(format="%75s", name), fragment; fragment= []; nleft-= nlines+not_top; } else if (nlines+not_top>7 && nleft>7 && (nlines-nleft>=6 || nlines>12)) { /* this entry runs over onto following pages */ if (not_top) grow, page, ""; nleft-= 1+not_top; if (nlines-nleft<6) nlines-= 6; else nlines= nleft; grow, page, swrite(format="%75s", name), fragment(1:nlines); fragment= fragment(nlines+1:); nleft= 0; } else { /* this entire entry goes on next page */ name= oname; fragment= []; break; } if (--n) { name_list= name_list(2:); doc_list= doc_list(2:); } if (nleft<3 || numberof(fragment)) break; not_top= 1; } /* output this page with dictionary headings */ mkdoc_page, f, fname, name, page; } close, f; } func mkdoc_scan (f) { /* Add extern, local, func, and struct declaration/definition names to the name_list. For extern and func, add the DOCUMENT comment to the doc_list. If no DOCUMENT comment appears within 10 non-blank lines, skip to the next extern or func. If subsequent extern lines precede the DOCUMENT comment, generate a cross-reference SEE ... to the first extern of the group. For struct, the entire struct definition, which is presumably commented, becomes the documentation text. */ extern name_list, doc_list; while (line= rdline(f)) { split= strtok(line); doctext= []; if (split(1)=="func" || split(1)=="extern" || split(1)=="local") { name= strtok(split(2), " \t(,;"); if (split(1)!="local") crossref= []; else crossref= mkdoc_cross(1, name(2), []); name= name(1); count= 10; /* like help_worker function defined in std.i */ while ((line= rdline(f)) && count--) { split= strtok(line); if (!split(1)) break; if (strmatch(line, "/* DOCUMENT")) { do { grow, doctext, [line]; if (strmatch(line, "*/")) break; } while (line= rdline(f)); } else if (split(1)=="extern") { crossref= mkdoc_cross(0, split(2), crossref); if (count==9) count= 10; } else if (split(1)=="local") { crossref= mkdoc_cross(1, split(2), crossref); if (count==9) count= 10; } } } else if (split(1)=="struct") { name= strtok(split(2), " \t(,;")(1); gotopen= 0; do { grow, doctext, [line]; if (!gotopen) gotopen= strmatch(line, "{"); if (gotopen && strmatch(line, "}")) break; } while (line= rdline(f)); crossref= []; } if (!is_void(doctext)) { grow, name_list, [name]; grow, doc_list, [&doctext]; n= numberof(crossref); for (i=1 ; i<=n ; i++) { grow, name_list, crossref(i); grow, doc_list, &[" /* SEE "+name+" */"]; } } } } func mkdoc_cross (loc, names, crossref) { split= strtok(names, " \t,;"); cross= crossref; while (split(1) && !strmatch(split(1), "/*")) { grow, cross, split(1:1); if (!loc) break; split= strtok(split(2), " \t,;"); } return cross; } func mkdoc_title (f, inames, name_list) { extern mkdoc_lpp; write, f, format="\n\n\n"+ " Yorick Documentation\n"+ " for functions, variables, and structures\n"+ " defined in file %s\n"+ " Printed: %s\n", inames(1), timestamp(); write, f, format="\n %s\n\n", "Contents:"; nleft= mkdoc_lpp-10; n= numberof(name_list); ncols= 1; while (n/ncols > nleft-3) ncols++; width= 80/ncols; len= max(strlen(name_list)); if (len > width-3) { /* Contents will overflow onto subsequent page(s). Hopefully rare. */ ncols= 80/(len+3); width= 80/ncols; } format= (width-len)/2 + 1; width-= format; format= swrite(format="%"+print(format)(1)+"s", ""); /* leading blanks */ format= format+"%-"+print(width)(1)+"s"; /* e.g.- " %-12s" */ len= (n-1)/ncols + 1; for (i=1 ; i<=len ; i++) { line= ""; for (j=i ; j<=n ; j+= len) line+= swrite(format=format, name_list(j)); j= strlen(line); while (strpart(line, j:j)==" ") j--; write, f, format="%s", strpart(line, 1:j)+"\n"; } } func mkdoc_page (f, fname, name, page) { extern mkdoc_lpp; write, f, format="\f\n%75s\n\n", swrite(format="FROM %s TO %s", fname, name); n= numberof(page); for (i=1 ; i<=n ; i++) write, f, format="%s\n", page(i); }