/* Text based printer configuration for the RHS install program */
/* Michael Fulbright - 1997                                     */
/*                                                              */
/* Mon Apr 14 1997 - fixed up for inclusion into 4.2 install    */
/* Mon Apr 14 1997 - Erik broke everything -- blame him		*/
/*                                                              */
/* Last editted by msf - 11:10 Mon Apr 22 1997                  */

#include <alloca.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <ctype.h>
#include <fcntl.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>

#include <newt.h>

#include "devices.h"
#include "install.h"
#include "log.h"
#include "printercfg.h"

/* db of all entries in the printerdb file */
/* we reallocate as we read entries, so we keep numread and numallocated */
DBEntry **thedb;
int numdb, numdb_alloc;

/* root_path is the base of any path we use. During install the actual */
/* system is mounted under something like /mnt, so we have to take this */
/* into account when creating system files for the final, installed sys */
char *root_path;

/* keep up with whether we actually accomplished anything or not */
int install_return_code;

#define NUM_PAPERSIZES 5
static char *PaperSizes[NUM_PAPERSIZES] = {
  "letter", "legal", "ledger", "a3", "a4" };

/* not sure what we're calling these yet */
#define NEXT_LABEL "Next"
#define PREV_LABEL "Previous"

/*******************************************/
/* Some useful utility routines come first */
/*******************************************/

/* convert the character sequence '\' \n' into a true '\n' */
/* handy for treating a string which contains the continuation char '\' */
static void insert_newlines( char *s ) {
    char *p, *q;
    char *tmp;
    
    if (strlen(s)<2)
	return;
    
    tmp = strdup( s );
    for (p=tmp, q=s; *(p+1); p++, q++)
	if (*p == '\\' && *(p+1)=='n') {
	    *q = '\n';
	    p++;
	} else
	    *q = *p;
    
    *q = '\000';
    free(tmp);
}
	

/* clean up that string */
static void trim_whitespace( char *s ) {
    char *f, *l, *p, *q;

    if (!(*s))
	return;
    
    for (f=s; *f && isspace(*f); f++) ;

    if (!*f) {
	*s = '\0';
	return;
    }
    
    for (l=f+strlen(f)-1; isspace(*l) ; l--)
    *l = '\0';
	
    q = s, p = f;
    while (*p)
	*q++ = *p++;
    
    *q = '\0';
}


/* set all values of a PCEntry to sane values */
void initialize_PCEntry( PCEntry *pcentry ) {
    pcentry->Queue=NULL;
    pcentry->SpoolDir=NULL;
    pcentry->Device=NULL;
    pcentry->IF=NULL;
    pcentry->AF=NULL;
    pcentry->Type=NULL;
    pcentry->db=NULL;
    pcentry->Resolution=NULL;
    pcentry->PaperSize=NULL;
    pcentry->AutoEOF=NULL;
    pcentry->BitsPerPixel=NULL;
    pcentry->CRLF=NULL;
    pcentry->RevPages=NULL;
    pcentry->RemoteHost=NULL;
    pcentry->RemoteQueue=NULL;
    pcentry->SMBHost=NULL;
    pcentry->SMBHostIP=NULL;
    pcentry->SMBShare=NULL;
    pcentry->SMBUser=NULL;
    pcentry->SMBPasswd=NULL;
}

/* allocate a new PrintCap Entry */
static PCEntry *new_PCEntry() {
    PCEntry *pcentry;
    
    pcentry = (PCEntry *) malloc(sizeof(PCEntry));
    initialize_PCEntry( pcentry );
    return pcentry;
}

/* duplicate an existing PrintCap Entry */
/* all strings are REALLOCATED, so new PCEntry is not dependent on original */
static PCEntry *dup_PCEntry( PCEntry *orig ) {
    PCEntry *pcentry;
    
    pcentry = new_PCEntry();
    
    if (orig->Queue)
	pcentry->Queue=strdup(orig->Queue);
    if (orig->SpoolDir)
	pcentry->SpoolDir=strdup(orig->SpoolDir);
    if (orig->Device)
	pcentry->Device=strdup(orig->Device);
    if (orig->IF)
	pcentry->IF=strdup(orig->IF);
    if (orig->AF)
	pcentry->AF=strdup(orig->AF);
    if (orig->Type)
	pcentry->Type=strdup(orig->Type);
    pcentry->db=orig->db;
    if (orig->Resolution)
	pcentry->Resolution=strdup(orig->Resolution);
    if (orig->PaperSize)
	pcentry->PaperSize=strdup(orig->PaperSize);
    if (orig->AutoEOF)
	pcentry->AutoEOF=strdup(orig->AutoEOF);
    if (orig->BitsPerPixel)
	pcentry->BitsPerPixel=strdup(orig->BitsPerPixel);
    if (orig->CRLF)
	pcentry->CRLF=strdup(orig->CRLF);
    if (orig->RevPages)
	pcentry->RevPages=strdup(orig->RevPages);
    if (orig->RemoteHost)
	pcentry->RemoteHost=strdup(orig->RemoteHost);
    if (orig->RemoteQueue)
	pcentry->RemoteQueue=strdup(orig->RemoteQueue);
    if (orig->SMBHost)
	pcentry->SMBHost=strdup(orig->SMBHost);
    if (orig->SMBHostIP)
	pcentry->SMBHostIP=strdup(orig->SMBHostIP);
    if (orig->SMBShare)
	pcentry->SMBShare=strdup(orig->SMBShare);
    if (orig->SMBUser)
	pcentry->SMBUser=strdup(orig->SMBUser);
    if (orig->SMBPasswd)
	pcentry->SMBPasswd=strdup(orig->SMBPasswd);
    
    return pcentry;
}



/* Free an existing PrintCap Entry */
static void free_PCEntry( PCEntry *pcentry) {
    free(pcentry->Queue);
    free(pcentry->SpoolDir);
    free(pcentry->Device);
    free(pcentry->IF);
    free(pcentry->AF);
    free(pcentry->Type);
    free(pcentry->Resolution);
    free(pcentry->PaperSize);
    free(pcentry->AutoEOF);
    free(pcentry->BitsPerPixel);
    free(pcentry->CRLF);
    free(pcentry->RevPages);
    free(pcentry->RemoteHost);
    free(pcentry->RemoteQueue);
    free(pcentry->SMBHost);
    free(pcentry->SMBHostIP);
    free(pcentry->SMBShare);
    free(pcentry->SMBUser);
    free(pcentry->SMBPasswd);
    free(pcentry);
}


/* Read lines from file, ignoring lines starting with a '#' and */
/* observing continuation lines (lines which end with a '\')    */
/* All leading and trailing spaces are removed, as well as \n   */
/* Returns zero if no more input available                      */
static char *pc_getline(FILE *file) {
    int done;
    char buf[256];
    char *fresult;
    char *line;
    int len;
    
    line = NULL;
    done=0;
    while (!done) {
	fresult=fgets( buf, 256, file );
	if (!fresult)
	    return NULL;
	
	trim_whitespace( buf );
	
	if (! (*buf) )
	    continue;
	
	if (*buf == '#')
	    continue;
	
	len = strlen( buf );
	if ( *(buf+len-1) != '\\' )
	    done = 1;
	else {
	    /* silly rule - make sure line ends with a space */
	    if ( len > 2 && *(buf+len-2) != ' ' ) {
		*(buf+len-1) = ' ';
		*(buf+len) = '\000';
	    } else
		*(buf+len-1) = '\000';
	}

	if (!line)
	    line = strdup(buf);
	else {
	    line = (char *) realloc( line, strlen(line) + strlen (buf) + 1 );
	    strcpy( line+strlen(line), buf );
	}
    }
    
    return line;
}

/* strips out the value of a Parameter in the printer db file */
static char *getfield( char *s ) {
    char *f, *p;
    
    f = strchr(s, ':');
    if (!f)
	return NULL;
    
    p = strdup(f+1);
    trim_whitespace(p);
    
    if (! (*p))
	return NULL;
    else
	return p;
}

