i/mkdoc

Home Manual Packages Global Index Keywords Quick Reference |
    /*
       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);
    }