/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:ptroff.c 12.0$ */
/* $ACIS:ptroff.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/ditroff/ptroff/RCS/ptroff.c,v $ */

#ifndef lint
static char *rcsid = "$Header:ptroff.c 12.0$";
#endif

#include <stdio.h>
#include <ctype.h>
#include <utils.h>
#include <hash.h>
#include <environ.h>
#include <header.h>
#include <setup_dit3812.h>
#include <spooljob.h>

#define DFL_PRINTER "pp"
extern char *getenv();

/***===================================================================***/

static  int 	mn_debug;		/* debugging flag */

static	char	*command=	NULL;
static	char	*files=		NULL;
static  int	hdr_fieldlen=	0;

/***===================================================================***/

static	char	*pmp_debug=	NULL;
static	char	*job_debug=	NULL;
static	char	*header=	NULL;
static	char	*hdr_layout=	NULL;
static	char	*error_out=	NULL;
static	char	*error_level=	NULL;
static	char	*hdr_fields=	NULL;
static	int	hdr_filelen=	0;
static	int	copies=		1;
static	int	elsewhere=	FALSE;
static	char	*hdr_message=	NULL;
static	char	*job_type=	"ditroff-output";
static  int	printascii=	0;

/***============================================================***/

#define EQN_COMMAND	"/usr/ibm/eqn"
#define TBL_COMMAND	"/usr/bin/tbl"
#define HEADER_COMMAND	"/usr/ibm/ptroff"
#define TROFF_COMMAND	"/usr/ibm/troff"
#define PIC_COMMAND	"/usr/ibm/pic"
#define OUTPUT_COMMAND	"lpr -n -h "

#define INVOKED			0x0001
#define NEEDS_PTR_TYPE		0x0002
#define NEEDS_PTR_NAME		0x0004
#define NEEDS_PTR_ORIENT	0x0008

#define PIC		0
#define TBL		1
#define EQN		2
#define TROFF		3
#define HEADER		4
#define OUTPUT		5
#define LAST		5

struct	{
	   char		*name;
	   char		*args;
	   int		 len;
	   unsigned	 flags;
	} filters[] = {
	    {PIC_COMMAND,    NULL, 0, NEEDS_PTR_TYPE},
	    {TBL_COMMAND,    NULL, 0, NULL},
	    {EQN_COMMAND,    NULL, 0, NEEDS_PTR_TYPE},
	    {TROFF_COMMAND,  NULL, 0, NEEDS_PTR_TYPE|INVOKED|NEEDS_PTR_ORIENT},
	    {HEADER_COMMAND, NULL, 0, NULL},
	    {OUTPUT_COMMAND, NULL, 0, NEEDS_PTR_NAME|INVOKED}};


#define _name(f)	filters[f].name
#define _args(f)	filters[f].args
#define _flags(f)	filters[f].flags
#define _add_arg(f,a)	u_strappend(a," ",&filters[f].args,&filters[f].len)
#define _set_flag(n,f)	(filters[n].flags|=f)
#define _clear_flag(n,f)	(filters[n].flags&=(~f))
#define _flip_flag(n,f)	(filters[n].flags^=f)

/***============================================================***/

#define PORTRAIT NULL
#define INVERTED "-inv"
#define LEFT     "-l"
#define RIGHT	 "-r"

static char *ptr_name=   NULL;
static char *ptr_type=   "3812";
static char *ptr_orient= PORTRAIT;

/***===================================================================***/

#define OUT_FILTER "lpr -P%s -h -%s"

static FILE *infil;
static FILE *outfil;

/***============================================================***/

help_message(name)
register1 char	*name;
{
    D_ENTRY1(mn_debug,"help_message(%s)\n",name);
    fprintf(stderr,"usage: %s [options] [files]\n",name);
    fprintf(stderr,"invoke troff on 'files' and print results on IBM 3812 ");
    fprintf(stderr,"pageprinter\n");
    fprintf(stderr,"some options are (see ptroff(1) for full list):\n");
    fprintf(stderr,"	-c n		print 'n' copies (not collated)\n");
    fprintf(stderr,"	-E 		Invoke formatter \"Elsewhere\"\n");
    fprintf(stderr,"	-J		suppress Job header page\n");
    fprintf(stderr,"	-L 		print page landscape (sideways)\n");
    fprintf(stderr,"	-M message	print 'message' on header page\n");
    fprintf(stderr,"	-P printer	send output to 'printer'\n");
    fprintf(stderr,"	-t		send output to stdout (*not* text)\n");
    fprintf(stderr,"%s also accepts any options recognized by troff\n",name);
}
    