/* looks for exactly flds fields, with each field enclosed in a {} pair. */
static int parsefields( char *s, char **flds) {
    char *f, *l;
    char *p;
    int n;
    int i;
    
    flds[0] = NULL;
    flds[1] = NULL;
    flds[2] = NULL;
    n = 0;
    
    p = s;
    for (i=0; i<3; i++) {
	f = strchr(p, '{');
	if (!f)
	    return n;
	
	l = strchr(f, '}');
	if (!l)
	    return n;
	
	flds[n] = (char *) malloc( l-f+2 );
	strncpy( flds[n], f+1, l-f-1 );
	*((flds[n])+(l-f)-1) = '\000';
	
	p = l+1;
	n++;
    }
    return n;
}

static int dbCompare(const void * a, const void * b) {
    const DBEntry * const *first = a;
    const DBEntry * const *second = b;

    return strcmp((*first)->Descr, (*second)->Descr);
}

/* Read the printer database dbpath into memory */
/* returns non-zero on error */
static int read_printer_db( char *dbpath ) {
    FILE *dbfile;
    char *line;
    char *tmpstr;
    char *fields[3];
    int nfield;
    DBEntry *entry;
    int nbpp, nres;
    int nbpp_alloc, nres_alloc;
    
    tmpstr=malloc(strlen(dbpath)+strlen(root_path)+2);
    strcpy(tmpstr,root_path);
    strcat(tmpstr,dbpath);
    if ((dbfile=fopen(tmpstr, "r")) == NULL) {
	free(tmpstr);
	return -1;
    }
    free(tmpstr);

    /* loop till we find the start of an entry */
    /* we obviously want to ignore any comments on the way */
    numdb = numdb_alloc = 0;
    while ((line=pc_getline(dbfile))) {
	if (!strncmp(line, "StartEntry:", 11)) {
	    entry = (DBEntry *) malloc( sizeof(DBEntry) );
	    entry->Entry = getfield(line);
	    entry->GSDriver = NULL;
	    entry->Descr = NULL;
	    entry->About = NULL;
	    entry->Resolution = NULL;
	    entry->ResDescr = NULL;
	    entry->BitsPerPixel = NULL;
	    entry->BppDescr = NULL;
	    
	    nres = nres_alloc = 0;
	    nbpp = nbpp_alloc = 0;
	    
	    free(line);
	    if (!entry->Entry) {
		free(entry);
		continue;
	    }

	    numdb++;
	    if (numdb >= numdb_alloc ) {
		numdb_alloc += 25;
		thedb=(DBEntry **)realloc(thedb,
					  sizeof(DBEntry *)*(numdb_alloc));
	    }

	    thedb[numdb - 1] = entry;
	    
	    while ((line=pc_getline(dbfile))) {
		if (!strncmp(line, "EndEntry", 8))
		    break;
		
		if (!strncmp(line, "GSDriver:", 9)) {
		    if (entry->GSDriver)
			break;
		    
		    entry->GSDriver=getfield(line);
		}

	      if (!strncmp(line, "Description:", 12)) {
		  if (entry->Descr)
		      break;

		  tmpstr=getfield(line);
		  nfield = parsefields(tmpstr, fields);
		  free(tmpstr);
		  
		  if (nfield == 1 )
		      entry->Descr=fields[0];
		  else
		      break;
	      }
	      
	      if (!strncmp(line, "About:", 6)) {
		  if (entry->About)
		      break;
		  
		  tmpstr=getfield(line);
		  nfield = parsefields(tmpstr, fields);
		  free(tmpstr);
		  if (nfield == 1) {
		      trim_whitespace(fields[0]);
		      insert_newlines(fields[0]);
		      entry->About=fields[0];
		  } else
		      break;
	      }

	      if (!strncmp(line, "Resolution:", 11)) {
		  tmpstr=getfield(line);
		  nfield = parsefields(tmpstr, fields);
		  free(tmpstr);
		  if (nfield == 3) {
		      nres++;
		      
		      if (nres >= nres_alloc) {
			  nres_alloc += 10;
			  entry->Resolution=
			    (char **)realloc(entry->Resolution,
					     nres_alloc*sizeof(char*));
			  entry->ResDescr=
			    (char **)realloc(entry->ResDescr,
					     nres_alloc*sizeof(char*));
		      }

		      entry->Resolution[nres-1]=
			(char *)malloc(strlen(fields[0])+strlen(fields[1])+10);
		      sprintf(entry->Resolution[nres-1],
			      "%sx%s", fields[0],fields[1]);

		      if (fields[2])
			  entry->ResDescr[nres-1]=strdup(fields[2]);
		      else
			  entry->ResDescr[nres-1]=NULL;
		  } else
		      break;
	      }

	      if (!strncmp(line, "BitsPerPixel:", 13)) {
		  tmpstr=getfield(line);
		  nfield = parsefields(tmpstr, fields);
		  free(tmpstr);
		  if (nfield == 2) {
		      nbpp++;
		      
		      if (nbpp >= nbpp_alloc) {
			  nbpp_alloc += 10;
			  entry->BitsPerPixel=
			    (char **)realloc(entry->BitsPerPixel,
					     nbpp_alloc*sizeof(char*));
			  entry->BppDescr=
			    (char **)realloc(entry->BppDescr,
					     nbpp_alloc*sizeof(char*));
		      }
		      
		      entry->BitsPerPixel[nbpp-1]=strdup(fields[0]);
		      
		      if (fields[1])
			  entry->BppDescr[nbpp-1]=strdup(fields[1]);
		      else
			  entry->BppDescr[nbpp-1]=NULL;
		  } else
		      break;
	      }
	      free(line);
	    }

	    entry->NumRes = nres;
	    entry->NumBpp = nbpp;
	} else	{
	    free(line);
	}
    }

    fclose(dbfile);

    qsort(thedb, numdb, sizeof(*thedb), dbCompare);

    return 0;
}

/* used by below routines - assumes everything is malloc'd          */
/* updates old to new if new is non-NULL                            */
/* frees the old value and points it at the new                     */
/* if new is NULL, means it wasnt updated and old remains unchanged */
static void update_changed( void *new, void **old ) {
    if (new) {
	if (*old) 
	    free(*old);
	*old = new;
    }
}

/* display summary line given by char * arg */
void show_printer_about( char *about ) {
    newtComponent infoform, infook, infobox, answer;
    
    newtOpenWindow(12, 4, 56, 15, "Printer Info");
    infoform = newtForm(NULL, NULL, 0);
    infobox=newtTextbox(3, 2, 50, 8, 
			NEWT_TEXTBOX_WRAP|NEWT_TEXTBOX_SCROLL);
    newtTextboxSetText( infobox, about );
    infook = newtButton( 23, 11, "Ok");
		
    newtFormAddComponents(infoform, infobox, infook, NULL);
    answer = newtRunForm( infoform );

    newtPopWindow();
    newtFormDestroy(infoform);
}

