/*************************************************************************
** ^FILE: unix_man.c - print manual templates for user command
**
** ^DESCRIPTION:
**    This file is the portion of parseargs(1) that prints a Unix
**    manual template of a given command on the standard output.
**    The template is formatted for {n|t}roff using the -man macros.
**
** ^HISTORY:
**    27/08/91 	Earl Chew 	<cechew@bruce.cs.monash.edu.au>
**    - Use ProgNameLen when accessing ProgName
**    - Use get_argpfx() to access names
**
**    01/02/91 	Brad Appleton 	<brad@ssd.csd.harris.com> 	Created
***^^**********************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <useful.h>
#include "strfuncs.h"

#define PARSEARGS_PRIVATE   /* include private definitions */
#include "parseargs.h"

EXTERN  VOID  syserr  ARGS((const char *, ...));
EXTERN  VOID  usrerr  ARGS((const char *, ...));

#define MAXCOLS  65
#define VAL(str)           ((str) ? str : "")
#define COMMENT            printf(".\\\"-----------------------------------\n")
#define TH(title,len,section)  printf(".TH %.*s %d\n", len, title, section )
#define SH(title)          printf(".SH %s\n", title )
#define TP(cols,title)     printf(".TP %d\n%s\n", cols, title )
#define PP                 printf(".PP\n" )


/***************************************************************************
** ^FUNCTION: fmtarg - format command-argument syntax
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   static int fmtarg( ad, buf )
/*
** ^PARAMETERS:
*/
   ARGDESC *ad;
/*    -- pointer to the argument to format
*/
   char *buf;
/*    -- character buffer to hold the formatted result
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Fmtarg will determine the proper command-line syntax for the
**    given argument and write the result to the given buffer.
**
** ^REQUIREMENTS:
**    buf must be large enough to hold the formatted result (100 characters
**    should do the trick).
**
** ^SIDE-EFFECTS:
**    buf is overwritten.
**
** ^RETURN-VALUE:
**    The number of printable characters in the argument-syntax-string
**
** ^ALGORITHM:
**    Print argument usage based on whether or not the argument is
**    positional, hidden, multi-valued (list or vector), etc ....
**    Optional arguments and values are enclosed in square braces.
***^^**********************************************************************/
#ifdef __ANSI_C__
   static int fmtarg( const ARGDESC *ad , char *buf )
#endif
{
      /* buf must already be large enough */
   char *pos;
   argName_t   name;

   (VOID) get_argname( arg_sname(ad), name );

   if (ARG_isPOSITIONAL(ad)) {
       if ( ARG_isMULTIVAL(ad) ) {
          sprintf( buf, "\\fI%s\\fP\\ ...", name );
          return   (strlen(name) + 4);
       }/*if list*/

       sprintf( buf, "\\fI%s\\fP", name );
       return  strlen(name);
   }/*if parm*/

#ifdef SVR4
   sprintf(buf, "\\f4\\-%c\\fP", arg_cname(ad));
#else
   sprintf(buf, "\\fB\\-%c\\fP", arg_cname(ad));
#endif
   pos = buf + strlen(buf);

   if ( ARG_isVALTAKEN(ad)  &&  !ARG_isBOOLEAN(ad)  &&  !ARG_isPSEUDOARG(ad) ) {
       if ( ARG_isMULTIVAL(ad) ) {
          if ( ARG_isVALOPTIONAL(ad) ) {
             sprintf(pos, "\\ \\s-1[\\s+1\\fI%s\\fP\\ ...\\s-1]\\s+1", name);
             return  (strlen(name) + 9);
          }/*if optarg*/

          sprintf( pos, "\\ \\fI%s\\ ...\\fP", name );
          return  (strlen(name) + 7);
       }/*if list*/

       if ( ARG_isVALOPTIONAL(ad) ) {
          sprintf( pos, "\\ \\s-1[\\s+1\\fI%s\\fP\\s-1]\\s+1", name );
          return  (strlen(name) + 5);
       }/*if optarg*/

       sprintf( pos, "\\ \\fI%s\\fP", name );
       return  (strlen(name) + 3);
   }/*if*/

   return  2;
}


/***************************************************************************
** ^FUNCTION: manpage - print the manual template
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   VOID manpage ( argd )
/*
** ^PARAMETERS:
*/
   ARGDESC *argd;