/***============================================================***/

static char	*banner_pages[]=	{
    						"banner",
						"box",
						"development",
						NULL
					};

static char	*error_reports[]=	{
						"mail",
						"message",
						"trailer",
						NULL
					};

static char	*error_levels[]=	{
						"all",
						"warning",
						"error",
						"none",
						NULL
					};

ErrorMessages(setting)
register3 char	*setting;
{
register1 char  *where;
register2 char	*level;

    D_ENTRY1(mn_debug,"ErrorMessages(%s)\n",setting);
    where= level= StrDup(setting);
    while ((*level!='\0')&&(*level!=',')) {
	level++;
    }
    if (*level==',') {
	*level++= '\0';
    }
    if (*where=='\0') {
	error_out= UNSPEC_ERROR_OUT;
    }
    else if (!u_SelectStr(error_reports,where,&error_out)) {
	error1("Don't know how to report errors to \"%s\"\n",where);
	if (error_out) 
	    action1("reporting to %s\n",error_out);
	else
	    action("using default error report\n");
    }
    if (*level=='\0') {
	error_level= UNSPEC_ERROR_LEVEL;
    }
    else if (!u_SelectStr(error_levels,level,&error_level)) {
	error1("Illegal error level %s requested\n",level);
	if (error_level)
	    action1("using error level %s\n",error_level);
	else
	    action("using default error level\n");
    }
    RETURN(TRUE);
}

/***===================================================================***/

