/* Text based printer configuration for the RHS install program */
/* Michael Fulbright - 1997                                     */
/*                                                              */
/* Heavily hacked by ewt for post-Red Hat 5.0 releases		*/

#include <alloca.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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 "intl.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 ) {
    memset(pcentry, 0, sizeof(*pcentry));
}

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

    return pcentry;
}

static char * safestrdup(char * item) {
    return item ? strdup(item) : NULL;
}

/* 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();
    
    pcentry->Queue = safestrdup(orig->Queue);
    pcentry->SpoolDir = safestrdup(orig->SpoolDir);
    pcentry->Device = safestrdup(orig->Device);
    pcentry->IF = safestrdup(orig->IF);
    pcentry->AF = safestrdup(orig->AF);
    pcentry->Type = orig->Type;
    pcentry->db = orig->db;
    pcentry->Resolution = safestrdup(orig->Resolution);
    pcentry->PaperSize = safestrdup(orig->PaperSize);
    pcentry->BitsPerPixel = safestrdup(orig->BitsPerPixel);
    pcentry->CRLF = orig->CRLF;
    pcentry->RemoteHost = safestrdup(orig->RemoteHost);
    pcentry->RemoteQueue = safestrdup(orig->RemoteQueue);
    pcentry->SMBHost = safestrdup(orig->SMBHost);
    pcentry->SMBHostIP = safestrdup(orig->SMBHostIP);
    pcentry->SMBShare = safestrdup(orig->SMBShare);
    pcentry->SMBUser = safestrdup(orig->SMBUser);
    pcentry->SMBPasswd = safestrdup(orig->SMBPasswd);
    pcentry->SMBWorkgroup = safestrdup(orig->SMBWorkgroup);
    pcentry->NCPHost = safestrdup(orig->NCPHost);
    pcentry->NCPQueue = safestrdup(orig->NCPQueue);
    pcentry->NCPUser = safestrdup(orig->NCPUser);
    pcentry->NCPPasswd = safestrdup(orig->NCPPasswd);
    
    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->Resolution);
    free(pcentry->PaperSize);
    free(pcentry->BitsPerPixel);
    free(pcentry->RemoteHost);
    free(pcentry->RemoteQueue);
    free(pcentry->SMBHost);
    free(pcentry->SMBHostIP);
    free(pcentry->SMBShare);
    free(pcentry->SMBUser);
    free(pcentry->SMBPasswd);
    free(pcentry->SMBWorkgroup);
    free(pcentry->NCPHost);
    free(pcentry->NCPQueue);
    free(pcentry->NCPUser);
    free(pcentry->NCPPasswd);
    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;
}

#if 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;
    }
}
#endif

/* display summary line given by char * arg */
void show_printer_about( char *about ) {
    newtWinMessage(_("Printer Information"), _("Ok"), about);
}

