i/mkdoc

yorick banner

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);
}