ParseArgs(argc,argv)
register2 	int	argc;
register3	char	*argv[];
{
register1 	int	ch;
register4 	int	tmp;
register5	char	*tmpstr;
		int	len;
extern 		char	*optstring;
extern 		char	*optarg;
extern 		int	optind;
extern 		int	optional;

    D_ENTRY2(mn_debug,"ParseArgs(%d,%x)\n",argc,argv);
    optional='?';
    while((ch=getopt(argc,argv,"ac:d:D?e?E?F:fH:h:ij?JL?M:m:n:o:P:p?qr:S:T:t?uX:"))!=EOF){
       switch (ch) {
#ifdef DEBUG_UTILS
	  when 'D':	if (optarg)
				pmp_debug= optarg;
			else		
				pmp_debug= UNSPEC_PDEBUG;
	  when 'j':
			if (optarg)
				job_debug= optarg;
			else
				job_debug= UNSPEC_DEBUG;
#endif DEBUG_UTILS
	  when 'c':
			copies=  atoi(optarg);
			if (copies<1) {
			    warning1("Illegal argument \"%s\" to -c flag\n",optarg);
			    action("resetting to 1 copy\n");
			    copies= 1;
			}
	  when 'e':	if (optarg) {
	      		    if (strncmp(optarg,"qn",2)==0) {
				if (optarg[2]!='\0') {
				     _add_arg(EQN,&optarg[2]);
				     _set_flag(EQN,INVOKED);
				}
				else _flip_flag(EQN,INVOKED);
			    }
			    else	ErrorMessages(optarg);
			}
	  		else		ErrorMessages(UNSPEC_ERROR_OUT);
	  when 'E':	job_type=	"ditroff-source";
	  		elsewhere= !elsewhere;
			if (optarg)	ptr_name= optarg;

	  when  'i':	/* passed through to troff w/o arg */
	  or 	'a':
	  or    'q':
	  or	'u':
	  or	'n':	/* passed through to troff w/argument */
	  or    'd':
	  or	'o':
	  or	'r':
	  or    'm':	
	  or	'F':	tmpstr= u_malloc(strlen(optarg)+3);
			sprintf(tmpstr,"-%c%s",ch,optarg);
			_add_arg(TROFF,tmpstr);
			u_free(tmpstr);
			if ((ch=='m')&&StrMatch(optarg,"e")) {
			    _add_arg(TROFF,"-rv3812");
			}
			if (ch=='a') {
			    printascii++;
			    _clear_flag(OUTPUT,INVOKED);
			}			
	  when 'f':
	  or   's':	
	  or   'w':	warning1("%c flag meaningless\n",ch);
			action("ignored.\n");

	  when 't':	if (optarg) {
	      		    if (strncmp(optarg,"bl",2)==0) {
				if (optarg[2]!='\0') {
				     _add_arg(TBL,&optarg[2]);
				     _set_flag(TBL,INVOKED);
				}
				else _flip_flag(TBL,INVOKED);
			    }
			    else {
				error1("unknown option \"t%s\"\n",optarg);
				help_message(argv[0]);
				exit(1);
			    }
			}
			else  {
			    _clear_flag(OUTPUT,INVOKED);
			}
	  when 'T':	ptr_type=	optarg;
	  when 'p':	if (optarg) {
	      		    if (strncmp(optarg,"ic",2)==0) {
				if (optarg[2]!='\0') {
				     _add_arg(PIC,&optarg[2]);
				     _set_flag(PIC,INVOKED);
				}
				else _flip_flag(PIC,INVOKED);
			    }
			    else {
				error1("unknown option \"-p%s\"\n",optarg);
				help_message(argv[0]);
				exit(1);
			    }
			}
			else  {
			    error("unknown option \"-p\"\n");
			    help_message(argv[0]);
			    exit(1);
			}
	  when 'P':	ptr_name=	optarg;
	  when 'h':	header=		optarg;
	  when 'H':	if (!u_SelectStr(banner_pages,optarg,&hdr_layout)) {
	      		    warning1("Illegal banner %s requested\n",optarg);
			    if (hdr_layout) 
				action1("using %s banner page\n",hdr_layout);
			    else 
				action("using default page instead\n");
			}
	  when 'J':	hdr_layout=	"no_header";
	  when 'L':	if (!optarg) 
	      		    ptr_orient= RIGHT;
			else switch (optarg[0]) {
			    when 'u':	ptr_orient= PORTRAIT;
			    when 'i':	ptr_orient= INVERTED;
			    when 'l':	ptr_orient= LEFT;
			    when 'r':	ptr_orient= RIGHT;
			    otherwise:
				warning1("unknown orientation '%c'\n",
								  optarg[0]);
				action("switching to right landscape\n");
				ptr_orient= RIGHT;
			}
	  when 'M':	hdr_message=	optarg;
	  when 'X':	u_strappend(optarg,",",&hdr_fields,&hdr_fieldlen);
	  otherwise:	help_message(argv[0]);
			exit(1);
       }
    }
    len=		0;
    command=	NULL;
    for (tmp=0;tmp<optind;tmp++) {
	u_strappend(argv[tmp]," ",&command,&len);
    }
    RETURN(TRUE);
}

/***============================================================***/

add_files(argc,argv,cmd,len)
int	  argc;
char	 *argv[];
char	**cmd;
int	 *len;
{
extern	int	optind;
int	  knt;

    D_ENTRY4(mn_debug,"add_files(%d,0x%x,0x%x,0x%x)\n",argc,argv,cmd,len);
    for (knt=optind;knt<argc;knt++) {
	u_strappend(argv[knt]," ",cmd,len);
	if (!header) {
	    u_strappend(argv[knt]," ",&files,&hdr_filelen);
	}
    }
    if (header) {
	files= header;
	hdr_filelen= strlen(header)+1;
    }
    RETURN(TRUE);
}

/***============================================================***/