/* 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 okay, cancel, form, listbox, answer;
    struct newtExitStruct event;
    long i, sel;
    int formdone;
    char *origtype;
    newtGrid grid, buttons;

    if (changes->db && changes->db->Descr) {
	origtype = alloca(strlen(changes->db->Descr)+2);
	strcpy(origtype, changes->db->Descr);
    } else
	origtype = NULL;
    
    newtPushHelpLine(_("<F1> will give you information on a particular "
		     "printer type"));

    grid = newtCreateGrid(1, 3);
    newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("What type of printer do you have?")),
		     0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);

    listbox = newtListbox(-1, -1, 10, NEWT_FLAG_RETURNEXIT | NEWT_FLAG_SCROLL);
    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);
	}
    }
    newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, listbox,
		     2, 1, 2, 0, 0, 0);

    /* 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;
	    }
    }
    
    buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL);
    newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons,
		     0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

    form = newtForm(NULL, NULL, 0);
    newtFormAddHotKey(form, NEWT_KEY_F1); 
    newtGridAddComponentsToForm(grid, form, 1);
    newtGridWrappedWindow(grid, _("Configure Printer"));
    newtGridFree(grid, 1);
    
    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, let's do stairstep correction */
    if (!strncmp(changes->db->Descr,"HP",2)) {
	changes->CRLF = 1;
    } else {
	if (origtype) {
	    if (strcmp(origtype, changes->db->Descr)) {
		changes->CRLF = 0;
	    }
	} else  {
	    changes->CRLF = 0;
	}
    }

    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 okay, cancel, form, text, papersz, res, answer, crlf;
    struct newtExitStruct event;
    long i;
    int formdone;
    char * xpos;
    newtGrid grid, subgrid, buttons, leftgrid, rightgrid;
    char crlfret;
    char buf[200];
    
    /* there has to be a current selection for this to work! */
    if (changes->db == NULL)
	return -1;
    
    newtPushHelpLine(_("<F1> will give you information on this "
		     "printer driver."));

    text = newtTextboxReflowed(-1, -1,
		       _("You may now configure the paper size and resolution "
		         "for this printer."), 52, 5, 5, 0);

    subgrid = newtCreateGrid(2, 1);
    leftgrid = newtCreateGrid(1, 2);
    rightgrid = newtCreateGrid(1, 2);

    /* Paper size listbox */
    newtGridSetField(leftgrid, 0, 0, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("Paper Size")),
		     0, 0, 0, 0, 0, 0);

    papersz = newtListbox( 8, 5, 5, 0);
    for (i=0; i < NUM_PAPERSIZES; i++) {
	newtListboxAddEntry( papersz, PaperSizes[i], (void *)i );
	if (changes->PaperSize &&
	    !strcmp(changes->PaperSize, PaperSizes[i]))
	    newtListboxSetCurrent(papersz, i);
    }

    newtGridSetField(leftgrid, 0, 1, NEWT_GRID_COMPONENT, papersz,
		     0, 0, 0, 0, 0, 0);
    newtGridSetField(subgrid, 0, 0, NEWT_GRID_SUBGRID, leftgrid,
		     0, 0, 0, 0, 0, 0);

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

    /* Resolution */
    newtGridSetField(rightgrid, 0, 0, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("Resolution")),
		     0, 0, 0, 0, 0, 0);
    res = newtListbox(-1, -1, 5, 
		      changes->db->NumRes < 5 ? 0 : NEWT_FLAG_SCROLL);

    if ( changes->db->NumRes > 0) {
	for (i=0; i < changes->db->NumRes; i++) {
	    xpos = strchr(changes->db->Resolution[i],'x');
	    *xpos = '\0';
	    sprintf(buf, "%4sx%4s %s", changes->db->Resolution[i],
		    xpos + 1, changes->db->ResDescr[i] ? 
		    		changes->db->ResDescr[i] : "");
	    *xpos = 'x';

	    newtListboxAddEntry(res, buf, (void *) i);
	    if (changes->Resolution &&
		!strcmp(changes->Resolution, changes->db->Resolution[i]))
		newtListboxSetCurrent(res, i);
	}
    } else {
	newtListboxAddEntry( res, _("Default"), (void *)0 );
	newtListboxSetCurrent(res, 0);
    }
    
    /* make sure something is picked */
    if (changes->Resolution==NULL)
	newtListboxSetCurrent(res, 0);

    newtGridSetField(rightgrid, 0, 1, NEWT_GRID_COMPONENT, res,
		     0, 0, 0, 0, 0, 0);
    newtGridSetField(subgrid, 1, 0, NEWT_GRID_SUBGRID, rightgrid,
		     0, 0, 0, 0, 0, 0);

    crlf = newtCheckbox(-1, -1, _("Fix stair-stepping of text?"), 
			changes->CRLF ? '*' : ' ', NULL,&crlfret);
    
    buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL);

    grid = newtCreateGrid(1, 4);
    newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
		     0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, subgrid,
		     4, 1, 4, 0, 0, NEWT_GRID_FLAG_GROWX);
    newtGridSetField(grid, 0, 2, NEWT_GRID_COMPONENT, crlf,
		     0, 1, 0, 0, 0, 0);
    newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons,
		     0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

    form = newtForm(NULL, NULL, 0);
    newtGridWrappedWindow(grid, changes->db->Descr); 
    newtGridAddComponentsToForm(grid, form, 1);
    newtGridFree(grid, 1);
    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 (crlfret == ' ')
	changes->CRLF = 0;
    else
	changes->CRLF = 1;
    
    newtPopHelpLine();
    newtPopWindow();
    newtFormDestroy(form);

    return 0;
}