/* select the printer type you have */
/* return < 0 if user aborted */
/* returns index into thedb[] of the selected entry otherwise*/
static int select_printer_type( PCEntry *changes) {
    newtComponent ok, cancel, form, listbox, answer;
    struct newtExitStruct event;
    long i, sel;
    int formdone;
    char *origtype;

    if (changes->db && changes->db->Descr) {
	origtype = alloca(strlen(changes->db->Descr)+2);
	strcpy(origtype, changes->db->Descr);
    } else
	origtype = NULL;
    
    newtOpenWindow(10, 3, 60, 18, "Configure Printer");
    newtPushHelpLine("<F1> will give you information on a particular "
		     "printer type");
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponent(form, newtLabel(3, 1, "What type of printer "
					 "do you have?"));
    listbox = newtListbox(6, 3, 10, NEWT_FLAG_RETURNEXIT);
    for (i = 0; i < numdb; i++) {
	newtListboxAddEntry(listbox, thedb[i]->Descr, (void *)i);
	
	if (changes->db && changes->db->Entry &&
	    !strcmp(changes->db->Entry,thedb[i]->Entry)) {
	    newtListboxSetCurrent(listbox, i);
	}
    }

    /* if no selection yet, default to PostScript if it exists */
    if (changes->db==NULL || changes->db->Entry==NULL  ) {
	/* set this just in case we find nothing that matches */
	newtListboxSetCurrent(listbox, 0);
	for (i = 0; i < numdb; i++)
	    if (!strcmp(thedb[i]->Descr,"PostScript printer")) {
		newtListboxSetCurrent(listbox, i);
		break;
	    }
    }
    
    ok = newtButton( 15, 14, NEXT_LABEL);
    cancel = newtButton( 31, 14, PREV_LABEL);
    newtFormAddComponents( form, listbox, ok, cancel, NULL);
    newtFormAddHotKey(form, NEWT_KEY_F1); 
    
    formdone = 0;
    answer = NULL;
    while (!formdone) {
	newtFormRun(form, &event);
	
	if (event.reason == NEWT_EXIT_HOTKEY) {
	    if (event.u.key == NEWT_KEY_F12)
		formdone = 1;
	    else if (event.u.key == NEWT_KEY_F1) {
		show_printer_about(
		    thedb[(long)newtListboxGetCurrent(listbox)]->About);
	    }
	} else if (event.reason == NEWT_EXIT_COMPONENT) {
	    formdone = 1;
	    if (event.u.co == cancel)
		answer = cancel;
	}
    }
    
    sel = (long) newtListboxGetCurrent(listbox);
    
    newtPopWindow();
    newtPopHelpLine();
    newtFormDestroy(form);
	
    if ( answer == cancel )
	return -1;

    /* store new values */
    changes->db = thedb[sel];

    /* MAJOR HACK */
    /* if the printer is an HP, lets do stairstep correction */
    if (!strncmp(changes->db->Descr,"HP",2)) {
	if (changes->CRLF)
	    free(changes->CRLF);
	changes->CRLF=strdup("YES");
    } else {
	if (origtype) {
	    if (strcmp(origtype, changes->db->Descr)) {
		if (changes->CRLF)
		    free(changes->CRLF);
		changes->CRLF=strdup("NO");
	    }
	} else  {
	    if (changes->CRLF)
		free(changes->CRLF);
	    changes->CRLF=strdup("NO");
	}
    }

    return sel;
}    
    

/* select the paper size and printer resolution you want */
/* return < 0 if user aborted */
/* returns 0 otherwise */
static int select_paper_size_and_res( PCEntry *changes ) {
    newtComponent ok, cancel, form, text, papersz, res, answer, crlf;
    struct newtExitStruct event;
    long i;
    int j;
    int formdone;
    int xpos;
    int maxlen;

    char **reslines;
    char crlfret;
    
    /* there has to be a current selection for this to work! */
    if (changes->db == NULL)
	return -1;
    
    newtOpenWindow(13, 3, 54, 18, changes->db->Descr); 
    newtPushHelpLine("<F1> will give you information on this "
		     "printer driver.");
    form = newtForm(NULL, NULL, 0);
    text = newtTextbox(1, 1, 52, 2, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, 
		       "You may now configure the paper size and resolution "
		       "for this printer.");
    newtFormAddComponent(form, text);

    /* Paper size listbox */
    newtFormAddComponent(form, newtLabel(7, 4, "Paper Size"));
    papersz = newtListbox( 8, 5, 5, NEWT_FLAG_NOSCROLL );
    for (i=0; i < NUM_PAPERSIZES; i++) {
	newtListboxAddEntry( papersz, PaperSizes[i], (void *)i );
	if (changes->PaperSize &&
	    !strcmp(changes->PaperSize, PaperSizes[i]))
	    newtListboxSetCurrent(papersz, i);
    }

    /* make sure something is picked */
    if (changes->PaperSize==NULL)
	newtListboxSetCurrent(papersz, 0);

    /* Resolution */
    if ( changes->db->NumRes > 0) {
	maxlen = 0;
	reslines = (char **) alloca( changes->db->NumRes * sizeof(char *));
	for (i=0; i < changes->db->NumRes; i++) {
	    if (changes->db->ResDescr && changes->db->ResDescr[i]) {
		reslines[i]=alloca(strlen(changes->db->Resolution[i])+
				   strlen(changes->db->ResDescr[i])+15);
		/* we want the 'x' in the 5th column */
		xpos = (strchr(changes->db->Resolution[i],'x')-
			changes->db->Resolution[i]);
		for (j=0; j<5-xpos; j++)
		    reslines[i][j]=' ';
		reslines[i][j]='\000';
		strcat(reslines[i],changes->db->Resolution[i]);
		/* pad to 13 spaces */
		for (j=strlen(reslines[i]); j < 13; j++)
		    strcat(reslines[i], " ");
		strcat(reslines[i],changes->db->ResDescr[i]);
	    } else {
		reslines[i]=alloca(strlen(changes->db->Resolution[i])+2);
		strcat(reslines[i],changes->db->Resolution[i]);
	    }
	    if (strlen(reslines[i]) > maxlen)
		maxlen = strlen(reslines[i]);
	    
	}

	newtFormAddComponent(form, newtLabel(34, 4, "Resolution"));
	res = newtListbox( 37-maxlen/2, 5, 5, 0 );

	for (i=0; i < changes->db->NumRes; i++) {
	    newtListboxAddEntry( res, reslines[i], (void *)i );
	    if (changes->Resolution &&
		!strcmp(changes->Resolution, changes->db->Resolution[i]))
		newtListboxSetCurrent(res, i);
	}
    } else {
	newtFormAddComponent(form, newtLabel(34, 4, "Resolution"));
	res = newtListbox( 32, 5, 4, 0 );
	newtListboxAddEntry( res, "Default", (void *)0 );
	newtListboxSetCurrent(res, 0);
	reslines=NULL;
    }
    
    /* make sure something is picked */
    if (changes->Resolution==NULL)
	newtListboxSetCurrent(res, 0);

    /* add stair-step correction toggle */
    if (!strcmp("YES", changes->CRLF))
        crlf = newtCheckbox(11, 11, "Fix stair-stepping of text?", '*', NULL,&crlfret);
    else
        crlf = newtCheckbox(11, 11, "Fix stair-stepping of text?", ' ', NULL,&crlfret);
    
    ok = newtButton( 11, 13, NEXT_LABEL);
    cancel = newtButton( 31, 13, PREV_LABEL);
    newtFormAddComponents(form,papersz,res,crlf,ok,cancel,NULL);
    newtFormAddHotKey(form, NEWT_KEY_F1); 
    
    formdone = 0;
    answer = NULL;
    
    while (!formdone) {
	newtFormRun(form, &event);
	
	if (event.reason == NEWT_EXIT_HOTKEY) {
	    if (event.u.key == NEWT_KEY_F12)
		formdone = 1;
	    else if (event.u.key == NEWT_KEY_F1) {
		show_printer_about( changes->db->About );
	    }
	} else if (event.reason == NEWT_EXIT_COMPONENT) {
	    formdone = 1;
	    if (event.u.co == cancel)
		answer = cancel;
	}
    }


    if ( answer == cancel ) {
	newtPopHelpLine();
	newtPopWindow();
	newtFormDestroy(form);
	return -1;
    }

    i = (long) newtListboxGetCurrent(papersz);

    free(changes->PaperSize);
    changes->PaperSize=strdup(PaperSizes[i]);

    free(changes->Resolution);
    if (changes->db->NumRes > 0) {
	i = (long) newtListboxGetCurrent(res);
	changes->Resolution=strdup(changes->db->Resolution[i]);
    } else
	changes->Resolution=strdup("Default");

    if (changes->CRLF)
	free(changes->CRLF);
    
    if (crlfret == ' ')
	changes->CRLF=strdup("NO");
    else
	changes->CRLF=strdup("YES");
    
    newtPopHelpLine();
    newtPopWindow();
    newtFormDestroy(form);

    return 0;
}