add_filter(ndx,cmd,len)
register1 int	  ndx;
register2 char	**cmd;
register3 int	 *len;
{
register4 char	 *str;
register5 int	  found;
static	  char	 *ptr_tyflag= NULL;
static	  char	 *ptr_nmflag= NULL;
	  char	 *ptr_tmpstr= NULL;

    D_ENTRY3(mn_debug,"add_filter(%d,0x%x,0x%x)\n",ndx,cmd,len);
    if (_flags(ndx)&NEEDS_PTR_TYPE) {
	found= FALSE;
	str=   _args(ndx);
	while (str= index(str,'T')) {
	    if (*(str-1)=='-') {
		found= TRUE;
		break;
	    }
	}
	if (!found) {
	    if (!ptr_tyflag) {
		ptr_tyflag= u_malloc(strlen(ptr_type)+3);
		sprintf(ptr_tyflag,"-T%s",ptr_type);
	    }
	    /* Add orientation info to ptr_type if filters NEEDS_PTR_ORIENT, 
	     * but store it in tmpstr, rather than tyflag, so that
	     * tyflag isn't permanently changed for other filters that 
	     * won't understand the orientation info.
	     */
	    if (_flags(ndx)&NEEDS_PTR_ORIENT) {
	        ptr_tmpstr= u_malloc(strlen(ptr_tyflag)+strlen(ptr_orient)+3);
	        sprintf(ptr_tmpstr,"%s%s", ptr_tyflag, ptr_orient);
	        _add_arg(ndx,ptr_tmpstr);
	    } else {
		_add_arg(ndx,ptr_tyflag);
	    }
	}
    }
    if (_flags(ndx)&NEEDS_PTR_NAME) {
	found= FALSE;
	str=   _args(ndx);
	while (str= index(str,'P')) {
	    if (*(str-1)=='-') {
		found= TRUE;
		break;
	    }
	}
	if (!found) {
	    if (!ptr_nmflag) {
		ptr_nmflag= u_malloc(strlen(ptr_name)+3);
		sprintf(ptr_nmflag,"-P%s",ptr_name);
	    }
	    _add_arg(ndx,ptr_nmflag);
	}
    }
    if ((_flags(ndx)&INVOKED)&&(cmd)&&(len)) {
	u_strappend(_name(ndx),"|",cmd,len);
	u_strappend(_args(ndx)," ",cmd,len);
    }
    RETURN(TRUE);
}


/***===================================================================***/

SetupIO(argc,argv)
register2 int	 argc;
register3 char	*argv[];
{
extern	    int	 optind;
extern	    FILE	*popen();
register1   int	 knt;
char	   *incmd=	NULL;
int	    inlen=	0;
char	   *outcmd= NULL;
int	    outlen=0;
int	    files_listed= FALSE;

    D_ENTRY2(mn_debug,"SetupIO(%d,0x%x)\n",argc,argv);

    for (knt=0;knt<HEADER;knt++) {
	if (!elsewhere) 
	    add_filter(knt,&incmd,&inlen);
	else
	    add_filter(knt,NULL,NULL);
	if ((!files_listed)&&(_flags(knt)&INVOKED)&&(!elsewhere)) {
	    add_files(argc,argv,&incmd,&inlen);
	    files_listed= TRUE;
	}
    }

    if (elsewhere) {
	u_strappend("soelim","|",&incmd,&inlen);
	add_files(argc,argv,&incmd,&inlen);
    }
    if (mn_debug) fprintf(stderr, "incmd = %s\n", incmd );
    infil= popen(incmd,"r");

    if (!infil) {
	fatal("couldn't set up input stream\n");
    }

    if ((knt=getc(infil))==EOF) {
	information("ptroff: empty input file\n");
	exit(1);
    }
    else ungetc(knt,infil);

    for (knt=OUTPUT;knt<=LAST;knt++) {
	add_filter(knt,&outcmd,&outlen);
    }
    if (outcmd)  {
        if (mn_debug) fprintf(stderr, "outcmd = %s\n", outcmd );
	outfil= popen(outcmd,"w");
    } else
	outfil= stdout;

    if (!outfil) {
	fatal("couldn't set up output stream\n");
    }
    RETURN(TRUE);
}

/***============================================================***/

CountWords(str)
register1 char *str;
{
register2 int   words= 0;

    D_ENTRY1(mn_debug,"CountWords(%s)\n",str);
    while (isspace(*str)) {
	str++;
    }
    while (*str!='\0') {
	words++;
	while ((*str!='\0')&&(!isspace(*str))) {
	    str++;
	}
	while (isspace(*str)) {
	    str++;
	}
    }
    RETURN(words);
}