/* pick the color depth, returns < 0 on cancel, 0 otherwise */
static int select_color_depth( PCEntry *changes, int direction ) {
    newtComponent okay, cancel, form, bpp, answer, text;
    struct newtExitStruct event;
    int i, width, height;
    int formdone, maxlen;
    newtGrid grid, buttons;
    char buf[200];
    char * reflowedText;

    /* 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 direction > 0 ? 0 : -1;
    }

    /* we know that changes->db->NumBpp > 0 */
    
    newtPushHelpLine(_("<F1> will give you information on this "
		       "printer driver."));

    bpp = newtListbox(-1, -1, (changes->db->NumBpp < 8) ? changes->db->NumBpp : 8, NEWT_FLAG_RETURNEXIT | NEWT_FLAG_SCROLL);
    maxlen = 0;
    for (i=0; i < changes->db->NumBpp; i++) {
	sprintf(buf, "%2s  %s", changes->db->BitsPerPixel[i],
		changes->db->BppDescr[i] ? changes->db->BppDescr[i] : "");
	
	/* limit the length of the line */
	if (strlen(buf) > 70)
	    buf[70] = '\000';

	newtListboxAddEntry(bpp, buf, (void *)i );
	if (changes->BitsPerPixel &&
	    !strcmp(changes->BitsPerPixel, changes->db->BitsPerPixel[i]))
	    newtListboxSetCurrent(bpp, i);

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

    if (strcmp(changes->db->GSDriver,"uniprint")) 
    	reflowedText = newtReflowText(_("You may now configure the color "
			"options for this printer."), maxlen + 5, 5, 3,
			&width, &height);
    else
    	reflowedText = newtReflowText(_("You may now configure the uniprint "
			"options for this printer."), maxlen + 5, 5, 3,
			&width, &height);
    text = newtTextbox(1, 1, width, height, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, reflowedText);
    free(reflowedText);

    buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL);
    form = newtForm(NULL, NULL, 0);
    newtFormAddHotKey(form, NEWT_KEY_F1); 

    grid = newtGridSimpleWindow(text, bpp, buttons);
    if (strcmp(changes->db->GSDriver,"uniprint"))
    	newtGridWrappedWindow(grid, _("Configure Color Depth"));
    else
    	newtGridWrappedWindow(grid, _("Configure Uniprint Driver"));
    newtGridAddComponentsToForm(grid, form, 1);
    newtGridFree(grid, 1);
    
    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 ) {
	free(changes->BitsPerPixel);
	i = (long) newtListboxGetCurrent(bpp);
	changes->BitsPerPixel=strdup(changes->db->BitsPerPixel[i]);
    }

    newtPopHelpLine();
    newtPopWindow();
    newtFormDestroy(form);
    
    return (answer == cancel) ? -1 : 0;
}

/* returns the full attribute of the selected printer filter */
/* returns < 0 if user cancelled selected                    */
static int select_filter( PCEntry *changes, int direction ) {
    
    int done;
    int stage;
    int abort;
    
    done = 0;
    stage = direction > 0 ? 1 : 3;
    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++;
		direction = 1;
	    }
	    break;

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

	    /* select color depth */
	  case 3:
	    if (select_color_depth( changes, direction ) < 0) 
		direction = -1;
	    else
		direction = 1;
	    stage += direction;
	    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, okay, device, text, detectedW;
    char buf[2000];
    char *device_result;
    char *devname;
    char * reflowedText;
    int width, height;
    int detected[3];
    int i, result;
    int npos;
    newtGrid grid, subgrid, buttons;