/* pick the color depth, returns < 0 on cancel, 0 otherwise */
static int select_color_depth( PCEntry *changes ) {
    newtComponent ok, cancel, form, bpp, answer, text;
    struct newtExitStruct event;
    long i;
    int formdone, maxlen;

    char **bpplines;

    /* have to have a selection to work */
    if (changes->db == NULL)
	return -1;

    /* if only one color depth choice, do nothing */
    if ( changes->db->NumBpp == 0) {
	changes->BitsPerPixel=strdup("Default");
	return 0;
    }
    
    newtOpenWindow(10, 3, 60, 15, "Configure Color Depth");
    newtPushHelpLine("<F1> will give you information on this printer driver.");
    form = newtForm(NULL, NULL, 0);

    text = newtTextbox(1, 1, 58, 2, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, 
		       "You may now configure the color options "
		       "for this printer.");
    newtFormAddComponent(form, text);

    newtFormAddComponent(form, newtLabel(22, 3, "Bits Per Pixel"));
    if ( changes->db->NumBpp > 0) {
	maxlen = 0;
	bpplines = (char **) alloca(changes->db->NumBpp*sizeof(char *));
	for (i=0; i < changes->db->NumBpp; i++) {
	    if (changes->db->BppDescr && changes->db->BppDescr[i]) {
		bpplines[i]=alloca(strlen(changes->db->BitsPerPixel[i])+
				   strlen(changes->db->BppDescr[i])+15);
		bpplines[i][0]='\000';
		strcat(bpplines[i],changes->db->BitsPerPixel[i]);
		if (bpplines[i][1] == '\000') {
		    bpplines[i][1] = bpplines[i][0];
		    bpplines[i][0] = ' ';
		    bpplines[i][2] = '\000';
		}
		strcat(bpplines[i],"  ");
		strcat(bpplines[i],changes->db->BppDescr[i]);
	    } else {
		bpplines[i]=alloca(strlen(changes->db->BitsPerPixel[i])+2);
		strcpy(bpplines[i], changes->db->BitsPerPixel[i]);
	    }
	    
	    /* limit the length of the line */
	    if (strlen(bpplines[i]) > 54)
		bpplines[i][50] = '\000';

	    if (strlen(bpplines[i]) > maxlen)
		maxlen = strlen(bpplines[i]);
	}
	
	bpp = newtListbox( 29-maxlen/2, 4, 5, NEWT_FLAG_RETURNEXIT );
	for (i=0; i < changes->db->NumBpp; i++) {
	    newtListboxAddEntry(bpp,bpplines[i], (void *)i );
	    if (changes->BitsPerPixel &&
		!strcmp(changes->BitsPerPixel,changes->db->BitsPerPixel[i]))
		newtListboxSetCurrent(bpp, i);
	}
	
    } else {
	fprintf(stderr, "How did we get here, numbpp = 0!\n");
	return -1;
#if 0	
	bpp = newtListbox( 29-strlen("Default")/2, 5, 4,NEWT_FLAG_RETURNEXIT );
	newtListboxAddEntry( bpp, "Default", (void *)0 );
	newtListboxSetCurrent(bpp, 0);
	bpplines=NULL;
#endif
    }

    /* make sure something is set */
    if (changes->BitsPerPixel==NULL)
	newtListboxSetCurrent(bpp,0);

    ok = newtButton( 11, 10, NEXT_LABEL);
    cancel = newtButton( 36, 10, PREV_LABEL);
    newtFormAddComponents(form,bpp,ok,cancel,NULL);
    newtFormAddHotKey(form, NEWT_KEY_F1); 
    
    formdone = 0;
    answer = NULL;
    while (!formdone) {
	newtFormRun(form, &event);
	
	if (event.reason == NEWT_EXIT_HOTKEY) {
	    if (event.u.key == NEWT_KEY_F12)
		formdone = 1;
	    else if (event.u.key == NEWT_KEY_F1) {
		show_printer_about( changes->db->About );
	    }
	} else if (event.reason == NEWT_EXIT_COMPONENT) {
	    formdone = 1;
	    if (event.u.co == cancel)
		answer = cancel;
	}
    }

    if ( answer == cancel ) {
	newtPopHelpLine();
	newtPopWindow();
	newtFormDestroy(form);
	return -1;
    }

    free(changes->BitsPerPixel);
    if (changes->db->NumBpp > 0) {
	i = (long) newtListboxGetCurrent(bpp);
	changes->BitsPerPixel=strdup(changes->db->BitsPerPixel[i]);
    } else {
	fprintf(stderr, "How did we get here, numbpp = 0!\n");
	return -1;
#if 0	
	changes->BitsPerPixel=strdup("Default");
#endif
    }

    newtPopHelpLine();
    newtPopWindow();
    newtFormDestroy(form);
    
    return 0;
}

/* returns the full attribute of the selected printer filter */
/* returns < 0 if user cancelled selected                    */
static int select_filter( PCEntry *changes ) {
    
    int done;
    int stage;
    int abort;
    
    done = 0;
    stage = 1;
    abort = 0;
    while (!done) {

	switch (stage) {

	    /* select printer model/driver */
	  case 1:
	    if (select_printer_type(changes) < 0) {
		done = 1;
		abort = 1;
		break;
	    } else
		stage++;
	    break;

	    /* select paper size and resolution */
	  case 2:
	    if (select_paper_size_and_res( changes ) < 0) {
		stage--;
		break;
	    } else
		stage++;
	    break;

	    /* select color depth */
	  case 3:
	    if (select_color_depth( changes ) < 0) {
		stage--;
		break;
	    } else
		stage++;
	    break;

	    /* we made it and we're done */
	  case 4:
	    done = 1;

	}
    }

    if (abort)
	return -1;

    return 0;
}


/* return < 0 for user cancel */
static int get_local_info( PCEntry *changes ) {

    newtComponent answer, cancel, form, ok, device, text;
    char *device_result;
    char *devname;
    int detected[3];
    int i, result;
    int npos;

#if defined(__i386__) && !defined(TESTING)
    if (loadModule("lp", DRIVER_OTHER, DRIVER_MINOR_NONE, NULL)) 
	return INST_ERROR;

#endif

#if 0
    /* old way Erik did this */
    devMakeInode("lp0", "/tmp/lp0");
    devMakeInode("lp1", "/tmp/lp1");
    devMakeInode("lp2", "/tmp/lp2");
    
    /* do auto-detect of lp ports */
    detected[0] = ((result=open("/tmp/lp0", O_WRONLY|O_NONBLOCK)) != -1);
    if (result >= 0) close(result);
    detected[1] = ((result=open("/tmp/lp1", O_WRONLY|O_NONBLOCK)) != -1);
    if (result >= 0) close(result);
    detected[2] = ((result=open("/tmp/lp2", O_WRONLY|O_NONBLOCK)) != -1);
    if (result >= 0) close(result);

    unlink("/tmp/lp0");
    unlink("/tmp/lp1");
    unlink("/tmp/lp2");
#else
    /* different approach */
    /* do auto-detect of lp ports */
    devname=alloca(strlen(root_path)+strlen("/dev/lpX")+2);
    strcpy(devname,root_path);
    strcat(devname,"/dev/lpX");
    npos=strlen(devname)-1;
    devname[npos]='0';
    detected[0] = ((result=open(devname, O_WRONLY|O_NONBLOCK)) != -1);
    if (result >= 0) close(result);
    devname[npos]='1';
    detected[1] = ((result=open(devname, O_WRONLY|O_NONBLOCK)) != -1);
    if (result >= 0) close(result);
    devname[npos]='2';
    detected[2] = ((result=open(devname, O_WRONLY|O_NONBLOCK)) != -1);
    if (result >= 0) close(result);
#endif
    
#if defined(__i386__) && !defined(TESTING)
    removeModule("lp");
#endif

    newtOpenWindow(18, 4, 44, 16, "Local Printer Device");
    form = newtForm(NULL, NULL, 0);

    text = newtTextbox(1, 1, 40, 3, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text,
	"What device is your printer connected to (note that /dev/lp0 is "
	"equivalent to LPT1:)?");

    newtFormAddComponent(form, text);
    newtFormAddComponent(form, newtLabel(3, 7, "Auto-detected ports:"));
    newtFormAddComponent(form, newtLabel(6, 8, "/dev/lp0:"));
    newtFormAddComponent(form, newtLabel(6, 9, "/dev/lp1:"));
    newtFormAddComponent(form, newtLabel(6, 10, "/dev/lp2:"));
    
    for (i=0; i<3; i++) {
	if (detected[i])
	    newtFormAddComponent(form, newtLabel(18,8+i, "Detected"));
	else
	    newtFormAddComponent(form, newtLabel(18,8+i, "Not Detected"));
    }

    newtFormAddComponent(form, newtLabel(3, 5, "Printer Device:"));
    if (changes->Device)
	device=newtEntry(19, 5, changes->Device, 15, &device_result,
			 NEWT_FLAG_RETURNEXIT);
    else
	device=newtEntry(19, 5, "/dev/lp1", 15, &device_result,
			 NEWT_FLAG_RETURNEXIT);
    
    ok = newtButton( 7, 12, NEXT_LABEL);
    cancel = newtButton( 24, 12, PREV_LABEL);
    newtFormAddComponents( form, device, ok, cancel, NULL);
    answer = newtRunForm(form);

    if ( answer != cancel ) {
	free(changes->Device);
	changes->Device = strdup(device_result);
	result = 0;
    } else
	result = -1;

    newtPopWindow();
    newtFormDestroy(form);
    return result;
}


