// -*- Pike -*-
// $Id: manpages,v 1.26 2000/02/29 20:35:12 neotron Exp $

#include "types.h"

#if __VERSION__ >= 0.6
import ".";
#endif /* __VERSION__ >= 0.6 */

inherit Stdio.File : out;

int verbose = 0;

string globalbase;
object windex;
object whatis;

// Conversion table:
//
// "*"		"\\(**"
// "\\"		"\\e"
// "()"		"(\\|)"
// " - "	" \\- "
// "..."	"\\&.\\|.\\|."
// ".."		"\\&.\\|"

string quote_text(string s)
{
  s=replace(s,({">","&","<"}),
	    ({">","&","<"}));
  return(replace(s, ({ "*", "\\", "()", " - ", "...", ".." }),
		 ({ "\\(**", "\\e", "(\\|)", " \\- ",
		    "\\&.\\|.\\|.", "\\&.\\|." })));
}

// Some versions of nroff don't like 8-bit chars at all.
string strip_eightbit(string s)
{
  return(s & String.strmult("\177", sizeof(s)));
}

string strip_empty_lines(string s)
{
  return(Array.filter(s/"\n", lambda(string line) {
				return(replace(line,
					       ({ "\t", " " }),
					       ({ "", "" })) != "");
			      })*"\n");
}

void make_page(string base, TAG tag, string ind, string fbase);

void mkdirhier(string what)
{
   string d=".";
   foreach (what/"/",string add)
   {
      d+="/"+add;
      mkdir(d);
   }
}

int stripws=1;

string make_manpage(string base, string|SGML data, string ind, string fbase)
{
  string res="";

  if (verbose) {
    werror(sprintf("data:%O, stripws:%d\n", data, stripws));
  }

  if (stringp(data)) {
    if (sizeof(data)) {
      if (stripws) {
	string d2 = data;
	sscanf(data,"%*[ \t\n\r]%s",data);
	if (stripws == -1) {
	  stripws = 1;
	  if (d2[0] == ' ') {
	    // Keep a single whitespace.
	    data = " " + data;
	  }
	}
      }
      res+=quote_text(data);
    }
  } else if(arrayp(data)) {
    int n;
    for (n = 0; n < sizeof(data); n++) {
      TAG tag = data[n];
      if (stripws && n) {
	// Keep a single whitespace between tags.
	stripws = -1;
      }
      if (objectp(tag)) {
	if (verbose) {
	  werror(sprintf("tag:%O, params:%O\n", tag->tag, tag->params));
	}
	switch (tag->tag) {
	case "method":
	case "function":
	case "constant":
	case "class":
	case "module":
	case "variable":
	  make_page(base,tag,ind,fbase);
	  continue;

	case "man_title":
	  res+="\n.SH "+quote_text(tag->params->title)+"\n";
	  stripws=1;
	  break;

	case "aarg":
	  stripws = 1;
	  res += "\n.br\n\\fB"+make_manpage(base,tag->data,ind,fbase)+"\\fR";
	  continue;

	case "adesc":
	  stripws = 1;
	  res += "\n.br\n.in +.8i .ll -.8i\n"+make_manpage(base,tag->data,ind,fbase) +"\n.in -.8i .ll +.8i\n";
	  continue;
	  
	case "p":
	  res+="\n.PP\n";
	  stripws=1;
	  break;
	case "br":
	case "ex_br":
	  res+="\n.br\n";
	  if (tag->params->keep_ws) {
	    stripws = 0;
	  } else {
	    stripws=1;
	  }
	  break;

	case "ex_indent":
	  res += "  ";
	  continue;

	case "link":
	case "i": 
	case "emboss":
	  // case "ex_identifier":
	  res+="\\f2"+make_manpage(base,tag->data,ind,fbase)+"\\f1";
	  continue;

	case "b": 
	case "strong":
	case "ex_keyword":
	  res+="\\f3"+make_manpage(base,tag->data,ind,fbase)+"\\f1";
	  continue;

	case "tt":
	  int ows = stripws;
	  stripws = 0;
	  // Should specify a fixed width font.
	  res += make_manpage(base,tag->data,ind,fbase);
	  stripws = ows;
	  continue;

	case "pre":
	case "example":
	  int ows = stripws;
	  stripws=0;
	  // Ought to specify a fixed width font.
	  res+="\n.nf\n"+
	    make_manpage(base,tag->data,ind,fbase)+
	    "\n.fi\n";
	  stripws = ows;
	  continue;
	}
	res+=make_manpage(base,tag->data,ind,fbase);
      } else if (stringp(tag)) {
	if (sizeof(tag)) {
	  if (stripws) {
	    string t2 = tag;
	    sscanf(tag,"%*[ \t\n\r]%s",tag);
	    if (stripws == -1) {
	      stripws = 1;
	      if (t2[0] == ' ') {
		// Keep a single whitespace.
		tag = " " + tag;
	      }
	    }
	  }
	  res+=quote_text(tag);
	}
	stripws=0;
      }
    }
  }
  
  return res;
}