#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

    reflowedText = newtReflowText(_("What device is your printer connected to "
			    "(note that /dev/lp0 is equivalent to LPT1:)?"), 
			    40, 5, 5, &width, &height);
    text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, reflowedText);
    free(reflowedText);

    strcpy(buf, _("Auto-detected ports:\n\n"));

    for (i=0; i<3; i++) {
	strcat(buf, "  /dev/lp");
	buf[strlen(buf) + 1] = '\0';
	buf[strlen(buf)] = i + '0';
	strcat(buf, ": ");
	if (!detected[i])
	    strcat(buf, _("Not "));

	strcat(buf, _("Detected\n"));
    }

    reflowedText = newtReflowText(buf, 40, 5, 5, &width, &height);
    detectedW = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(detectedW, reflowedText);
    free(reflowedText);

    subgrid = newtCreateGrid(2, 1);
    newtGridSetField(subgrid, 0, 0, NEWT_GRID_COMPONENT,
		     newtLabel(3, 5, _("Printer Device:")),
		     0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);

    device = newtEntry(19, 5, changes->Device ? changes->Device : "/dev/lp0", 
			15, &device_result, NEWT_FLAG_RETURNEXIT);
    newtGridSetField(subgrid, 1, 0, NEWT_GRID_COMPONENT, device,
		     1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);
    
    buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL);

    grid = newtCreateGrid(1, 4);
    newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
		     0, 0, 0, 0, 0, 0);
    newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, subgrid,
		     2, 1, 0, 0, 0, 0);
    newtGridSetField(grid, 0, 2, NEWT_GRID_COMPONENT, detectedW,
		     2, 1, 0, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons,
		     0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

    form = newtForm(NULL, NULL, 0);
    newtGridAddComponentsToForm(grid, form, 1);
    newtGridWrappedWindow(grid, _("Local Printer Device"));
    newtGridFree(grid, 1);
 
    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 ) {
    char *queue_result = changes->RemoteQueue;  
    char *host_result = changes->RemoteHost;
    int result;
    struct newtWinEntry entries[3] = {
	{ NULL, &host_result, NEWT_ENTRY_SCROLL },
	{ NULL, &queue_result, NEWT_ENTRY_SCROLL },
	{ NULL, NULL, 0 } };
	
    entries[0].text = _("Remote hostname:");
    entries[1].text = _("Remote queue:");
    
    result = newtWinEntries(_("Remote lpd Printer Options"), 
		_("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."),
		50, 5, 5, 20, entries, _("Ok"), _("Back"), NULL);

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

    return result;
}

static int get_ncp_info( PCEntry *changes ) {
    int result;
    char *host_result = changes->NCPHost;
    char *queue_result = changes->NCPQueue;
    char *user_result = changes->NCPUser;
    char *passwd_result = changes->NCPPasswd;
    struct newtWinEntry entries[] = {
	{ NULL, &host_result, NEWT_ENTRY_SCROLL },
	{ NULL, &queue_result, NEWT_ENTRY_SCROLL },
	{ NULL, &user_result, NEWT_ENTRY_SCROLL },
	{ NULL, &passwd_result, NEWT_ENTRY_SCROLL | NEWT_ENTRY_HIDDEN },
	{ NULL, NULL, 0 } };

    entries[0].text = _("Printer Server:");
    entries[1].text = _("Print Queue Name:");
    entries[2].text = _("User name:");
    entries[3].text = _("Password:");

    result = newtWinEntries(_("NetWare Printer Options"),
      _("To print to a NetWare printer, you need to provide the "
	"NetWare print server name (this is not always the same as the machines "
	"TCP/IP hostname) "
	"as well as the print queue name for the printer you wish to access and any "
	"applicable user name and password."),
		50, 4, 4, 20, entries, _("Ok"), _("Back"), NULL);

    if ( result != 2 ) {
	free(changes->NCPHost);
	changes->NCPHost = host_result;
	free(changes->NCPQueue);
	changes->NCPQueue = queue_result;
	free(changes->NCPUser);
	changes->NCPUser = user_result;
	free(changes->NCPPasswd);
	changes->NCPPasswd = passwd_result;
	
	result = 0;
    } else {
	result = -1;
	free(host_result);
	free(queue_result);
	free(user_result);
	free(passwd_result);
    }

    return result;
}

/* return < 0 for user cancel */
static int get_smb_info( PCEntry *changes ) {
    int result;
    char *host_result = changes->SMBHost;
    char *hostip_result = changes->SMBHostIP;
    char *share_result = changes->SMBShare;
    char *user_result = changes->SMBUser;
    char *passwd_result = changes->SMBPasswd;
    char *workgroup_result = changes->SMBWorkgroup;
    struct newtWinEntry entries[] = {
	{ NULL, &host_result, NEWT_ENTRY_SCROLL },
	{ NULL, &hostip_result, NEWT_ENTRY_SCROLL },
	{ NULL, &share_result, NEWT_ENTRY_SCROLL },
	{ NULL, &user_result, NEWT_ENTRY_SCROLL },
	{ NULL, &passwd_result, NEWT_ENTRY_SCROLL | NEWT_ENTRY_HIDDEN },
	{ NULL, &workgroup_result, NEWT_ENTRY_SCROLL },
	{ NULL, NULL, 0 } };

    entries[0].text = _("SMB server host:");
    entries[1].text = _("SMB server IP:");
    entries[2].text = _("Share name:");
    entries[3].text = _("User name:");
    entries[4].text = _("Password:");
    entries[5].text = _("Workgroup:");

    result = newtWinEntries(_("SMB/Windows 95/NT Printer Options"),
      _("To print to a SMB printer, you need to provide the "
	"SMB 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, password, and workgroup information."),
		50, 5, 5, 20, entries, _("Ok"), _("Back"), NULL);

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

    return result;
}