/* return < 0 for user cancel */
static int get_remote_info( PCEntry *changes ) {
    newtComponent answer, cancel, form, ok, rhost, rqueue, text;
    char *queue_result;
    char *host_result;
    int result;
    
    newtOpenWindow(19, 5, 42, 14, "Remote lpd Printer Options");
    form = newtForm(NULL, NULL, 0);

    text = newtTextbox(1, 1, 40, 5, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, 
	"To use a remote lpd print queue, you need to supply the "
	"hostname of the printer server and the queue name on that "
	"server which jobs should be placed in.");
    
    newtFormAddComponent(form, text);
    newtFormAddComponent(form, newtLabel(2, 7, "Remote hostname:"));
    newtFormAddComponent(form, newtLabel(2, 8, "Remote queue:"));

    if (changes->RemoteHost)
	rhost =newtEntry(19, 7, changes->RemoteHost, 20, &host_result, 0);
    else
	rhost =newtEntry(19, 7, "", 20, &host_result, 0);

    if (changes->RemoteQueue)
	rqueue =newtEntry(19, 8, changes->RemoteQueue, 20, &queue_result, 0);
    else
	rqueue =newtEntry(19, 8, "lp", 20, &queue_result, 0);

    ok = newtButton( 7, 10, NEXT_LABEL);
    cancel = newtButton( 24, 10, PREV_LABEL);
    newtFormAddComponents( form, rhost, rqueue, ok, cancel, NULL);
    answer = newtRunForm(form);

    if ( answer != cancel ) {
	free(changes->RemoteHost);
	changes->RemoteHost = strdup(host_result);
	free(changes->RemoteQueue);
	changes->RemoteQueue = strdup(queue_result);
	result = 0;
    } else
	result = -1;

    newtPopWindow();
    newtFormDestroy(form);
    return result;
}


/* return < 0 for user cancel */
static int get_smb_info( PCEntry *changes ) {
    newtComponent answer, cancel, form, ok, text;
    newtComponent host, hostip, share, user, passwd;
    
    int result;
    char *host_result;
    char *hostip_result;
    char *share_result;
    char *user_result;
    char *passwd_result;

    newtOpenWindow(10, 3, 60, 18, "LAN Manager Printer Options");
    form = newtForm(NULL, NULL, 0);

    text = newtTextbox(1, 1, 58, 6, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text,
	"To print to a LAN manager printer, you need to provide the "
	"LAN manager host name (this is not always the same as the machines "
	"TCP/IP hostname) and possibly the IP address of the print server, as "
	"well as the share name for the printer you wish to access and any "
	"applicable user name and password.");	

    newtFormAddComponent(form, text);
    
    newtFormAddComponent(form, newtLabel(10, 8, "LAN Manager Host:"));
    newtFormAddComponent(form, newtLabel(10, 9, "LAN Manager IP:"));
    newtFormAddComponent(form, newtLabel(10, 10, "Share Name:"));
    newtFormAddComponent(form, newtLabel(10, 11, "Username:"));
    newtFormAddComponent(form, newtLabel(10, 12, "Password:"));

    if (changes->SMBHost)
	host   = newtEntry(28, 8, changes->SMBHost, 15, &host_result, 0);
    else
	host   = newtEntry(28, 8, "", 15, &host_result, 0);
    if (changes->SMBHostIP)
	hostip = newtEntry(28, 9, changes->SMBHostIP, 15, &hostip_result, 0);
    else
	hostip = newtEntry(28, 9, "", 15, &hostip_result, 0);
    if (changes->SMBShare)
	share  = newtEntry(28, 10, changes->SMBShare, 15, &share_result, 0);
    else
	share  = newtEntry(28, 10, "", 15, &share_result, 0);
    if (changes->SMBUser)
	user   = newtEntry(28, 11, changes->SMBUser, 15, &user_result, 0);
    else
	user   = newtEntry(28, 11, "", 15, &user_result, 0);
    if (changes->SMBPasswd)
	passwd = newtEntry(28, 12, changes->SMBPasswd, 15, 
			   &passwd_result, NEWT_ENTRY_HIDDEN);
    else
	passwd = newtEntry(28, 12, "", 15, &passwd_result, NEWT_ENTRY_HIDDEN);

    ok = newtButton( 15, 14, NEXT_LABEL);
    cancel = newtButton( 35, 14, PREV_LABEL);
    newtFormAddComponents( form, host, hostip, 
			 share, user, passwd, ok, cancel, NULL);
    answer = newtRunForm(form);

    if ( answer != cancel ) {
	free(changes->SMBHost);
	changes->SMBHost = strdup(host_result);
	free(changes->SMBHostIP);
	changes->SMBHostIP = strdup(hostip_result);
	free(changes->SMBShare);
	changes->SMBShare = strdup(share_result);
	free(changes->SMBUser);
	changes->SMBUser = strdup(user_result);
	free(changes->SMBPasswd);
	changes->SMBPasswd = strdup(passwd_result);
	
	result = 0;
    } else
	result = -1;

    newtPopWindow();
    newtFormDestroy(form);
    return result;
}

/* return < 0 for user cancel */
static int get_std_info( PCEntry *changes ) {
    newtComponent answer, cancel, form, ok, queue, spool, text;
    char *queue_result;
    char *spool_result;
    int result;

    newtOpenWindow(14, 5, 52, 14, "Standard Printer Options");
    form = newtForm(NULL, NULL, 0);

    text = newtTextbox(1, 1, 50, 5, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, 
	"Every print queue (which print jobs are directed to) needs a "
	"name (often lp) and a spool directory associated with it. What "
	"name and directory should be used for this queue? ");
    
    newtFormAddComponent(form, newtLabel(2, 7, "Name of queue:"));
    newtFormAddComponent(form, newtLabel(2, 8, "Spool directory:"));
    if (changes->Queue)
	queue =newtEntry(20, 7, changes->Queue, 30, &queue_result, 0);
    else
	queue =newtEntry(20, 7, "lp", 30, &queue_result, 0);

    if (changes->SpoolDir)
	spool =newtEntry(20, 8, changes->SpoolDir, 30, &spool_result, 0);
    else
	spool =newtEntry(20, 8, "/var/spool/lpd/lp", 30, &spool_result, 0);

    ok = newtButton( 11, 10, NEXT_LABEL);
    cancel = newtButton( 27, 10, PREV_LABEL);
    newtFormAddComponents(form, text, queue, spool, ok, cancel, NULL);
    answer = newtRunForm(form);

    if ( answer != cancel ) {
	free(changes->Queue);
	changes->Queue = strdup(queue_result);
	free(changes->SpoolDir);
	changes->SpoolDir = strdup(spool_result);
	result = 0;
    } else {
	result = -1;
    }

    newtPopWindow();
    newtFormDestroy(form);
    return result;
}
 

static int query_config(void) {
    newtComponent answer, cancel, form, ok; 
    int result;

    newtOpenWindow(20, 7, 40, 7, "Add Printers");
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponent(form,
			 newtLabel(1, 1,
				   "Would you like to configure a printer?"));
    
    ok = newtButton( 6, 3, "Yes");
    cancel = newtButton( 25, 3, "No");
    newtFormAddComponents( form, ok, cancel, NULL);

    answer = newtRunForm(form);

    result = ( answer == cancel ? -1 : 0 );

    newtPopWindow();
    newtFormDestroy(form);
    return result;

}