int pages=0;

void make_page(string base, TAG tag, string ind, string fbase)
{
//   werror(ind+tag->tag+" "+tag->params->name+"\n");
 
   werror("manpage "+(++pages)+"...\r");

   mixed err = catch {

     string *outfiles,*names,*s_outfiles;
     string obase=base;
     string q;
     //     werror(sprintf("%s %s %s %s \n", tag->params->name, fbase, globalbase, base));
     outfiles=Array.map(names=tag->params->name/",",
			lambda(string s,string t,string u,string base)
			{
			  s=replace(s,"->",".");
			  // We want the man pages to be Image.image.foo for example... a man page for "create" is kinda uninteresting...
			  //			  sscanf(s,t+".%s",s);
			  return u+"/"+s+"."+base;
			},fbase,globalbase,base);

     // verbose = (names[0] == "Stdio.sendfile");

     sscanf(outfiles[0],"%*s/man%*s/%s",q);
     if (q) {
       array(string) a = q/".";
       if (sizeof(a) > 1) {
	 q = a[..sizeof(a)-2]*".";
       }
     }
   
//   werror("files: "+outfiles*", "+"\n");

     if (tag->params->mansuffix)
     {
       base+=tag->params->mansuffix;
       fbase=(tag->params->name/",")[0];
       //       mkdirhier(globalbase+base);
     }

     // verbose = ((names*",") == "Thread.Mutex");

     string page=strip_empty_lines(make_manpage(base,tag->data,ind+" ",fbase));

     //     werror("creating "+outfiles[0]+"...\n"); 
   
     object f=Stdio.File(outfiles[0],"wtc");
     string t=ctime(time());
     f->write(strip_eightbit(".\\\" t\n"
			     ".\\\" automatically generated from wmml\n"
			     ".TH " + names*"," + " " + upper_case(obase) +
			     " \"" + t[8..9] + t[3..7] + t[20..23] + "\""
			     " Pike \"Pike Manual\" Pike\n" +
			     page + "\n"));
     f->close();

     foreach (outfiles[1..], string s)
     {
       string name;
       sscanf(outfiles[0], "man/%s", name);
       //     werror("creating "+s+" -> "+name+"...\n"); 
       object f=Stdio.File(s, "wtc");
       f->write(".so "+name+"\n");
       f->close();
     }

     foreach (({q})+names, string s) {
       windex->write(sprintf("%-15s %-15s - %s\n",
			     s,q+" ("+obase+")",
			     tag->params->title || (names*", ")));
       whatis->write(sprintf("%-23s - %s\n", s  + " (" + obase + ")",
			     tag->params->title || (names*", ")));
     }
   
   };
   if (err) {
     werror("Error while making manpage for "+tag->name+" ("+base+"):\n"
	    +master()->describe_backtrace(err));
   }
}

void make_pages(string base, SGML data, string ind, string fbase)
{
   if (arrayp(data))
      foreach (data, TAG tag)
	 if (objectp(tag))
	    if ((<"method","function","class","module">)[tag->tag])
	       make_page(base,tag,ind,fbase);
	    else
	       make_pages(base,tag->data,ind,fbase);
}

void output(string base, WMML data)
{
   globalbase="man/man3";
   //   mkdirhier("man/man3p");
   mkdirhier("man/man3");
   windex=Stdio.File("man/windex", "wtc");
   whatis=Stdio.File("man/whatis", "wtc");
   //   make_pages("3p", data->data, "", "");
   make_pages("3p", data->data, "", "");
   werror("\n");
   // Used by Xman.
   // NB The Xman(1) man-page describes only the old file-format.
   // The format is either (old format)
   //   <section suffix (one char)><section name><lf>
   // or
   //   <section suffix><tab><section name>[<tab>suffix|fold|foldsuffix]<lf>
   // The latter is the only one usable for us.
   object mandesc = Stdio.File("man/mandesc", "wtc");
   mandesc->write("3p\t(3P) Pike Library Functions\n"
		  "3pi\t(3PI) Pike Image Module\n"
		  "3pii\t(3PII) Pike Image.image Class\n");
   mandesc->close();
}

Sgml.Tag|string illustration(object o, mapping|void options)
{
  return "";
}

Sgml.Tag|string illustration_jpeg(object o, mapping|void options)
{
  return "";
}