/* return < 0 for user cancel */
static int get_std_info( PCEntry *changes ) {
    char *queue_result = changes->Queue ? changes->Queue : "lp";
    char *spool_result = changes->SpoolDir ? 
			    changes->SpoolDir : "/var/spool/lpd/lp";
    int result;
    struct newtWinEntry entries[3] = {
	{ NULL, &queue_result, NEWT_ENTRY_SCROLL },
	{ NULL, &spool_result, NEWT_ENTRY_SCROLL },
	{ NULL, NULL, 0 } };

    entries[0].text = _("Name of queue:");
    entries[1].text = _("Spool directory:");

    result = newtWinEntries(_("Standard Printer Options"),
	_("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?"),
		50, 5, 5, 20, entries, _("Ok"), _("Back"), NULL);

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

    return result;
}
 

#define QUERY_YES	0
#define QUERY_NO	1
#define QUERY_BACK	2
static int queryConfig(void) {
    int result;

    result = newtWinTernary(_("Configure Printer"), _("Yes"), _("No"), 
			       _("Back"),
			       _("Would you like to configure a printer?"));
    
    if (result == 2) 
	return QUERY_NO;
    else if (result == 3)
	return QUERY_BACK;

    return QUERY_YES;
}

#if 0
static int query_add_more() {
    int result;

    result = newtWinChoice(_("Add Printer"), _("Yes"), _("No"), 
				   _("Would you like to add another printer?"));

    if (result == 2) 
	return -1;
    return 0;
}
#endif

static char * connToString(int connType) {
    switch (connType) {
	case PRINTER_LOCAL:		return "LOCAL";
	case PRINTER_LPRREM:		return "REMOTE";
	case PRINTER_SMB: default:	return "SMB";
	case PRINTER_NCP:		return "NCP";
    }
}

/* returns < 0 for user cancel, otherwise uses #defines in printercfg.h */
static int get_prn_connx(int connType) {
    int sel = connType, rc;
    char * connectionTypes[5];

    connectionTypes[0] = _("Local");
    connectionTypes[1] = _("Remote lpd");
    connectionTypes[2] = _("SMB/Windows 95/NT");
    connectionTypes[3] = _("NetWare");
    connectionTypes[4] = NULL;

    sel--;
    rc = newtWinMenu(_("Select Printer Connection"),
		     _("How is this printer connected?"), 40, 5, 5,
		     5, connectionTypes, &sel, _("Ok"), _("Back"), NULL);

    if (rc == 2) return -1;

    return sel + 1;
}