static int query_add_more() {
    newtComponent answer, cancel, form, ok; 
    int result;

    newtOpenWindow(20, 7, 40, 7, "Add Printers");
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponent(form,
			 newtLabel(1, 1,
				   "Would you like to add another printer?"));
    
    ok = newtButton( 3, 3, "Yes");
    cancel = newtButton( 25, 3, "No");
    newtFormAddComponents( form, ok, cancel, NULL);

    answer = newtRunForm(form);

    result = ( answer == cancel ? -1 : 0 );

    newtPopWindow();
    newtFormDestroy(form);
    return result;

}


/* returns 1 if they want to quit, 0 otherwise */
static int you_are_sure(void) {
    newtComponent answer, cancel, form, ok, text1, text2; 
    int result;

    newtOpenWindow(19, 6, 42, 14, "Confirm Termination");
    form = newtForm(NULL, NULL, 0);
    
    text1 = newtTextbox(1, 1, 40, 3, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text1,
		       "If you choose 'Exit', you will return to the main "
		       "installation without a printer being configured. ");
    newtFormAddComponent(form, text1);

    text2 = newtTextbox(1, 5, 40, 3, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text2,
 		       "Choose 'Continue' to return to configuring "
		       "your printer.");
    newtFormAddComponent(form, text2);

    ok = newtButton( 6, 9 , "Exit");
    cancel = newtButton( 24, 9, "Continue");
    newtFormAddComponents( form, ok, cancel, NULL);

    answer = newtRunForm(form);

    result = ( answer == cancel ? 0 : 1 );

    newtPopWindow();
    newtFormDestroy(form);
    return result;

}


/* returns < 0 for user cancel, otherwise uses #defines in printercfg.h */
static int get_prn_connx( char *Type ) {
    newtComponent answer, cancel, form, ok, listbox;
    int sel;

    newtOpenWindow(23, 6, 33, 12, "Select Printer Connection");
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponent(form,
			 newtLabel(1, 1, "How is this printer connected?"));
    
    listbox = newtListbox(9, 3, 0, NEWT_FLAG_NOSCROLL | NEWT_FLAG_RETURNEXIT);
    newtListboxAddEntry(listbox, "Local", (void *)LOCAL_CONNX);
    newtListboxAddEntry(listbox, "Remote lpd", (void *)REMOTE_LPD_CONNX);
    newtListboxAddEntry(listbox, "LAN Manager", (void *)SMB_CONNX);

    if (Type)
	if (!strcmp(Type,"LOCAL"))
	    newtListboxSetCurrent(listbox, 0);
	else if (!strcmp(Type,"REMOTE"))
	    newtListboxSetCurrent(listbox, 1);
	else if (!strcmp(Type,"SMB"))
	    newtListboxSetCurrent(listbox, 2);
    
    ok = newtButton( 4, 7, NEXT_LABEL);
    cancel = newtButton( 17, 7, "Cancel");
    newtFormAddComponents(form, listbox, ok, cancel, NULL);

    answer = newtRunForm(form);

    if ( answer == cancel ) {
	newtPopWindow();
	newtFormDestroy(form);
	return -1;
    } else {
	sel = (long) newtListboxGetCurrent(listbox);
	newtPopWindow();
	newtFormDestroy(form);
	return sel;
    }
}

/* edit the PrintCap Entry pcentry */
/* return < 0 if user aborts or error occurs */
static int edit_pcentry( PCEntry **pcentry ) {

    int result;
    int stage;
    int done;
    int abort;
    PCEntry *changes;

    /* copy current into temporary */
    changes = dup_PCEntry( *pcentry );

    done = 0;
    abort = 0;
    stage = 0;

    while (!done) {

	switch (stage) {
	  case 0:
	    switch (get_prn_connx(changes->Type)) {
	      case -1:
		if (you_are_sure()) {
		    done = 1;
		    abort = 1;
		}
		break;
		
	      case LOCAL_CONNX:
		free(changes->Type);
		changes->Type = strdup("LOCAL");
		stage++;
		break;
	  
	      case REMOTE_LPD_CONNX:
		free(changes->Type);
		changes->Type = strdup("REMOTE");
		stage++;
		break;
		
	      case SMB_CONNX:
		free(changes->Type);
		changes->Type = strdup("SMB");
		stage++;
		break;
	  
	      default:
		break;
	    }
	    break;
	  
	  case 1:
    	    result=get_std_info(changes);
	    if (result < 0)
		stage--;
	    else 
		stage++;
	    break;

	  case 2:
	    if (!strcmp(changes->Type, "SMB"))
		result=get_smb_info(changes);
	    else if (!strcmp(changes->Type, "REMOTE"))
		result=get_remote_info(changes);
	    else if (!strcmp(changes->Type, "LOCAL"))
		result=get_local_info(changes);
	    else
		return -1;

	    if (result < 0)
		stage--;
	    else
		stage++;
	    break;

	  case 3:
	    result=select_filter(changes);
	    if (result < 0)
		stage--;
	    else
		stage++;
	    break;

	  case 4:
	    done = 1;
	    break;
	}
    }

    if (!abort) {
	free_PCEntry(*pcentry);
	*pcentry = changes;
	return 0;
    } else {
	free_PCEntry(changes);
	return -1;
    }
}

/* returns 0 on success, -1 on abort, and -2 if user wishes to re-edit */
static int verify_pcentry( PCEntry *pcentry ) {

    newtComponent form, ok, edit, cancel, answer;
    char tmpstr[256];
    int row;
    int done;
    int result;

    newtOpenWindow(10, 3, 60, 18, "Verify Printer Configuration");
    form = newtForm(NULL, NULL, 0);

    newtFormAddComponent(form, newtLabel(3, 1, 
	    "Please verify that this printer information is correct:"));

    row=3;
    sprintf(tmpstr, "Printer Type    : %s\n",pcentry->Type);
    newtFormAddComponent(form, newtLabel(5, row++, tmpstr));

    sprintf(tmpstr, "Queue           : %s\n",pcentry->Queue);
    newtFormAddComponent(form, newtLabel(5, row++, tmpstr));

    sprintf(tmpstr, "Spool directory : %s\n",pcentry->SpoolDir);
    newtFormAddComponent(form, newtLabel(5, row++, tmpstr));

    if (!strcmp(pcentry->Type, "LOCAL")) {
	sprintf(tmpstr, "Printer device  : %s\n",pcentry->Device);
	newtFormAddComponent(form, newtLabel(5, row++, tmpstr));
    } else if (!strcmp(pcentry->Type, "REMOTE")) {
	sprintf(tmpstr, "Remote Host     : %s\n",pcentry->RemoteHost);
	newtFormAddComponent(form, newtLabel(5, row++, tmpstr));
	sprintf(tmpstr, "Remote Queue    : %s\n",pcentry->RemoteQueue);
	newtFormAddComponent(form, newtLabel(5, row++, tmpstr));
    } else if (!strcmp(pcentry->Type, "SMB")) {
	if (pcentry->SMBHostIP && pcentry->SMBHostIP[0])
	    sprintf(tmpstr, "Server          : %s [%s]\n",
		    pcentry->SMBHost, pcentry->SMBHostIP);
	else
	    sprintf(tmpstr, "Server          : %s\n",pcentry->SMBHost);
	newtFormAddComponent(form, newtLabel(5, row++, tmpstr));
	sprintf(tmpstr, "Share           : %s\n",pcentry->SMBShare);
	newtFormAddComponent(form, newtLabel(5, row++, tmpstr));
	sprintf(tmpstr, "User            : %s  <Password hidden>\n",
		pcentry->SMBUser);
	newtFormAddComponent(form, newtLabel(5, row++, tmpstr));
    }

    sprintf(tmpstr, "Printer Driver  : %s\n",pcentry->db->Descr);
    newtFormAddComponent(form, newtLabel(5, row++, tmpstr));

    sprintf(tmpstr, "Paper Size      : %s\n",pcentry->PaperSize);
    newtFormAddComponent(form, newtLabel(5, row++, tmpstr));

    sprintf(tmpstr, "Resolution      : %s\n",pcentry->Resolution);
    newtFormAddComponent(form, newtLabel(5, row++, tmpstr));

    sprintf(tmpstr, "Bits Per Pixel  : %s\n",pcentry->BitsPerPixel);
    newtFormAddComponent(form, newtLabel(5, row++, tmpstr));


    ok = newtButton( 11, 14,   "Done");
    edit = newtButton( 26, 14, "Edit");
    cancel = newtButton( 41, 14, "Cancel");
    newtFormAddComponents( form, ok, edit, cancel, NULL);

    done = 0;
    result = 0;
    while (!done) {
	answer = newtRunForm(form);

	if ( answer == cancel ) {
	    if (you_are_sure()) {
		done = 1;
		result = -1;
	    }
	} else if (answer == edit) {
	    done = 1;
	    result = -2;
	} else {
	    done = 1;
	    result = 0;
	}
    }

    newtPopWindow();
    newtFormDestroy(form);

    return result;
}