/*    -- the command-argdesc-array
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Manpage print on standard output, a Unix manual template for the
**    given command. The NAME, SYNOPSIS, and OPTIONS sections are filled
**    in and the DESCRIPTION section is partially filled in with any
**    known command-description. The FILES, SEE ALSO, DIAGNOSTICS, CAVEATS,
**    BUGS, AUTHOR, and HISTORY section titles are provided but the body
**    of each section must be composed by the documentor.
**
** ^REQUIREMENTS:
**    Argd should be a valid command-line descriptor array.
**
** ^SIDE-EFFECTS:
**    Prints on standard-output.
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**    - print .TH setting followed by NAME section.
**    - cycle through each argument and print is syntax for the SYNOPSIS.
**    - print the description (if it exists).
**    - cycle through each argument again and print its syntax and description
**      for the OPTIONS section.
**    - print just the headings for the remaining sections.
***^^**********************************************************************/
#ifdef __ANSI_C__
   void manpage( const ARGDESC *argd )
#endif
{
   register CONST ARGDESC *ad, *args, *cmd;
   argName_t  name;
   CONST char *program, *purpose, *description;
   int   len, maxlen, positionals, namelen, programlen, purposelen;

      /* allow null argument descriptor */
   if ( !argd )  return;

   if ( !CMD_isINIT(argd) )   init_args( (ARGDESC *)argd );
   cmd = argd;

   name[0] = 0;
   namelen = 0;
   if ( cmd_name(cmd) ) {
      (VOID) strcpy( name, cmd_name(cmd) );
      if ( !BTEST(cmd_state(cmd), ps_USERNAME|ps_FREENAME) ) {
         namelen = get_argpfx( name );
         name[namelen] = 0;
      }
      else {
         namelen = strlen( cmd_name(cmd) );
      }
   }

   program = 0;
   programlen = 0;
   if ( cmd_name(cmd) ) {
      program = cmd_name(cmd);
      programlen = namelen;
   }
   else if ( cmd_argv0(cmd) ) {
      program = cmd_argv0(cmd);
      programlen = strlen(program);
   }

   purpose = cmd_purpose(cmd);
   if ( !BTEST(cmd_state(cmd), ps_USERPURPOSE|ps_FREEPURPOSE) && purpose ) {
      purpose = get_argdesc(purpose, &purposelen);
   }
   else {
     purposelen = strlen(VAL(purpose));
   }
   description = cmd_description(cmd);

   printf(".\\\"---------- TO PRINT, USE: {n,t}roff -man file ----------\n");
   printf(".if n .po 1\n");
   printf(".if n .ll 78\n");

   COMMENT;
#ifdef SVR4
   TH( name, namelen, 1 );
#else
   TH( strupr(name), namelen, 1 );
#endif

   COMMENT;
   SH( "NAME" );
   printf( "%s \\- %.*s\n", VAL(program), purposelen, VAL(purpose) );

   COMMENT;
   SH( "SYNOPSIS" );

   len = strlen( program ) + 1;
#ifdef SVR4
   sprintf( name, "\\f4%.*s\\fP", programlen, program );
#else
   sprintf( name, "\\fB%.*s\\fP", programlen, program );
#endif
   TP( len, name );

   maxlen = 0;
   for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
     for ( args = argd ; args ; args = cmd_defargs(args) ) {
        for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
           argName_t  buf;

              /* don't display hidden arguments */
           if ( ARG_isHIDDEN(ad) )  continue;
           if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
           if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;

           len = fmtarg( ad, buf );
           if ( len > maxlen )   maxlen = len;

           if ( ARG_isREQUIRED(ad) ) {
               printf( "%s\n", buf );
           }
           else {
               printf( "[%s]\n", buf );
           }
        }/*for each ad */
     }/* for each argd */
   }/* for each parm-type */

   COMMENT;
   SH( "DESCRIPTION" );
   PP;
   indent_para(stdout, MAXCOLS, 0, "", 0, VAL(description) );

   COMMENT;
   SH( "OPTIONS" );

   for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
      for ( args = argd ; args ; args = cmd_defargs(args) ) {
         for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
            argName_t  buf;
            char  *desc;
            int  desclen;

               /* don't display hidden arguments */
            if ( ARG_isHIDDEN(ad) )  continue;
            if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
            if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;

            (VOID) fmtarg( ad, buf );
            TP( maxlen + 2, buf );
            desc = get_argdesc(arg_description(ad), &desclen);
            indent_para(stdout, MAXCOLS, 0, "", 0, desc, desclen );
         }/*for each ad */
      }/* for each argd */
   }/* for each parm-type */

   COMMENT;
   SH( "FILES" );
   PP;

   COMMENT;
   SH( "SEE ALSO" );
   PP;

   COMMENT;
   SH( "DIAGNOSTICS" );
   PP;

   COMMENT;
   SH( "CAVEATS" );
   PP;

   COMMENT;
   SH( "BUGS" );
   PP;

   COMMENT;
   SH( "AUTHOR" );
   PP;

   COMMENT;
   SH( "HISTORY" );
   PP;
}