/* returns 0 on success, -1 on cancel */
static int verify_pcentry( PCEntry *pcentry ) {
    char tmpstr[500];
    int done;
    int result;
    int i, max;
    char info[2000];
    int lens[15];

    lens[0] = strlen(_("Printer type:"));
    lens[1] = strlen(_("Queue:"));
    lens[2] = strlen(_("Spool directory:"));
    lens[3] = strlen(_("Printer device:"));
    lens[4] = strlen(_("Remote host:"));
    lens[5] = strlen(_("Remote queue:"));
    lens[6] = strlen(_("Server:"));
    lens[7] = strlen(_("Share:"));
    lens[8] = strlen(_("User:"));
    lens[9] = strlen(_("Printer driver:"));
    lens[10] = strlen(_("Paper size:"));
    lens[11] = strlen(_("Resolution:"));
    if (strcmp(pcentry->db->GSDriver,"uniprint")) {
        lens[12] = strlen(_("Bits per pixel:"));
    } else {
        lens[12] = strlen(_("Uniprint driver:"));
    }

    max = 0;
    for (i = 0; i <= 12; i++)
	if (lens[i] > max) max = lens[i];

    strcpy(info, _("Please verify that this printer information is "
		   "correct:\n\n"));

    sprintf(tmpstr, "  %-*s %s\n  %-*s %s\n  %-*s %s\n", 
	    max, _("Printer type:"), connToString(pcentry->Type),
	    max, _("Queue:"), pcentry->Queue,
	    max, _("Spool directory:"), pcentry->SpoolDir);
    strcat(info, tmpstr);

    if (pcentry->Type == PRINTER_LOCAL) {
	sprintf(tmpstr, "  %-*s %s\n", max, _("Printer device"), 
		pcentry->Device);
    } else if (pcentry->Type == PRINTER_LPRREM) {
	sprintf(tmpstr, "  %-*s %s\n  %-*s %s\n", 
		max, _("Remote host:"), pcentry->RemoteHost,
		max, _("Remote queue:"), pcentry->RemoteQueue);
    } else if (pcentry->Type == PRINTER_NCP) {
	sprintf(tmpstr, "  %-*s %s\n  %-*s %s\n  %-*s %s <Password hidden>\n",
		 max, _("Server:"), pcentry->NCPHost,
		 max, _("Queue:"), pcentry->NCPQueue,
		 max, _("User:"), pcentry->NCPUser);
    } else /* if (pcentry->Type == PRINTER_SMB) */ {
	sprintf(tmpstr, "  %-*s %s", max, _("Server:"), pcentry->SMBHost);
	if (pcentry->SMBHostIP && pcentry->SMBHostIP[0]) {
		strcat(tmpstr, " <");
		strcat(tmpstr, pcentry->SMBHostIP);
		strcat(tmpstr, ">");
	}

	strcat(info, tmpstr);

	sprintf(tmpstr, "  %-*s %s\n  %-*s %s\n %-*s %s <Password hidden>\n", 
		max, _("Share:"), pcentry->SMBShare,
		max, _("Workgroup:"), pcentry->SMBWorkgroup,
		max, _("User:"), pcentry->SMBUser);
    }

    strcat(info, tmpstr);

    sprintf(tmpstr, "  %-*s %s\n  %-*s %s\n  %-*s %s\n  %-*s %s", 
	    max, _("Printer driver:"), pcentry->db->Descr,
	    max, _("Paper size:"), pcentry->PaperSize,
	    max, _("Resolution:"), pcentry->Resolution,
	    max, 
	    (strcmp(pcentry->db->GSDriver,"uniprint")) ? _("Bits per pixel:")
	    : _("Uniprint driver:"), pcentry->BitsPerPixel);
    strcat(info, tmpstr);

    done = 0;
    result = 0;
    while (!done) {
	i = newtWinChoice(_("Verify Printer Configuration"), _("Ok"), 
			    _("Back"), info);

	if (i == 2) {
	    done = 1;
	    result = -1;
	} else {
	    done = 1;
	    result = 0;
	}
    }

    return result;
}