/* given the path queue_path, we create all the required spool directory */
static int create_spool_dir( char *queue_path ) {
    int result, done;
    int oldmask;
    
    struct group *grent;
    gid_t lpgid = 0;
    
    FILE *groupfile;
    char groupfilename[80];
    

    oldmask=umask(0);
    result=mkdir(queue_path, 0755);
    if ( result < 0)
	if (errno != EEXIST)
	    return -1;
    umask(oldmask);
  
    strcpy(groupfilename, root_path);
    strcat(groupfilename, "/etc/group");
    if ((groupfile=fopen(groupfilename, "r"))==NULL)
	return -1;
    
    done=0;
    while (!done) {
	grent=fgetgrent(groupfile);
	if (grent==NULL)
	    return -1;
      
	if (!strcmp(grent->gr_name, "lp")) {
	    lpgid=grent->gr_gid;
	    done |= 2;
	}
    }

    fclose(groupfile);
  
    if (!done)
	return -1;
  
    result=chown(queue_path, 0, lpgid );
    if ( result < 0)
	return -1;

    return 0;
}


/* given the input spec file 'input', and the target output file 'output' */
/* we set the fields specified by fieldname to the values in fieldval     */
/* nval  is the number of fields to set                                   */
/* Doesnt currently catch error exec'ing sed yet                          */
static int create_config_file( char *input, char *output, 
			char **fieldname, char **fieldval, 
			int nval ) {
    int status;
    int infd, outfd;
    int childpid;
    int i, j;
    
    int oldmask;

    char **sedargs;
    char *sedcmd;
    
    oldmask = umask(0);
    
    outfd=creat(output, 0755 );
    umask(oldmask);
    if (outfd == -1)
	return -1;
  
    infd=open(input, O_RDONLY);
    if (infd == -1)
	return -1;

    /* we have to setup the args to sed */
    /* need the name to exec, plus 2 args per field, plus NULL at end */
    sedargs = (char **) malloc( (nval + 2) * sizeof( char * ) );
    if (!sedargs)
	return -1;

    sedargs[0] = "/bin/sed";
    sedargs[nval+1] = NULL;
    for (i=0; i<nval; i++) {
	sedargs[i+1] = (char *) malloc(25 + strlen(fieldname[i]) +
				       strlen(fieldval[i]));
	/* if we get an error, try to cleanup */
	if (!sedargs[i+1]) {
	    for (j=0; j<i; j++)
		if (sedargs[j+1])
		    free(sedargs[j+1]);
	    
	    if (sedargs)
		free(sedargs);
	    
	    return -1;
	}
	sprintf(sedargs[i+1], "-e s/@@@%s@@@/%s/g", fieldname[i], fieldval[i]);
    }

    sedcmd = malloc(strlen(root_path)+15);
    if (!sedcmd)
	return -1;
    
    strcpy(sedcmd, root_path);
    strcat(sedcmd, "/bin/sed");
    
    if (!(childpid = fork())) {
	close(0);
	close(1);
	dup2(infd, 0);
	dup2(outfd, 1);
	close(infd);
	close(outfd);
	execv(sedcmd, sedargs);
	exit(-1);
    }

    close(infd);
    close(outfd);

    waitpid(childpid, &status, 0);

    if (sedcmd)
	free(sedcmd);

    for (i=0; i<nval; i++)
	if (sedargs[i+1])
	    free(sedargs[i+1]);

    if (sedargs)
	free(sedargs);

    return 0;
}

/* copy master filter to the spool dir */
static int copy_master_filter( char *queue_path ) {
    int childpid, status;
    char *masterfilter, *cpcmd, *dest;
    
    masterfilter=malloc(strlen(PRINTER_FILTER_DIR)+strlen(root_path)+20);
    strcpy(masterfilter,root_path);
    strcat(masterfilter,PRINTER_FILTER_DIR);
    strcat(masterfilter,"master-filter");
    
    dest=malloc(strlen(queue_path)+20);
    strcpy(dest,queue_path);
    strcat(dest,"filter");
    
    cpcmd = malloc(strlen(root_path)+15);
    if (!cpcmd)
	return -1;
    
    strcpy(cpcmd, root_path);
    strcat(cpcmd, "/bin/cp");
    
    if (!(childpid = fork())) 
	execl(cpcmd, cpcmd, masterfilter, dest, NULL);

    waitpid(childpid, &status, 0);
    
    free(masterfilter);
    free(cpcmd);
    free(dest);
    return 0;
}