/***============================================================***/

GetEnvironDefaults(argc,argv)
register5 int	  *argc;
register6 char	 **argv[];
{
register1 char	  *defaults;
register2 char	 **default_v;
register3 int	   knt;
register4 int	   arg_knt;

    D_ENTRY2(mn_debug,"GetEnvironDefaults(0x%x,0x%x)\n",argc,argv);
    defaults= getenv("PTROFF_DEFAULTS");
    if (!defaults) 
	RETURN(FALSE);
    *argc+= CountWords(defaults);
    default_v= (char **)u_calloc(*argc,sizeof(char *));
    while (isspace(*defaults)) 
	defaults++;
    knt= 0;
    default_v[knt++]= *argv[0];
    while (*defaults!='\0') {
	default_v[knt++]= defaults;
	while ((*defaults!='\0')&&(!isspace(*defaults))) {
	    defaults++;
	}
	if (isspace(*defaults)) {
	    *defaults='\0';
	    defaults++;
	}
	while (isspace(*defaults)) {
	    defaults++;
	}
    }
    arg_knt= 1;
    while (knt<*argc) {
	default_v[knt++]= (*argv)[arg_knt++];
    }
    *argv= default_v;
    RETURN(TRUE);
}

/***===================================================================***/

Init(argc,argv)
int  argc;
char *argv[];
{

    D_ENTRY(mn_debug,"Init()\n");
    GetEnvironDefaults(&argc,&argv);
    ParseArgs(argc,argv);
    if (!ptr_name) 
	ptr_name= DFL_PRINTER;
    if (!error_out) 
	error_out= DFL_ERRORS;
    SetupIO(argc,argv);
    if (!printascii) 
	PrintFormat();
    RETURN(TRUE);
}

/***===================================================================***/

PrintFormat()
{
register1 char *tokens= SJ_ENVTOKENS;

    D_ENTRY(mn_debug,"PrintFormat()\n");
    env_HdrPrint(outfil,SJ_ENVNAME,tokens);
    if (hdr_layout)
       env_StrPrint(outfil,"hdr_layout",hdr_layout,tokens);
    if (hdr_message)
       env_StrPrint(outfil,"hdr_message",hdr_message,tokens);
    if (hdr_fields)
       env_SetPrint(outfil,hdr_fields,tokens);
    if (command)
       env_StrPrint(outfil,"command",command,tokens);
    if (files)
       env_StrPrint(outfil,"files",files,tokens);
    if (error_out)
       env_StrPrint(outfil,"error_messages",error_out,tokens);
    if (error_level)
       env_StrPrint(outfil,"error_level",error_level,tokens);
    if (copies)
       env_IntPrint(outfil,"copies",copies,tokens);
    if (pmp_debug)
       env_StrPrint(outfil,"pmp_debug",pmp_debug,tokens);
    if (job_debug)
       env_StrPrint(outfil,"job_debug",job_debug,tokens);
    env_StrPrint(outfil,"job_type",job_type,tokens);
    if (elsewhere) {
	if (_flags(EQN)&INVOKED) 
	    env_StrPrint(outfil,"eqn_args",_args(EQN),tokens);
	if (_flags(TBL)&INVOKED) 
	    env_StrPrint(outfil,"tbl_args",_args(TBL),tokens);
	if (_flags(PIC)&INVOKED) 
	    env_StrPrint(outfil,"pic_args",_args(PIC),tokens);
	env_StrPrint(outfil,"troff_args",_args(TROFF),tokens);
    }
    env_EndPrint(outfil,tokens);
    RETURN(TRUE);
}

/***===================================================================***/

Print()
{
register1 int ch;

   D_ENTRY(mn_debug,"Print()\n");
   while ((ch=getc(infil))!=EOF) {
       putc(ch,outfil);
   }
   RETURN(TRUE);
}

/***===================================================================***/

main(argc,argv)
register2 int  argc;
register3 char *argv[];
{

    Init(argc,argv);
    Print();
    pclose(infil);
    pclose(outfil);
    return(0);
}