/* edit the PrintCap Entry pcentry */
/* return < 0 if user aborts or error occurs */
static int edit_pcentry( PCEntry **pcentry, int direction ) {
    int result;
    int stage;
    int done;
    int abort;
    PCEntry *changes;

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

    done = 0;
    abort = 0;
    stage = direction > 0 ? 0 : 4;

    while (!done) {

	switch (stage) {
	  case 0:
	    if ((result = get_prn_connx(changes->Type)) == -1) {
		done = 1;
		abort = 1;
	    } else {
		changes->Type = result;
		stage++;
		direction = 1;
	    }
	    break;

	  case 1:
    	    result=get_std_info(changes);
	    if (result < 0)
		direction = -1;
	    else 
		direction = 1;
	    stage += direction;
	    break;

	  case 2:
	    if (changes->Type == PRINTER_SMB)
		result=get_smb_info(changes);
	    else if (changes->Type == PRINTER_LPRREM)
		result=get_remote_info(changes);
	    else if (changes->Type == PRINTER_LOCAL)
		result=get_local_info(changes);
	    else if (changes->Type == PRINTER_NCP)
	        result=get_ncp_info(changes);
	    else
		return -1;

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

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

	  case 4:
	    result = verify_pcentry(changes);
	    if (result < 0)
		direction = -1;
	    else 
		direction = 1;
	    stage += direction;
	    break;

	  case 5:
	    done = 1;
	    break;
	}
    }

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

/* 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, *ncpconfig;
   
    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] = connToString(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->db->GSDriver,"uniprint"))  {
	       if (strcmp(pcentry->BitsPerPixel,"Default")) {
		  strcpy(colorstr,"-dBitsPerPixel=");
		  strcat(colorstr,pcentry->BitsPerPixel);
		  fieldval[3] = colorstr;
	       } else {
		  fieldval[3] = "";
	       }
	    } else {
	       strcpy(colorstr,pcentry->BitsPerPixel);
	       fieldval[3] = colorstr;
	    }
	}
	
	fieldval[4] = "NO";

	/* 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] = "";
	fieldval[1] = pcentry->CRLF ? "YES" : "";
	fieldval[2] = "1";
	
	if (create_config_file(input, output, fieldname, fieldval, nfield) < 0)
	    return -1;
	
	/* simple config file required if SMB printer */
	if (pcentry->Type == PRINTER_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);
	    fprintf(smbconfig,"workgroup='%s'\n",pcentry->SMBWorkgroup);

	    fclose(smbconfig);
	}
	
	/* same for NCP printer */
	if (pcentry->Type == PRINTER_NCP) {
	    output=malloc(strlen(queue_path)+15);
	    strcpy(output,queue_path);
	    strcat(output,".config");
	    ncpconfig = fopen(output, "w");
	    
	    fprintf(ncpconfig,"server=%s\n",pcentry->NCPHost);
	    fprintf(ncpconfig,"queue=%s\n",pcentry->NCPQueue);
	    fprintf(ncpconfig,"user=%s\n",pcentry->NCPUser);
	    fprintf(ncpconfig,"password=%s\n",pcentry->NCPPasswd);
	    
	    fclose(ncpconfig);
	}

	/* 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 (!testing) unlink(output);
    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);

    fputs("\n",printcap);
    fprintf(printcap, "##PRINTTOOL3## %s %s %s %s %s %s %s %d \n",
	    connToString(pcentry->Type),
	    pcentry->db->GSDriver,
	    pcentry->Resolution,
	    pcentry->PaperSize,
	    "{}",
	    pcentry->db->Entry,
	    pcentry->BitsPerPixel,
	    pcentry->CRLF);
    fprintf(printcap, "%s:\\\n",pcentry->Queue);
    fprintf(printcap, "\t:sd=%s:\\\n",pcentry->SpoolDir);
    fprintf(printcap, "\t:mx#0:\\\n\t:sh:\\\n");
    
    if (pcentry->Type == PRINTER_LOCAL) {
	fprintf(printcap,"\t:lp=%s:\\\n",pcentry->Device);
    } else if (pcentry->Type == PRINTER_LPRREM) {
	fprintf(printcap,"\t:rm=%s:\\\n",pcentry->RemoteHost);
	fprintf(printcap,"\t:rp=%s:\\\n",pcentry->RemoteQueue);
    } else /* (pcentry->Type == (PRINTER_SMB | PRINTER_NCP)) */ {
	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 direction) {

    int done, result;
    static PCEntry *pcentry = NULL;
    int stage = (direction > 0 || !pcentry) ? 1 : 2;
    char * printcapFile;

    printcapFile = alloca(strlen("/etc/printcap") + strlen(theroot) + 2);
    strcpy(printcapFile, theroot);
    strcat(printcapFile, "/etc/printcap");

    /* 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;

    while (stage <= 2) {
	switch (stage) {
	  case 1:
	    result = queryConfig();
	    if (result == QUERY_BACK) 
		return INST_CANCEL;
	    else if (result == QUERY_NO) {
		if (pcentry) free(pcentry);
		pcentry = NULL;
		return 0;
	    }
	    stage = 2, direction = 1;
	    break;

	  case 2:
	    if (!pcentry) pcentry = new_PCEntry();
	    result = edit_pcentry(&pcentry, direction);
	    
	    if (result == -1) {
		stage = 1, direction = -1;
	    } else {
		configure_queue( pcentry );
		stage = 3;
	    }
	    break;
	}
    }

    return 0;
}