/* given a PrintCap Entry, create the spool dir and special */
/* rhs-printfilters related config files which are required */
static int configure_queue( PCEntry *pcentry ) {
    char *queue_path;
    char *output;
    char *input;
    char colorstr[80]; /* holds the '-dBitsPerPixel=nn' */
    
    char **fieldname;
    char **fieldval;
    int  nfield;

    FILE *printcap, *smbconfig;

    if (testing) return 0;
    
    if (pcentry->SpoolDir) {
	/* create the spooldir and set to root.lp ownership */
	queue_path=malloc(strlen(root_path)+strlen(pcentry->SpoolDir)+5);
	strcpy(queue_path, root_path);
	strcat(queue_path, pcentry->SpoolDir);
	strcat(queue_path, "/");
	if (create_spool_dir(queue_path) < 0)
	    return -1;
	
	/* make the cfg files we need */
	output=malloc(strlen(queue_path)+strlen("general.cfg")+2);
	strcpy(output,queue_path);
	strcat(output,"general.cfg");
	input=malloc(strlen(root_path)+strlen(PRINTER_FILTER_DIR)+
		     strlen("general.cfg.in")+2);
	strcpy(input,root_path);
	strcat(input,PRINTER_FILTER_DIR);
	strcat(input,"general.cfg.in");

	/* setup the field arrays */
	nfield = 4;
	fieldname = (char **) malloc( nfield * sizeof(char *) );
	fieldname[0] = "desiredto";
	fieldname[1] = "papersize";
	fieldname[2] = "printertype";
	fieldname[3] = "ascps_trans";
	
	fieldval = (char **) malloc( nfield * sizeof(char *) );

	if (strcmp(pcentry->db->GSDriver, "TEXT"))
	    fieldval[0] = "ps";
	else
	    fieldval[0] = "asc";

	if (!pcentry->PaperSize)
	    fieldval[1] = "letter";
	else
	    fieldval[1] = pcentry->PaperSize;

	fieldval[2] = pcentry->Type;
	
	if (strcmp(pcentry->db->GSDriver, "POSTSCRIPT"))
	    fieldval[3] = "NO";
	else
	    fieldval[3] = "YES";

	if (create_config_file(input, output, fieldname, fieldval, nfield) < 0)
	    return -1;

	/* successfully created general.cfg, now do postscript.cfg */
	free(fieldname);
	free(fieldval);
	free(output);
	free(input);

	output=malloc(strlen(queue_path)+strlen("postscript.cfg")+2);
	strcpy(output,queue_path);
	strcat(output,"postscript.cfg");
	input=malloc(strlen(root_path)+strlen(PRINTER_FILTER_DIR)+
		     strlen("postscript.cfg.in")+2);
	strcpy(input,root_path);
	strcat(input,PRINTER_FILTER_DIR);
	strcat(input,"postscript.cfg.in");

	/* setup the field arrays */
	nfield = 10;
	fieldname = (char **) malloc( nfield * sizeof(char *) );
	fieldname[0] = "gsdevice";
	fieldname[1] = "papersize";
	fieldname[2] = "resolution";
	fieldname[3] = "color";
	fieldname[4] = "reversepages";
	fieldname[5] = "extragsoptions";
	fieldname[6] = "pssendeof";
	fieldname[7] = "nup";
	fieldname[8] = "rtlftmar";
	fieldname[9] = "topbotmar";
	
	fieldval = (char **) malloc( nfield * sizeof(char *) );
	
	/* gsdriver */
	fieldval[0] = pcentry->db->GSDriver;
	
	/* papersize */
	if (!pcentry->PaperSize)
	    fieldval[1] = "letter";
	else
	    fieldval[1] = pcentry->PaperSize;
	
	/* resolution */
	if (!pcentry->Resolution)
	    fieldval[2] = "";
	else
	    if (strcmp(pcentry->Resolution,"Default"))
		fieldval[2] = pcentry->Resolution;
	    else
		fieldval[2] = "";

	/* color depth */
	if (!pcentry->BitsPerPixel)
	    fieldval[3] = "";
	else
	    if (strcmp(pcentry->BitsPerPixel,"Default")) {
		strcpy(colorstr,"-dBitsPerPixel=");
		strcat(colorstr,pcentry->BitsPerPixel);
		fieldval[3] = colorstr;
	    } else
		fieldval[3] = "";
	
	/* reverse pages? */
	if (!pcentry->RevPages)
	    fieldval[4] = "";
	else
	    fieldval[4] = pcentry->RevPages;

	/* extra gs options */
	fieldval[5] = "";

	/* ps send eof */
	if (strcmp(pcentry->db->GSDriver, "POSTSCRIPT"))
	    fieldval[6] = "NO";
	else
	    fieldval[6] = "YES";

	/* nup */
	fieldval[7] = "1";
	
	/* rtlftmar */
	fieldval[8] = "18";
	
	/* topbotmar */
	fieldval[9] = "18";
	
	if (create_config_file(input, output, fieldname, fieldval, nfield) < 0)
	    return -1;
	
	/* finally, make textonly.cfg */
	free(fieldval);
	free(fieldname);
	free(output);
	free(input);
	
	output=malloc(strlen(queue_path)+strlen("textonly.cfg")+2);
	strcpy(output,queue_path);
	strcat(output,"textonly.cfg");
	input=malloc(strlen(root_path)+strlen(PRINTER_FILTER_DIR)+
		     strlen("textonly.cfg.in")+2);
	strcpy(input,root_path);
	strcat(input,PRINTER_FILTER_DIR);
	strcat(input,"textonly.cfg.in");
	
	/* setup the field arrays */
	nfield = 3;
	fieldname = (char **) malloc( nfield * sizeof(char *) );
	fieldname[0] = "textonlyoptions";
	fieldname[1] = "crlftrans";
	fieldname[2] = "textsendeof";
	
	fieldval = (char **) malloc( nfield * sizeof(char *) );
	fieldval[0] = "";
	if (!pcentry->CRLF)
	    fieldval[1] = "";
	else
	    fieldval[1] = pcentry->CRLF;
	fieldval[2] = "YES";
	
	if (create_config_file(input, output, fieldname, fieldval, nfield) < 0)
	    return -1;
	
	/* simple config file required if SMB printer */
	if (!strcmp(pcentry->Type, "SMB")) {
	    output=malloc(strlen(queue_path)+15);
	    strcpy(output,queue_path);
	    strcat(output,".config");
	    smbconfig = fopen(output, "w");
	    
	    fprintf(smbconfig,"share='\\\\%s\\%s'\n",
		    pcentry->SMBHost, pcentry->SMBShare);
	    fprintf(smbconfig,"hostip=%s\n",pcentry->SMBHostIP);
	    fprintf(smbconfig,"user=%s\n",pcentry->SMBUser);
	    fprintf(smbconfig,"password=%s\n",pcentry->SMBPasswd);

	    fclose(smbconfig);
	}

	/* we're done with config files - need to copy filter over now  */
	free(fieldval);
	free(fieldname);
	free(output);
	free(input);
	
	if ( copy_master_filter(queue_path) < 0 )
	    return -1;
    } else
	return -1; /* we have to have a spool dir! */

    /* assume that spool dir got setup ok, now add printcap entry */
    output=alloca(strlen("/etc/printcap")+strlen(root_path)+2);
    strcpy(output,root_path);
    strcat(output,"/etc/printcap");
    if (access(output, F_OK)) {
	printcap=fopen(output, "w");
	if (printcap == NULL)
	    return -1;
	
	fputs("#\n", printcap);
	fputs("# Please don't edit this file directly unless you know what "
		"you are doing!\n", printcap);
	fputs("# Be warned that the control-panel printtool requires a very "
		"strict format!\n", printcap);
	fputs("# Look at the printcap(5) man page for more info.\n", printcap);
	fputs("#\n", printcap);
	fputs("# This file can be edited with the printtool in the "
		"control-panel.\n", printcap);
	fputs("\n", printcap);

	fclose(printcap);
    }

    printcap = fopen(output, "a");
    
    fputs("\n",printcap);
    fprintf(printcap, "##PRINTTOOL3## %s %s %s %s %s %s %s %s \n",
	    pcentry->Type,
	    pcentry->db->GSDriver,
	    pcentry->Resolution,
	    pcentry->PaperSize,
	    "{}",
	    pcentry->db->Entry,
	    pcentry->BitsPerPixel,
	    "{}");
    fprintf(printcap, "%s:\\\n",pcentry->Queue);
    fprintf(printcap, "\t:sd=%s:\\\n",pcentry->SpoolDir);
    fprintf(printcap, "\t:mx#0:\\\n\t:sh:\\\n");
    
    if (!strcmp(pcentry->Type, "LOCAL")) {
	fprintf(printcap,"\t:lp=%s:\\\n",pcentry->Device);
    } else if (!strcmp(pcentry->Type, "REMOTE")) {
	fprintf(printcap,"\t:rm=%s:\\\n",pcentry->RemoteHost);
	fprintf(printcap,"\t:rp=%s:\\\n",pcentry->RemoteQueue);
    } else if (!strcmp(pcentry->Type, "SMB")) {
	fprintf(printcap,"\t:lp=/dev/null:\\\n");
	fprintf(printcap,"\t:af=%s/%s\\\n",pcentry->SpoolDir,"acct");
    }

    /* cheating to get the input filter! */
    fprintf(printcap,"\t:if=%s/%s:\n",pcentry->SpoolDir,"filter");
    fclose(printcap);
    return 0;
}


int doConfigurePrinters (char *theroot) {

  int done, result;
  PCEntry *pcentry = NULL;

  /* root under which the installed system lives */
  root_path = theroot;

  /* read the printer database into memory -- ignore these errors as they
     probably indicate missing packages */
  if (read_printer_db(PRINTER_DB_FILE)) return INST_NOP;

  install_return_code = 0;
  done = 0;

  if (!query_config()) {
      pcentry = new_PCEntry();

      /* setup some defaults needed for all printer types */
      pcentry->AutoEOF = strdup("NO");
      pcentry->CRLF = strdup("NO");
      pcentry->RevPages = strdup("NO");

      result = edit_pcentry( &pcentry );
      
      while ((result != -1) && (result=verify_pcentry( pcentry ))==-2) 
	  result = edit_pcentry( &pcentry );
      
      if (result == -1)
	  free_PCEntry( pcentry );
      else {
	  configure_queue( pcentry );
	  free_PCEntry( pcentry );
	  install_return_code=0;
      }
  }

  return install_return_code;
}


#ifdef TESTING
int testing = 1;

int main() {
    int result;

    /* get the type of connection we want */
    newtInit();
    newtCls();
    newtPushHelpLine(NULL);
    newtDrawRootText(0, 0, "Printer Config - (C) 1997 Red Hat Software");

    result = doConfigurePrinters( "/" );

    newtFinished();

    printf("The result code is %d\n",result);

    return result;
}
#endif
     
