#!/usr/local/bin/wish -f
#
set copyright {
Package: LPRngTool
Version: 1.3.2
Authors: Geoff Silver <geoff\@uslinux.net>
         Patrick Powerll <papowell\@astart.com>
Last Modified: Fri Feb 22 19:51:33 PST 2002

This program is released under the GNU General Public License (GPL).
The original LPRngTool is copyright Geoff Silver, US Linux Networks LLC,
and Patrick Powell except portions which have been borrowed from
RedHat's Printtool which are copyright RedHat and their respective owners.

                NO WARRANTY

  BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

Modifications, bug fixes, suggestions for improvement always welcome.

 FILTERS

IFHP - LPRng's Filter Package
www://www.lprng.com/
ftp://ftp.lprng.com/pub/LPRng/FILTERS

These filters have support for printer status queries and other
facilities.
}


#############################################
## Parameter Checking
#############################################

for { set i 0 } { $i < $argc } { incr i } {
	set arg [lindex $argv $i]
	if { $arg == "-V" } {
		puts $copyright
		exit 0
	} else {
		puts stderr \
"usage: lprngtool \[-V\]
  -V  - version information
"
		exit 1
	}
}

# Build DB and miscellaneous Options

set version "1.3.2"
 
set cookie_status_dialog 0
set cookie_count_dialog 0
set atalkprint "atalkprint"
set smbprint "smbprint"
set ncpprint "ncpprint"
set alwayssync "yes"
set detection 0

set auto_ascii_to_ps ""
set auto_color ""
set auto_color_choice "Default"
set auto_crlf_trans ""
set auto_desired_to ""
set auto_eof ""
set auto_extra_gs_options ""
set auto_filtertype ""
set auto_gsdevice ""
set auto_ifhp_options ""
set auto_nup "1"
set auto_pager "/usr/local/bin/a2ps"
set auto_papersize ""
set auto_printerdb_entry ""
set auto_ps_send_eof ""
set auto_resolution ""
set auto_reverse_order ""
set auto_rtlft_margin "18"
set auto_sendeof ""
set auto_texteof ""
set auto_topbot_margin "18"
set auto_extra_pager_options ""
set ifhp_option_status 0
set ifhp_option_pagecount 0
set ifhp_option_pagecount_ps 0
set ifhp_option_pagecount_pjl 0
set ifhp_option_sync 0
set ifhp_option_sync_ps 0
set ifhp_option_sync_pjl 0
set ifhp_option_waitend 0
set ifhp_option_waitend_ps 0
set ifhp_option_waitend_pjl 0
set ifhp_nostatus 0
set smb_share ""
set smb_host ""
set smb_printer ""
set smb_hostip ""
set smb_password ""
set smb_password_dup ""
set smb_username ""
set smb_workgroup ""
set smb_crlf ""
set smb_authfile ""
set smb_remote_mode ""

# variables that set information about filter
set auto_vars {
	auto_filtertype
	auto_ifhp_options
	auto_printerdb_entry
	}

set ifhp_vars { ifhp_option_status
	ifhp_option_pagecount ifhp_option_pagecount_ps ifhp_option_pagecount_pjl
	ifhp_option_sync ifhp_option_sync_ps ifhp_option_sync_pjl
	ifhp_option_waitend ifhp_option_waitend_ps ifhp_option_waitend_pjl
	}
set smb_vars {
	smb_authfile smb_crlf smb_hostip smb_host smb_password
	smb_printer smb_remote_mode smb_share smb_username smb_workgroup }

set rw_device 1
set bp_list "/usr/local/libexec/filters/pclbanner /usr/local/libexec/filters/psbanner /usr/local/libexec/filters/lpbanner"
set delete_index ""
set filtersrcdir "/usr/local/libexec/filters"
set group "@GROUPID@"
set ghostscript "/usr/local/bin/gs"
set gs_installed_drivers ""
set gs_upp_drivers ""
set gsupdir "@GSUPDIR@"
set ifhp_conf "/usr/local/etc/ifhp.conf"
set ifhp_path "/usr/local/libexec/filters/ifhp"
set lprngtoolconf "/usr/local/etc/lprngtool.conf"
set pager "/usr/local/bin/a2ps"
set mpage "MPAGE_NOT_FOUND"
set a2ps "/usr/local/bin/a2ps"

set orient_list "default landscape portrait"
set duplex_list "default simplex duplex duplexshort tumble"
set media_list "default bond cardstock color glossy heavy labels letterhead \
	plain preprinted prepunched recycle transparency"
set papersize_list "default letter legal ledger 11x17 tabloid oversize \
	executive envelope a0 a1 a2 a3 a4"
set intray_list "default manual inupper inlower inmiddle inlarge inright intray1 intray1 \
	intray2 intray3 intray4 intray5 intray6 intray7 intray8 intray9 \
	intray10 inlargecapacity inhighcapacity"
set outbin_list "default outupper outlower outleft outbin2 outbin3 outbin4 \
	outbin5 outbin6 outbin7 outbin8 outbin9"
set resolution_list "default 300 600 1200"

set printer_type_list ""
set printerdb "printerdb"
set printerdb_debug 0
set printerdb_GSDriver(0) ""
set printerdb_about(0) ""
set printerdb_color(0) ""
set printerdb_count 0
set printerdb_descr(0) ""
set printerdb_descr2entry(0) ""
set printerdb_entry(0) ""
set printerdb_error 0
set printerdb_res(0) ""
set printerdb_model(0) ""
set printerdb_ifhpgsdriver(0) ""
set printerdb_ifhpgsoptions(0) ""
set printerdb_ifhpsupports(0) ""
set smb_printer_types { SMB Novell AppleTalk User }
set rh_filter master-filter
set rootwarn 1
set smbclient "/usr/local/bin/smbclient"
set smbwarn 1
set spooldirectory "/var/spool/lpd"
set sysprintcapfile "/usr/local/etc/printcap"
set printcapfile $sysprintcapfile
set sysprinter_type_list {DEVICE QUEUE SMB SOCKET PRINTPOOL DUMMY}
set trigger 0
set trigger2_help 0
set trigger3_help 0
set trigger_2 0
set trigger_3 0
set trigger_help 0
set update_interval 10
set update_count 0
set update_status 0
set update_msg ""
set uid -1
set userprintcapfile $env(HOME)/.printcap
set userprinter_type_list {DEVICE QUEUE SMB SOCKET DUMMY}
set whopsr ""
set checkforgsdevice 1
set status_length 0
set status_length_flags ""

set print_count 0
set printer_comments(0) ""
set printer_entry(0) ""
set printer_names(0) ""
set printer_status(0) ""
set printer_type(0) ""
set remote_type(0) ""
set printer_info(0) ""
set printer_used_by(0) "" 
set printer_force_localhost(0) "" 

set used_by ""
set change_local ""
set change 0
set force_localhost ""
set mode 0
set pc_showing 0
set entrywidth 35
set wideentrywidth 50
set minwidth 40
set maxwidth 80
set rhostq ""
set uid 0
set test_for_utilities 1
set font_fixed courier
set font_fixed -adobe-courier-medium-r-normal--12*
#set font_fixed -adobe-courier-medium-r-normal--10*
set font_helvetica -adobe-helvetica-medium-r-normal--12*
#set font_helvetica -adobe-helvetica-medium-r-normal--10*
set bglistbox white
set bgtext white
set bgentry white

# we define these options
# if we add more to the
set reserved_options {
	af bp client cm direct filter
	lprngtooloptions
	force_localhost ifhp lf lp
	mc mx of prefix_z rm rp rw sd server sh xfer_options ss stty sv tc }
set reserved_ifhp_options { model status sync pagecount waitend }

set ifhpkey "IFHP"
set userkey "User"
set novellkey "Novell"
set appletalkkey "AppleTalk"

############################ HELP START #################################

# Help with the accounting file field
proc accounting_help {} {
    popup_info_dialog "
Accounting File:
----------------

This is the file where printer accounting
information is written.  Accounting info can
be used to track paper usage, number of jobs
per person, or simply when a job was printed.

The default location is generally acceptable,
however, you may change this if you would like.

Generally, it is a bad idea to log all
accounting information for all queues to one
location.  This is because the logs can grow
quite unweildy, and make it difficult to locate
specific information.  The accounting information
files are not truncated.

If you do not wish to log accounting info, set
this to blank.  This is not recommended,
unless this is your personal desktop.  Generally
you can just accept the default value continue.

If you do keep accounting information you should
periodically truncate the accounting file.  This
can be done with 'checkpc -t SIZE'.  For example,
'checkpc -t 100k' will truncate log and accounting
information to 100k bytes.
"
}

# Help menu when adding a printer
proc printertype_help {} {
    help_dialog "Printer Type" "
Printer Type

Device: lp=/dev/...
  Used if your printer is attached to the serial or parallel port
  of the computer.

Queue:  lp=queue@server
  Send jobs to a print queue on a print server using the BSD LPD
  Print Spooler (RFC1179) network protocol.

TCP/IP Socket: lp=host%port
  Open a connection to the specified TCP/IP port on the remote
  computer.  This is commonly called AppSocket,  TCP/IP Printing,
  or direct socket printing.  You need to specify the IP address
  or DNS name of the printer and the port number.  By default,
  the HP JetDirect card and most HP printers use port 9100.

SMB/Novell/Appletalk: lp=|/.../program
  Use a program to transfer a print job to the remote host.
    Microsoft SMB Printer: use Samba smbclient program
    Novell Print Server:   use ntprint program
    Appletalk:  use pap program
  These program usually require a user name and password
  to be supplied for them.

Printer Pool Master Queue:
  pr:sv=p1,p2,...  # destination printers
  p1:ss=pr         # server printers
  p2:ss=pr

  Jobs sent to the master queue are forwarded to the first available
  server queue.  The :sv and :ss entries are used to specify the
  Master and Server queue relationships.

Dummy:
  This printcap entry is strictly a dummy entry. The queue name
  must start with a period, i.e. - .dummy.  These entries are used
  with the :tc=  (printcap include) facility.

Unknown:
  Unknown printcap type.

"
}

proc job_options_help {} {
    popup_info_dialog "
                Filter Options

You can specify options to be passed to the outpufilter using
lpr -Z option,option,...  or lp -o option,option
method.  You can also have these options automatically added
to your job by specifying them here.
"
}


proc select_options_help {} {
    popup_info_dialog "
                Job Options

This screen allows you to set two types of options used by LPR:

   LPR command line options  - used only by LPR
     Example:  -C class -M mail

   Filter options - passed to the filter
     Example:  -Z 'landscape,startpage=1'
     The ifhp filter provides support
     for the indicated options.  You can use the
     User Specified Options to add more.

"
}


proc outputstty_help {} {
    popup_info_dialog "
                Serial Port Configuration

If you have a printer on a serial port,  then you will
you can configure the serial port by specifying
the options to an STTY command here.  Normally you will
want to use RAW mode, 8 Bits/Char, and Hardware Flow Control.
For example:
   raw crtscts cs8 19200

If your printer does not do RTS/CTS flow control,  then you
will have to use software flow control.  This is much more
difficult to use and not as reliable.  For example:
   cbreak cs8 19200
"
}



proc balanceserver_help {} {
    popup_info_dialog "
          Load Balance Server Queue

If you want to use this printer to service a load
balance queue, you must specify the 'master'
or input load balance queue.

Each printer can only service a single load balance queue.
"
}

proc useroption_help {} {
	global reserved_options
	set nv ""
	set lv ""
	foreach i $reserved_options {
		append nv " $i"
		if { [string length $nv ] > 40 } {
			append lv $nv "\n"
			set nv ""
		}
	}
	append lv $nv "\n"

    popup_info_dialog "
                User Printcap Options

There are many options that can be used to modify the
printer operation.  You can enter them in this box,
one per line or many on a line with colon (:) separators.

However,  you should be aware that LPRngTool will not let
you specify user values for the following options:

$lv

"
}


# Help on what the comment field is for
proc comment_help {} {
    popup_info_dialog "
Comments:
---------

This field can serve two purposes.  First, many people use it to
store a description of the printer.  For instance, vauhp8k6804 is
a MUCH less readable name than \"HP 8000, Building VAU, Cube 6804\".

Secondly, this field can be used to notate if a printer is offline,
broken, especially fast, color, etc.

Use this field to your advantage, though it is useful to have a
policy at your site or corporation which specifies what will/won't
be allowed in this field.

" }


# Help with the Scoket IP/Hostname field
proc directip_help {} {
    popup_info_dialog "
IP Address/Hostname:
-------------------

Enter the fully qualified domain name (FQDN)
or IP address of the network print spooler or
JetDirect interface for the printer.

Example: laser.myschool.edu or 10.0.0.30.

While it doesn't have to be a fully-qualified domain
name (FQDN), the FQDN eliminates many problems with
DNS lookups.
"
}


# Help with the Socket Port field
proc directport_help {} {
    popup_info_dialog "
Port:
------------

Enter the port number of the network print
server or JetDirect interface.
The default JetDirect port is 9100.

If you have a network print server with
multiple printers attached to it,  each
printer interface is usually assigned a
different port.  Consult the manufacturers
documentation for the correct information.
"
}


# File Limit Help
proc filelimit_help {} {
     popup_info_dialog "
File Limit:
-----------

The maximum file size that can be printed.
If this value is set to '0', then there is
no limit to the size of the file.  It is
generally a bad idea to set a maximum file
size, since a 400 page book might be 6 meg,
but a 22 page brochure might be 15 megs.
If you want to conserve paper, you would be
better off setting a maximum number of pages.

It's probably safe to leave the default value
alone, unless you really know what you're doing.
"
}

# File Limit Help
proc banner_help {} {
	global bp_list
    regsub -all " " $bp_list "\n" bp_list
     popup_info_dialog "
Banner Printing:
----------------

The LPRng system uses a banner generation program
to create banners.  This allows you to create your
own spiffy banners by simply modifying a simple
shell script.  The following banner generation
programs are available:

$bp_list

pclbanner : generates banners for PCL printers
   If your printer can accept PCL then
   you can use this banner.  This works with
   a surprisingly large number of printers.
   If this does not work,  then you might try the
   ps banner.
psbanner  : generates PostScript banner
   If your printer can accept PostScript then
   you can use this banner.  If it does not work,
   then you can try the ugly lpbanner.
lpbanner : this is an ugly text based banner
   for printers that cannot understand
   PostScript and PCL.
"
}

proc ifhp_help {} {
     popup_info_dialog "
This field contains the Model information for the printer.
You cannot not directly set this field,  but must use the
Select Printer Model and Options to choose it.
"
}

# RH PrintFilter Help
proc inputfilter_help {} {
	global ifhp_conf
     popup_info_dialog "

PrintFilter:
------------

A print filter is a program that will be used to
convert your print jobs into a format that is
acceptible to the printer.  The type of conversion
required depends strongly on the Model of printer.

If you select the IFHP filter package you
will need to use the Select Model and Options to
configure the Filter.

Use the User Specifed filter option to directly specify the
filter you want to use.

IFHP Filter:
------------

The IFHP filter is the basic filter used with the LPRng print
spooler,  although just about any filter can be uses.  The IFHP
filter has a very flexible configuration system based on the IFHP
configuration file $ifhp_conf.  This file contains configuration
information for the various printers.

Use IFHP Model option to select the model of printer.
By default,  the IFHP filter assumes that you have a
PostScript and PJL enabled Hewlett-Packard LaserJet 4
compatible printer or equivalent.

If you are using a DEVICE or SOCKET printer configuration,
the IFHP filter assumes that you can get status information
from the printer and will report your printer status.  You
can see the status by using the lpq command;  lpq -l
displays a bit more, lpq -ll more, and lpq -L provides
an overwhelming amount of status.


User Specified Filter:
----------------------

This is for the desperation cases when you need to use a
a special filter.  You can specify the path to the filter
directly.
"
}


# Select input filter

set Status_ret "
Status Is Returned By Printer (status) -

  IFHP is pretty clever about determining if status information
  is going to be returned by the printer and will try to use
  the status information to inform users of problems.  However,
  even though some printers return status,  the communications
  channel is unidirectional.  For example,  your printer may be
  attached to a print spooler box that does not return status
  information.  In this case,  you should disable status.

  If you disable status information,  then you automatically
  disable sync, pagecount, and waitend operations.
"
proc ifhp_status_help {} {
	global Status_ret Wait_ready Get_pagecounter Wait_until
	help_dialog "Status Help" $Status_ret
}

set Wait_ready "
Wait Until Printer is Ready (sync) -

  IFHP will try to make sure that the last job is finished
  before sending the next job.  There is a certain overhead
  in doing this.  If you want fast but unreliable printing,
  do not select this option.

  IFHP will send either PJL or PostScript to the printer to
  cause it to respond with status information;  the default
  method used depends on the printer.  You can force PJL
  or PostScript to be used by selecting the option.

"

proc ifhp_sync_help {} {
	global Status_ret Wait_ready Get_pagecounter Wait_until
	help_dialog "Sync Help" $Wait_ready
}

set Get_pagecounter "
Get Pagecounter Value From Printer (pagecount) -

  Some printers support a hardware pagecounter that can be
  used to do accounting.  If you do not want accounting or
  the overhead of getting the pagecounter,  do not select
  this option.

  IFHP will send either PJL or PostScript to the printer to
  cause it to respond with pagecount information;  the default
  method used depends on the printer.  You can force PJL
  or PostScript to be used by selecting the option.
"
proc ifhp_pagecount_help {} {
	global Status_ret Wait_ready Get_pagecounter Wait_until
	help_dialog "Pagecount Help"  $Get_pagecounter
}


set Wait_until "
Wait Until Printer Reports END OF JOB (waitend) -

  Some printers will accept a job entirely before they
  start printing it.  If you terminate operations before the
  job is finished you will never get any error status.
  If you do not care about error status,  do not select
  this option.

  IFHP will send either PJL or PostScript to the printer to
  cause it to respond with pagecount information;  the default
  method used depends on the printer.  You can force PJL
  or PostScript to be used by selecting the option.
"



set Additional_options "
Additonal User Options - GS and GS_UNIPRINT Printer Drivers

   GS or UNIPRINT - run GhostScript to convert to output device

  User options:
   gs_device=<GhostScript Device> or <UNIPRINT .upp file>
   gs_options=additional options for GhostScript

   The 'gs' driver allows you to specify the GhostScript device.
   The GhostScript conversion command has the format:
      gs -dSAFER -dBATCH -q -sOutputFile=-
          -sDEVICE=\$gs_device \$gs_options -

   The 'uniprint' driver allows you to use a UPP file for
   the output device specification.  The GhostScript
   conversion command has the format:
       gs -q -dSAFER -dBATCH -sOutputFile=-
           @\$gs_device \$gs_options -

   The gs_options entry allows you to specify any additional options
   to the GhostScript driver as shown above.
"
proc ifhp_waitend_help {} {
	global Status_ret Wait_ready Get_pagecounter Wait_until
	help_dialog "Pagecount Help"  $Wait_until
}


proc ifhp_selectfilter_help {} {
	global Status_ret Wait_ready Get_pagecounter Wait_until
	help_dialog "Help for selecting an IFHP print filter" "

What does the print filter do?
------------------------------

  First the IFHP filter uses the UNIX file(1) program to determine
the type of file that you have supplied.  It does not try to do
'automagic' conversions - experience has shown that the output
is usually not as good as users would like and they will end up
hand-tuning the conversion process.

 Next the IFHP filter will run any conversion programs that are
needed.  For example,  you might have tried to print a compressed
PostScript file;  IFHP will decompress the file.  Also,  you might
be trying to print a Text file on a PostScript printer.  The
appropriate text conversion program will be invoked.  Finally,  you
might be trying to print a PostScript file on a non-PostScript
printer.  GhostScript will be used to convert the file to a format
appriate to the printer.

 Finally the converted output will be sent to the printer.  During
the transmission process any error status or other information returned
by the printer will be reported in the print log.

Help for selecting an IFHP print filter
---------------------------------------

First choose the type of printer you have.

Find a printer in the listing which is close to the model you own.
This will configure the print system to use GhostScript for that
type of printer.  If you do not find your printer in the list,
know that Ghostscript has an output device or a UPP definition file
for the device, then you can use the GS or UNIPRINT printer driver
definition described below.

Many printers are compatible with other brands; for example, many
printers are compatible with HP printers.

If your printer is attached by a bidirection channel (Network Printer,
serial printer,  and possibly a parallel port printer),  it may return
status information.  You can then enable the IFHP filter to use the
returned status to report job errors and implement much more reliable
printing.

$Status_ret
$Wait_ready
$Get_pagecounter
$Wait_until
$Additional_options

"
}


# Help menu for the log file field
proc logfile_help {} {
    popup_info_dialog "
Log File:
---------

This log file is used to hold trace and debugging
information, as well as error messages.  Its main
use is for debugging.

By default, LPRng will truncate the log file
at a maximum size of 1000K.  See 'max_log_file_size'
option in the LPRng documentation for details.

If you do not feel you need this log, you can
set the value to blank.  The default value (log) is
probably okay to leave alone.
"
}

# Help for the Maximum number of copies field
proc maxcopies_help {} {
    popup_info_dialog "
Maximum Copies:
---------------

If you would like to set a maximum number of
copies a user may print of a document, you may
enter the number here.  Beware of this restriction,
as you may receive many calls from users who are
unable to print multiple copies of documents for
meetings, training, etc.  If you are concerned
about paper usage, this might be reasonable.

This will not totally prevent abuse as
users will simply resend multiple copies of the
same job.

If you do not wish to set a maximum,  set this
to 0 or blank.  Leaving the field blank is normal..
"
}


# Help for the No Banners
proc suppress_headers_help {} {
    popup_info_dialog "
No Banners Pages:
-----------------

By default, LPRng will not produce a banner page and goes
to great efforts NOT to produce banner pages.
If you want banner pages then must explicitly set the
Suppress Header option OFF, i.e.- :sh@:.
and provide a program to actually generate banners.
You may need to provide an OF filter that will
set up the printer to print banners.
"
}

# Help files, "About" files, Troubleshooting, etc
proc menu_about {} {
    global version

    popup_info_dialog "
      LPRng Printer Management Toolkit (Version $version)
      ------------------------------------------
      Written by Geoff Silver <geoff\@uslinux.net>
                   and
          Patrick Powell <papowell\@astart.com>

LPRngTool allows you to easily administer printers
when using LPRng as your print server.  Do NOT run this
program if your system uses the Berkeley lpd.

Please feel free to send comments, bug fixes, and requests
for new features to Patrick Powell <papowell\@lprng.com>
or Geoff Silver <geoff\@uslinux.net>, or go ahead and
fix it yourself/write your own.  Out of courtesy, however,
please send any changes you have made for inclusion in
future versions.

Copyright 2000.  This software is licensed under the
GNU General Public License (GPL).  If you are unfamiliar
with the terms of the GPL, please see read the LICENSE
file which should have been included in your package, or
visit http://www.gnu.org/copyleft/gpl.html for more info.

"
}


# Names Help
proc names_help {} {
     popup_info_dialog "
Names:
------

The name to be used for the printer or print queue
followed by a list of '|' separated aliases.

For example:
   lp2|HP5Si-4204|HP5SiMX
is printer lp2, with aliases HP5SI-4204 and HP5SIMX.

LPRng uses caseless names - that is,  it converts them all to lower
case.  The names HP, Hp, hP, and hp will all be the same.

Your printer names should be easy to remember and type.  You may
want to add aliases to make it easier to remember which printer
this really is, particularly in a large enterprise or when using
the computer as a Samba print server.

You can also use the special '*' to do wild card matches
for printers.  This is useful on print servers where you want
to have one printer simulate a group of printers but do
not want to enter all of the aliases.  For example:
   pr|pr_*
would match pr, pr_landscape, pr_portrait, pr_2up, and so
forth.  You can use a special LPRng input filter to convert
these names into options for filters.

You can also use the following entry to have all jobs
forwarded to a print server.  This only works for 'client'
printcap entries:
   *:lp=%P@hostname
The * will match the printer name and %P will substitute
the name.  The lp=name@hostname will direct your job to
the print queue 'name' on server 'hostname'
"
}

# Help for the output filter field
proc outputfilter_help {} {
    popup_info_dialog "
OF Filter (Accounting + Banners):
---------------------------------

The OF filter is used to perform overall job
setup and actions,  such as sending banner pages
and doing per-job accounting.  It is started
when the printer is opened,  processes any
banner page or setup information,  and will
then suspend itself.  The lpd spooler then
uses the Job File Filter to do per job file
processing.  At the end of the job the OF
filter is woken up and is responsible for job
cleanup and per-job accounting.

Unless you are using banner pages or requiring
advanced accounting procedures that need to
query the printer for job page counts,  you do
not need an OF filter.
"
}


# Help on setting up a printer pool
proc pool_help {} {
    popup_info_dialog "
Printer Pool:
-------------

A printer pool is a \"logical\" printer to
which users send their print jobs.  The
printer pool contains a list of real printers
to which it can redirect its output.  The
real printers must be the same type, since
there is no way to determine which printer
will receive a job sent to the printer pool.

Printer pool jobs can be serviced faster, since
the queue is spread over several printers, but
it is helpful if the printers are in the same
physical location, since the job could be sent
to any printer.

Although multiple printers can be assigned
to the printer pool, you can still print to
the individual printers (unless they are
individually marked as \"server only\").

In this field, give a comma-separated list
of printer names.  For example, if you have
four printers (lp0, lp1, lp2, and lp3), you
could define a printer pool named \"lp\" in
which the pool of printers was
\"lp0,lp1,lp2,lp3\".
"
}


# Help on the local port selection field
proc printdev_help {} {
    popup_info_dialog "
Print Device:
------------

The pathname or device name to which the printer is attached.

Generally, this is probably a parallel port as /dev/lp0.  However,
if your machine has multiple parallel ports, you can specify a
different one (eg. /dev/lp1).

If you are using a serial printer, specify the serial port (eg.
/dev/ttyS0).  You should use the Advanced Options to set the serial
line characteristics.

"
}


# Help on the read/write selection field
proc printrw_help {} {
    popup_info_dialog "
Open Device Read/Write:
-----------------------

If you have bidirectional parallel port IO or a serial line and
your printer supplies status information back over the parallel
port or serial line,  then you may want to try to open the device
read/write rather than write only.   If you are using a parallel
port then you may encounter serious problems if your Operating
System does not support bidirectional communications.

"
}


# Help menu for redirect jobs
proc redirect_jobs_help {} {
    popup_info_dialog "
Redirect Jobs:
--------------

Using this command, you can redirect all jobs
sent to a print queue to be forwarded to a
different print queue automatically.  This can
be especially useful if you need to take a
printer offline temporarily.  This command will
redirect all current and future jobs to the
new queue.
"
}


# Help menu on what releasing jobs actually does
proc release_jobs_help {} {
    popup_info_dialog "
Releasing Jobs:
---------------

The top window contains the output from running lpq on the selected
printer.  If there are any jobs in queue, they will be listed in
this window in the order they will be printed.

To release held jobs from the queue and allow them to resume
printing, type the job numbers, user names, or a 'glob' match
pattern that will be applied to the Job ID field and press the 'OK'
button.  NOTE that the job number is NOT the Rank!

" }

proc hold_jobs_help {} {
    popup_info_dialog "
Pause Jobs:
----------

You may hold any number of jobs currently queued until you decide
to let them print.  This could be useful if, for example, you notice
a 500 page document right before several 2 or 3 page documents
which your manager needs pronto.

To hold jobs in the queue and prevent them from printing, type the
job numbers, user names, or a 'glob' match pattern that will be
applied to the Job ID field and press the 'OK' button.  NOTE that
the job number is NOT the Rank!

"
}


# Help on what the move jobs screen does
proc move_jobs_help {} {
    popup_info_dialog "
Move Jobs:
----------

You may move any number of jobs from the current queue to another
queue.  This can be used for on-the-fly load balancing, or to better
route print jobs if a printer goes down or must be taken offline.

To select jobs in the queue for moving, type the job numbers, user
names, or a 'glob' match pattern that will be applied to the Job
ID field and press the 'OK' button.  NOTE that the job number is
NOT the Rank!

In the second field, you must enter the destination queue which
has the format 'queue' or 'queue@host'.

"
}


# Help menu on what removing jobs does and how to do it
proc remove_jobs_help {} {
    popup_info_dialog "
Removing Jobs:
--------------

The top window contains the output from running
lpq on the selected printer.  If there are any
jobs in queue, they will be listed in this
window in the order they will be printed.

To remove (delete) a job from the queue, type the
job number (listed in the 'Job' column) in the
field and press the 'Remove Jobs' button.  NOTE
that the job number is NOT the Rank!

To delete multiple jobs, simply list each job
number, seperating them with a space.  To delete
all jobs in the queue, type \"all\" in the field.
"
}

# Remote Host Help
proc remotehost_help {} {
     popup_info_dialog "
Remote Host:
------------

The fully qualified domain name (FQDN) or IP address
of the network print server or print spooler host.
This is required.

While it doesn't have to be a fully-qualified domain
name (FQDN), the FQDN eliminates many problems with
DNS lookups.
"
}

# Remote Queue Help
proc remotequeue_help {} {
     popup_info_dialog "
Remote Queue:
-------------

The name of the remote print queue on the remote
host.  This can be a regular name (such as 'lp'
or 'lp3'), or an alias (such as 'HP5SiMX').
The default value '%P' causes LPRng to use the
name of the printer as the Remote Queue.

If you are printing to a network print server with
multiple printer ports,  check the manufacturers
documentation to make sure that the right Remote
Queue name is used.  The queue names may be case
sensitive on some network print servers.
"
}

# Help menu on what releasing jobs actually does
proc reprint_jobs_help {} {
    popup_info_dialog "
Re-Printing Jobs:
----------------

The top window contains the output from running
lpq on the selected printer.  If there are any
jobs in queue, they will be listed in this
window in the order they will be printed.

To reprint errored jobs from the queue and allow
them to restart printing, type the job numbers
(listed in the 'Job' column) in the field and
press the 'Re-Print Jobs' button.  NOTE that the
job number is NOT the Rank!

To reprint multiple jobs, simply list each job
number, seperating them with a space.  To reprint
all jobs in the queue, type \"all\" in the field.
"
}

# Help menu on selecting a remote queue
proc select_remote_queue_help {} {
    popup_info_dialog "
Selecting a Remote Queue:
-------------------------

The main window above show all available
print queues on the selected print server.
In the text field, enter the name of the
queue which you would like to work with.
This menu performs a similiar function to
highlighting a local print queue, except
it works with remote queues.
"
}

# Menu to provide help on the SMB Workgroup/Domain field
proc smbdomain_help {} {
    popup_info_dialog "
SMB Workgroup/Domain:
---------------------

Enter in this field the name of the
Samba or Windows Workgroup, or the
NT Domain.
"
}

# Help menu for the smb password field
proc smbpass_help {} {
    popup_info_dialog "
Password:
---------

Enter the password for the user account which will be
used to log in or be used for authentication.
"
}

# Help with the smb share/netbios name field
proc smbprinter_help {} {
    popup_info_dialog "
SMB/NetBIOS Name:
-----------------

Enter either the SMB share name, the Novell print queue name,
or the AppleTalk printer name.  It is not necessary to enter
the \\'s for the SMB share name.
"
}

# Help on the SMB Server IP optional field
proc smbserverip_help {} {
   popup_info_dialog "
IP Address of Server:
---------------------

If this is an SMB share printer you can provide the IP address or
address of the print server.  This can be used to speed up SMB-to-IP
queries when printing but almost always isn't necessary.  In fact, if
the IP of the server changes this can cause printing to fail in
strange and mysterious manners, especially if your print server is
using DHCP to get an address.
"
}

# help on the smb username field
proc smbuser_help {} {
    popup_info_dialog "
Username:
---------

Enter a user name that will be used for login or authenticatication.

The use of a remote SMB/Novell/AppleTalk printer may require a
username and password for operation.  The user name and password
are stored unencrypted in a file the Spool Directory.  Older versions
of the software used to send print jobs to the printer required
the user name and password to be passed as command line paramters.
This information was viewable by using the ps command on the server.

Newer versions of the software allow users to put the information
in an authentication file and specify the location of this file.
Clearly this file should not be readable by ordinary users.

For these reasons it is recommended that the username and password
for remote printer NOT BE the same as that of a user account on
the local host.  That way, if this information is compromised damage
is limited to unauthorized use of the printer."

}

# Spool Directory Help
proc spool_help {} {
     popup_info_dialog "
Spool Directory:
----------------

The directory in which files are spooled, converted, and queued
before being sent to the printer.

The default is to use a path based on the printer name (%P expands
to the printer name).  By default, the log (:lf) and accounting
(:af) files are created relative to the spool directory.

WARNING:  make sure that all of your print queues use different
spool directories or you will get unreliable operation.
"
}

# Help menu on what removing jobs does and how to do it
proc topq_jobs_help {} {
    popup_info_dialog "
Re-Ordering Jobs:
----------------

The top window contains the output from running
lpq on the selected printer.  If there are any
jobs in queue, they will be listed in this
window in the order they will be printed.

To move a job to the top of the queue, type the
job number (listed in the 'Job' column) in the
field and press the 'Move To Top' button.  NOTE
that the job number is NOT the Rank!

To reorder multiple jobs, simply list each job
number, seperating them with a space.  To reorder
all jobs in the queue, type \"all\" in the field.
"
}

proc unknown_help {} {
    popup_info_dialog "
Unknown Printer Configuration :lp Entry
---------------------------------------

You can classify these as follows:

DEVICE    /dev/lp      - local device
QUEUE    queue@host   - lpd queue on server
SOCKET    host%port    - TCP/IP connection to port on printer
SMB       |/comprog    - program support for transfer
"
}


# Help menu for the smb/netbios hostname field
proc smbhostname_help {} {
    popup_info_dialog "
Print Server Host Name:
-----------------------------------

Enter the host name of the printer to which you wish to print.
For SMB (Microsoft) this will be the NetBIOS name (no \\'s required).
For Novell this will be the server name (case insensitive)
For AppleTalk this will be the server or printer
"
}


# Help menu for the smb/netbios hostname field
proc tc_help {} {
    popup_info_dialog "
TC \(Include\):
-------------

The ':tc=' facility allows you to 'include' printcap
entries similar to the '#include' facility of C.
The entries are put at the start of the printcap file.
This allows you to have a 'generic' printer definition
and then put 'overrides' in your actual printcap entry.

You can put multiple entries in the :tc field; each is
included in turn.

Printcap entries or print queues whose names start with
a period (.) such as .dummy are treated as 'dummy' entries
for use with the ':tc' facility.

Example:
  .dummy:option1=oneval:option2
  real:tc=.dummy:option1=X
    -> real:option=X:option2
"
}


# Help menu for the smb/netbios hostname field
proc spooltype_help {} {
    popup_info_dialog "
Printcap Entries:
-----------------

The LPRng printcap database supports the following broad types
of printcap entries in System Mode and User Mode.  In System Mode
you are creating printcap entries in the main printcap file used
by the LPRng lpd server and the lpr, lprm, lpq, etc. clients.
In User Mode you are creating entries in your user printcap file.

Force Spooling to Localhost (:force_localhost) -

  Users ALWAYS send jobs to the LPD server running on the Localhost.
  This is the most reliable and effective way to do printing but
  it requires a print spooler running on the localhost.
  For most non-enterprise level systems this is the normal (default)
  configuration.  This is normally the default for the LPRng
  software and the :force_localhost option does not need to
  be explicitly specified.

  Example:
    lp:force_localhost

Spooling to Remote Host (:force_localhost@) -

  Users send jobs to the LPD server running on the specified Remote Host.
  This is normally the done in enterprise level printing when the
  remote host has a print spooler that controls access to multiple
  printers.

  Example:
    lp:lp=pr@server:force_localhost@:

  You can also use this facility in User Mode to create a printcap
  entry that will allow to you spool directly to a remote printer
  WITHOUT using a print spooler.  This may be useful if you are printing
  large jobs or simply do not want to run a print spooler on your
  system.

Printcap Entry for CLIENT, SERVER, and BOTH
  If a printcap entry is marked for CLIENT (:client) use, then only the
  LPRng clients such as lpr, lpq, lpc, and lprm will use the information.
  The SERVER (:server) entries are used only the the LPD server running
  on the localhost.

  Entries that are not reserved for CLIENT or SERVER use are used by
  both clients and servers.  If you have set :force_localhost or the
  LPRng clients default to using :force_localhost,  then this has
  the same effect as having a CLIENT entry for the clients and a
  SERVER entry for the LPD server.

  However, if you set :force_localhost@ (i.e. - clients connect to the
  remote LPD server) and you have and :lp=queue@host value, then the
  LPRng clients will connect directly to the Remote Queue 'queue' on
  Remote Host 'host'.  Normally this has not immediate consequences
  except to prevent spooling to the local LPD server.  You can always
  force this by using:  lpr -Pqueue@localhost, i.e. - specify the
  Remote Host explicitly as localhost. 

  Example:
  lp:lp=pr@server
    :lp=/dev/lp
    :sd=/var/spool/lpd/%P
    :filter=/usr/local/libexec/filters/ifhp

  LPR sends jobs    LPD on          LPD on
  to localhost   -> localhost   -> host 'server'
                    runs filter
"

}


# Help menu for the options

proc lpr_help {} {
    popup_info_dialog "
LPR Options
-----------

These options are used by LPR when spooling a job.
The following options are usually useful:

-Cclass

The class option is used by LPR to set the job class.  This can
also be done using the -C command line option.  By default,  the
job class is 'A'.  The job priority is usually taken from the first
letter of the job class as well.

The job class is usually used when you have form based printing
and need to indicate what forms are needed for a job.  The 'lpc
class PR form1' command will make printer 'PR' print only jobs with
class 'form1'.  You can select the classes for printing using the
'Queues' menu.

Note:  These options are processed BEFORE the normal command line
options.  There is no sanity checking on these options.  You should
If you need to pass a quoted option value such as for the job name
it should be:

CORRECT:  \"-JMy Job\" OR -J \"My Job\" OR \'-JMy Job\' OR -J \'My Job\'
"

}


# Help menu for the smb/netbios hostname field
proc options_help {} {
    popup_info_dialog "
LPR -Z Options or lp -o Options (IFHP Filter):
----------------------------------------------

These options are added to a job by LPR job when it is sent to a spool
queue OR when it arrives at the spool queue.  This is done to ensure
that the options are set in the job.

When spooling a job using LPR,  this has the same effect as:

  lpr -Zoption,option,...  OR lp -o \"option,option,...\"

When a job arrives at a spool queue that has options specified,
these options are PREFIXED to any existing options.  Since the
options are processed in first to last order then any user command
line specified options will have precedence.  For example:

   lpr -Zportrait                -> options=portrait
   Spool queue:  landscape       -> options=landscape,portrait
   Final Result: portrait

The IFHP filter will use these options to set up various printer
configurations.  The following general class of options can
be used.  Only a single option in these classes can be selected.

Orientation:  default, portrait, landscape, ...
    The orientation to be used
Duplex:       default, duplex, simplex, ...
    Duplex format to be used if the printer supports it.
Resolution:   default, 300, 600, ...
    The printer resolution to be used.  This may NOT be
    the resolution used by any job language conversion program
    such as GhostScript,  but is the native printer resolution.

Paper Size, Media, and Input Tray Selections:

    These options provide a way to select paper by size, media, or
    input tray.  It is strongly recommended that you experiment
    with your particular printer,  as the various permutations,
    combinations,  flaws,  variations, and conflicts for input
    selection is beyond the scope of a mere mortal to understand
    yet alone document.

    Each Model of printer differs on how they handle these options,
    as well has how the front panel or hardware input tray support
    effects this.  Enjoy!  Patrick \(\"Pass the painkillers\"\) Powell

Output Bin:
    The destination for output

User Specified Options:
    Additional options specified by the user.  These will
    be added at the end of the option list.  These have the format:

          option  OR option@  OR option=value

    The 'value' must not contain any commas or colons. If the 'value' is a
    list of items,  then these should be separated by ';' or spaces.
    If you MUST pass a comma or colon,  use \054  (comma) or \072 (colon)
    instead.
"

}


############################ HELP END   #################################


###########  MAIN USER INTERFACE START  ################################

#
# User Interface
#

#wm font . $font_helvetica
# . $font_helvetica

frame .menuf -relief raised -borderwidth 1

# File Menu
menubutton .menuf.fsm -text "File" -padx 4m -menu .menuf.fsm.menu
menu .menuf.fsm.menu

.menuf.fsm.menu add command -label "View Printcap File" -command view_printcap
.menuf.fsm.menu add separator
.menuf.fsm.menu add command -label "Backup Configuration" -command menu_backup_printcap
.menuf.fsm.menu add command -label "Restore Configuration" -command menu_restore_printcap
.menuf.fsm.menu add command -label "Reload Configuration" -command menu_reload
.menuf.fsm.menu add command -label "Write Configuration" -command menu_write_printcap
.menuf.fsm.menu add command -label "Clear Configuration" -command menu_clear_printcap
.menuf.fsm.menu add command -label "Reload Print Filter Database" -command menu_reload_printerdb
#.menuf.fsm.menu add separator
#.menuf.fsm.menu add command -label "About LPRngTool" -command menu_about
.menuf.fsm.menu add separator
.menuf.fsm.menu add command -label "Exit" -command menu_quit

# Queue modification/status menus
menubutton .menuf.status -text "Status" -padx 1m -menu .menuf.status.menu
menu .menuf.status.menu

.menuf.status.menu add command -label "Summary Status of Queues (lpc -a status)" -command {queue_status -a}
.menuf.status.menu add command -label "Summary Status of Selected Queue (lpc status)" -command {queue_status {}}
.menuf.status.menu add command -label "Status of Jobs in Queues (lpq -a)" -command {job_status -a}
.menuf.status.menu add command -label "Status of Jobs in Selected Queue (lpq)" -command {job_status {}}

menubutton .menuf.queues -text "Queue Admin" -padx 1m -menu .menuf.queues.menu
menu .menuf.queues.menu

.menuf.queues.menu add command -label "Disable Printing & Queueing (lpc down)" -command queue_down
.menuf.queues.menu add command -label "Enable Printing & Queueing (lpc up)" -command queue_up
.menuf.queues.menu add separator
.menuf.queues.menu add command -label "Stop Printing (Jobs continue to queue) (lpc stop)" -command print_stop
.menuf.queues.menu add command -label "Restart Printing (lpc start)" -command print_start
.menuf.queues.menu add command -label "Disable Queueing (Printing continues) (lpc disable)" -command queue_disable
.menuf.queues.menu add command -label "Enable Queueing (lpc enable)" -command queue_enable

.menuf.queues.menu add separator
.menuf.queues.menu add command -label "Abort Current Job Printing (lpc abort)" -command queue_abort
.menuf.queues.menu add command -label "Abort Current Job Printing and Restart (lpc kill)" -command queue_kill

menubutton .menuf.jobs -text "Job Admin" -padx 1m -menu .menuf.jobs.menu
menu .menuf.jobs.menu
.menuf.jobs.menu add command -label "Remove Jobs from a Queue (lprm)" -command remove_jobs

.menuf.jobs.menu add separator

.menuf.jobs.menu add command -label "Reorder Jobs to the Top of the Queue (lpc topq)" -command topq_jobs
.menuf.jobs.menu add command -label "Move Selected Jobs to Another Queue (lpc move)" -command move_jobs
.menuf.jobs.menu add command -label "Redirect All Jobs to Another Queue (lpc redirect)" -command redirect_jobs
.menuf.jobs.menu add command -label "Stop Job Redirection (lpc redirect off)" -command redirect_jobs_stop
.menuf.jobs.menu add command -label "Print Jobs With Class (lpc class)" -command class_jobs
.menuf.jobs.menu add command -label "Stop Print Jobs With Class (lpc class off)" -command class_jobs_stop
#
.menuf.jobs.menu add separator
.menuf.jobs.menu add command -label "Hold Jobs (lpc hold)" -command hold_jobs
.menuf.jobs.menu add command -label "Release Jobs (lpc release)" -command release_jobs
.menuf.jobs.menu add command -label "Reprint Errored Jobs (lpc redo)" -command reprint_jobs

menubutton .menuf.server -text "Server Admin" -padx 1m -menu .menuf.server.menu
menu .menuf.server.menu

.menuf.server.menu add command -label "LPD Server PID (lpc lpd)" -command lpc_lpd
.menuf.server.menu add command -label "LPD Reload Databases (lpc reread)" -command restart_lprng
.menuf.server.menu add command -label "LPD Server Printcap Database (lpc printcap)" -command show_printcap
.menuf.server.menu add command -label "Fix Printcap Info (checkpc -f)" -command {run_checkpc ""}
.menuf.server.menu add separator
.menuf.server.menu add command -label "Select Server Host" -command change_rhost

#
# Test Page Menus
menubutton .menuf.tests -text "Tests" -padx 3m -menu .menuf.tests.menu
menu .menuf.tests.menu

.menuf.tests.menu add command -label "Print ASCII test page" -command print_ascii_testpage
.menuf.tests.menu add command -label "Print Postscript (Letter) test page" -command print_ps_testpage
.menuf.tests.menu add command -label "Print Postscript (A4) test page" -command "print_ps_testpage a4"
.menuf.tests.menu add command -label "Print ASCII directly to local port" -command print_ascii_direct_testpage

# View Menus
menubutton .menuf.view -text "Local" -menu .menuf.view.menu
menu .menuf.view.menu

.menuf.view.menu add command -label "Add A New Printer/Queue" -command button_add
.menuf.view.menu add command -label "Delete A Printer/Queue" -command button_delete
.menuf.view.menu add command -label "Edit Printer/Queue Attributes" -command button_edit
#.menuf.view.menu add separator
#.menuf.view.menu add command -label "View Current Queue Status" -command view_queue_status
#.menuf.view.menu add command -label "View Jobs in Queue" -command view_queue_jobs
.menuf.view.menu add separator
.menuf.view.menu add command -label "View Queue Logs" -command view_log_file
.menuf.view.menu add command -label "View Queue Accounting File" -command view_acct_file
.menuf.view.menu add command -label "View Queue Status History" -command view_statushistory

# Help Menus
menubutton .menuf.help -text "Help" -menu .menuf.help.menu
menu .menuf.help.menu
.menuf.help.menu add command -label "General Help" -command menu_genhelp
.menuf.help.menu add command -label "Frequently Asked Questions" -command menu_faq
.menuf.help.menu add command -label "Inclusions in the Next Version" -command menu_todo
.menuf.help.menu add separator
.menuf.help.menu add command -label "Troubleshooting" -command menu_trouble
.menuf.help.menu add command -label "Release Notes & Known Bugs" -command menu_bugs
.menuf.help.menu add separator
.menuf.help.menu add command -label "About LPRngTool" -command menu_about

pack .menuf.fsm .menuf.view .menuf.tests .menuf.status \
	 .menuf.queues .menuf.jobs .menuf.server -side left -in .menuf
pack .menuf.help -side right -in .menuf
tk_menuBar .menuf .menuf.fsm .menus.tests .menuf.status \
	 .menuf.queues .menuf.jobs .menuf.server .menus.help

label .header -font $font_fixed -text "Printer Queues"

frame .main -relief flat -borderwidth 1
listbox .list -bg $bglistbox  -font $font_fixed \
	 -yscrollcommand ".sb set" \
	 -xscrollcommand ".sbx set" \
	 -setgrid 1 -exportselection 0 -selectmode extended
scrollbar .sb  -orient ver -command ".list yview"
scrollbar .sbx -orient hor -command ".list xview"
pack .sb -side right -fill y -in .main
pack .sbx -side bottom -fill x -in .main
pack .list -side right -expand true -fill both -in .main

frame .buttons
button .exit -text "Exit" -width 10 -command menu_quit
button .pc_showing -text "Showing" -width 18 -command button_pc_showing
button .add -text "Add" -width 10 -command button_add
button .edit -text "Edit" -width 10 -command button_edit
button .delete -text "Remove" -width 10 -command button_delete
button .mode -text "View System Printcap" -width 18 -command button_mode
pack .exit .pc_showing .mode .add .edit .delete -side left -expand true -ipady 1 -padx 0.25c -in .buttons

pack .menuf -side top -fill x
pack .header -side top
pack .main -side top -expand true -fill both -padx 4
pack .buttons -side top -fill x -padx 4 -pady 4

set title "LPRng Printer Management Toolkit (Version $version)"
set subtitle ""
wm title . $title
update
scan [wm geometry .] "%d%*c%d" xmin ymin
wm minsize . $xmin $ymin

# Double Click (Left) shows queue jobs
bind .list <Double-Button-1> "
    .list selection clear 0 end
    .list selection set \[.list nearest %y\]
    view_queue_jobs
"

# Right Click edits settings/properties
bind .list <Button-3> "
    .list selection clear 0 end
    .list selection set \[.list nearest %y\]
    button_edit
"

# End of User Interface

###########  MAIN USER INTERFACE END    ################################

###################### UTILITIES START  #################################


# runjob { job } 
#   - job is a string with the command and options
#   returns: { {ok} {result}}
#     ok -     1 if ok, 0 if error
#     result - output result
# 
proc runjob { job } {
	#puts "runjob '$job'"
	set result ""
	set ok 1
	set joblist [split $job ";"]
	set r ""
	#puts "runjob NOW '$joblist'"
	foreach j $joblist {
		if {[ catch { eval exec $j } result ]} {
			popup_error_dialog "Error executing command '[clean_cmd $job]'\n$result"
			set ok 0 
		}
		#puts "j '$j'  RESULT '$result'"
		if { $r != "" } { append r "\n" }
		append r $result
	}
	#puts "runjob '$job' OK '$ok' RESULT '$r'"
	return [ list $ok $r]
}

# must_runjob { job }
#  - runs job, gets { OK result }
# if OK == 1, then returns result else exits

proc must_runjob { job } {
	set result [runjob $job ]
	if {[lindex $result 0]} {
		return [lindex $result 1]
	}
	exit 1
}

# ok_runjob { job }
#  - runs job, gets { OK result }
# returns "OK" value

proc ok_runjob { job } {
	set result [runjob $job ]
	return [lindex $result 0]
}

# put out a warning message and a popup dialog
proc warnmsg { m } {
		puts stderr "$m\n"
		popup_error_dialog $m
}
proc diemsg { m } {
		puts stderr "$m\n"
		popup_error_dialog $m
		exit 1
}

proc at_rhostq {} {
	global rhostq
	if { $rhostq != "" } {
		return "@$rhostq"
	}
	return ""
}

proc server_rhostq {} {
	global rhostq
	if { $rhostq != "" } {
		return "-S$rhostq"
	}
	return ""
}


proc notroot {} {
	global mode rootwarn uid
	if {![info exists rootwarn] } {
		set rootwarn 1
	}
	#puts "notroot: uid '$uid'"
    if { $mode == 0 && $uid != 0 && $rootwarn } {
		set rootwarn 0
		popup_error_dialog "
	  Warning:
	  --------
You are not running as root.  To
modify system files you may need administrator privileges.
Changes you request may be made to system files.
"
	}
}

# change the mode of operation from 'system' to user
# you need to update the button and then load the printcap database
proc setmode {} {
	global sysprintcapfile userprintcapfile printcapfile mode
	global sysprinter_type_list userprinter_type_list printer_type_list
	global pc_showing title subtitle

	if { $pc_showing } {
		if { $mode } {
			.mode config -text "View System Printcap"
			set printcapfile $userprintcapfile
			set printer_type_list $userprinter_type_list
			set subtitle "User Printcap [at_rhostq]- $printcapfile"
		} else {
			.mode config -text "View User Printcap"
			set printcapfile $sysprintcapfile
			set printer_type_list $sysprinter_type_list
			set subtitle "System Printcap [at_rhostq]- $printcapfile"
		}
		.header config -text $subtitle \
		    -font -adobe-helvetica-bold-r-normal--12*
		wm title . "$title - $subtitle"
		reload
		redisplay
	}
}

# call back for button to change mode
proc button_mode {} {
	global mode

	set mode [ expr ! $mode ]
	setmode
}


# change what is showing from printers to printcaps
# you need to update the button and then load the printcap database
proc set_pc_showing {} {
	global pc_showing title subtitle

#menubutton .menuf.view -text "Local" -menu .menuf.view.menu
	stop_refresh
	if { $pc_showing } {
		.menuf.view config -state normal
		.add config   -state normal -text "Add" -command button_add
		.edit config   -state normal -text "Edit" -command button_edit
		.delete config   -state normal -text "Remove"
		.mode config   -state normal
		.pc_showing config -text "View Printers"
		setmode
		redisplay
	} else {
		.menuf.view config -state disabled
		.add config   -state normal -text "Refresh" -command new_timerefresh
		.edit config   -state normal -text "Hold" -command stop_refresh
		.delete config  -state disabled -text ""
		.mode config  -state disabled -text ""
		.pc_showing config -text "View Printcaps"
		set subtitle "Printer Status"
		.header config -text $subtitle \
		    -font -adobe-helvetica-bold-r-normal--12*
		wm title . "$title - $subtitle"
		new_timerefresh
	}
}

proc timerefresh { mycookie } {
	global update_interval update_count update_msg update_status
	global cookie_status_dialog
	#puts "timerefresh: start"
	if { $cookie_status_dialog == 0 || $cookie_status_dialog != $mycookie } { return }
	set date [runjob [list date]]
	#puts $date
	if { $update_count == 0 } {
		# get new status
		redisplay
	}
	incr update_count
	if { $update_count >= $update_interval } {
		set	update_count 0
	}
	set c [ expr $update_interval - $update_count ]
	set c [ format {%02d} $c ]
	set subtitle "Printer Status - [lindex $date 1], refresh in $c seconds"
	.header config -text $subtitle \
		-font -adobe-helvetica-bold-r-normal--12*
	if { $update_interval > 0 } {
		after 1000 [list timerefresh $mycookie]
	}
}

proc new_timerefresh {} {
    global cookie_status_dialog cookie_count_dialog update_count
	#puts "new_timerefresh: start"
	incr cookie_count_dialog
	set cookie_status_dialog $cookie_count_dialog
	set update_count 0
	timerefresh $cookie_status_dialog
}

proc stop_refresh {} {
    global cookie_status_dialog cookie_count_dialog update_count
	#puts "stop_refresh: start"
	set cookie_status_dialog 0
	set update_count 0
}


# call back for button to change mode
proc button_pc_showing {} {
	global pc_showing

	set pc_showing [ expr ! $pc_showing ]
	set_pc_showing
}

# Actual function that decides how to create a window, what bitmaps to use, etc
proc popup_dialog {w title text bitmap entry font default args} {
    global tk_priv entrywidth font_fixed font_helvetica
	global minwidth maxwidth subtitle
	global bgtext bgentry


	# we set the minwidth to minimum width
	# we set the maxwidth to maximum width
    # if we have lines longer than maxsize we add scroll
	set width $minwidth
	set addxscroll 0
	set lines [ split $text "\n" ]
	set needlines [llength $lines]
	for { set i 0 } { $i < [llength $lines] } { incr i } {
		set len [string length [lindex $lines $i] ]
		if { $len > $width } {
			set width $len
			if { $width > $maxwidth } {
				set addxscroll 1
				set width $maxwidth
				#incr needlines [expr $width % $maxwidth]
			}
		}
	}
	set linecount [ get_linecount $needlines ]

    catch {destroy $w}
    toplevel $w -class Dialog
    wm withdraw $w
    wm transient $w .
    wm group $w .
    wm title $w "$title - $subtitle"
    wm iconname $w Dialog
    frame $w.top -relief raised -bd 1
    pack $w.top -side top -fill both
    frame $w.bot -relief raised -bd 1
    pack $w.bot -side bottom -fill both

    frame $w.mframe
	if { !$entry } {
		text $w.msg -bg $bgtext -setgrid true \
			-height $linecount \
			-width $width \
			-font $font -wrap none
			# -font $font_fixed
			# -wrap word
		if { $linecount < [llength $lines] } {
			$w.msg config -yscrollcommand "$w.msg_sy set"
			scrollbar $w.msg_sy -orient vert -command "$w.msg yview"
			pack $w.msg_sy -side right -fill y -in $w.mframe
		}
		if { $addxscroll } {
			$w.msg config -xscrollcommand "$w.msg_sx set"
			scrollbar $w.msg_sx -orient hor -command "$w.msg xview"
			pack $w.msg_sx -side bottom -fill x -in $w.mframe
		}
		$w.msg insert 1.0 $text
		$w.msg config -state disabled
	} else {
		label $w.msg -text "$text" -justify left
	}
    pack $w.msg    -side left -fill both -expand true -in $w.mframe
    pack $w.mframe -side right -fill both -expand true -in $w.top

    if {$bitmap != ""} {
        label $w.bitmap -bitmap $bitmap
        pack $w.bitmap -in $w.top -side left -padx 5m -pady 5m
    }
	if { $entry } {
		entry $w.entry -bg $bgentry -relief sunken -borderwidth 2 -width $entrywidth 
#		-font $font_fixed
        pack $w.entry -side top
	}

    set i 0
    foreach but $args {
        button $w.button$i -text $but -command "set tk_priv(button) $i"
        if {$i == $default} {
            frame $w.default -relief sunken -bd 1
            raise $w.button$i $w.default
            pack $w.default -in $w.bot -side left -expand 1 \
			 -padx 1m -pady 1m
			# -padx 3m -pady 2m
            pack $w.button$i -in $w.default
			#	-padx 2m -pady 2m -ipadx 2m -ipady 1m
            bind $w <Return> "$w.button$i flash; set tk_priv(button) $i"
        } else {
            pack $w.button$i -in $w.bot -side left -expand 1
            #	-padx 3m -pady 3m -ipadx 2m -ipady 1m
        }
        incr i
    }

	#top_center_dialog $w
    center_dialog $w
    set oldFocus [focus]
    focus $w
    catch { grab $w }

	set value ""
	while { 1 } {
		tkwait variable tk_priv(button)
		set value $tk_priv(button)
		if { $entry } {
			set value [list $tk_priv(button) [ $w.entry get ]]
			if { $value == "" } {
				popup_dialog .entry "Error" $s "Please enter a value" error $font_fixed 0 0 "Continue"
				continue
			}
		}
		break
	}

	catch { grab release $w }
    focus $oldFocus
    destroy $w
	update idletasks
    return $value
}

# Create a popup box that will let you continue/cancel
proc popup_continue_dialog {s {font ""} } {
	global font_fixed font_helvetica
	if { $font == "" } { set font $font_helvetica }
	return [popup_dialog .error "Notice" $s question 0 $font 1 "Continue" "Cancel"]
}

# Create a popup dialog box if it's an error message
proc popup_error_dialog {s {font ""} } {
	global font_fixed font_helvetica
	if { $font == "" } { set font $font_helvetica }
	popup_dialog .error "Error" $s error 0 $font 0 "Ok"
}

# Create a popup window with some information in it
proc popup_info_dialog {s {font ""} } {
	global font_fixed font_helvetica
	if { $font == "" } { set font $font_helvetica }
    popup_dialog .error "Info" $s info 0 $font 0 "Ok"
}

# Create a popup box that asks a "yes or no" question
proc popup_yesno_dialog {s c {font ""} } {
	global font_fixed font_helvetica
	if { $font == "" } { set font $font_helvetica }
	return [popup_dialog .error "Question" $s question 0 $font $c "Yes" "No"]
}

# Display a help window that is scrollable
proc help_dialog { title msg {font ""} } {
    global trigger_help
	global font_fixed font_helvetica

	if { $font == "" } { set font $font_helvetica }
	regsub -all "\n" $msg  "\n      " v
    return [popup_dialog .hf $title $v "" 0 $font 0 "Continue"]
}

proc timedisplay { w msg cmd mycookie } {
	global update_interval update_count update_msg update_status
	global cookie_status_dialog status_length_flags
	#puts "timedisplay: msg '$msg' cmd '$cmd' mycookie '$mycookie' cookie_status_dialog '$cookie_status_dialog'"
	if { $cookie_status_dialog == 0 || $cookie_status_dialog != $mycookie } { return }
	set date [runjob [list date]]
	#puts $date
	if { $update_count == 0 } {
		catch {
			$w config -state normal
			$w delete 1.0 end
		}
		set c [subst $cmd]
		set m [clean_cmd $msg]
		#puts "timedisplay: m '$m', c '$c'"
		set result [ runjob $c ]
		#puts "timedisplay: m '$m', c '$c', result '$result'"
		set update_msg "$m[lindex $result 1]"
		set update_status [lindex $result 0]
	}
	incr update_count
	if { $update_count >= $update_interval } {
		set	update_count 0
	}
	set c [ expr $update_interval - $update_count ]
	#puts "timedisplay: interval $update_interval, count $update_count, msg '$update_msg'"
	if {[catch {
    $w config -state normal
	$w delete 1.0 end
	set c [ format {%02d} $c ]
	$w insert end "[lindex $date 1], refresh in $c seconds\n"
#	$w insert end $m
#	$w insert end [lindex $result 1]
	$w insert end $update_msg
    $w config -state disabled
	} errmsg] == 0 } {
		if { $update_interval > 0 } {
#			after [ expr $update_interval * 1000] [list timedisplay $w $msg $cmd $mycookie]
			after 1000 [list timedisplay $w $msg $cmd $mycookie]
		}
	}
}

proc new_timedisplay { w msg cmd } {
    global cookie_status_dialog cookie_count_dialog update_count
	#puts "new_timedisplay w '$w', msg '$msg', cmd '$cmd'"
	incr cookie_count_dialog
	set cookie_status_dialog $cookie_count_dialog
	set update_count 0
	timedisplay $w $msg $cmd $cookie_status_dialog
}

proc more_detail { w msg cmd } {
	global update_interval cookie_status_dialog status_length_flags
	global status_length status_length_flags
	incr status_length
	set_status_length

	set text $msg
	if { $cmd != "" } {
		set c [ subst $cmd ]
		#puts "more_detail: running '$c'"
		set result [ runjob $c ]
		#puts "more_detail: result '$result'"
		if {[lindex $result 0]} {
			append text [lindex $result 1]
		}
	}
	set linecount [get_linecount [llength [split $text "\n"]]]
	#puts "more_detail: linecount '$linecount'"
	set current [$w cget -height ]
	if { $current < $linecount } {
		$w configure -height $linecount
	}
	new_timedisplay $w $msg $cmd
	#puts "more_detail $status_length $status_length_flags"
}
proc less_detail { w msg cmd } {
	global status_length status_length_flags
	#puts "less_detail IN $status_length $status_length_flags"
	if { $status_length > 0 } {
		set status_length [expr $status_length - 1 ]
	}
	set_status_length
	new_timedisplay $w $msg $cmd
	#puts "less_detail $status_length $status_length_flags"
}

proc set_status_length {} {
	global status_length status_length_flags
	set status_length_flags ""
	if { $status_length > 0 } {
		set status_length_flags "-"
		for { set i 0 } { $i < $status_length } { incr i } {
			append status_length_flags "l"
		}
	}
}

# Display a dialog window that is scrollable
proc status_dialog { title msg detail {cmd ""} } {
    global trigger_help status_length_flags font_fixed font_helvetica
	global trigger_help cookie_status_dialog update_count
	global subtitle
	global bgtext

    catch {destroy .df}
    toplevel .df -class Dialog
    wm withdraw .df
    wm transient .df .
    wm group .df .
    wm title .df "$title - $subtitle"

	#puts "status_dialog: title '$title' detail '$detail' cmd '$cmd'"
	set text $msg
	if { $cmd != "" } {
		set c [ subst $cmd ]
		#puts "status_dialog: running '$c'"
		set result [ runjob $c ]
		#puts "status_dialog: result '$result'"
		if {[lindex $result 0]} {
			append text [lindex $result 1]
		}
	}
	#puts "status_dialog: lines '$lines'"
	set linecount [get_linecount [llength [split $text "\n"]]]
    frame .df.helpf
	text .df.helpf.msg -bg $bgtext -relief sunken -setgrid true \
		-wrap none \
		-width 85 -height $linecount \
		-yscrollcommand ".df.helpf.msg_sy set" \
		-xscrollcommand ".df.helpf.msg_sx set" \
		-font $font_fixed
    scrollbar .df.helpf.msg_sy -orient vert -command ".df.helpf.msg yview"
    scrollbar .df.helpf.msg_sx -orient hor -command ".df.helpf.msg xview"

	if { $cmd != "" } {
		new_timedisplay .df.helpf.msg $msg $cmd
	} else {
		.df.helpf.msg insert 1.0 $msg
	}
    .df.helpf.msg config -state disabled

	set trigger_help 0

    pack .df.helpf.msg_sy -side right -fill y -in .df.helpf
    pack .df.helpf.msg_sx -side bottom -fill x -in .df.helpf
    pack .df.helpf.msg    -side left -fill both -expand true -in .df.helpf

    frame .df.okframe
    button .df.b1 -text "Cancel" -width 10 \
		-command "set trigger_help 1; set cookie_status_dialog 0; set update_count 0;"
    pack .df.b1 -side left -expand true -ipady 1 -in .df.okframe
	if { $cmd != "" } {
		button .df.b2 -text "Refresh" -width 10 -command [list new_timedisplay .df.helpf.msg $msg $cmd ]
		pack .df.b2 -side left -expand true -ipady 1 -in .df.okframe
		if { $detail } {
			button .df.b3 -text "More Detail" -width 10 -command  [list more_detail .df.helpf.msg $msg $cmd ]
			pack .df.b3 -side left -expand true -ipady 1 -in .df.okframe
			button .df.b4 -text "Less Detail" -width 10 -command  [list less_detail .df.helpf.msg $msg $cmd ]
			pack .df.b4 -side left -expand true -ipady 1 -in .df.okframe
		}
	}

    pack .df.helpf   -side top -expand true -fill both -in .df
    pack .df.okframe -side bottom -in .df

    center_dialog .df
    set oldFocus [focus]
    focus .df
    catch { grab .df }

    update
    wm protocol .df WM_DELETE_WINDOW { set trigger_help 2 }
    scan [wm geometry .df] "%d%*c%d" xmin ymin
    wm minsize .df $xmin $ymin

	while { 1 } {
		tkwait variable trigger_help
		if { $trigger_help } {
			#puts "DONE"
			set trigger_help 0; set cookie_status_dialog 0; set update_count 0;
			break
		}
	}
	catch { grab release .df }
    focus $oldFocus
    destroy .df
	update idletasks
}

proc check_job_selection { y select } {
    set index [$select nearest $y]
	set value [$select get $index]
	set jobid [lindex $value 3]
	#puts "y $y, select $select, index $index, value '$value', jobid '$jobid'"
	if {[regexp {[^0-9]} $jobid]} {
		#puts "no selection"
		$select selection clear $index
		return -code break
	}
}

proc get_linecount { linecount } {
	set linecount [expr $linecount + 2]
	if { $linecount > 24 } {
		set linecount 24
	}
	return $linecount
}

# Display a window w/ output at top (scrollable) & a field to run a cmd at the bottom
#  You can brutally assume that you will get a lpq display
proc status_info_dialog { helpfunc buttonmsg topdata fieldname printer {fieldname2 "" } } {
    global trigger_help rhostq entrywidth font_fixed font_helvetica
	global bglistbox bgentry

    catch {destroy .sid}
    toplevel .sid -class Dialog
    wm withdraw .sid
    wm group .sid .
    wm transient .sid .
    wm title .sid "$buttonmsg - Print Queue $printer[at_rhostq]"

	set lines [ split $topdata "\n" ]
	set linecount [get_linecount [llength $lines]]
    frame .sid.helpf
	listbox .sid.helpf.msg -bg $bglistbox -relief sunken -setgrid true \
		-width 85 -height $linecount -yscrollcommand ".sid.helpf.msg_sy set" \
		-font $font_fixed \
		-selectmode extended
    scrollbar .sid.helpf.msg_sy -orient vert -command ".sid.helpf.msg yview"
	for { set i 0 } { $i < [ llength $lines ] } { incr i } {
		set line [lindex $lines $i]
		set job  [lindex $line 3]
		if { [regexp {Printer:} $line] || [regexp {Rank} $line ] } {
			.sid.helpf.msg insert end $line
		} elseif { $job != "" && ![regexp {[^0-9]} $job] } {
			.sid.helpf.msg insert end $line
		}
	}
    bind .sid.helpf.msg <Button-1> [list check_job_selection %y .sid.helpf.msg]
    #.sid.helpf.msg config -state disabled
    pack .sid.helpf.msg_sy -side right -fill y -in .sid.helpf
    pack .sid.helpf.msg    -side left -fill both -expand true -in .sid.helpf

    frame .sid.field
    label .sid.label -text "$buttonmsg\n$fieldname" -justify left
	entry .sid.value -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    pack .sid.label -side top -expand true -anchor sw -in .sid.field
    pack .sid.value -side top -expand true -fill x -anchor se -in .sid.field
	if { $fieldname2 != "" } {
		label .sid.label2 -text "$fieldname2" -justify left
		entry .sid.value2 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
		pack .sid.label2 -side top -expand true -anchor sw -in .sid.field
		pack .sid.value2 -side top -expand true -fill x -anchor se -in .sid.field
	}

    frame .sid.okframe
    button .sid.b1 -text "OK" -width 10 -command "set trigger_help 1"
    pack .sid.b1 -side left -expand true -ipady 1 -padx 1c -in .sid.okframe
    button .sid.b2 -text "Cancel" -width 10 \
		-command "set trigger_help 0; set cookie_status_dialog 0; set update_count 0;"
	pack .sid.b2 -side left -expand true -ipady 1 -padx 1c -in .sid.okframe
    button .sid.b3 -text "Help" -width 10 -command $helpfunc
    pack .sid.b3 -side left -expand true -ipady 1 -padx 1c -in .sid.okframe

    pack .sid.helpf   -side top -expand true -fill both -in .sid
    pack .sid.field   -side top -expand true -in .sid
    pack .sid.okframe -side top -in .sid

    center_dialog .sid
    set oldFocus [focus]
    focus .sid
    catch { grab set .sid }


    wm protocol .sid WM_DELETE_WINDOW { set trigger_help 0 }
    update
    scan [wm geometry .sid] "%d%*c%d" xmin ymin
    wm minsize .sid $xmin $ymin

	while { 1 } {
		tkwait variable trigger_help
		set listval ""
		if {$trigger_help == 1} {
			set v [.sid.helpf.msg curselection]
			#puts "curselection $v"
			foreach index [.sid.helpf.msg curselection] {
				set value [.sid.helpf.msg get $index]
				set jobid [lindex $value 3]
				#puts "index $index, value '$value', jobid '$jobid'"
				if {[regexp {[0-9]} $jobid]} {
					append listval [lindex $value 1] " "
				}
			}
			#puts "listval '$listval'"
			append listval [.sid.value get]
			#puts "final '$listval'"
			if { [llength $listval] == 0 } {
				popup_error_dialog "Please select a job or enter information"
				continue;
			}
			if { $fieldname2 != "" } {
				set v [.sid.value2 get]
				if { $v == "" } {
					popup_error_dialog "Please enter additional information"
					continue;
				}
				append listval " " $v
			}
		}
		break
	}
	catch { grab release .sid }
    focus $oldFocus
    destroy .sid
	update idletasks
    return $listval
}



# Center the dialog in the popup window
proc center_dialog {w {p ""}} {
	update idletasks
	if {$p == ""} {
			catch {set p [winfo parent $w]}
	}

	set py [expr [winfo y $p] + [winfo vrooty $p]]
	set px [expr [winfo x $p] + [winfo vrootx $p]]
	set wx [expr 0 - [winfo reqwidth $w]/2 ]
	set wy [expr 0 - [winfo reqheight $w]/2 ]
	#puts "center_diaglog: px $px, py $py, wx $wx, y $wy"
	if {$p != "" && [winfo ismapped $p] == 1} {
		# winfo vrootx $p = ul corner location of virtual root window (parent)
		# winfo x $p      = ul corner location with respect to parent (parent)
		# [winfo x $p] + [winfo vrootx $p] = ul location
		# -[winfo reqwidth $w]/2 + [winfo x $p] + [winfo vrootx $p] = middle location
		set x [expr $wx + [winfo width $p]/2 + $px]
		set y [expr $wy + [winfo height $p]/2 + $py]
	} else {
		# window seems to have no parent (or parent is unmapped) - center is root
		set x [expr $wx + [winfo screenwidth $w]/2]
		set y [expr $wy + [winfo screenheight $w]/2]
	}
	# if you want 'nested' use:
	#if { $x < $px } { set x $px }
	#if { $y < $py } { set y $py }
	# if you want 'centered' use:
	if { $x < 0 } { set x 0 }
	if { $y < 0 } { set y 0 }
	#puts "center_diaglog: px $px, py $py, wx $wx, wy $wy, x $x, y $y"
	wm group $w $p
	wm geometry $w +$x+$y
	wm deiconify $w
}

# Window that appears when you decide to add a printer
proc printer_type_dialog {} {
    global type_name trigger_3
    global printer_type_list subtitle

    # MJC
    # display list of printer types

    .list selection clear 0 end

    catch {destroy .pt}
    toplevel .pt -class Dialog
    wm withdraw .pt
    wm transient .pt .
    wm group .pt .
    wm title .pt "Add a Printer - $subtitle"

    label .pt.l1 -text "Type of Printer:" -anchor w -pady 3m
    pack .pt.l1 -side top
    foreach t $printer_type_list {
		set readabletypeproc ${t}_name
		radiobutton .pt.t_$t -text [$readabletypeproc] -variable type_name \
			-value $t -anchor w
		pack .pt.t_$t -side top -fill x -padx 2
    }
    set type_name [lindex $printer_type_list 0]

    frame .pt.buttons
    button .pt.ok -text "Create" -width 10 -command "set trigger_3 1"
    pack .pt.ok -side left -expand true -ipady 1 -pady 1 -in .pt.buttons

    button .pt.cancel -text "Cancel" -width 10 \
		-command "set trigger_3 0; set cookie_status_dialog 0; set update_count 0;"
    pack .pt.cancel -side left -expand true -ipady 1 -pady 1 -in .pt.buttons

    button .pt.help -text "Help" -width 10 -command printertype_help
    pack .pt.help -side left -expand true -ipady 1 -pady 1 -in .pt.buttons

    pack .pt.buttons -side top

    center_dialog .pt
    set oldFocus [focus]
    focus .pt
    catch { grab .pt }

    update
    scan [wm geometry .pt] "%d%*c%d" xmin ymin
    wm minsize .pt $xmin $ymin
    wm maxsize .pt 10000 $ymin
    set trigger_3 0
    tkwait variable trigger_3

	catch { grab release .pt }
    focus $oldFocus
    destroy .pt
	update idletasks

    if {$trigger_3 == 1} {
		return $type_name
    }
	#puts "printer_type_dialog: trigger_3 '$trigger_3'"

    return {}
}

# Create a spool directory
proc create_dir {spool_dir} {
    global user group mode uid

	if {$mode} {
		diemsg "LOGIC ERROR!  For some reason you are creating a directory in user mode!"
	}
	file mkdir $spool_dir
	if {![ file exists $spool_dir] } {
		warnmsg "Directory $spool_dir does not exist"
		return 1
	}
	if {[catch { file attributes $spool_dir -permissions 0700 } stuff]} {
		warnmsg "error fixing $spool_dir - $stuff"
	}
	return 0
}

# Force localhost to be udpated
proc resolve_spool_dir { spool_dir i } {
    global printer_names
    set queue [lindex [split $printer_names($i) '|'] 0]
	regsub {%P} $spool_dir $queue spool_dir
	#puts "resolve_spool_dir: $i, queue $queue, $spool_dir\n"
	return "$spool_dir"
}


# see if ghostscipt is installed and which drivers compiled in
proc find_gs {} {
    global gs_installed_drivers ghostscript gs_upp_drivers gsupdir

    set gs_installed_drivers ""
	set cmd [list $ghostscript -help]
	set gshelp [runjob $cmd]
	#puts "gshelp '$gshelp'"

    if { [string first "couldn't execute" $gshelp] != -1 } {
	popup_error_dialog "Ghostscript does not appear to be installed as $ghostscript.
This severely restricts your printing options unless either:

 a) You want to setup your printer to print text only, or
 b) Your printer can handle PostScript natively.

If neither of these apply, it would be best to install GhostScript now.
"
		lappend gs_installed_drivers {TEXT} {POSTSCRIPT}
		return
	}

    set avail [string first "Available devices:" [lindex $gshelp 1] ]
    incr avail [string length "Available devices:"]
    set availstr [string range $gshelp $avail end]
	regsub "\n\[A-Z\].*" $availstr {} availstr
	regsub -all "\n" $availstr { } availstr
	regsub -all "  *" $availstr { } availstr
	regsub  "^  *" $availstr {} availstr
	#puts "have '$availstr'"

	if { [ llength $availstr ] == 0 } {
			warnmsg "Error - GhostScript did not report available devices:
$gshelp
"
	}
    append gs_installed_drivers $availstr

#
# following two DO NOT depend upon ghostscript being installed
# please handle this more cleanly in future!
#
#    lappend gs_installed_drivers {TEXT} {POSTSCRIPT}
	#puts "gs_installed_drivers '$gs_installed_drivers'"
	if {[ lsearch $gs_installed_drivers "uniprint"] != -1 } {
		#puts "check '$gsupdir/*/lib/*.upp'"
		append gs_upp_drivers [ glob -nocomplain "$gsupdir/*/*.upp" ] " "
		append gs_upp_drivers [ glob -nocomplain "$gsupdir/*/*/*.upp" ] " "
		#puts "gs_upp_drivers '$gs_upp_drivers'"
	}
}

# see if pager is installed
proc check_pager { pager } {
	global a2ps mpage

	#puts "checking '$pager'"
	if { $pager == "mpage" } {
		set path $mpage
	} elseif { $pager == "a2ps" } {
		set path $a2ps
	} else {
		warnmsg "LOGIC ERROR: you have selected a bad pager '$pager'"
		return
	}
	#puts "path '$path'"
    catch {exec $path -= -x -XX </dev/null > /dev/null} input
	#puts "got '$input'"
    if { [string first "couldn't execute" $input] != -1 } {
	popup_error_dialog "
The text to PostScript conversion program '$pager' does not
appear to be installed as '$path'.  This program is used by the print
filters and its absence may cause problems.

It would probably be best to install $pager now.
"
    }
}

# see if smbclient is installed
proc find_samba {} {
    global smbclient smbwarn
	if { $smbwarn } {
		catch {exec $smbclient -= -X < /dev/null > /dev/null"} input
		if { [string first "couldn't execute" $input] != -1 } {
		warnmsg "
The Samba smbclient '$smbclient' program does not appear to be installed.

You will not be able to print to a SMB/Windows 95/NT printer
without it installed.  Please exit and install samba before
configuring any SMB/Windows 95/NT printer entries.
"
		}
		set smbwarn 0
	}
}


# Get a particular item from a list
proc get_selected_index {} {
	set v [.list curselection]
	if {[llength $v] > 1} {
		popup_error_dialog "You have multiple printers selected"
		return ""
	}
	return $v
}
proc get_selected_indexes {} {
	return [.list curselection]
}

###########  UTILITIES END            ################################

###########  WINDOW POPUPS            ################################

# Function to make something happen when you push the "Add" button
proc button_add {} {
    global print_count printer_names printer_entry printer_comment
    global printer_info printer_type spooldirectory printer_used_by printer_force_localhost
	global pc_showing
	#puts "button_add: pc_showing '$pc_showing'"
    if {!$pc_showing} {
		popup_error_dialog "Not viewing Printcap information"
		return
    }
		
    set type [printer_type_dialog]
    # user may have selected "Cancel"
	if { $type == {} } { return }

    # First find a candidate lpX
    # (Make first printer added "lp")
    set done 0
    set num 0
    if {$print_count == 0} {
		set lpx "lp"
		set done 1
    }
    while {$done == 0} {
        regsub -all "_" $num "" num
        regsub -all " " $num "" num
		set lpx "lp$num"
		set done 1
		for {set i 0} {$i < $print_count} {incr i} {
			if {[regexp $lpx $printer_names($i)] == 1} {
			set done 0
			break
			}
		}
		incr num
    }

    set i $print_count
    incr print_count

    set printer_names($i) "$lpx"
    set printer_type($i) $type
    set remote_type($i) ""
    set printer_info($i) {}
    set printer_entry($i) "$lpx:sd=$spooldirectory/%P:"
    set printer_used_by($i) ""
    set printer_force_localhost($i) ""
    set printer_comments($i) ""

#
#   default to suppress headers on
#
    set_printcap_switch $i "sh" 1
    set suppress_headers [printcap_switch $i "sh"]


    if {[edit_entry $i] != 1} {
		incr print_count -1
    }
}

# Function to make something happen when you push the "Edit" button
proc button_edit {} {
	global pc_showing
    if {!$pc_showing} {
		popup_error_dialog "Not viewing Printcap information"
		return
    }
		
    set i [get_selected_index]
    if {$i == ""} {
        popup_error_dialog "Please highlight a print queue first"
		return
    }
    edit_entry $i
}


proc ct {} {
	global change trigger
	#puts "ct: change"
	set change 1
	set trigger 1
}


# Edit a printer database entry
proc edit_entry {i} {
    global print_count printer_names printer_entry printer_type printer_type_list
    global trigger printer_used_by printer_force_localhost change
	global ifhp_path ifhp_conf mode
    global smb_share smb_host smb_printer smb_hostip
	global smb_password smb_password_dup smb_username smb_workgroup smb_crlf
	global entrywidth smbwarn remote_type smb_remote_mode
	global auto_filtertype printer_info
	global ifhpkey userkey novellkey appletalkkey
	global change_local entrywidth font_fixed font_helvetica
	global subtitle auto_vars ifhp_vars smb_vars bgentry

	foreach v $auto_vars {
		if {![info exists $v]} {
			global $v
		}
	}
	foreach v $ifhp_vars {
		if {![info exists $v]} {
			global $v
		}
	}
	foreach v $smb_vars {
		if {![info exists $v]} {
			global $v
		}
	}

	notroot
	set orig_printcap $printer_entry($i)
	set orig_printer_info $printer_info($i)
	set orig_printer_used_by $printer_used_by($i)
	set orig_printer_force_localhost $printer_force_localhost($i)

    if {$printer_type($i) == "SMB"} {
		if { !$smbwarn } {
			set smbwarn 1
			popup_info_dialog \
"               - WARNING -

The use of a remote SMB/Windows 95/NT Printer, Novell Printer, or
AppleTalk printer may require a username and password for its
server.  These are stored unencrypted in a file the Spool Directory.
Older versions of the Samba smbclient required the user name and
password to be passed as command line paramters and this information
was viewable by using the ps command when running as a user on the
server.  The programs that connect to Novell and AppleTalk printers
have the same behavior.

It is therefore recommended that the username and password for
remote printer NOT BE the same as that of a user account on the
local host.  That way, if this information is compromised damage
is limited to unauthorized use of the printer.

Newer releases of Samba allow the user name and password to be
placed in a file and uses the -A <filename> option to have smbclient
read the information from the file.  The other applications should
be modified in a similar manner.  "
		}
    }





	set change 1
	while { 1 } {
		if { $change } {
			set change 0
			catch {destroy .e}
			toplevel .e -class Dialog
			wm withdraw .e
			wm transient .e .
			wm group .e .
			wm title .e "Edit [$printer_type($i)_name] Entry - $subtitle"

			set traversal_list ""
			
			frame .e.f1

			frame .e.f1.name
			label .e.f1.name.l1 -text "Names (name|alias1|...)"
			entry .e.f1.name.v1 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
			button .e.f1.name.h1 -text "?" -font $font_fixed -width 0 -command names_help
			.e.f1.name.v1 insert 0 $printer_names($i)
			lappend traversal_list .e.v1
			pack .e.f1.name.l1 -side left -anchor w
			pack .e.f1.name.h1 -side right -expand false -ipady 1
			pack .e.f1.name.v1 -side right -fill x -anchor e
			pack .e.f1.name -fill x -side top

			frame .e.f1.comment
			label .e.f1.comment.l20 -text "Comments"
			entry .e.f1.comment.v20 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
			button .e.f1.comment.h20 -text "?" -font $font_fixed -width 0 -command comment_help
			.e.f1.comment.v20 insert 0 [printcap_field $i "cm=" ""]
			lappend traversal_list .e.f1.comment.v20
			pack .e.f1.comment.l20 -side left -anchor w
			pack .e.f1.comment.h20 -side right -expand false -ipady 1
			pack .e.f1.comment.v20 -side right -fill x -anchor e
			pack .e.f1.comment -fill x -side top

			if { $mode == 0 || $printer_used_by($i) != "CLIENT" } {
				frame .e.f1.spool
				label .e.f1.spool.l2 -text "Spool Directory"
				entry .e.f1.spool.v2 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
				button .e.f1.spool.h2 -text "?" -font $font_fixed -width 0 -command spool_help
				.e.f1.spool.v2 insert 0 [printcap_field $i "sd=" ""]
				lappend traversal_list .e.f1.spool.v2
				pack .e.f1.spool.l2 -side left -anchor w
				pack .e.f1.spool.h2 -side right -expand false -ipady 1
				pack .e.f1.spool.v2 -side right -fill x -anchor e
				pack .e.f1.spool -fill x -side top
			}
			pack .e.f1 -side top -fill both -padx 2 -pady 1

			# fill in defaults for select_filter panel
			parse_auto_filter_params $printer_info($i)
			parse_ifhp_params $auto_ifhp_options

			frame .e.f2
			$printer_type($i)_addpanel .e.f2 $i
			pack .e.f2 -side top -fill both

			set change 0
			set change_local $printer_type($i)

			#puts "edit_entry: change_local '$change_local'"

			frame .e.change
			frame .e.change.label
			label .e.change.label.lcba -text "Printer Type:"
			button .e.change.label.bcba -text "?" -font $font_fixed -width 0 -command printertype_help
			pack .e.change.label.lcba -side left
			pack .e.change.label.bcba -side right
			pack .e.change.label -side top -fill x

			frame .e.change.f1
			frame .e.change.f2
			radiobutton .e.change.f1.cb1 -text "Device" -variable change_local -value "DEVICE" -command ct
			radiobutton .e.change.f1.cb2 -text "Queue" -variable change_local -value "QUEUE" -command ct
			radiobutton .e.change.f1.cb3 -text "TCP/IP Socket" -variable change_local -value "SOCKET" -command ct
			radiobutton .e.change.f1.cb4 -text "SMB/Novell/AppleTalk" -variable change_local -value "SMB" -command ct
			radiobutton .e.change.f2.cb5 -text "Load Balance" -variable change_local -value "PRINTPOOL" -command ct
			radiobutton .e.change.f2.cb6 -text "Dummy" -variable change_local -value "DUMMY" -command ct
			radiobutton .e.change.f2.cb7 -text "Unknown" -variable change_local -value "UNKNOWN" -command ct
			pack .e.change.f1.cb1 -side left -fill x -expand true
			pack .e.change.f1.cb2 -side left -fill x -expand true
			pack .e.change.f1.cb3 -side left -fill x -expand true
			pack .e.change.f1.cb4 -side left -fill x -expand true
			pack .e.change.f2.cb5 -side left -fill x -expand true
			pack .e.change.f2.cb6 -side left -fill x -expand true
			pack .e.change.f2.cb7 -side left -fill x -expand true

			pack .e.change.f1 -fill x -side top -anchor c
			pack .e.change.f2 -fill x -side top -anchor c
			pack .e.change -fill x -side top

			frame .e.buttons

			button .e.buttons.b1 -text "OK" -width 10 -command "set trigger 1"
			pack .e.buttons.b1 -side left -expand true -ipady 1
			button .e.buttons.b2 -text "Cancel" -width 10 \
				-command "set trigger 0"
			pack .e.buttons.b2 -side left -expand true -ipady 1
			button .e.buttons.b3 -text "Advanced Options" -width 16 -command "advanced_options .e $i"
			pack .e.buttons.b3 -side left -expand true -ipady 1
			pack .e.buttons -side bottom -expand true -fill x -padx 4 -pady 4
		}


		center_dialog .e
		set oldFocus [focus]
		focus .e
		catch { grab set .e }

		update
		scan [wm geometry .e] "%d%*c%d" xmin ymin
		wm minsize .e $xmin $ymin
		wm maxsize .e 10000 $ymin

		tkwait variable trigger
		#puts "edit_entry: trigger '$trigger', update change $change"
		if { $trigger == 1 } {
			# fix up the spool queue entry
			#puts "edit_entry: update change $change"
			set name [.e.f1.name.v1 get]
			set names [split $name '|']
			set name ""
			set fail 0
			for { set j 0 } { $j < [llength $names] } { incr j } {
				set n [string trim [ lindex $names $j] ]
				if { $n != "" } {
					set msg [ check_printer_name $n $j ]
					if { $msg != "" } {
						popup_error_dialog $msg
						set fail 1
						continue
					}
					if { $name != "" } {
						append name "|"
					 } else {
						set myname $n
					}
					append name "$n"
				}
			}
			if { $fail } { continue }
			#puts "new printer name '$name', myname $myname"
			if { $name == "" } {
				popup_error_dialog "Please set the printer name"
				continue
			}
			set printer_names($i) $name
			regsub {^[^:]*:} $printer_entry($i) "$name:" printer_entry($i)

			if { $mode != 0 } {
				set_printcap_field $i "sd=" ""
			}

			regsub ":" [.e.f1.comment.v20 get] "-" cmfield
			set_printcap_field $i "cm=" $cmfield

			if {[printcap_field $i "mx=" ""] == ""} {
				set_printcap_field $i "mx=" "0"
			}

			if { $change } {
				set $printer_type($i) $change_local
				${change_local}_new $i
				continue
			}
			set change_local $printer_type($i)

			$printer_type($i)_updateentry .e.f2 $i

			#puts "edit_entry: change_local '$change_local', device '$printer_type($i)', force_localhost '$printer_force_localhost($i)', used_by '$printer_used_by($i)'"
			if { $printer_type($i) == "DEVICE" } {
				set lp [printcap_field $i "lp=" ""]
				#puts "edit_entry: lp '$lp', change_local '$change_local', force_localhost '$printer_force_localhost($i)'"
				if { $lp == "" } {
					popup_error_dialog "Please set device"
					continue
				}
				if { $printer_used_by($i) == "CLIENT" } {
					if { $printer_force_localhost($i) != "NO" || 
						[printcap_switch $i "direct"] == 0 } {
						popup_error_dialog "Client printing to device requires ':force_localhost@' and 'direct'"
						continue;
					}
				}
			} elseif { $printer_type($i) == "QUEUE" } {
				#puts "printcap '$printer_entry($i)'"
				set lp [ printcap_field $i "lp=" "" ]
				#puts "lp '$lp'"
				set lp [split $lp @]
				set queue [lindex $lp 0]
				set host [lindex $lp 1]
				#puts "lp '$lp' queue '$queue' host '$host'"
				if { $host == "" } {
					if { $printer_used_by($i) == "CLIENT" || $printer_used_by($i) == "BOTH" } {
						if { $printer_force_localhost($i) != "YES" } {
							popup_error_dialog "Please set Remote Host or force_localhost"
							continue
						}
					}
					if { $printer_used_by($i) == "SERVER" || $printer_used_by($i) == "BOTH" } {
						popup_error_dialog "Please set Remote Host"
						continue
					}
				}
			} elseif { $printer_type($i) == "SMB" } {
				set spool_dir [ resolve_spool_dir [printcap_field $i "sd=" ""] $i ]
				if { $smb_password != $smb_password_dup } {
					popup_error_dialog "Passwords do not match"
					continue
				}

				if { $remote_type($i) == "SMB" \
					&& ( $smb_host == "" || $smb_printer == "" \
					|| $smb_workgroup == "" ) } {
					popup_error_dialog "Please set Print Server Host,
Printer SMB Name, and Workgroup
"
					continue
				} elseif { $remote_type($i) == $novellkey \
					&& ( $smb_host == "" || $smb_printer == "" \
					) } {
					popup_error_dialog "
Please set Print Server Host,
Print Queue Name
"
					continue
				} elseif { $remote_type($i) == $appletalkkey \
					&& ( $smb_host == "" || $smb_printer == "" \
					) }  {
					popup_error_dialog "
Please set Print Server Host,
Printer Name
"
					continue
				}
				if { $remote_type($i) != $userkey } {
					# now we get the authorization file
					set auth_file ""
					#puts "checking smb_authfile '$smb_authfile'"
					if {[regexp {^/} $smb_authfile]} {
						set auth_file $smb_authfile
					} elseif { $spool_dir != ""} {
						set auth_file $spool_dir/$smb_authfile
					}
					#puts "checking auth_file '$auth_file'"
					if { $mode == 0 && $spool_dir == "" } {
						popup_error_dialog "You need to specify a spool queue directory"
						continue
					}
					if { $auth_file == "" } {
						popup_error_dialog "Bad or missing authentication file"
						continue
					}
				}
			} elseif { $printer_type($i) == "SOCKET" } {
				set lp [printcap_field $i "lp=" ""]
				if { [regexp {@} $lp ] } {
					popup_error_dialog "Your lp entry has queue\@host not host\%port"
					continue
				}
				set lp [split $lp '%']
				set host [lindex $lp 0]
				set port [lindex $lp 1]
				if { $host == "" || $port == "" } {
					popup_error_dialog "Please set both Remote Host and Port"
					continue
				}
				if { $printer_used_by($i) == "CLIENT" } {
					if { $printer_force_localhost($i) != "NO" || 
						[printcap_switch $i "direct"] == 0 } {
						popup_error_dialog "Client printing to socket requires ':force_localhost@' and ':direct'"
						continue;
					}
				}
			} elseif { $printer_type($i) == "PRINTPOOL" } {
				set sv [printcap_field $i "sv=" ""]
				if { $sv == "" } {
popup_error_dialog "Please set the server queues for the load balance queue "
					continue
				}
				foreach pr [ split $sv "," ] {
					if { $pr == "" } { continue }
					# find the printer
					set found 0
					set j 0
					for { set j 0 } { $j < $print_count } { incr j } {
						set name $printer_names($j)
						set names [split $name '|']
						#puts "split $names"
						set n [ lsearch -exact $names $pr ]
						if { $n != -1 } {
							set found 1
							break
						}
					}
					if { $found == 0 } {
						popup_error_dialog \
						"Your printer pool is '$sv' and you do not have printer '$pr'"
					} else {
						set ss [printcap_field $j "ss=" "" ]
						if { $ss != $myname } {
						popup_error_dialog \
						"Your printer pool is '$sv' and printer $name is not servicing it"
						}
					}
				}
			} elseif { $printer_type($i) == "DUMMY" } {
				set p [string first "." $printer_names($i) ]
				if { $p != 0 } {
					popup_error_dialog "printer name '$printer_names($i)' -  period (.) not first character"

				}
			} elseif { $printer_type($i) == "UNKNOWN" } {
				set lp [printcap_field $i "lp=" ""]
				if { $lp == "" } {
				popup_error_dialog \
				"
Missing :lp value.  You should edit this printcap entry by
hand or modify its type by using the ADVANCED options.
"
				}
			} else {
				popup_error_dialog \
				"Your printer type is '$printer_type($i)' - no action taken"
				continue
			}
			set ss [printcap_field $i "ss=" ""]
			if { $printer_type($i) != "DUMMY" && $ss != "" } {
				for { set j 0 } { $j < $print_count } { incr j } {
					set sv [printcap_field $j "sv=" "" ]
					set name $printer_names($j)
					set names [split $name '|']
					#puts "split $names"
					set n [ lsearch -exact $names $myname ]
					if { $n != -1 } {
						set found 1
						break
					}
				}
				if { $found == 0 } {
					popup_error_dialog \
					"Your are servicing Printer Pool '$ss' and it is not in printcap"
				} else {
					popup_error_dialog \
					"Your are servicing Printer Pool '$ss' and it only has servers '$sv'"
				}
			}

			# now we prepare to update
			remove_printcap_field $i "server"
			remove_printcap_field $i "client"
			remove_printcap_field $i "force_localhost"
			set printer_info($i) [set_printer_info]
			#puts "before cleanup '$printer_entry($i)'"
			set printer_entry($i) [cleanup_printer_entry $printer_entry($i)]
			if { ![ popup_yesno_dialog "PRINTCAP:
[make_printer_entry $i]

Do you want to write this printcap entry?
" 0 $font_fixed ]			 } {
				#puts "change_local '$change_local', type $printer_type($i)";
				$printer_type($i)_setup $i
				sync $i
			} else {
				continue
			}
		} else {
			set $printer_entry($i) orig_printcap
			set $printer_info($i) orig_printer_info
			set $printer_used_by($i) orig_printer_used_by
			set $printer_force_localhost($i) orig_printer_force_localhost
		}
		break
	}

	catch { grab release .e }
    focus $oldFocus
    destroy .e
	update idletasks
    return $trigger
}

proc check_printer_name { v location } {
	set len [string length $v ]
	if { $len == 0 } {
		return "name '$v' too short"
	}
	if { $len == 0 || $len > 32 } {
		return "name '$v' too long"
	}
	if { $location == 0 } {
		if { [ regexp {[^\-a-zA-Z0-9_\*.]} $v found ] } {
			return "bad character '$found' in '$v'"
		}
		set p [string first "." $v ]
		if { $p > 0 } {
			return "period (.) not first character"
		}
	} else {
		if { [ regexp {[^\-a-zA-Z0-9_\*]} $v found ] } {
			return "bad character '$found' in '$v'"
		}
	}
	return ""
}

# Create a Window with Advanced Options to also process.
proc advanced_options {e i} {
    global print_count printer_names printer_entry printer_type printer_type_list
    global trigger2 global suppress_headers
	global entrywidth reserved_options
	global change_local entrywidth
	global font_fixed font_helvetica subtitle
	global bgtext bgentry

    set queue [lindex [split $printer_names($i) '|'] 0]

    catch {destroy .e.a}
    toplevel .e.a -class Dialog
    wm withdraw .e.a
    wm transient .e.a .
    wm group .e.a .
    wm title .e.a "Advanced Capabilities - $subtitle"

    frame .e.a.f1
    frame .e.a.f3


    set traversal_list ""
    # accounting file
    frame .e.a.af
    label .e.a.l12 -text "Accounting File"
	entry .e.a.v12 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h12 -text "?" -font $font_fixed -width 0 -command accounting_help
    .e.a.v12 insert 0 [printcap_field $i "af=" "acct"]
    lappend traversal_list .e.a.v12
    pack .e.a.l12 -side left -anchor w -in .e.a.af
    pack .e.a.h12 -side right -expand false -ipady 1 -in .e.a.af
    pack .e.a.v12 -side right -fill x -anchor e -in .e.a.af
    pack .e.a.af -fill x -side top -in .e.a.f1

    # log file
    frame .e.a.lf
    label .e.a.l19 -text "Log File"
	entry .e.a.v19 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h19 -text "?" -font $font_fixed -width 0 -command logfile_help
    .e.a.v19 insert 0 [printcap_field $i "lf=" "log"]
    lappend traversal_list .e.a.v19
    pack .e.a.l19 -side left -anchor w -in .e.a.lf
    pack .e.a.h19 -side right -expand false -ipady 1 -in .e.a.lf
    pack .e.a.v19 -side right -fill x -anchor e -in .e.a.lf
    pack .e.a.lf -fill x -side top -in .e.a.f1


	# file length
    frame .e.a.mx
    label .e.a.l3 -text "File Limit in Kb (0 = no limit)" -anchor w
	entry .e.a.v3 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h3 -text "?" -font $font_fixed -width 0 -command filelimit_help
    .e.a.v3 insert 0 [printcap_field $i "mx=" "0"]
    lappend traversal_list .e.a.v3
    pack .e.a.l3 -side left -in .e.a.mx
    pack .e.a.h3 -side right -expand false -ipady 1 -in .e.a.mx
    pack .e.a.v3 -side right -fill x -in .e.a.mx
    pack .e.a.mx -fill x -side top -in .e.a.f1


    # maximum number of copies
    frame .e.a.mc
    label .e.a.l17 -text "Maximum Copies"
	entry .e.a.v17 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h17 -text "?" -font $font_fixed -width 0 -command maxcopies_help
    .e.a.v17 insert 0 [printcap_field $i "mc=" ""]
    lappend traversal_list .e.a.v17
    pack .e.a.l17 -side left -anchor w -in .e.a.mc
    pack .e.a.h17 -side right -expand false -ipady 1 -in .e.a.mc
    pack .e.a.v17 -side right -fill x -anchor e -in .e.a.mc
    pack .e.a.mc -fill x -side top -in .e.a.f1


    # suppress header
    frame .e.a.sh
	set shset [printcap_field $i "sh" "NO" ]
	if { $shset == "NO" } {
		set suppress_headers 1
	} else {
		set suppress_headers [printcap_switch $i "sh"]
	}
    checkbutton .e.a.v18 -text "No Banner Pages (:sh)" -anchor w -variable suppress_headers
    button .e.a.h18 -text "?" -font $font_fixed -width 0 -command suppress_headers_help
    lappend traversal_list .e.a.v18
    pack .e.a.h18 -side right -expand false -ipady 1 -in .e.a.sh
    pack .e.a.v18 -side left -fill x -anchor w -in .e.a.sh
    pack .e.a.sh -fill x -side top -in .e.a.f1

    # banner printer
    frame .e.a.bp
    label .e.a.l23 -text "Banner Generator Program"
	entry .e.a.v23 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h23 -text "?" -font $font_fixed -width 0 -command banner_help
    .e.a.v23 insert 0 [printcap_field $i "bp=" ""]
    lappend traversal_list .e.a.v23
    pack .e.a.l23 -side left -anchor w -in .e.a.bp
    pack .e.a.h23 -side right -expand false -ipady 1 -in .e.a.bp
    pack .e.a.v23 -side right -fill x -anchor e -in .e.a.bp
    pack .e.a.bp -fill x -side top -in .e.a.f1

    # output filter
    frame .e.a.of
    label .e.a.l16 -text "OF Filter (Accounting + Banners)"
	entry .e.a.v16 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h16 -text "?" -font $font_fixed -width 0 -command outputfilter_help
    .e.a.v16 insert 0 [printcap_field $i "of=" ""]
    lappend traversal_list .e.a.v16
    pack .e.a.l16 -side left -anchor w -in .e.a.of
    pack .e.a.h16 -side right -expand false -ipady 1 -in .e.a.of
    pack .e.a.v16 -side right -fill x -anchor e -in .e.a.of
    pack .e.a.of -fill x -side top -in .e.a.f1


    # main load balance queue
    frame .e.a.ss
    label .e.a.l16b -text "Service Load Balance Queue"
	entry .e.a.v16b -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h16b -text "?" -font $font_fixed -width 0 -command balanceserver_help
    .e.a.v16b insert 0 [printcap_field $i "ss=" ""]
    lappend traversal_list .e.a.v16b
    pack .e.a.l16b -side left -anchor w -in .e.a.ss
    pack .e.a.h16b -side right -expand false -ipady 1 -in .e.a.ss
    pack .e.a.v16b -side right -fill x -anchor e -in .e.a.ss
    pack .e.a.ss -fill x -side top -in .e.a.f1

    # main load balance queue
    frame .e.a.tc
    label .e.a.l16c -text "TC (Include) Entries"
	entry .e.a.v16c -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h16c -text "?" -font $font_fixed -width 0 -command tc_help
    .e.a.v16c insert 0 [printcap_field $i "tc=" ""]
    lappend traversal_list .e.a.v16c
    pack .e.a.l16c -side left -anchor w -in .e.a.tc
    pack .e.a.h16c -side right -expand false -ipady 1 -in .e.a.tc
    pack .e.a.v16c -side right -fill x -anchor e -in .e.a.tc
    pack .e.a.tc -fill x -side top -in .e.a.f1


    # stty
    frame .e.a.stty
    label .e.a.l24 -text "Serial Port (stty) Configuration"
	entry .e.a.v24 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button .e.a.h24 -text "?" -font $font_fixed -width 0 -command outputstty_help
    .e.a.v24 insert 0 [printcap_field $i "stty=" ""]
    lappend traversal_list .e.a.v24
    pack .e.a.l24 -side left -anchor w -in .e.a.stty
    pack .e.a.h24 -side right -expand false -ipady 1 -in .e.a.stty
    pack .e.a.v24 -side right -fill x -anchor e -in .e.a.stty
    pack .e.a.stty -fill x -side top -in .e.a.f1

    frame .e.a.options
    label .e.a.l33 -text "User Specified Printcap Options (One per Line)"
    button .e.a.h33 -text "?" -font $font_fixed -width 0 -command useroption_help
    pack .e.a.l33 -side left -anchor w -in .e.a.options
    pack .e.a.h33 -side right -expand false -ipady 1 -in .e.a.options
    pack .e.a.options -fill x -side top -in .e.a.f1


    frame .e.a.area
	text .e.a.t33 -bg $bgtext -width $entrywidth -height 5 -setgrid true \
		-yscrollcommand {.e.a.s33 set }
	set options [ get_printer_value $printer_entry($i) ]
	set options [remove_fields_in_printcap $options $reserved_options]
	#puts "options '$options'"
	regsub {:$} $options "" options
	regsub {^:} $options "" options
	regsub -all {:} $options "\n:" options
	if { $options != "" } {
		.e.a.t33 insert end ":$options"
	}
	scrollbar .e.a.s33 -command { .e.a.t33 yview }
	pack .e.a.s33 -side right -fill y -in .e.a.area
	pack .e.a.t33 -side left -fill both -expand true -in .e.a.area
    pack .e.a.area -fill x -side top -in .e.a.f1
#
#    frame .e.a.change
#    frame .e.a.change.f1
#    frame .e.a.change.f2
#    frame .e.a.change.label
#    label .e.a.lcba -text "Change Printer Type"
#    button .e.a.bcba -text "?" -font $font_fixed -width 0 -command printertype_help
#    radiobutton .e.a.cb0 -text "No" -variable change_local -value "NO"
#    radiobutton .e.a.cb1 -text "Device" -variable change_local -value "DEVICE"
#    radiobutton .e.a.cb2 -text "Queue" -variable change_local -value "QUEUE"
#    radiobutton .e.a.cb3 -text "SMB/Novell/AppleTalk" -variable change_local -value "SMB"
#    radiobutton .e.a.cb4 -text "TCP/IP Socket" -variable change_local -value "SOCKET"
#    radiobutton .e.a.cb5 -text "Load Balance" -variable change_local -value "PRINTPOOL"
#    radiobutton .e.a.cb6 -text "Dummy" -variable change_local -value "DUMMY"
#    radiobutton .e.a.cb7 -text "Unknown" -variable change_local -value "UNKNOWN"
#	pack .e.a.cb0 -side left -fill x -expand true -in .e.a.change.f1
#	pack .e.a.cb1 -side left -fill x -expand true -in .e.a.change.f1
#	pack .e.a.cb2 -side left -fill x -expand true -in .e.a.change.f1
#	pack .e.a.cb3 -side left -fill x -expand true -in .e.a.change.f1
#	pack .e.a.cb4 -side left -fill x -expand true -in .e.a.change.f2
#	pack .e.a.cb5 -side left -fill x -expand true -in .e.a.change.f2
#	pack .e.a.cb6 -side left -fill x -expand true -in .e.a.change.f2
#	pack .e.a.cb7 -side left -fill x -expand true -in .e.a.change.f2
#	pack .e.a.lcba -side left  -in .e.a.change.label
#	pack .e.a.bcba -side right -in .e.a.change.label
#    pack .e.a.change.label -side top -fill x -in .e.a.change
#    pack .e.a.change.f1 -fill x -side top -anchor c -in .e.a.change
#    pack .e.a.change.f2 -fill x -side top -anchor c -in .e.a.change
#    pack .e.a.change -fill x -side top -in .e.a.f1

    frame .e.a.buttons
    button .e.a.b1 -text "OK" -width 10 -command "set trigger2 1"
    pack .e.a.b1 -side left -expand true -ipady 1 -in .e.a.buttons
    button .e.a.b2 -text "Cancel" -width 10 \
		-command "set trigger2 0; set cookie_status_dialog 0; set update_count 0;"
    pack .e.a.b2 -side left -expand true -ipady 1 -in .e.a.buttons

    pack .e.a.buttons -side bottom -expand true -fill x -padx 4 -pady 4 -in .e.a
    pack .e.a.f3 -side bottom -expand true -fill x

    pack .e.a.f1 -side top -fill both -padx 2 -pady 1 -in .e.a

    center_dialog .e.a
    set oldFocus [focus]
    focus .e.a
    catch { grab .e.a }

    update
    scan [wm geometry .e.a] "%d%*c%d" xmin ymin
    wm minsize .e.a $xmin $ymin
    wm maxsize .e.a 10000 $ymin
    tkwait variable trigger2

    if {$trigger2 == 1} {
        # Replace colons, since LPRng thinks they're new lines
        # Set printcap fields

		${change_local}_new $i

		set propts [ get_fields_in_printcap $printer_entry($i) $reserved_options ]
		#puts "new options '[.e.a.t33 get 1.0 end]'"
		set options [remove_fields_in_printcap ":[.e.a.t33 get 1.0 end ]:" $reserved_options ]
		#puts "propts '$propts' new options '$options'"
		set printer_entry($i) "$propts$options"
		#puts "pc entry '$printer_entry($i)'"

		set_printcap_switch $i "sh" $suppress_headers
        regsub ":" [.e.a.v3 get] "-" mx
		set_printcap_field $i "mx=" $mx
        regsub ":" [.e.a.v12 get] "-" af
		set_printcap_field $i "af=" $af
        regsub ":" [.e.a.v16 get] "-" of
		set_printcap_field $i "of=" $of
        regsub ":" [.e.a.v17 get] "-" mc
		set_printcap_field $i "mc=" $mc
        regsub ":" [.e.a.v19 get] "-" lf
		set_printcap_field $i "lf=" $lf
        regsub ":" [.e.a.v23 get] "-" bp
		set_printcap_field $i "bp=" $bp
        regsub ":" [.e.a.v24 get] "-" stty
		set_printcap_field $i "stty=" $stty
        regsub ":" [.e.a.v16b get] "-" ss
		set ss [cleanup_list_entry $ss ]
		regsub {^,} $ss "" ss
		regsub {,$} $ss "" ss
		set_printcap_field $i "ss=" $ss
        regsub ":" [.e.a.v16c get] "-" tc
		set tc [cleanup_list_entry $tc ]
		regsub {^,} $tc "" tc
		regsub {,$} $tc "" tc
		set_printcap_field $i "tc=" $tc
    }

	set printer_entry($i) [ cleanup_printer_entry $printer_entry($i) ]

	catch { grab release .e.a }
    focus $oldFocus
    destroy .e.a
	update idletasks
    return
}

proc button_delete {} {
    global printer_names delete_index printer_type user
	global pc_showing
    if {!$pc_showing} {
		popup_error_dialog "Not viewing Printcap information"
		return
    }
		

    set i [.list curselection]
    if {$i == ""} {
        popup_error_dialog "Please highlight a print queue first"
		return
    }


	set res [popup_continue_dialog "Delete $printer_names($i)?"]
    if {$res == 1} {
		return
    }
    set delete_index $i
	if { $printer_type($i) != "DUMMY" } {
		set spool_dir [ resolve_spool_dir [printcap_field $i "sd=" ""] $i ]
		if {$spool_dir != "" } {
			set res [popup_continue_dialog "Delete spool directory '$spool_dir'?"]
			if {$res != 1} {
				set cmd [list /bin/rm -Rf $spool_dir]
				set status [runjob $cmd]
				if {![lindex $status 0]} {
					popup_error_dialog "Could not run $cmd - \n[lindex $status 1]"
				}
			}
		}
	}
    sync -1
}


#######################  ACTION IMPLEMENTATIONS START ######################

# Change the rhostq value
proc change_rhost {} {
    global rhostq localhostname entrywidth
    global trigger3_help
	global font_fixed font_helvetica
	global bgtext bgentry

    catch {destroy .rhost}
    toplevel .rhost -class Dialog
    wm withdraw .rhost
    wm transient .rhost .
    wm group .rhost .
    wm title .rhost "Select Host to Monitor Queues"
	set n $rhostq
	if { $n == "" } {
		set n "the default server information"
	}

    set msg "Currently, $n is being used for the server.  You can explicitly
specify a server to monitor,  or set the value to 'default' which
will use the default host.  The server information is obtained from
the printcap information and is displayed as 'queue@host' in the
status information.

By specifying a server host name you can perform management operations
on a specific server.  This may be necessary when there is a complex
print spooling system.

"
    frame .rhost.helpf
	text .rhost.helpf.msg -bg $bgtext -setgrid true -width 60 -height 8 -font $font_helvetica -relief flat
    .rhost.helpf.msg insert 1.0 $msg
    .rhost.helpf.msg config -state disabled
    pack .rhost.helpf.msg    -side left -fill both -expand true -in .rhost.helpf

    frame .rhost.newhost
    label .rhost.l99 -text "Monitor Queues on Host "
	entry .rhost.v99 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    .rhost.v99 insert 0 "$rhostq"
    button .rhost.b0 -text "Use defaults" -width 10 -command "change_rhost_local " \
		     -font  $font_helvetica

    pack .rhost.l99 -side left -anchor e -in .rhost.newhost
    pack .rhost.b0 -side right -anchor e -expand true -ipady 1 -padx 5m -in .rhost.newhost
    pack .rhost.v99 -side right -anchor w -expand false -pady 1 -in .rhost.newhost

    frame .rhost.okframe
    button .rhost.b1 -text "Change Host" -width 10 -command "set trigger3_help 1"
    pack .rhost.b1 -side left -expand true -ipady 1 -padx 5m -in .rhost.okframe
    button .rhost.b2 -text "Cancel" -width 10 \
		-command "set trigger3_help 0; set cookie_status_dialog 0; set update_count 0;"
    pack .rhost.b2 -side left -expand true -ipady 1 -padx 5m -in .rhost.okframe

    pack .rhost.helpf   -side top -expand true -fill both -in .rhost
    pack .rhost.newhost -fill x -side top -in .rhost -ipady 2m
    pack .rhost.okframe -side bottom -in .rhost -ipady 2m

    center_dialog .rhost
    set oldFocus [focus]
    focus .rhost
    catch { grab .rhost }

    wm protocol .rhost WM_DELETE_WINDOW { set trigger_help 0 }
    update
    scan [wm geometry .rhost] "%d%*c%d" xmin ymin
    wm minsize .rhost $xmin $ymin

    tkwait variable trigger3_help
    if {$trigger3_help == "1"} {
		set rhostq [.rhost.v99 get]
		.menuf.queues configure -text "Queues[at_rhostq]"
    }
	catch { grab release .rhost }
    focus $oldFocus
    destroy .rhost
	update idletasks
    return
}

# Simple subroutine to change a field name when a button is pressed
proc change_rhost_local { } {
    .rhost.v99 delete 0 end
}

#######################  ACTION IMPLEMENTATIONS END ######################


proc spooljobs_addpanel { w i directly } {
	global mode used_by printer_used_by printer_force_localhost font_fixed
	global printer_type
	global force_localhost change_local

	frame $w.clorsrv -borderwidth 1 -relief raised
	frame $w.clorsrv.legend
	label $w.clorsrv.l22 -text "Printcap for: " -anchor w
	pack $w.clorsrv.l22 -side left -fill x -in $w.clorsrv.legend
    button $w.clorsrv.help -text "?" -font $font_fixed -width 0 -command spooltype_help
	pack $w.clorsrv.help -side right -fill x -in $w.clorsrv.legend
	pack $w.clorsrv.legend -side top -fill x -in $w.clorsrv

	frame $w.clorsrv.use
	set used_by $printer_used_by($i)
	if { $used_by == "" } {
		set used_by "BOTH" 
		set $printer_used_by($i) "BOTH"
	}
	set force_localhost $printer_force_localhost($i)
	if { $used_by == "" && $mode != 0 } {
		set used_by "CLIENT"
	}
	set legend "Server and Client (BOTH)"
	radiobutton $w.v22a -text $legend \
		-variable used_by -value "BOTH" -anchor w
	set legend "Server Only (:server)"
	radiobutton $w.v22b -text $legend \
		-variable used_by -value "SERVER" -anchor w
	set legend "Client Only (:client)"
	radiobutton $w.v22c -text $legend \
		-variable used_by -value "CLIENT" -anchor w

	pack $w.v22a -side left -anchor w -in $w.clorsrv.use
	pack $w.v22b -side left -anchor w -in $w.clorsrv.use
	pack $w.v22c -side left -anchor w -in $w.clorsrv.use
	pack $w.clorsrv.use -side top -fill x -in $w.clorsrv

	label $w.clorsrv.l23 -text "Spool action: " -anchor w
	pack $w.clorsrv.l23 -side top -fill x -in $w.clorsrv


	frame $w.clorsrv.localhost
	set legend "Localhost (:force_localhost)"
	radiobutton $w.v22d -text $legend \
		-variable force_localhost -value "YES" -anchor w

	set legend "Remote Queue or Device (:force_localhost@)"
	radiobutton $w.v22e -text $legend \
		-variable force_localhost -value "NO" -anchor w

	set legend "Default"
	radiobutton $w.v22f -text $legend \
		-variable force_localhost -value "" -anchor w

	pack $w.v22d -side left -anchor w -in $w.clorsrv.localhost
	pack $w.v22e -side left -anchor w -in $w.clorsrv.localhost
	pack $w.v22f -side left -anchor w -in $w.clorsrv.localhost

	pack $w.clorsrv.localhost -side top -fill x -in $w.clorsrv

	pack $w.clorsrv -fill x -side top
	#puts "spooljobs_addpanel: used_by '$used_by'"
	#puts "spooljobs_addpanel: force_localhost '$force_localhost'"
}

proc spooljobs_updateentry { i } {
	global printer_used_by printer_force_localhost used_by force_localhost
	#puts "spooljobs_setup: used_by $used_by"
	set printer_used_by($i) $used_by
	set printer_force_localhost($i) $force_localhost
}

# change job type support

proc NO_new { i } { }
proc DEVICE_new { i } {
	global printer_type
	set printer_type($i) DEVICE
}
proc QUEUE_new { i } {
	global printer_type
	set printer_type($i) QUEUE
}
proc SMB_new { i } {
	global printer_type
	set printer_type($i) SMB
}
proc SOCKET_new { i } {
	global printer_type
	set printer_type($i) SOCKET
}
proc PRINTPOOL_new { i } {
	global printer_type
	set printer_type($i) PRINTPOOL
}
proc DUMMY_new { i } {
	global printer_type
	set printer_type($i) DUMMY
}
proc UNKNOWN_new { i } {
	global printer_type
	set printer_type($i) UNKNOWN
}

# Window displayed when creating/editing a direct printer
proc SOCKET_addpanel {w i} {
	global entrywidth
	global printer_type auto_printerdb_entry
	global entrywidth font_fixed font_helvetica bgentry

    set config [SOCKET_config $i]

	#puts "SOCKET_addpanel START config $config"
    frame $w.printer_ip
    label $w.l5 -text "Hostname/IP of Printer" -anchor w
	entry $w.v5 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h5 -text "?" -font $font_fixed -width 0 -command directip_help
    $w.v5 insert 0 [lindex $config 0]
    pack $w.l5 -side left -fill x -anchor w -in $w.printer_ip
    pack $w.h5 -side right -expand false -ipady 1 -in $w.printer_ip
    pack $w.v5 -side right -fill x -anchor e -in $w.printer_ip
    pack $w.printer_ip -side top -fill x

    frame $w.port
    label $w.l6 -text "Port number" -anchor w
	entry $w.v6 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h6 -text "?" -font $font_fixed -width 0 -command directport_help
    if {[lindex $config 1] == ""} {
        $w.v6 insert 0 "9100"
    } else {
        $w.v6 insert 0 [lindex $config 1]
    }
    pack $w.l6 -side left -fill x -anchor w -in $w.port
    pack $w.h6 -side right -expand false -ipady 1 -in $w.port
    pack $w.v6 -side right -fill x -anchor e -in $w.port
    pack $w.port -side top -fill x

	#puts "SOCKET_addpanel FILTERED"
    FILTERED_addpanel $w $i
    miscopts_addpanel $w $i
	spooljobs_addpanel $w $i 1
}

# Configure a direct printer
proc SOCKET_config {i} {
	#puts "SOCKET_config START"
	global socket_printer_ip socket_port
	set socket_printer_ip ""
	set socket_port ""

    set temp [printcap_field $i "lp=" ""]
	if { $temp != "" } {
		set temp [split $temp "%"]
		set socket_printer_ip [lindex $temp 0]
		set socket_port [lindex $temp 1]
        return [list $socket_printer_ip $socket_port]
	}
    return [list "" ""]
}

# Name to return when editing/adding a SOCKET printer
proc SOCKET_name {} {
    return "TCP/IP Socket (JetDirect or similar)"
}

# Set up a SOCKET printer
proc SOCKET_setup {i} {
    global socket_printer_ip socket_port

	#puts "SOCKET_setup $socket_printer_ip%$socket_port"
	set_printcap_field $i "lp=" "$socket_printer_ip%$socket_port"

	FILTERED_setup $i
}

# Summary line sent to the main menu when the printer is a direct printer
proc SOCKET_summaryline {i} {
    set printer [FILTERED_summaryline $i "printer"]
    set config [SOCKET_config $i]
    set printer_ip  [lindex $config 0]
    set port [lindex $config 1]
    return "SOCKET - $printer at $printer_ip%$port"
}

# Update the SOCKET printer entry after adding/editing a SOCKET printer
proc SOCKET_updateentry {w i} {
    global socket_printer_ip socket_port mode

    set socket_printer_ip [$w.v5 get]
    set socket_port         [$w.v6 get]

	#puts "SOCKET_updateentry lp=$socket_printer_ip%$socket_port\n"
    set_printcap_field $i "lp=" "$socket_printer_ip%$socket_port"
	if { $mode } { set_printcap_field $i "remote_support=" "R" }
#
#   get the auto_filter setup first
#
#   it will be the input filter, so it sets the printcap entry 'filter='
#
    FILTERED_updateentry $i
    miscopts_updateentry $i
	spooljobs_updateentry $i
}

# Filtering Support

proc set_auto_filtertype { filter model } {
	global rh_filter filtersrcdir auto_filtertype ifhp_path
	global auto_printerdb_entry ifhpkey 

	$filter config -state normal
	$filter delete 0 end
	$model config -state normal
	$model delete 0 end
	set autoprinterdb_entry ""
	if { $auto_filtertype == $ifhpkey } {
		$filter delete 0 end
		#puts "setting path '$ifhp_path'"
		$filter insert 0 "$ifhp_path"
		$filter config -state disabled
	}
}


# Create the add/edit printer window with "filter" panels
proc FILTERED_addpanel {w i} {
	global entrywidth  printer_info auto_printerdb_entry
	global printer_type auto_printerdb_entry auto_filtertype
	global filter_entry ifhpkey userkey entrywidth font_fixed font_helvetica bgentry

    frame $w.filter
	#puts "FILTERED_addpanel START auto_filtertype '$auto_filtertype'"
	if { $auto_filtertype == "" } { set auto_filtertype $userkey }
    radiobutton $w.c10a -text "IFHP" -variable auto_filtertype -value $ifhpkey -command "set_auto_filtertype $w.v10 $w.v11"
    radiobutton $w.c10c -text "User Specified" -variable auto_filtertype -value $userkey -command "set_auto_filtertype $w.v10 $w.v11"
    label $w.l10 -text "Filter" -anchor w
	entry $w.v10 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth -textvariable filter_entry
	if { $auto_filtertype == $userkey } { $w.v10 config -state normal }
    button $w.b10 -text "?" -font $font_fixed -width 0 -command inputfilter_help
    $w.v10 delete 0 end
    $w.v10 insert 0 [ printcap_field $i "filter=" "" ]

#    pack $w.m10 -side left -anchor e  -in $w.filter
    pack $w.c10a -side left -anchor w -in $w.filter
    pack $w.c10c -side left -anchor w -in $w.filter
    pack $w.b10 -side right -anchor e -expand false -ipady 1 -in $w.filter
    pack $w.v10 -side right -anchor e -in $w.filter
    pack $w.l10 -side right -anchor e -in $w.filter
    pack $w.filter -fill x -side top

    frame $w.ifhp
    button $w.l11 -text "Select Printer Model and Filter Options" -command "Select_filter $w.v11 $i"
	entry $w.v11 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth -textvariable model
    button $w.b11 -text "?" -font $font_fixed -width 0 -command ifhp_help
    $w.v11 delete 0 end
    $w.v11 insert 0 $auto_printerdb_entry
    $w.v11 configure -state disabled
#    pack $w.c11 -side left -anchor w  -in $w.ifhp
    pack $w.b11 -side right -anchor e -in $w.ifhp
    pack $w.v11 -side right -anchor e -in $w.ifhp
    pack $w.l11 -side right -anchor e -expand false -ipady 1 -in $w.ifhp
    pack $w.ifhp -fill x -side top


    frame $w.job_options
    label $w.l13a -text "Job Options"
    button $w.c13a -text "Select LPR Job and Filter Options" -command "Select_joboptions $w.v13a $i"
	entry $w.v13a -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -textvariable job_options -width $entrywidth
    button $w.h13a -text "?" -font $font_fixed -width 0 -command job_options_help
	$w.v13a delete 0 end
    $w.v13a insert 0 [printcap_field $i "prefix_z=" ""]
	$w.v13a config -state disabled
    pack $w.l13a -side left -anchor w -in $w.job_options
    pack $w.h13a -side right -expand false -ipady 1 -in $w.job_options
    pack $w.v13a -side right -fill x -anchor e -in $w.job_options
    pack $w.c13a -side right -fill x -anchor e -in $w.job_options
    pack $w.job_options -fill x -side top
}

# Set up the filtering options
proc FILTERED_setup { i } {
}

# Summary line returned when checking for the type of filter
proc FILTERED_summaryline {i def} {
    global printer_info
    global printerdb_descr uid

    set info $printer_info($i)
	#puts "FILTERED_summaryline info '$info'"
	set printer ""
	regexp {printerdb_entry=([^ ]*).*$} $info junk printer
	#puts "printer $printer"
    if { $printer == "" } { set printer $def }
    return "$printer"
}

# Update the filtering entries
proc FILTERED_updateentry {i} {
    global printer_info
    global filter_entry auto_filtertype
	global filter_updated ifhpkey

	set filter_updated 0
	remove_printcap_field $i "filter"
	remove_printcap_field $i "lprngtooloptions"
	if { $filter_entry != "" } {
		set_printcap_field $i "filter=" $filter_entry
	}
	if { $auto_filtertype == $ifhpkey  } {
		set_printcap_field $i "lprngtooloptions=" [set_printer_info 1]
	}
}

# find parallel port devices

proc Find_lp { i } {
	set found ""
	if { [file exists "/dev/lp$i"] && [catch {open "/dev/lp$i" a } junkfp] == 0} {
		set found "Parallel Port lp0 (/dev/lp$i) : Found\n"
		close $junkfp
	} elseif { [file exists "/dev/lpt$i"] && [catch {open "/dev/lpt$i" a } junkfp] == 0} {
		set found "Parallel Port lpt$i (/dev/lpt$i) : Found\n"
		close $junkfp
	} else {
		set found "Parallel Port $i (/dev/lp$i, /dev/lpt$i) : Not Found\n"
	}
	return $found
}

# Window displayed when creating a local printer
proc DEVICE_addpanel {w i} {
	global rw_device entrywidth
	global entrywidth detection
	global font_fixed font_helvetica bgentry

#
#   if no printer device is specified, lets suggest one
#
	set lp [printcap_field $i "lp=" ""]
	set rw_device [printcap_switch $i "rw" 1 ]
    if { $detection == 0 } {
		set detection 1
		set found ""
		append found [ Find_lp 0 ]
		append found [ Find_lp 1 ]
		append found [ Find_lp 2 ]

		popup_info_dialog "Auto-detection found the following:

$found

You may disregard this message
if you are setting up a serial printer.

This auto-detection may not always work
on Sparc and Alpha architectures.

If no devices were detected, this could
indicate a hardware problem that justifies
further investigation."
    }

    frame $w.dev
    label $w.l8 -text "Printer Device" -anchor w
	entry $w.v8 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h8 -text "?" -font $font_fixed -width 0 -command printdev_help
    pack $w.h8 -side right -expand false -ipady 1 -in $w.dev
    $w.v8 insert 0 "$lp"
    pack $w.l8 -side left -anchor w -in $w.dev
    pack $w.v8 -side right -anchor e -in $w.dev
    pack $w.dev -side top -fill x


    frame $w.rw
#    label $w.l15 -text "Read/Write Device" -anchor w
#    entry $w.v15 -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    checkbutton $w.l15 -text "Open Device Read/Write" -variable rw_device
    button $w.h15 -text "?" -font $font_fixed -width 0 -command printrw_help
#    $w.v15 insert 0 "$lp"
    pack $w.l15 -side left -anchor w -in $w.rw
#    pack $w.v15 -side right -anchor e -in $w.rw
    pack $w.h15 -side right -expand false -ipady 1 -in $w.rw
    pack $w.rw -side top -fill x


    FILTERED_addpanel $w $i
    miscopts_addpanel $w $i
	spooljobs_addpanel $w $i 1
}

# Name to return in the title bar when editing/adding a local printer
proc DEVICE_name {} {
    return "Printer Port (/dev/...)"
}

#
# handles picking a new printer type in print filter window.
#
# updates description, resolution, color depth, and paper size listboxes based
# on the printer db entry
#
# you pass it the index of printer type in printerdb_entry()
# and names of the various widgets
#

# Select the printer type from the filter menu for local printers
proc DEVICE_select_printer_type {index printer about res color paper ifhp } {

    global printerdb_entry printerdb_descr
    global printerdb_about printerdb_GSDriver gs_upp_drivers
    global printerdb_res   printerdb_color
    global printerdb_descr2entry printerdb_ifhpsupports
    global printerdb_count
    global papersize_list
    global auto_papersize
    global auto_color_choice
    global auto_resolution
    global auto_nup
    global auto_ascii_to_ps
	global existing_entry


#   set the printer type selection
	#puts "DEVICE_select_printer_type: index '$index' '$printer' ifhp '$ifhp'"
	if { $index < 0 } { return }
    $printer selection clear 0 end
    $printer selection set $index
    $printer see $index


#    set pentry $printerdb_entry([$printer curselection])
    set pentry [lindex $existing_entry $index]
	#puts "DEVICE_select_printer_type index $index '$pentry'"

#   update the 'about' info
    $about config -state normal
    $about delete 1.0 end
	$about insert end $printerdb_about($pentry)
    $about config -state disabled
#
#   rebuild resolution list
#
	catch {
		$res delete 0 end
		if [llength [array names printerdb_res $pentry]] {
			foreach x $printerdb_res($pentry) {
				set g [format "%4sx%-4s %-20s"  [lindex $x 0] [lindex $x 1] [lindex $x 2]]
				$res insert end $g
			}
		} else {
			$res insert end [format "%4sx%-4s %-20s"  Default {} {}]
		}

#	   set the resolution to the first available
#	    $res selection clear 0 end
#	    $res selection set 0
#	
#	   set resolution to
#	
		if { [string compare $auto_resolution "Default"] == 0 } {
			set rresentry 0
		} elseif { [array names printerdb_res $pentry] == {} } {
			set rresentry 0
		} else {	
			set rresentry 0
			set reslist [split $auto_resolution x]
			set resxdpi [lindex $reslist 0]
			set resydpi [lindex $reslist 1]
			for {set i 0} {$i < [llength $printerdb_res($pentry)]} {incr i} {
				set curres [lindex $printerdb_res($pentry) $i]
				if { [lindex $curres 0] == $resxdpi && [lindex $curres 1] == $resydpi } {
				set rresentry $i
				break
				}
			}
		}
		$res selection clear 0 end
		$res selection set $rresentry
		$res see $rresentry
	}

#
#   rebuild color list
#
	catch {
	    $color delete 0 end
#
#	 ugly - if no color info assume default
#
		#puts "gs driver '$printerdb_GSDriver($pentry)' color '$printerdb_color($pentry)'"
	    if { [llength $printerdb_color($pentry)] } {
	    	foreach x $printerdb_color($pentry) {
				#puts "color $x"
				if { $printerdb_GSDriver($pentry) == "uniprint" } {
					# it's a uniprint upp entry
					# check to make sure the upp file exists
					set fname [ format "*/%s.upp" [lindex $x 0] ]
					if { [lsearch -glob $gs_upp_drivers $fname ] != -1 } {
						#puts "found $fname"
						set g [format "%8s, %-30s"  [lindex $x 0] [lindex $x 1]]
						$color insert end $g
					} else {
						set g "WARNING: [lindex $x 0].upp not found"
						$color insert end $g
					}
				} else {
					set g [format "%8s, %-30s"  [lindex $x 0] [lindex $x 1]]
					$color insert end $g
				}
			}
	    } else {
			$color insert end "Default"
	    }


#	    set the color depth to the first available
#	    $color selection clear 0 end
#	    $color selection set 0
#
#	    if this is default, then set accordingly
#
	    if { [string compare $auto_color_choice "Default"] == 0 } {
			set rcolentry 0
	    } elseif { [array names printerdb_color $pentry] == {} } {
			set rcolentry 0
	    } else {	
			set rcolentry 0
			for {set i 0} {$i < [llength $printerdb_color($pentry)]} {incr i} {
				set colentry [lindex $printerdb_color($pentry) $i]
				if { [lindex $colentry 0] == $auto_color_choice } {
					set rcolentry $i
					break
				}
			}
	    }
	    $color selection clear 0 end
	    $color selection set $rcolentry
	    $color see $rcolentry
	}

#   set the paper to the first available
#    $paper selection clear 0 end
#    $paper selection set 0
#
#  if there is no active selection in the paper size listbox
#  make the current paper size the selection
#
	catch {
		set papselect [$paper curselection]
		if { $papselect != "" } {
			set papentry [$paper get $papselect]
			set papindex [lsearch $papersize_list $papentry]
			if { $papindex == -1 } {
				set papindex 0
			}
		} else {
			set papindex [lsearch $papersize_list $auto_papersize]
			if { $papindex == -1 } {
				set papindex 0
			}
		}
		$paper selection clear 0 end
		$paper selection set $papindex
		$paper see $papindex
	}

# now we deal with the IFHP stuff
# we need to find out what is enable vis a vis communcations
	parse_ifhp_params $printerdb_ifhpsupports($pentry)
	ifhp_status $ifhp
}

#
# select filter options
#
proc Select_filter {e selection} {
    global trigger_2 rw_device
    global papersize_list change_local rw_device
    global auto_gsdevice auto_resolution auto_papersize
	global checkforgsdevice font_fixed font_helvetica printer_type
	global bglistbox bgtext bgentry

#
# newer printerdb globals, some supercede some above
#
    global printerdb_entry printerdb_descr
    global printerdb_about printerdb_GSDriver
    global printerdb_res   printerdb_color
    global printerdb_count
    global printerdb_descr2entry
	global printerdb_model printerdb_ifhpgsdriver printerdb_ifhpgsoptions
	global printerdb_ifhpsupports
    global gs_installed_drivers
    global pager gsupdir
	global ifhpkey userkey ifhpgsdriver ifhpgsoptions
	global reserved_ifhp_options
	global existing_entry pager auto_vars ifhp_vars ifhp_nostatus
	global ifhp_vars

	foreach v $ifhp_vars {
		if {![info exists $v]} {
			global $v
		}
	}

	set ifhp_nostatus 1
	#puts "Select_filter: change_local $change_local, rw '$rw_device'"
	if { ($change_local == "DEVICE" && $rw_device) || $change_local == "SOCKET" } {
		set ifhp_nostatus 0
	}
	foreach v $auto_vars {
		if {![info exists $v]} { global $v }
	}
	foreach v $ifhp_vars {
		if {![info exists $v]} { global $v }
	}


	#puts "Select_filter: auto_nup '$auto_nup'"
	#puts "Selected GSDriver      -> $auto_gsdevice"
	#puts "Selected printer entry -> $auto_printerdb_entry"
	#puts "Selected resolution    -> $auto_resolution"
	#puts "Selected color         -> $auto_color_choice"
	#puts "Selected paper size    -> $auto_papersize"
	#puts "Selected eof setting   -> $auto_eof"
	#puts "Selected cr/lf trans   -> $auto_crlf_trans"
	#puts "Selected reverse pages -> $reverse_order"

	if { $auto_filtertype == $userkey } {
		popup_error_dialog "You need to select IFHP filter"
		return
	}
	reload_printerdb
	if { $printerdb_count == 0 } {
		warnmsg "There are no entries in the printer information database"
		return
	}
    set all_descr ""
    set existing_entry ""
	#puts "printerdb_count '$printerdb_count'"

    for {set i 0} {$i < $printerdb_count} {incr i} {
		set this_entry $printerdb_entry($i)
		set this_descr $printerdb_descr($this_entry)
		set this_gsdriver $printerdb_GSDriver($this_entry)
		set this_model $printerdb_model($this_entry)
		#puts "model '$this_model', entry '$this_entry', desc '$this_descr', gs '$this_gsdriver'"
		# we get IFHP only entries
		if { $this_model != "" } {
			if { $auto_filtertype != $ifhpkey } { continue }
			set this_gsdriver $printerdb_ifhpgsdriver($this_entry)
		}
		lappend all_descr $this_descr
		#puts "entry '$this_entry', desc '$this_descr', gs '$this_gsdriver'"
		if { !$checkforgsdevice \
			 || $this_gsdriver == "" \
			 || [lsearch $gs_installed_drivers $this_gsdriver] != -1 } {
			#puts "FOUND $this_entry"
#			.sf.drivers.list insert end $this_entry
			lappend existing_entry $this_entry
		}
    }
	if { [llength $existing_entry ] == 0 } {
		set t ""
		foreach v $all_descr {
			append t "  " $v "\n"
		}
		if { $t == "" } { set t "NO ENTRIES IN DATABASE" }
		popup_error_dialog "
There are no valid '$auto_filtertype' entries in the
printer information database.  You may have a missing
PostScript interpreter or your database may not have
any entries for the filter.  Entries in the database include:
$t
"
		return
	}
    catch {destroy .sf}
    toplevel .sf -class Dialog
    wm withdraw .sf
    wm transient .sf .
    wm group .sf .
    wm title .sf "Configure Filter"

#
# create these in an order that makes stacking work out right
#
# .cf's are container frames
#
#
# trying something different - define vars with names of Tk widgets here
#  then use names later on. We can allocate names as needed

#
# add buttons to exit this dialog
#
    frame .sf.buttons
    button .sf.buttons.b1 -text "OK" -width 10 -command "set trigger_2 1"
    pack .sf.buttons.b1 -side left -expand true -ipady 1 -padx 5m -in .sf.buttons
    button .sf.buttons.b2 -text "Cancel" -width 10 \
		-command "set trigger_2 0; set cookie_status_dialog 0; set update_count 0;"
    pack .sf.buttons.b2 -side left -expand true -ipady 1 -padx 5m -in .sf.buttons
	if { $auto_filtertype == $ifhpkey } {
		button .sf.buttons.b3 -text "HELP" -width 10 -command ifhp_selectfilter_help
	} else {
		button .sf.buttons.b3 -text "HELP" -width 10
	}
    pack .sf.buttons.b3 -side left -expand true -ipady 1 -padx 5m -in .sf.buttons
    pack .sf.buttons -side bottom -ipady 2m -in .sf

#
# First we'll handle the listbox containing the printer drivers available
#
	frame .sf.leftcol
	set w 40
	if { $auto_filtertype == $ifhpkey } {
		label .sf.leftcol.l1 -text "IFHP Printer Driver" -anchor w -width 16
		set w 50
	} else {
		warnmsg "You don't have a filter type selected"
		return
	}
    pack .sf.leftcol.l1 -side top -fill x -in .sf.leftcol
	

    frame .sf.drivers
	listbox .sf.drivers.list -bg $bglistbox \
		-yscrollcommand [list .sf.drivers.sy set] \
		-xscrollcommand [list .sf.drivers.sx set] \
		-width $w -setgrid true -selectmode single \
		-exportselection false
		 #-height 20

    scrollbar .sf.drivers.sy -orient vertical   -command [list .sf.drivers.list yview]
    scrollbar .sf.drivers.sx -orient horizontal -command [list .sf.drivers.list xview]

#
# we want to display a sorted list of descriptions, but lets not
# change the order of the description array
#
    foreach v $existing_entry {
		#puts "ADDING $v"
		if { $printerdb_descr($v) != "" } {
			if { $auto_filtertype == $ifhpkey } {
				.sf.drivers.list insert end "$printerdb_descr($v) ($v)"
			} else {
				.sf.drivers.list insert end "$printerdb_descr($v) ($v)"
			}
		} else {
			.sf.drivers.list insert end $v
		}
    }
    pack .sf.drivers.sy -side right -fill y -in .sf.drivers
    pack .sf.drivers.sx -side bottom -fill x -in .sf.drivers
    pack .sf.drivers.list -side left -fill both -expand true -in .sf.drivers
    pack .sf.drivers -side top -expand true -fill y -in .sf.leftcol
	pack .sf.leftcol -side left -expand true -fill y -in .sf


#
# Now add a verbose description of the driver
#
# we disable the text widget so it is read-only
#
	frame .sf.rightcol
    label .sf.rightcol.l1 -text "Driver Description" -anchor w -width 20
    pack .sf.rightcol.l1      -side top -fill x -in .sf.rightcol

    frame .sf.descr
	text .sf.descr.text -bg $bgtext -relief sunken -setgrid true -wrap word \
			 -width 35 -height 4 -yscrollcommand ".sf.descr.sy set" \
		     -font  $font_helvetica
    scrollbar .sf.descr.sy -orient vert -command ".sf.descr.text yview"
    #.sf.descr.text insert 1.0 "Should be initializing this elsewhere!!"
    .sf.descr.text config -state disabled
    pack .sf.descr.sy -side right -fill y -in .sf.descr
    pack .sf.descr.text -side left -fill both -expand true -in .sf.descr
    pack .sf.descr -side top -fill x -in .sf.rightcol

# ---
	if { $auto_filtertype == $ifhpkey } {
		#puts "Select_filter: ifhp filter"
		if { $ifhp_nostatus } {
			label .sf.opts -text "IFHP Printer Status Monitoring: Not available"
			pack .sf.opts -side top -anchor w -fill x -in .sf.rightcol 
		} else {
			label .sf.opts -text "IFHP Status Monitoring Options:"
			pack .sf.opts -side top -anchor w -fill x -in .sf.rightcol 

			frame .sf.status
			frame .sf.status.sb
			checkbutton .sf.status.status -text "Status Is Returned By Printer (status)" \
				 -anchor w -variable ifhp_option_status -command "ifhp_status .sf.status"
			
			button .sf.status.bstatus -text "?" -font $font_fixed -width 0 -command ifhp_status_help
			pack .sf.status.status -side left -anchor w -fill x -in .sf.status.sb
			pack .sf.status.bstatus -side right -anchor e -fill x -in .sf.status.sb
			pack .sf.status.sb -side top -anchor w -fill x -in .sf.status

			frame .sf.status.yb
			checkbutton .sf.status.sync -text "Wait Until Printer Ready (sync)" \
				-anchor w -variable ifhp_option_sync
			button .sf.status.bsync -text "?" -font $font_fixed -width 0 -command ifhp_sync_help
			pack .sf.status.sync -side left -anchor w -fill x -in .sf.status.yb
			pack .sf.status.bsync -side right -anchor e -fill x -in .sf.status.yb
			pack .sf.status.yb -side top -anchor w -fill x -in .sf.status

			frame .sf.status.ybch
			label .sf.status.syncforce -text "  (Force:"
			checkbutton .sf.status.sync_ps -text "PostScript" \
				-anchor w -variable ifhp_option_sync_ps
			checkbutton .sf.status.sync_pjl -text "PJL)" \
				-anchor w -variable ifhp_option_sync_pjl
			pack .sf.status.syncforce -side left -anchor w -fill x -in .sf.status.ybch
			pack .sf.status.sync_ps -side left -anchor w -fill x -in .sf.status.ybch
			pack .sf.status.sync_pjl -side left -anchor w -fill x -in .sf.status.ybch
			pack .sf.status.ybch -side top -anchor w -fill x -in .sf.status

			frame .sf.status.pb
			checkbutton .sf.status.pagecount -text "Get Pagecounter Value From Printer (pagecount)" \
				-anchor w -variable ifhp_option_pagecount
			button .sf.status.bpagecount -text "?" -font $font_fixed -width 0 -command ifhp_pagecount_help
			pack .sf.status.pagecount -side left -anchor w -fill x -in .sf.status.pb
			pack .sf.status.bpagecount -side right -anchor e -fill x -in .sf.status.pb
			pack .sf.status.pb -side top -anchor w -fill x -in .sf.status

			frame .sf.status.pbch
			label .sf.status.pagecountforce -text "   (Force:"
			checkbutton .sf.status.pagecount_ps -text "PostScript" \
				-anchor w -variable ifhp_option_pagecount_ps
			checkbutton .sf.status.pagecount_pjl -text "PJL)" \
				-anchor w -variable ifhp_option_pagecount_pjl
			pack .sf.status.pagecountforce -side left -anchor w -fill x -in .sf.status.pbch
			pack .sf.status.pagecount_ps -side left -anchor w -fill x -in .sf.status.pbch
			pack .sf.status.pagecount_pjl -side left -anchor w -fill x -in .sf.status.pbch
			pack .sf.status.pbch -side top -anchor w -fill x -in .sf.status

			frame .sf.status.wb
			checkbutton .sf.status.waitend -text "Wait Until Printer Reports END OF JOB (waitend)" \
				-anchor w -variable ifhp_option_waitend
			button .sf.status.bwaitend -text "?" -font $font_fixed -width 0 -command ifhp_waitend_help
			pack .sf.status.waitend -side left -anchor w -fill x -in .sf.status.wb
			pack .sf.status.bwaitend -side right -anchor e -fill x -in .sf.status.wb
			pack .sf.status.wb -side top -anchor w -fill x -in .sf.status

			frame .sf.status.wbch
			label .sf.status.waitendforce -text "   (Force:"
			checkbutton .sf.status.waitend_ps -text "PostScript" \
				-anchor w -variable ifhp_option_waitend_ps
			checkbutton .sf.status.waitend_pjl -text "PJL)" \
				-anchor w -variable ifhp_option_waitend_pjl
			pack .sf.status.waitendforce -side left -anchor w -fill x -in .sf.status.wbch
			pack .sf.status.waitend_ps -side left -anchor w -fill x -in .sf.status.wbch
			pack .sf.status.waitend_pjl -side left -anchor w -fill x -in .sf.status.wbch
			pack .sf.status.wbch -side top -anchor w -fill x -in .sf.status

			pack .sf.status -side top -anchor w -in .sf.rightcol
		}

		label .sf.rightcol.opts -text "\nIFHP User Specified Options (One per Line)"
		pack .sf.rightcol.opts -side top -anchor w -in .sf.rightcol

		frame .sf.ifhp
		text .sf.ifhp.options -bg $bgtext -width 35 -height 10 -setgrid true \
			 -yscrollcommand {.sf.ifhp.sy set}
		scrollbar .sf.ifhp.sy -command { .sf.ifhp.options yview }
		pack .sf.ifhp.sy -side right -fill x -anchor e -in .sf.ifhp
		pack .sf.ifhp.options -fill x -side top -in .sf.ifhp
		pack .sf.ifhp -fill x -fill x -side top -in .sf.rightcol

		set options $auto_ifhp_options
		set options [ remove_fields_in_list $options $reserved_ifhp_options ]
		#puts "options '$options'"
		regsub {,$} $options "" options
		regsub {^,} $options "" options
		regsub -all {,} $options "\n" options
		if { $options != "" } {
			.sf.ifhp.options insert end "$options"
		}
	}

	pack .sf.rightcol -side right -in .sf
#
# set binding for selection in printer type window
#
    bind .sf.drivers.list <Button-1> "DEVICE_select_printer_type \
		\[.sf.drivers.list nearest %y\] .sf.drivers.list .sf.descr.text \
	.sf.res_info.list .sf.color.list .sf.paper_info.list .sf.status"
#
#   select entry from auto_printerdb_entry if available
#

	set current -1
	if { $auto_printerdb_entry != "" } {
		set current [lsearch -exact $existing_entry $auto_printerdb_entry]
		if { $current == -1 } {
			warnmsg "Cannot find printer type '$auto_printerdb_entry' in database"
		} else {
		DEVICE_select_printer_type $current .sf.drivers.list .sf.descr.text .sf.res_info.list .sf.color.list .sf.paper_info.list .sf.status
		}
	}

	parse_ifhp_params $auto_ifhp_options
	ifhp_status .sf.status

    center_dialog .sf
    set oldFocus [focus]
    focus .sf
    catch { grab .sf }

    wm protocol .sf WM_DELETE_WINDOW { set trigger_2 0 }
    update
    scan [wm geometry .sf] "%d%*c%d" xmin ymin
    wm minsize .sf $xmin $ymin
    wm maxsize .sf $xmin $ymin

	set x_ifhp_option_status $ifhp_option_status
	set x_ifhp_option_sync $ifhp_option_sync
	set x_ifhp_option_sync_ps $ifhp_option_sync_ps
	set x_ifhp_option_sync_pjl $ifhp_option_sync_pjl
	set x_ifhp_option_pagecount $ifhp_option_pagecount
	set x_ifhp_option_pagecount_ps $ifhp_option_pagecount_ps
	set x_ifhp_option_pagecount_pjl $ifhp_option_pagecount_pjl
	set x_ifhp_option_waitend $ifhp_option_waitend
	set x_ifhp_option_waitend_ps $ifhp_option_waitend_ps
	set x_ifhp_option_waitend_pjl $ifhp_option_waitend_pjl

	while { 1 } {
		tkwait variable trigger_2
		if {$trigger_2 == 1} {
			set index [.sf.drivers.list curselection]
			if { $auto_filtertype == $ifhpkey } {
				set ifhpgsdriver ""
				set ifhpgsoptions ""
				set auto_gsdevice ""
				set auto_printerdb_entry ""
				set model ""
				if { $index != "" } {
					set pentry [lindex $existing_entry $index]
					#puts "index $index, pentry $pentry"
					set model "model=$pentry,"
					set auto_gsdevice $printerdb_GSDriver($pentry)
					set auto_printerdb_entry $pentry

					set ifhpgsdriver $printerdb_ifhpgsdriver($pentry)
					set ifhpgsoptions $printerdb_ifhpgsoptions($pentry)
				}

				set options [cleanup_list_entry [ .sf.ifhp.options get 1.0 end ]]
				#puts "new options '$options'"
				set options [ remove_fields_in_list $options $reserved_ifhp_options ]

				#puts "at end ifhp_option_status '$ifhp_option_status'"
				#puts "at end ifhp_option_sync '$ifhp_option_sync'"
				#puts "at end ifhp_option_pagecount '$ifhp_option_pagecount'"
				#puts "at end ifhp_option_waitend '$ifhp_option_waitend'"
				if { $ifhp_option_status != "1" } {
					append options "status\@,sync\@,pagecount\@,waitend\@,"
				} else {
					append options "status,"
					append options "sync"
					if { $ifhp_option_sync != 0 } {
						if { $ifhp_option_sync_ps } {
							append options "=ps"
						} elseif { $ifhp_option_sync_pjl } {
							append options "=pjl"
						}
					} else {
						append options "@"
					}
					append options ","
					append options "pagecount"
					if { $ifhp_option_pagecount != 0 } {
						if { $ifhp_option_pagecount_ps } {
							append options "=ps"
						} elseif { $ifhp_option_pagecount_pjl } {
							append options "=pjl"
						}
					} else {
						append options "@"
					}
					append options ","
					append options "waitend"
					if { $ifhp_option_waitend != 0 } {
						if { $ifhp_option_waitend_ps } {
							append options "=ps"
						} elseif { $ifhp_option_waitend_pjl } {
							append options "=pjl"
						}
					} else {
						append options "@"
					}
					append options ","
				}
				#puts "fixed options '$options'"
				regsub {,$} $options "" options
				regsub {^,} $options "" options
				set auto_ifhp_options $options
				set options "$model$options"
				#puts "final options '$options'"
				set_printcap_field $selection "ifhp=" $options
			}

			#puts "Select_filter: auto_printerdb_entry into $e '$auto_printerdb_entry'"
			$e config -state normal
			$e delete 0 end
			$e insert end "$auto_printerdb_entry"
			$e config -state disabled
		} else {
			set ifhp_option_status $x_ifhp_option_status
			set ifhp_option_sync $x_ifhp_option_sync
			set ifhp_option_sync_ps $x_ifhp_option_sync_ps
			set ifhp_option_sync_pjl $x_ifhp_option_sync_pjl
			set ifhp_option_pagecount $x_ifhp_option_pagecount
			set ifhp_option_pagecount_ps $x_ifhp_option_pagecount_ps
			set ifhp_option_pagecount_pjl $x_ifhp_option_pagecount_pjl
			set ifhp_option_waitend $x_ifhp_option_waitend
			set ifhp_option_waitend_ps $x_ifhp_option_waitend_ps
			set ifhp_option_waitend_pjl $x_ifhp_option_waitend_pjl
		}
		break;
	}
    destroy .sf
	update idletasks
    return $trigger_2
}

proc ifhp_status { status {default 0} } {
	global ifhp_vars ifhp_nostatus

	foreach v $ifhp_vars {
		if {![info exists $v]} {
			global $v
		}
	}

	if { $ifhp_nostatus } {
		set ifhp_option_status 0
		set ifhp_option_status 0
		set ifhp_option_sync 0
		set ifhp_option_pagecount 0
		set ifhp_option_waitend 0
		set ifhp_option_sync_ps 0
		set ifhp_option_pagecount_ps 0
		set ifhp_option_waitend_ps 0
		set ifhp_option_sync_pjl 0
		set ifhp_option_pagecount_pjl 0
		set ifhp_option_waitend_pjl 0
	}
	if {[ catch {
			$status.status configure -state normal
			$status.sync configure -state normal
			$status.pagecount configure -state normal
			$status.waitend configure -state normal
		} stuff ] } {
		#puts "ifhp_status: stuff '$stuff'"
		return
	}
	#puts "ifhp_status: nostatus '$ifhp_nostatus', status $status, default $default, status '$ifhp_option_status'"

	if { $ifhp_option_status == 0 } {
		$status.sync deselect; $status.sync configure -state disabled
		$status.sync_ps deselect; $status.sync_ps configure -state disabled
		$status.sync_pjl deselect; $status.sync_pjl configure -state disabled
		$status.pagecount deselect; $status.pagecount configure -state disabled
		$status.pagecount_ps deselect; $status.pagecount_ps configure -state disabled
		$status.pagecount_pjl deselect; $status.pagecount_pjl configure -state disabled
		$status.waitend deselect; $status.waitend configure -state disabled
		$status.waitend_ps deselect; $status.waitend_ps configure -state disabled
		$status.waitend_pjl deselect; $status.waitend_pjl configure -state disabled
	} else {
		$status.sync configure -state normal; $status.sync_ps configure -state normal; $status.sync_pjl configure -state normal
		$status.pagecount configure -state normal; $status.pagecount_ps configure -state normal; $status.pagecount_pjl configure -state normal
		$status.waitend configure -state normal; $status.waitend_ps configure -state normal; $status.waitend_pjl configure -state normal
	}
}

#
# Select job options -  $w is the place where we show the options
#
#
proc Select_joboptions { e i } {
	# first we set up the window
	global trigger_3 subtitle
	global papersize_list intray_list outbin_list media_list
	global orient_list duplex_list resolution_list
	global font_fixed font_helvetica entrywidth
	global ifhp_vars bglistbox bgtext bgentry

	foreach v $ifhp_vars {
		if {![info exists $v]} {
			global $v
		}
	}

    catch {destroy .opts}
    toplevel .opts -class Dialog
    wm withdraw .opts
    wm transient .opts .
    wm group .opts .
    wm title .opts "Select LPR Job and Filter Options - $subtitle"

#
# add buttons to exit this dialog
#
    frame .opts.buttons
    button .opts.buttons.b1 -text "OK" -width 10 -command "set trigger_3 1"
    pack .opts.buttons.b1 -side left -expand true -ipady 1 -padx 5m -in .opts.buttons
    button .opts.buttons.b2 -text "Cancel" -width 10 \
		-command "set trigger_3 0; set cookie_status_dialog 0; set update_count 0;"
    pack .opts.buttons.b2 -side left -expand true -ipady 1 -padx 5m -in .opts.buttons
	button .opts.buttons.b3 -text "HELP" -width 10 -command select_options_help
    pack .opts.buttons.b3 -side left -expand true -ipady 1 -padx 5m -in .opts.buttons
    pack .opts.buttons -side bottom -ipady 2m -in .opts


#
# we have the orientation selection
#

    frame .opts.name
    label .opts.l1 -text "LPR Options"
	entry .opts.v1 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
	.opts.v1 insert 0 [printcap_field $i "lpr=" ""]
    button .opts.h1 -text "?" -font $font_fixed -width 0 -command lpr_help
    pack .opts.l1 -side left -anchor w -in .opts.name
    pack .opts.h1 -side right -expand false -ipady 1 -in .opts.name
    pack .opts.v1 -side right -fill x -anchor e -in .opts.name
    pack .opts.name -fill x -side top -in .opts

	frame .opts.first

    frame .opts.zopts
    label .opts.zopts.l1 -text "Filter Options"
    button .opts.zopts.h1 -text "?" -font $font_fixed -width 0 -command options_help
    pack .opts.zopts.l1 -side left -anchor w -in .opts.zopts
    pack .opts.zopts.h1 -side right -expand false -ipady 1 -in .opts.zopts
    pack .opts.zopts -fill x -side top -in .opts

	frame .opts.orient_info
	label .opts.orient_info.l1 -text "Orientation" -anchor w -width 12
	pack .opts.orient_info.l1 -side top -fill x -in .opts.orient_info

	listbox .opts.orient_info.list -bg $bglistbox -yscrollcommand [list .opts.orient_info.sy set] \
		-width 11 -height 3 -setgrid true -selectmode single \
		-exportselection false -font $font_fixed
	scrollbar .opts.orient_info.sy -orient vertical \
		 -command [list .opts.orient_info.list yview]
	foreach x $orient_list {
		.opts.orient_info.list insert end $x
	}
	pack .opts.orient_info.sy -side right -fill y
	pack .opts.orient_info.list -side left -fill both -expand true
	pack .opts.orient_info -side left -expand yes -anchor e -padx 10 -in .opts.first

#
# we have the duplex selection
#
	frame .opts.duplex_info
	label .opts.duplex_info.l1 -text "Duplex Options" -anchor w -width 12
	pack .opts.duplex_info.l1 -side top -fill x -in .opts.duplex_info

	listbox .opts.duplex_info.list -bg $bglistbox -yscrollcommand [list .opts.duplex_info.sy set] \
		-width 11 -height 3 -setgrid true -selectmode single \
		-exportselection false -font $font_fixed
	scrollbar .opts.duplex_info.sy -orient vertical -command [list .opts.duplex_info.list yview]
	foreach x $duplex_list {
		.opts.duplex_info.list insert end $x
	}
	pack .opts.duplex_info.sy -side right -fill y
	pack .opts.duplex_info.list -side left -fill both -expand true
	pack .opts.duplex_info -side left -expand yes -anchor e -padx 10 -in .opts.first

#
# we have the resolution selection
#
	frame .opts.resolution_info
	label .opts.resolution_info.l1 -text "Resolution" -anchor w -width 12
	pack .opts.resolution_info.l1 -side top -fill x -in .opts.resolution_info

	listbox .opts.resolution_info.list -bg $bglistbox -yscrollcommand [list .opts.resolution_info.sy set] \
	-width 11 -height 3 -setgrid true -selectmode single \
	-exportselection false -font $font_fixed
	scrollbar .opts.resolution_info.sy -orient vertical -command [list .opts.resolution_info.list yview]
	foreach x $resolution_list {
		.opts.resolution_info.list insert end $x
	}
	pack .opts.resolution_info.sy -side right -fill y
	pack .opts.resolution_info.list -side left -fill both -expand true
	pack .opts.resolution_info -side left -expand yes -anchor e -padx 10 -in .opts.first

	pack .opts.first -side top -in .opts

	frame .opts.second

	frame .opts.papersize_info
	label .opts.papersize_info.l1 -text "Paper Size" -anchor w -width 12
	pack .opts.papersize_info.l1 -side top -fill x -in .opts.papersize_info

	listbox .opts.papersize_info.list -bg $bglistbox -yscrollcommand [list .opts.papersize_info.sy set] \
		-width 11 -height 3 -setgrid true -selectmode single \
		-exportselection false -font $font_fixed
	scrollbar .opts.papersize_info.sy -orient vertical -command [list .opts.papersize_info.list yview]
	foreach x $papersize_list {
		.opts.papersize_info.list insert end $x
	}
	pack .opts.papersize_info.sy -side right -fill y
	pack .opts.papersize_info.list -side left -fill both -expand true
	pack .opts.papersize_info -side left -expand yes -anchor e -padx 10 -in .opts.second

#
# we have the media selection
#
	frame .opts.media_info
	label .opts.media_info.l1 -text "Media" -anchor w -width 12
	pack .opts.media_info.l1 -side top -fill x -in .opts.media_info

	listbox .opts.media_info.list -bg $bglistbox -yscrollcommand [list .opts.media_info.sy set] \
		-width 11 -height 3 -setgrid true -selectmode single \
		-exportselection false -font $font_fixed
	scrollbar .opts.media_info.sy -orient vertical -command [list .opts.media_info.list yview]
	foreach x $media_list {
		.opts.media_info.list insert end $x
	}
	pack .opts.media_info.sy -side right -fill y
	pack .opts.media_info.list -side left -fill both -expand true
	pack .opts.media_info -side left -expand yes -anchor e -padx 10 -in .opts.second


#
# we have the intray selection
#

	frame .opts.intray_info
	label .opts.intray_info.l1 -text "Input Tray" -anchor w -width 12
	pack .opts.intray_info.l1 -side top -fill x -in .opts.intray_info

	listbox .opts.intray_info.list -bg $bglistbox -yscrollcommand [list .opts.intray_info.sy set] \
		-width 11 -height 3 -setgrid true -selectmode single \
		-exportselection false -font $font_fixed
	scrollbar .opts.intray_info.sy -orient vertical -command [list .opts.intray_info.list yview]
	foreach x $intray_list {
		.opts.intray_info.list insert end $x
	}
	pack .opts.intray_info.sy -side right -fill y
	pack .opts.intray_info.list -side left -fill both -expand true
	pack .opts.intray_info -side left -expand yes -anchor e -padx 10 -in .opts.second


#
# we have the outbin selection
#

	frame .opts.outbin_info
	label .opts.outbin_info.l1 -text "Output Bin" -anchor w -width 12
	pack .opts.outbin_info.l1 -side top -fill x -in .opts.outbin_info

	listbox .opts.outbin_info.list -bg $bglistbox -yscrollcommand [list .opts.outbin_info.sy set] \
		-width 11 -height 3 -setgrid true -selectmode single \
		-exportselection false -font $font_fixed
	scrollbar .opts.outbin_info.sy -orient vertical -command [list .opts.outbin_info.list yview]
	foreach x $outbin_list {
		.opts.outbin_info.list insert end $x
	}
	pack .opts.outbin_info.sy -side right -fill y
	pack .opts.outbin_info.list -side left -fill both -expand true
	pack .opts.outbin_info -side left -expand yes -anchor e -padx 10 -in .opts.second

# put the frame into place
	pack .opts.second -side top -in .opts


    frame .opts.options
    label .opts.l33 -text "Additional Filter Options (One per Line)"
    button .opts.h33 -text "?" -font $font_fixed -width 0 -command options_help
    pack .opts.l33 -side left -anchor w -in .opts.options
    pack .opts.h33 -side right -expand false -ipady 1 -in .opts.options
    pack .opts.options -fill x -side top -in .opts


    frame .opts.area
	text .opts.useropts -bg $bgtext -width $entrywidth -height 5 -setgrid true -yscrollcommand {.opts.suseropts set }
	scrollbar .opts.suseropts -command { .opts.useropts yview }
	pack .opts.suseropts -side right -fill y -in .opts.area
	pack .opts.useropts -side left -fill both -expand true -in .opts.area
    pack .opts.area -fill x -side top -in .opts



	#puts "J: i $i"
	set zopts [cleanup_list_entry [printcap_field $i "prefix_z=" "" ] ]
	#puts "J: zopts '$zopts'"
	set values [split $zopts "," ]
	#puts "J: values '$values'"
	set options ""
	foreach value $values {
		#puts "J: value '$value'"
		if { $value == "" } { continue }
		set found -1
		foreach list {duplex intray media orient outbin papersize resolution} {
			eval set lv \$${list}_list
			#puts "lv '$lv'"
			set found [lsearch -exact $lv $value]
			if { $found != -1 } {
				#puts "found '$found'"
				set sel .opts.${list}_info.list
				#puts "sel '$sel'"
				$sel selection clear 0 end
				$sel selection set $found
				$sel see $found
				break
			}
		}
		if { $found == -1 } {
			append options $value ","
		}
	}
	#puts "options '$options'"
	regsub {,$} $options "" options
	regsub {^,} $options "" options
	regsub -all {,} $options "\n" options
	if { $options != "" } {
		.opts.useropts insert end "$options"
	}

    center_dialog .opts
    set oldFocus [focus]
    focus .opts
    catch { grab .opts }

    wm protocol .opts WM_DELETE_WINDOW { set trigger_2 0 }
    update
    scan [wm geometry .opts] "%d%*c%d" xmin ymin
    wm minsize .opts $xmin $ymin
    wm maxsize .opts $xmin $ymin

	set options ","
	while { 1 } {
		tkwait variable trigger_3
		if {$trigger_3 == 1} {
			set value [ string trim [.opts.v1 get]]
			set_printcap_field $i "lpr=" $value
			#puts "LPR '$value'"
			foreach list {duplex intray media orient outbin papersize resolution} {
				set sel .opts.${list}_info.list
				#puts "check '$sel'"
				set index [$sel curselection]
				#puts "index $index"
				if { $index != "" && $index > 0 } {
					eval set lv \$${list}_list
					set value [lindex $lv $index]
					#puts "value $value"
					append options $value ","
				}
			}
			append options [.opts.useropts get 1.0 end]
			#puts "options '$options'"
			set options [cleanup_list_entry $options]
			regsub {,default,} $options {} options
			regsub {^\,} $options {} options
			regsub {\,$} $options {} options
			#puts "final options '$options'"
			set_printcap_field $i "prefix_z=" $options
			$e config -state normal
			$e delete 0 end
			$e insert end $options
			$e config -state disabled
		}
		break;
	}

    destroy .opts
	update idletasks
    return $trigger_3
}

# Set up a local printer
proc DEVICE_setup {i} {
    global rw_device
	set rw_device [printcap_switch $i "rw" 1]

    FILTERED_setup $i
}

# Main menu status line for a "local" printer (/dev/lpt)
proc DEVICE_summaryline {i} {
    set printer [FILTERED_summaryline $i "DEVICE printer"]
    set port [printcap_field $i "lp=" "(unspecified device)" ]
    return "$printer on $port"
}

# Update the local printer entry (after adding a new printer or modifying one)
proc DEVICE_updateentry {w i} {
    global rw_device mode

    set_printcap_field $i "lp=" [$w.v8 get]
    #set lp [printcap_field $i "lp=" ""]
	#puts "DEVICE_updateentry set $lp"

	#puts "DEVICE_updatentry rw '$rw_device'"
    set_printcap_switch $i "rw" $rw_device
	if { $mode } { set_printcap_field $i "remote_support=" "R" }

    FILTERED_updateentry $i
    miscopts_updateentry $i
	spooljobs_updateentry $i
}


# Printer Pool

# Window displayed when creating/editing a remote printer
proc DUMMY_addpanel {w i} {
    global printer_names entrywidth

    FILTERED_addpanel $w $i
    miscopts_addpanel $w $i
}

# Name to return when editing/adding a printer pool
proc DUMMY_name {} {
    return "Dummy Printcap Entry For :tc Includes"
}

# Set up a printer pool
proc DUMMY_setup {i} {
	#puts "DUMMY_setup $i"
}

# Summary line sent to the main menu when this is a printer pool
proc DUMMY_summaryline {i} {
    return "Dummy Printcap Entry For :tc Includes"
}

# Update the printer pool printcap entry
proc DUMMY_updateentry {w i} {
	global printer_entry

    FILTERED_updateentry $i
    miscopts_updateentry $i
}

# Printer Pool

# Window displayed when creating/editing a remote printer
proc PRINTPOOL_addpanel {w i} {
    global printer_names entrywidth
	global font_fixed font_helvetica bgentry

    frame $w.pool

	set temp [cleanup_list_entry  [printcap_field $i "sv=" ""] ]
	regsub {^,} $temp "" temp
	regsub {,$} $temp "" temp

    label $w.l21 -text "Pool (printer1,printer2,...)" -anchor w
	entry $w.v21 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    $w.v21 insert 0 $temp

    pack $w.l21  -side left -anchor w -fill x -in $w.pool
    button $w.h21 -text "?" -font $font_fixed -width 0 -command pool_help
    pack $w.h21 -side right -expand false -ipady 1 -in $w.pool
    pack $w.v21  -side right -anchor e -fill x -in $w.pool

    pack $w.pool -side top -fill x
    miscopts_addpanel $w $i

	spooljobs_addpanel $w $i 0
}

# Name to return when editing/adding a printer pool
proc PRINTPOOL_name {} {
    return "Load Balancing Printer Pool"
}

# Set up a printer pool
proc PRINTPOOL_setup {i} {
	#puts "PRINTPOOL_setup $i"
    #set spool_dir [ resolve_spool_dir [printcap_field $i "sd=" ""] $i ]
}

# Summary line sent to the main menu when this is a printer pool
proc PRINTPOOL_summaryline {i} {
    global printer_names

    set printers [printcap_field $i "sv=" "(unspecified printers)"]
    return "PRINTER POOL for '$printers'"
}

# Update the printer pool printcap entry
proc PRINTPOOL_updateentry {w i} {
	global printer_entry

    regsub ":" [$w.v21 get] "-" temp
	set temp [cleanup_list_entry  $temp]
	regsub {^,} $temp "" temp
	regsub {,$} $temp "" temp

    set_printcap_field $i "sv=" $temp
	#puts "PRINTPOOL_updatentry '$temp'"

    miscopts_updateentry $i
	spooljobs_updateentry $i
}

# Remote Unix lpd Queue Support

# Summary line returned to the main menu if it's a remote printer or lpd queue
proc QUEUE_summaryline {i} {
    global printer_names printer_force_localhost printer_used_by

    set pr [lindex [split $printer_names($i) '|'] 0]
    set lp [printcap_field $i "lp=" ""]
    set localhost $printer_force_localhost($i)

	set lp [split $lp '@']
	set queue [ lindex $lp 0 ]
	set host [ lindex $lp 1 ]
	#puts "QUEUE_summaryline: lp '$lp', queue '$queue', host '$host', localhost '$localhost' "
	if { $queue == "" } {
		set queue $pr
	}
	if { $host == "" } {
		if { $localhost == "YES" } {
			set host "(localhost)"
		} else {
			set host "(unknown)"
		}
	}
	
	regsub {%P} $queue $pr queue

    return "QUEUE '$queue' on server '$host'" 
}

# Name returned to the title bar when editing/adding a remote printer
proc QUEUE_name {} {
    return "Print Queue"
}


# Window displayed when creating/editing a remote printer
proc QUEUE_addpanel {w i} {
	global printer_entry entrywidth font_fixed font_helvetica bgentry

    frame $w.rhost
    frame $w.rqueue

    set temp [printcap_field $i "lp=" ""]
	set rm [printcap_field $i "rm=" ""]
	set rp [printcap_field $i "rp=" ""]
	#puts "QUEUE_addpanel entry $lp, lp '$temp', rm '$rm', rp '$rp'"
    if {$temp != ""} {
		set temp [split $temp "@"]
		set rp [lindex $temp 0]
		set rm [lindex $temp 1]
	}
	#puts "QUEUE_addpanel lp '$temp', rm '$rm', rp '$rp'"
	#if { $rp == "" } { set rp "%P" }

    label $w.l5 -text "Remote Host" -anchor w
	entry $w.v5 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h5 -text "?" -font $font_fixed -width 0 -command remotehost_help
    $w.v5 insert 0 $rm

    label $w.l6 -text "Remote Queue (default %P)" -anchor w
	entry $w.v6 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h6 -text "?" -font $font_fixed -width 0 -command remotequeue_help
    $w.v6 insert 0 $rp

    pack $w.l5  -side left -anchor w -fill x -in $w.rhost
    pack $w.h5 -side right -expand false -ipady 1 -in $w.rhost
    pack $w.v5  -side right -anchor e -fill x -in $w.rhost

    pack $w.l6  -side left  -anchor w -fill x -in $w.rqueue
    pack $w.h6 -side right -expand false -ipady 1 -in $w.rqueue
    pack $w.v6  -side right  -anchor e -fill x -in $w.rqueue

    pack $w.rhost -side top -fill x
    pack $w.rqueue -side top -fill x

    FILTERED_addpanel $w $i
    miscopts_addpanel $w $i
	spooljobs_addpanel $w $i 0
}

# Update the remote printer printcap entry
proc QUEUE_updateentry {w i} {
    set queue [$w.v6 get]
    set host [$w.v5 get]

	if { $queue == "" && $host != "" } {
		set queue "%P"
	}
	if { $queue != "" && $host != "" } {
		set lp "$queue@$host"
		regsub ":" "$lp" "-" lp
		set_printcap_field $i "lp=" $lp
	}
	#puts "QUEUE_updateentry $lp"

    FILTERED_updateentry $i
    miscopts_updateentry $i
	spooljobs_updateentry $i
}

# Set up a remote printer
proc QUEUE_setup {i} {
	#puts "QUEUE_setup $i"
    FILTERED_setup $i
}

# Samba Support

# Window to display if editing/adding an SMB printer share
proc SMB_addpanel {w i} {
	global novellkey appletalkkey userkey mode
    global smb_crlf filtersrcdir
    global smb_password_dup
	global entrywidth smb_remote_mode remote_type wideentrywidth
	global font_fixed font_helvetica smb_vars bgentry

	foreach v $smb_vars {
		if {![info exists $v]} {
			global $v
		}
	}

    SMB_config $i

	frame $w.chx -borderwidth 1 -relief raised
	radiobutton $w.chxa -text "SMB (Microsoft)" -variable smb_remote_mode -value "SMB" -anchor w -command "update_smb_remote_mode $i $w.v4"
	pack $w.chxa -side left -fill x -anchor w -expand true -in $w.chx
	radiobutton $w.chxb -text $novellkey -variable smb_remote_mode -value "Novell" -anchor w -command "update_smb_remote_mode $i $w.v4"
	pack $w.chxb -side left -fill x -anchor w -expand true -in $w.chx
	radiobutton $w.chxc -text $appletalkkey -variable smb_remote_mode -value $appletalkkey -anchor w -command "update_smb_remote_mode $i $w.v4"
	pack $w.chxc -side left -fill x -anchor w -expand true -in $w.chx
	radiobutton $w.chxd -text "User Specified" -variable smb_remote_mode -value $userkey -anchor w -command "update_smb_remote_mode $i $w.v4"
	pack $w.chxd -side left -fill x -anchor w -expand true -in $w.chx
    pack $w.chx -side top -fill x


    frame $w.lp
    label $w.l4 -text "Communications Program" -anchor w
	entry $w.v4 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    $w.v4 insert 0 [ get_smb_remote_mode $i ]
	if { $smb_remote_mode != $userkey } {
		$w.v4 config -state disabled
	}
    button $w.h4 -text "?" -font $font_fixed -width 0 -command smbhostname_help
    pack $w.h4 -side right -expand false -ipady 1 -in $w.lp
    pack $w.l4 -side left -fill x -anchor w -in $w.lp
    pack $w.v4 -side right -fill x -anchor e -in $w.lp
    pack $w.lp -side top -fill x

    frame $w.smbhost
    label $w.l5 -text "Print Server Host (SMB/Novell/AppleTalk) Name" -anchor w
	entry $w.v5 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h5 -text "?" -font $font_fixed -width 0 -command smbhostname_help
    $w.v5 insert 0 $smb_host
    pack $w.h5 -side right -expand false -ipady 1 -in $w.smbhost
    pack $w.l5 -side left -fill x -anchor w -in $w.smbhost
    pack $w.v5 -side right -fill x -anchor e -in $w.smbhost
    pack $w.smbhost -side top -fill x

    frame $w.smbname
    label $w.l6 -text "Printer (SMB Share/Novell Printqueue/AppleTalk Name) Name" -anchor w
	entry $w.v6 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h6 -text "?" -font $font_fixed -width 0 -command smbprinter_help
    $w.v6 insert 0 $smb_printer
    pack $w.h6 -side right -expand false -ipady 1 -in $w.smbname
    pack $w.l6 -side left -fill x -anchor w -in $w.smbname
    pack $w.v6 -side right -fill x -anchor e -in $w.smbname
    pack $w.smbname -side top -fill x

    frame $w.smbuser
    label $w.l7 -text "Username for Login/Authentication" -anchor w
	entry $w.v7 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h7 -text "?" -font $font_fixed -width 0 -command smbuser_help
    $w.v7 insert 0 $smb_username
    pack $w.h7 -side right -expand false -ipady 1 -in $w.smbuser
    pack $w.l7 -side left -fill x -anchor w -in $w.smbuser
    pack $w.v7 -side right -fill x -anchor e -in $w.smbuser
    pack $w.smbuser -side top -fill x

    frame $w.smbpasswd
    label $w.l8 -text "Password for Login/Authentication" -anchor w
	entry $w.v8 -bg $bgentry -show "*" -font $font_fixed -relief sunken -borderwidth 2 -width 16 
    label $w.l8a -text " (Confirm) " -anchor w
	entry $w.v8a -bg $bgentry -show "*" -font $font_fixed -relief sunken -borderwidth 2 -width 16 
    button $w.h8 -text "?" -font $font_fixed -width 0 -command smbpass_help
    $w.v8 insert 0 $smb_password
    $w.v8a insert 0 $smb_password
    pack $w.h8 -side right -expand false -ipady 1 -in $w.smbpasswd
    pack $w.l8 -side left -fill x -anchor w -in $w.smbpasswd
    pack $w.v8a -side right -fill x -anchor e -in $w.smbpasswd
    pack $w.l8a -side right -fill x -anchor e -in $w.smbpasswd
    pack $w.v8 -side right -fill x -anchor e -in $w.smbpasswd
    pack $w.smbpasswd -side top -fill x

    frame $w.smbworkgroup
    label $w.l9 -text "Workgroup, NT Domain, AppleTalk Zone" -anchor w
	entry $w.v9 -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h9 -text "?" -font $font_fixed -width 0 -command smbdomain_help
    $w.v9 insert 0 $smb_workgroup
    pack $w.h9 -side right -expand false -ipady 1 -in $w.smbworkgroup
	pack $w.l9 -side left -fill x -anchor w -in $w.smbworkgroup
	pack $w.v9 -side right -fill x -anchor e -in $w.smbworkgroup
    pack $w.smbworkgroup -side top -fill x

    frame $w.smbip
    label $w.l6_hostip -text "IP Address of Server (optional)" -anchor w
	entry $w.v6_hostip -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h6_hostip -text "?" -font $font_fixed -width 0 -command smbserverip_help
    $w.v6_hostip insert 0 $smb_hostip
    pack $w.h6_hostip -side right -expand false -ipady 1 -in $w.smbip
    pack $w.l6_hostip -side left -fill x -anchor w -in $w.smbip
    pack $w.v6_hostip -side right -fill x -anchor e -in $w.smbip
    pack $w.smbip -side top -fill x


    frame $w.smbauthfile
	if { $mode == 0 } {
		label $w.l6_authfile -text "Authentication file (relative to Spool Directory)" -anchor w
	} else {
		label $w.l6_authfile -text "Authentication file (full pathname)" -anchor w
	}
	entry $w.v6_authfile -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h6_authfile -text "?" -font $font_fixed -width 0 -command smbserverip_help
    $w.v6_authfile insert 0 $smb_authfile
    pack $w.h6_authfile -side right -expand false -ipady 1 -in $w.smbauthfile
    pack $w.l6_authfile -side left -fill x -anchor w -in $w.smbauthfile
    pack $w.v6_authfile -side right -fill x -anchor e -in $w.smbauthfile
    pack $w.smbauthfile -side top -fill x

#
# we'll handle cr/lf translation through the print filter if necessary
#
    FILTERED_addpanel $w $i
    miscopts_addpanel $w $i
	spooljobs_addpanel $w $i 0
}

# Read the configuration file for a SMB printer
#  return [list $hostname $printername $user $password $workgroup $hostip ]
#                0          1            2     3         4          5
#
proc SMB_config {i} {
	global smb_vars

	foreach v $smb_vars {
		if {![info exists $v]} { global $v }
	}
    set spool_dir [ resolve_spool_dir [printcap_field $i "sd=" ""] $i ]
    set info [printcap_field $i "xfer_options=" ""]

    set smb_crlf 0
	set smb_host ""
	set smb_printer ""
	set smb_username ""
	set smb_password ""
	set smb_workgroup ""
	set smb_hostip ""
    set smb_share ""
    set smb_authfile "auth"
    set smb_remote_mode ""
	foreach v [ split $info " "] {
		set stringval [regexp {=} $v]
		set key $v
		set value ""
		if { $stringval } {
			set parts [ split $v "=" ]
			set key [lindex $parts 0]
			set value [lindex $parts 1]
			#puts "SMB_config: raw key '$key' value '$value'"
			regsub {^\"} $value {} value
			regsub {\"$} $value {} value
			#puts "SMB_config: fixed value '$value'"
		}
		if { $key != "" } {
			set name smb_${key}
			if {![info exists $name]} {
				warnmsg "LOGIC ERROR - SMB config value '$name'!"
				continue
			}
			#puts "setting $name '$value'"
			set $name $value
		}
	}
	# now we get the authorization file
	#puts "smb_authfile '$smb_authfile'"
	if {[regexp {^/} $smb_authfile]} {
		set authfile $smb_authfile
	} elseif { $spool_dir != ""} {
		set authfile $spool_dir/$smb_authfile
	}
	#puts "using authfile '$authfile'"

	if { $authfile != "" && [file exists $authfile] } {
		if { [catch { set fd [open $authfile r] } why ] == 1 } {
			warnmsg "Cannot open '$authfile' for reading - $why"
		} else {
			while { [gets $fd s] != -1 } {
				#puts "read auth '$s'"
				regexp "username[ 	]*=[ 	]*\(.*\)" $s dummy smb_username
				regexp "password[ 	]*=[ 	]*\(.*\)" $s dummy smb_password
			}
			#puts "smb_username '$smb_username', smb_password '$smb_password'"
		}
	}
	if { $smb_remote_mode == "" } {
		set smb_remote_mode SMB
	}
	set s ""
	foreach v $smb_vars {
		eval set t \$$v
		append s "$v=\"$t\" "
	}
	#puts "SMB_config: $s"
}

# Name returned for the title bar/etc if this is a SMB printer
proc SMB_name {} {
    return "SMB (Microsoft), Novell, AppleTalk (Filter and Print)"
}



# Set up an SMB printer
proc SMB_setup {i} {
    global smb_vars filtersrcdir uid mode

	#puts "SMB_setup: START"
	foreach v $smb_vars {
		if {![info exists $v]} { global $v }
	}

#
#   now do smbprint related setup
#
#
	#puts "SMB_setup $i"
    set spool_dir [ resolve_spool_dir [printcap_field $i "sd=" ""] $i ]
	# now we get the authorization file
	set authfile ""
	if {[regexp {^/} $smb_authfile]} {
		set authfile $smb_authfile
	} elseif { $spool_dir != ""} {
		set authfile $spool_dir/$smb_authfile
	}

	#puts "SMB_setup: spool_dir '$spool_dir' authfile $authfile"
	set dir [file dirname $authfile]
	if { $mode == 0 && ![file exists $dir] } {
		create_dir $dir
	}

    if {[catch {set fd [open "$authfile" w 0640]} stuff] == 1} {
		popup_error_dialog "Cannot write SMB config file $authfile - $stuff"
		return
	}
#    if {$smb_crlf} { set translate yes } else { set translate no }
    puts $fd "username=$smb_username"
	puts $fd "password=$smb_password"
    close $fd
	if { $mode == 0 && [catch { file attributes $authfile -permissions 0640 } stuff]} {
		warnmsg "error fixing $authfile - $stuff"
	}
#
#       now setup the auto_filter
#
    FILTERED_setup $i
}

# Summary line shown on the main menu if this is a SMB printer
# Config info
#   $hostname $printername $user $password $workgroup $hostip $type
#     0          1            2     3         4          5       6
proc SMB_summaryline {i} {
	global remote_type smb_host smb_printer smb_remote_mode
    set printer [FILTERED_summaryline $i "printer"]
	SMB_config $i
    return "$smb_remote_mode $printer on //$smb_host/$smb_printer"
}

# Update the SMB printcap database entry after making any changes/additions
proc SMB_updateentry {w i} {
	global remote_type mode smb_vars smb_password_dup

	foreach v $smb_vars {
		if {![info exists $v]} { global $v }
	}


    set smb_host [ string trim [$w.v5 get] ]
    set smb_printer [ string trim [$w.v6 get] ]
    set smb_share "//$smb_host/$smb_printer"
    set smb_hostip   [ string trim [$w.v6_hostip get]]
    set smb_username   [ string trim [$w.v7 get]]
    set smb_password  [ string trim [$w.v8 get]]
    set smb_password_dup  [ string trim [$w.v8a get]]
	set smb_workgroup [ string trim [$w.v9 get]]
	set smb_authfile [ string trim [$w.v6_authfile get]]
	set remote_type($i) $smb_remote_mode
    set_printcap_field $i "lp=" [ string trim [$w.v4 get]]

	#puts "SMB_updateentry smb_remote_mode $smb_remote_mode"
	set remote_type($i) $smb_remote_mode
	if { $mode } { set_printcap_field $i "remote_support=" "R" }

	set s ""
	foreach v $smb_vars {
		if { $v == "smb_password" || $v == "smb_username" } { continue }
		eval set t \$$v
		regsub {smb_} $v {} name
		append s "$name=\"$t\" "
	}
    set_printcap_field $i "xfer_options=" $s
	#puts "SMB_update_entry: $s"
#
#   get the auto_filter setup first
#
    FILTERED_updateentry $i
    miscopts_updateentry $i
	spooljobs_updateentry $i
}



# Main menu status line if we can't determine the type of print queue/printer
proc UNKNOWN_summaryline {i} {
    global printcapfile
    return "Unrecognizable printer.  Is $printcapfile corrupt?"
}

# If the printer type is unknown, this could appear, but shouldn't ever, really
proc UNKNOWN_name {} {
    return "UNKNOWN printer type"
}

# We should never be able to add an unknown printer
proc UNKNOWN_addpanel {w i} {
	global entrywidth font_fixed font_helvetica bgentry

    frame $w.lp
    label $w.l5lp -text ":lp Value" -anchor w
	entry $w.v5lp -bg $bgentry -font $font_fixed -relief sunken -borderwidth 2 -width $entrywidth
    button $w.h5lp -text "?" -font $font_fixed -width 0 -command unknown_help
	$w.v5lp insert 0 [printcap_field $i "lp=" ""]
    pack $w.l5lp -side left -fill x -anchor w -in $w.lp
    pack $w.h5lp -side right -expand false -ipady 1 -in $w.lp
    pack $w.v5lp -side right -fill x -anchor e -in $w.lp
    pack $w.lp -side top -fill x
}

# We shouldn't be able to update an unknown printer
proc UNKNOWN_updateentry {w i} {
    #puts "UNKNOWN_updateentry called--shouldn't happen"
	regsub ":" [$w.v5lp get] "-" lp
	set_printcap_field $i "lp=" $lp
}

# We shouldn't be able to setup an unknown printer
proc UNKNOWN_setup {i} {
    #puts "UNKNOWN_setup called--shouldn't happen"
}


# Backup /etc/printcap (for safety)
proc menu_backup_printcap {} {
    global printcapfile

	set status ""
	set res [popup_yesno_dialog "Copy '$printcapfile' to '$printcapfile.old'?" 0]
	if { $res != 0 } { return ; }
	if {[catch {file copy -force $printcapfile $printcapfile.old} status]} {
		popup_info_dialog "Cannot copy '$printcapfile' to '$printcapfile.old' - $status."
		return
	}
	popup_info_dialog "File '$printcapfile' has been copied to '$printcapfile.old'. $status"
}

# Known Bugs/Issues Menu
proc menu_bugs {} {
    global printcapfile

    help_dialog "Release Notes" "
                            Release Notes

As of the final release, there are no known bugs in LPRngTool.  However,
there are a few issues which could cause your printer configuration
database ($printcapfile) to become corrupt.  Please read on to ensure
you never have to worry about these problems.

It is very important that your $printcapfile file has not been modified
by hand.  LPRngTool is an extremely effective product; however, it is
unable to compensate for some user errors.  If you tamper with your
printcap configuration file, you may find that LPRngTool is unable to
determine the type of printer you have, and may be unable to edit some
or all of your printers.  If this is the case, you may need to delete
some of your printers, or clear the configuration and recreate the
database.  If you use LPRngTool for all your printer modification needs,
everything should always be fine.  Why would you possibly want to learn
all the bizarre syntax and abbreviations if this program works fine?

Colons (:) are converted to hyphens (-).  This is not really a bug.  As
a matter of fact, it's a way to avoid all sorts of bugs.  LPRng assumes
a colon starts a new field.  If you use colons in your fields, LPRng would
assume these were new lines, and you configuration file would become
corrupt.  In order to avoid this, LPRngTool converts all colons you
enter to hyphens.

LPRngTool will cause LPRng to reread the printer configuration file
after making any modifications to it.  This is to ensure that any
changes you make will become active.  Additionally, you can request
LPRng to reread the printcap file at any time by using the File menu.

"
}

# Clear /etc/printcap (in case the file is corrupt)
proc menu_clear_printcap {} {
    global printcapfile spooldirectory

	set res [popup_yesno_dialog "Copy '$printcapfile' to '$printcapfile.old' and clear '$printcapfile'?" 0]
	if { $res != 0 } { return ; }
	if {[catch {file copy -force $printcapfile $printcapfile.old} status]} {
		popup_info_dialog "Cannot copy '$printcapfile' to '$printcapfile.old'. $status"
		return
	}
	if {[catch {set fd [open "$printcapfile" w 0644]} stuff] == 1} {
		warnmsg "Printcap file '$printcapfile' not writeable - $stuff."
		return
	}
    popup_info_dialog "The '$printcapfile' has been copied to '$printcapfile.old'
and the '$printcapfile' truncated.

The spool queue directories have not been removed.  You can
do this by using 'rm -rf $spooldirectory/*'
"
	reload
	redisplay
}


# Write /etc/printcap (in case the file is corrupt)
proc menu_write_printcap {} {
    global printcapfile spooldirectory

	write_printcap
	reload
	redisplay
}



# Write /etc/printcap (in case the file is corrupt)
proc menu_reload_printerdb {} {
	global printerdb_loaded
	set printer_db_loaded 0
	reload_printerdb
}

# Frequently Asked Questions
proc menu_faq {} {
    global printcapfile
	global spooldirectory

    help_dialog "Frequently Asked Questions" "

            Frequently Asked Questions

       Author: Geoff Silver (geoff\@uslinux.net)
        Update by: Patrick Powell (papowell\@astart.com)

Q: LPRngTool looks a little like Red Hat's Printtool.
A: Yes, it does.  When designing LPRngTool, I wanted a consistant
   user interface that most people (who might be familiar with
   Red Hat's lpd) would feel confortable with.  It should seem
   familiar, but with all sorts of new features, help fields, and
   new queue management routines.

Q: Why are colons replaced with hyphens when I save my changes?
A: LPRng thinks colons separate fields.  In order to keep from
   breaking LPRng and LPRngTool I had to replace something which
   would cause problems with something that wouldn't.  This
   was the only thing I found.  Just use something besides a
   colon, and you'll never have any problems.

Q: What is a 'System Queue' and a 'User Queue'
A: LPRng now has 'user defined printcaps' \(\$HOME/.printcap\) where
   users can add or modify printer definitions.

Q: Why can't I add an input filter to a printer pool?
A: Because LPRng does not process jobs in the printer pool 
   queue before sending them to the actual server.  The
   printer pool queue is literal just a holding place for
   the 'real printer'.

Q: Why isn't feature XXX part of LPRngTool?  LPRng supports it.
A: If you are absolutely desperate to use these features, then
   you can set the required printcap option values using
   the Advanced options selection.

Q: Why can't I add/remove/modify remote printers?  I can see
   the queues and control them.
A: This would require modification of the /etc/printcap file
   on the remote host,  and LPRng does not provide a way to
   edit this file.  There are also some security and authentication
   issues that would make this difficult to support in a simple
   manner.

Q: What is the purpose of the %P value in the Spool Directory field?
A: By default the LPRngTool sets the spool queue name
   to the $spooldirectory/%P,  and LPRng itself will substitute
   the name of the printer for %P.

   You should be aware that LPRng gets very upset if two
   print queues use the same spool directory,  and your print
   jobs may vanish into limbo.  By using the %P value,  different
   print queues will always have different spool directories.
"
}

# General Help Menu
proc menu_genhelp {} {
    global printcapfile

    help_dialog "General Help" \
"
General Help
   by
Geoff Silver <geoff\@uslinux.net>
Patrick Powerll <papowell\@astart.com>

LPRngTool is a sophisticated printer management system for any
system which supports Tcl/Tk and LPRng.  Using this toolkit you
can add, delete, and edit printers directly on the local machine,
and check accounting and status logs.  You can also monitor and
manipulate the print queues on any remote LPRng server.

LPRngTool can update the system printcap file, usually /etc/printcap,
or the user printcap file,  usually \$\(HOME\)/.printcap.  The user
printcap file allows users to create their own private printer
definitions.  The user definitions are used only to transfer jobs to
a printer or print queue.  You can change the mode of operation from
User to System by clicking on the button with the desired mode.

In the main window below the left column contains the printer
or print queue names and any aliases (separated by '|' symbols).

The right column contains the type of printer (Remote, Device, SMB,
etc), the remote queue name or port, remote machine name, and other
information if applicable.  The printer types are:

TYPE		lp= value		Type of printer

DEVICE  /dev/lp     - local device
QUEUE   queue@host  - lpd queue on server
SOCKET  host%port   - TCP/IP connection
SMB     |/comprog   - program support for transfer
DUMMY                   - template for 'tc=xxx' include facility
PRINTPOOL               - load balance spool queue
UNKNOWN                 - unknown type

You may ADD a new printer by simply clicking the ADD button.  You
may also EDIT or DELETE a printer by selecting the printer name
and then clicking the appropriate button.  Changes are made to your
printer configuration file ($printcapfile) as soon as you press
'OK'.  If in doubt, select 'Backup Configuration' from the 'File'
menu before continuing.  After making modifications to the print
queue the LPRngTool will ask if you want to use the checkpc program
to create the spool directory and files and if you want to inform
the lpd server that you want it to use the modified entry.

On the 'File' menu, you can Backup your current configuration,
Restore a previously backed-up configuration, Reload the current
configuration, and Clear the current configuration.

You may print several different test pages from the 'Test Pages'
menu to ensure your configuration is working properly.  You can
print an ASCII or PostScript test page from this menu (Note that
your printer must be capable of printing PostScript, or you must
have a printer driver listed in order for a PostScript page to
work).  For testing purposes, you can also print a test page directly
to the printer port.  If printing directly to a printer port fails,
check your printer cable, your port setup, and ensure the printer
works (and is not a WinPrinter) before attempting to continue.

For more detailed help, select the 'Help' button on the menu in
question.

"
}

proc menu_todo {} {
    global printcapfile

    help_dialog "Future Inclusions and Modifications To Be Done" "
Future Inclusions

There are a number of things to do.  If you would like to help out,
please email Patrick Powell <papowell@lprng.com> or Geoff Silver
<geoff@uslinux.net> or check out http://lprng.com for the latest
development information.

Planned Inclusions:

* Drag and drop printing
* Right-click menus which give widget-specific help, editing, etc
  capabilities.
* The ability to modify the /etc/lpd.perms file lprngtool.
* Remote printer administration (adding, removing, editing printers),
  and editing remote /etc/lpd.perms files using ssh and 'expect'

Unplanned Inclusions:
* Any bug fixes which might arise.
* Additional LPRng features which can be added to $printcapfile to
  add useful functionality.
"
}

# Re-read /etc/printcap and reload the menu
proc menu_reload {} {
    global selected_hint

    .list delete 0 end
    update idletasks
    reload
    redisplay
}

# Restore an old /etc/printcap (in case we really messed up)
proc menu_restore_printcap {} {
    global printcapfile

	set res [popup_yesno_dialog "Copy '$printcapfile.old' to '$printcapfile'?" 0]
	if { $res != 0 } { return ; }
	if {[catch {file copy -force $printcapfile.old $printcapfile} status]} {
		popup_info_dialog "Cannot copy '$printcapfile.old' to '$printcapfile'. $status"
		return
	}
	popup_info_dialog "Your $printcapfile file has been restored from $printcapfile.old."
	reload
	redisplay
}

# Troubleshooting Help
proc menu_trouble {} {
        help_dialog "Troubleshooting" "
Troubleshooting

First try printing a test page from under the 'Tests':

    Choosing 'Print ASCII directly to port' sends text straight
    to the printer, without using lpr. This will only work if your
    printer is attached to a printer port on the local machine. It
    will not work if your printer is configured as a QUEUE or SMB
    printer.

    You should at least see an indicator light on your printer blink
    if your printer port is configured correctly. If this doesn't
    work, check your cabling and printer device specification.
    When adding a new printer, printtool tries to detect the port
    the printer is attached to. In case this detection failed, try
    changing the printer device to /dev/lp0, /dev/lp1, and /dev/lp2.
    These are the most common ports on PC hardware.

More complex test pages are available as well, but these use lpr to print.

    If your printer is TEXT-ONLY, choose 'Print ASCII test page'.

    Otherwise try 'Print PostScript test page'.

If ASCII seems to print and PostScript does not, check that you have
the correct filter configured for your printer. If your printer model
does not appear in the list of possible choices, try picking a similar
model printer. If this fails, see if your printer is compatible with
another printer in the available choices. Many printers, for example,
are compatible with HP printers. Your printer manual should contain this
information.

The 'No Banner Pages' option (:sh) is on by default.
If you want banner pages then you must turn it OFF (i.e. :sh@)

For text-only printers, if you get stair-stepped text, try turning on the
'LF->CR/LF' option.

For all printers, if your printer appears to accept data (some printers
have a light which blinks when its accepting data), and nothing prints,
try enabling the 'Send EOF' option.

If you get an extra page at the end of every job, try turning off the
'Send EOF' option.

For JetDirect boxes, be sure to send non-text output to the 'raw' queue
on the JetDirect box.
"
}

# Function to exit
proc menu_quit {} {
    # We could sync here but we should always be synced
    exit 0
}


# Create the add/edit printer window with "suppress headers" and accessible panels
proc miscopts_addpanel {w i} {
}

# Update the filtering entries
proc miscopts_updateentry {i} {
}


# create the printer information so that it can be used as a header
# in the /etc/printcap file
proc set_printer_info { {filteropts 0} } {
	global auto_vars auto_filtertype userkey ghostscript pager
	if { $auto_filtertype == $userkey } {
		return ""
	}
	set final ""
	foreach v $auto_vars {
		if {![info exists $v]} {
			global $v
		}
		eval set value \"\$$v\"
		set name ""
		regsub {auto_} $v "" name
		#puts "name $name"
		if { $filteropts } {
			set name [string toupper $name]
			append final "$name=\"$value\" "
		} else {
			lappend final $name=$value
		}
		#puts "v '$v' name '$name' value '$value'"
	}
	#puts "set_printer_info '$final'"
	return $final
}



# Parse through the filter parameters in the ##LPRNGTOOL## line
# 
#  this has the form 'key=value' -> auto_key=value

proc parse_auto_filter_params { info } {
	global auto_vars auto_filtertype
    global printerdb_count printerdb_entry printerdb_GSDriver
    global printcapfile userkey

	foreach v $auto_vars {
		if {![info exists $v]} {
			global $v
		}
		if { $v == "auto_filtertype" } { continue }
		set $v ""
	}
	set auto_ascii_to_ps "NO"
	set auto_color_choice "Default"
	set auto_nup "1"
	set auto_ps_send_eof "NO"
	set auto_ps_send_eof "NO"
	set auto_rtlft_margin "18"
	set auto_sendeof "NO"
	set auto_sendeof "NO"
	set auto_texteof "NO"
	set auto_texteof "NO"
	set auto_topbot_margin "18"
	if { $auto_filtertype == "" } {
		set auto_filtertype $userkey
	}

#
# see if the printcap entry has any auto_filter info
#
	#puts "parse_auto_filter_params: info '$info'"
	foreach arg $info {
		#puts "arg '$arg'"
		set v [string first = $arg]
		if { $v != -1 } {
			set key [string trim [string range $arg 0 [expr $v - 1 ]]]
			set value [string trim [string range $arg [expr $v + 1 ] end]]
			#puts "key '$key' value '$value'"
			if {![info exists auto_$key]} {
				global auto_$key
			}
			set auto_$key $value
		}
	}
}

proc parse_ifhp_params { info } {
	global ifhp_vars
	foreach v $ifhp_vars {
		if {![info exists $v]} {
			global $v
		}
	}
	foreach v [ split $info ,] {
		set stringval [regexp {=} $v]
		set key $v
		if { $stringval } {
			set parts [ split $v "=" ]
			set key [lindex $parts 0]
			set value [lindex $parts 1]
		} elseif { [regsub {@.*} $key {} key] } {
			set value 0
		} else {
			set value 1
		}
		if { $key == "" } { continue }
		set name ifhp_option_${key}
		if {![info exists $name]} {
			#puts "parse_ifhp_params gobal '$name'"
			global $name
		}
		#puts "parse_ifhp_params: setting $name '$value'"
		set $name $value
	}
	set ifhp_option_sync_ps 0; set ifhp_option_sync_pjl 0;
	if { $ifhp_option_sync == "ps" } { set ifhp_option_sync_ps 1 }
	if { $ifhp_option_sync == "pjl" } { set ifhp_option_sync_pjl 1 }
	if { $ifhp_option_sync != 0 } { set ifhp_option_sync 1 }
	set $ifhp_option_pagecount_ps 0; set ifhp_option_pagecount_pjl 0;
	if { $ifhp_option_pagecount == "ps" } { set ifhp_option_pagecount_ps 1 }
	if { $ifhp_option_pagecount == "pjl" } { set ifhp_option_pagecount_pjl 1 }
	if { $ifhp_option_pagecount != 0 } { set ifhp_option_pagecount 1 }
	set $ifhp_option_waitend_ps 0; set ifhp_option_waitend_pjl 0;
	if { $ifhp_option_waitend == "ps" } { set ifhp_option_waitend_ps 1 }
	if { $ifhp_option_waitend == "pjl" } { set ifhp_option_waitend_pjl 1 }
	if { $ifhp_option_waitend != 0 } { set ifhp_option_waitend 1 }
}

# print the ascii test page directly to printer port
proc print_ascii_direct_testpage {} {
    global printer_names
    global printer_type

    set i [get_selected_index]
    if {$i == ""} {
        popup_error_dialog "Please highlight a printer first"
		return
    }

    if {$printer_type($i) != "DEVICE"} {
		popup_error_dialog "Can only print directly to a DEVICE printer"
		return
    }

    set port [printcap_field $i {lp=} ""]

    if {$port == ""} {
		popup_error_dialog "Please set the printer device for this printer first"
		return
    }

	set result [runjob [list printf "This text should appear on the printer on $port\n\014" \
		 > $port ] ]
    if { [lindex $result 0] } {
		popup_info_dialog "Test page printed to port [lindex $result 1]"
    }
}

# print the ascii test page
proc print_ascii_testpage {} {
    global printer_names
    global filtersrcdir rhostq localhostname

	set i [get_selected_index]
	if {$i == ""} {
		popup_error_dialog "Please highlight a printer first"
		return
	}
	set queue [string trim [lindex [split $printer_names($i) "|"] 0]]

    if { $queue != "" } {
		set result [runjob [list lpr -P$queue[at_rhostq] $filtersrcdir/testpage.asc] ]
		if { [lindex $result 0] } {
			popup_info_dialog "Test page printed to queue $queue[at_rhostq]"
		}
    }
}

# Get a field from the printcap database if we know the "key" (eg :lp=)
proc printcap_field {i field default} {
    global printer_entry
    if {[regexp ":$field\(\[^:\]*\)" $printer_entry($i) dummy val] == 1} {
		return $val
    } else {
		return $default
    }
}

# Is this switch set in /etc/printcap?
#  return:
#  0 if 'switch@'
#  1 if 'switch'
#  default if not found
proc printcap_switch {i field {default 0}} {
    global printer_entry
	if { [regexp ":$field@:" $printer_entry($i)] } {
		return 0 
	}
	if { [regexp ":$field:" $printer_entry($i)] } {
		return 1
	}
	return $default
}
proc prompt_for_queue { fieldlabel } {
    global trigger2_help rhostq entrywidth font_fixed font_helvetica
	global bglistbox

    catch {destroy .pr}
    toplevel .pr -class Dialog
    wm withdraw .pr
    wm transient .pr .
    wm group .pr .
	set n $rhostq
	if { $n != "" } { set n " On Host '$n'" }
    wm title .pr "$fieldlabel$n"

	set status [runjob [list lpc [server_rhostq] status all]]
	if { ![lindex $status 0] } { return }
	set vlist [split [lindex $status 1] "\n"]
	label .pr.label -text "$fieldlabel$n\n[lindex $vlist 0]" \
		-font $font_fixed

    frame .pr.helpf
	listbox .pr.helpf.msg -bg $bglistbox \
		-setgrid true -width 85 -height 8 \
		-font $font_fixed \
		-relief flat -yscrollcommand ".pr.helpf.msg_sy set" \
		-selectmode single -exportselection false

    scrollbar .pr.helpf.msg_sy -orient vert -command ".pr.helpf.msg yview"
	for { set i 1 } { $i < [llength $vlist] } { incr i } {
		.pr.helpf.msg insert end [lindex $vlist $i]
	}
    #.pr.helpf.msg config -state disabled
    pack .pr.helpf.msg_sy -side right -fill y -in .pr.helpf
    pack .pr.helpf.msg    -side left -fill both -expand true -in .pr.helpf

    frame .pr.okframe
    button .pr.b1 -text "OK" -width 10 -command "set trigger2_help 1"
    pack .pr.b1 -side left -expand true -ipady 1 -padx 5m -in .pr.okframe
    button .pr.b2 -text "Cancel" -width 10 \
		-command "set trigger2_help 0; set cookie_status_dialog 0; set update_count 0;"
    pack .pr.b2 -side left -expand true -ipady 1 -padx 5m -in .pr.okframe
    button .pr.b3 -text "Help" -width 10 -command select_remote_queue_help
    pack .pr.b3 -side left -expand true -ipady 1 -padx 5m -in .pr.okframe

    pack .pr.label   -side top -fill both -in .pr
    pack .pr.helpf   -side top -expand true -fill both -in .pr
    pack .pr.okframe -side bottom -in .pr -ipady 2m

    center_dialog .pr
    set oldFocus [focus]
    focus .pr
    catch { grab .pr }

    wm protocol .pr WM_DELETE_WINDOW { set trigger2_help 0 }
    update
    scan [wm geometry .pr] "%d%*c%d" xmin ymin
    wm minsize .pr $xmin $ymin

	while { 1 } {
		tkwait variable trigger2_help
		set printer ""
		if {$trigger2_help == "1"} {
			set index [.pr.helpf.msg curselection]
			#puts "prompt_for_queue: index $index"
			if { $index == "" } {
				popup_error_dialog "You did not select a Queue"
				continue
			}
			set printer [.pr.helpf.msg get $index ]
			#puts "prompt_for_queue: index $index, printer '$printer'"
			set printer [lindex [split $printer { }] 0]
			set printer [lindex [split $printer {@}] 0]
		}
		break
	}
	catch { grab release .pr }
    focus $oldFocus
    destroy .pr
	update idletasks

	#puts "prompt_for_queue: printer '$printer'"
    return $printer
}

proc getqueue { question } {
	global rhostq printer_names
    if {$rhostq == ""} {
		set i [get_selected_index]
		if {$i == ""} {
			popup_error_dialog "Please select a printer first"
			return $i
		}
		set queue [string trim [lindex [split $printer_names($i) "|"] 0]]
    } else {
		set queue [prompt_for_queue $question]
    }
	return $queue
}

# handle multiple queues
proc getqueues { question } {
	global rhostq printer_names
	set queue ""
    if {$rhostq == ""} {
		set i [get_selected_indexes]
		if {$i == ""} {
			popup_error_dialog "Please select a printer first"
			return
		}
		foreach v $i {
			append queue [string trim [lindex [split $printer_names($v) "|"] 0]] " "
		}
    } else {
		set queue [prompt_for_queue $question]
    }
	return $queue
}

# print the PS test page
proc print_ps_testpage { { paper_type "" } } {
    global printer_names printer_type printer_info
    global filtersrcdir rhostq localhostname

	set i [get_selected_index]
	if {$i == ""} {
		popup_error_dialog "Please highlight a printer first"
		return
	}
	set queue [string trim [lindex [split $printer_names($i) "|"] 0]]

	if { $queue == "" } { return }
#
#   make sure that we should be printing PS to this queue
#
	#puts "QUEUE $queue, TYPE $printer_type($i)";
#	set prn_type ""
#	if { $printer_type($i) != "" } {
#		set sumline [$printer_type($i)_summaryline $i]
#		if { $sumline == "" } {
#			warnmsg "Something is really wrong in print_ps_testpage"
#			return
#		}
#		set prn_type [string trim [lindex [split $sumline] 0]]
#		#set paper_type [string trim [lindex [split $printer_info($i)] 2]]
#		if { $prn_type == "type unrecognized" || $prn_type == "Text-only" } {
#			popup_error_dialog \
#	"You have tried to print a PostScript document
#	to a printer which you have configured to
#	have no PostScript support. This will probably
#	not do what you want. Try printing an ASCII
#	test page instead..."
#			return
#		}
#	}

	if { $paper_type == "a4" } {
		set result [runjob [list lpr -P$queue[at_rhostq] $filtersrcdir/testpage-a4.ps]]
	} else {
		set result [runjob [list lpr -P$queue[at_rhostq] $filtersrcdir/testpage.ps]]
	}

	if { [lindex $result 0] } {
		popup_info_dialog "Test page printed to queue $queue[at_rhostq]\n[lindex $result 1]"
	}
}

proc clean_cmd { cmd } {
	global status_length_flags
	set v [ subst $cmd ]
	regsub -all \[\{\}\] $v "" v
	regsub -all {  *} $v " " v
	#puts "clean_cmd '$cmd' -> '$v'"
	return $v
}

# Function to call 'lpc start' to restart printing from this queue
proc print_op { op msg } {
	set queue [getqueue $msg ]
	if { $queue == "" } { return }
	set cmd [list lpc [server_rhostq] $op $queue ]
	set res [popup_yesno_dialog "Do '$op' on queue '$queue'?" 0]
	if { $res == 0 } {
		set status [runjob $cmd ]
		if {[lindex $status 0]} {
			popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
		}
	}
}

proc print_start {} {
	print_op  start "Printer to start printing jobs" 
}
proc print_stop {} {
	print_op  stop "Printer to stop printing jobs \(job printing will finish\)" 
}
proc queue_abort {} {
	print_op  abort "Printer to abort current job and suspend operations" 
}
proc queue_kill {} {
	print_op  kill "Printer to abort current job and restart operations" 
}
proc queue_disable {} {
	print_op  disable "Queue to stop accepting jobs for printing" 
}
proc queue_enable {} {
	print_op  enable "Queue to enable accepting jobs for printing" 
}
proc queue_down {} {
	print_op  down "Queue to stop accepting jobs and stop printing \(job printing will finish\)" 
}
proc queue_up {} {
	print_op  up "Queue to start accepting jobs and start printing jobs" 
}

# Function to call 'lpc status' to check the status of a queue
proc queue_status { all } {
    global rhostq

	set cmd ""
	if { $all == "" } {
		set queue [getqueues "Name of Queue" ]
		if { $queue == "" } { return }
		foreach q $queue {
			if { $cmd != "" } {
				append cmd " ; "
			}
			append cmd [list lpc [server_rhostq] status $q ]
		}
	} else {
		set cmd [list lpc [server_rhostq] status all ]
	}
	set n ""
	if {$rhostq != ""} {
		set n " on $rhostq"
	}
	status_dialog "Status of Print Queues$n"\
"                      Status of Print Queues$n \($cmd\)
----------------------------------------------------------------------------------
" 0 $cmd
}

# Function to call 'lpc redirect' to redirect all jobs to another queue
proc redirect_jobs {} {
    global rhostq entrywidth status_length font_fixed

	set queue [getqueue "Name of Queue that needs to have jobs redirected"]
	if { $queue == "" } { return }
    set status [runjob [ list lpq -P$queue[at_rhostq] ]]
    if {![lindex $status 0]} { return }

	set val [popup_dialog .error "Redirect Queue" \
		"Name of new queue for jobs ('queue' or 'queue@host'):" \
		"" 1 $font_fixed 0 "Ok" "Cancel"]
	#puts "val $val"
	if { ![lindex $val 0] } {
		# we have ok
		set newprinter [lindex $val 1]
		if {$newprinter != "" } { 
			set cmd [list lpc [server_rhostq] redirect $queue $newprinter ]
			set status [runjob $cmd]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}

# Stop redirecting jobs from one printer to another
proc redirect_jobs_stop {} {
	set queue [getqueue "Name of Queue that needs to have job redirection stopped"]
	if { $queue == "" } { return }
	set cmd [list lpc [server_rhostq] redirect $queue off ]
    set status [runjob $cmd]
    if {[lindex $status 0]} {
		popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
	}
}


# Function to call 'lpc class' to class all jobs to another queue
proc class_jobs {} {
    global rhostq entrywidth status_length font_fixed

	set queue [getqueue "Name of Queue for Class Control"]
	if { $queue == "" } { return }
    set status [runjob [ list lpq -P$queue[at_rhostq] ]]
    if {![lindex $status 0]} { return }

	set val [popup_dialog .error "Class Controlled Queue" \
		"Job Class:" \
		"" 1 $font_fixed 0 "Ok" "Cancel"]
	#puts "val $val"
	if { ![lindex $val 0] } {
		# we have ok
		set newprinter [lindex $val 1]
		if {$newprinter != "" } { 
			set cmd [list lpc [server_rhostq] class $queue $newprinter ]
			set status [runjob $cmd]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}

# Stop classing jobs from one printer to another
proc class_jobs_stop {} {
	set queue [getqueue "Name of Queue to stop Class Control"]
	if { $queue == "" } { return }
	set cmd [list lpc [server_rhostq] class $queue off ]
    set status [runjob $cmd]
    if {[lindex $status 0]} {
		popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
	}
}

# Function to call 'lpc release' to release held jobs and reprint errors
proc release_jobs {} {
	global status_length
	set queue [getqueue "Queue to release jobs"]
	if { $queue == "" } { return }
	set result [runjob [ list lpq -P$queue[at_rhostq] ] ]
	if {[lindex $result 0]} {
        set jobs [status_info_dialog "release_jobs_help" "Release Jobs" [lindex $result 1]  \
			"Select by clicking on one or more jobs or by entering\nJob Number, User Name, Glob Pattern (matched to Job ID), or 'all'" \
			$queue]
		set cmd [ list lpc [server_rhostq] release $queue $jobs ]
        if {$jobs != "" } {
			set status [runjob $cmd ]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}

# Function to call 'lpc hold' to pause jobs in a queue
proc hold_jobs {} {
	global status_length
	set queue [getqueue "Queue to hold jobs"]
	if { $queue == "" } { return }
	set result [runjob [ list lpq -P$queue[at_rhostq] ] ]
	if {[lindex $result 0]} {
        set jobs [status_info_dialog "hold_jobs_help" "Hold Jobs" [lindex $result 1]  \
			"Select by clicking on one or more jobs or by entering\nJob Number, User Name, Glob Pattern (matched to Job ID), or 'all'" \
			$queue]
		set cmd [ list lpc [server_rhostq] hold $queue $jobs ]
        if {$jobs != "" } {
			set status [runjob $cmd ]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}

# Function to call 'lpc move' to move jobs to a new printer
proc move_jobs {} {
	global status_length
	set queue [getqueue "Queue to move jobs"]
	if { $queue == "" } { return }
	set result [runjob [ list lpq -P$queue[at_rhostq] ] ]
	if {[lindex $result 0]} {
        set jobs [status_info_dialog "move_jobs_help" "Move Jobs" [lindex $result 1]  \
			"Select by clicking on one or more jobs or by entering\nJob Number, User Name, Glob Pattern (matched to Job ID), or 'all'" \
			$queue "Destination queue \('queue' or 'queue\@host'\)"]
		#puts "move_jobs: jobs '$jobs'"
		set cmd [ list lpc [server_rhostq] move $queue $jobs ]
        if {$jobs != "" } {
			set status [runjob $cmd ]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}


# Function to call 'lprm' to remove a print job
proc remove_jobs {} {
    global printer_names rhostq localhostname status_length
	set queue [getqueue "Queue to Remove Jobs"]
	if { $queue == "" } { return }
	set result [runjob [ list lpq -P$queue[at_rhostq] ] ]
	if {[lindex $result 0]} {
        set jobs [status_info_dialog "remove_jobs_help" "Remove Jobs" [lindex $result 1]  \
			"Select by clicking on one or more jobs or by entering\nJob Number, User Name, Glob Pattern (matched to Job ID), or 'all'" \
			$queue]
		set cmd [ list lprm -P$queue[at_rhostq] $jobs ]
        if {$jobs != "" } {
			set status [runjob $cmd ]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}


# Function to call 'lpc redo' to release a print job
proc reprint_jobs {} {
    global printer_names rhostq localhostname status_length
	set queue [getqueue "Queue to Redo Jobs"]
	if { $queue == "" } { return }
	set result [runjob [ list lpq -P$queue[at_rhostq] ] ]
	if {[lindex $result 0]} {
        set jobs [status_info_dialog "reprint_jobs_help" "Reprint Jobs" [lindex $result 1]  \
			"Select by clicking on one or more jobs or by entering\nJob Number, User Name, Glob Pattern (matched to Job ID), or 'all'" \
			$queue]
		set cmd [ list lpc [server_rhostq] redo $queue $jobs ]
        if {$jobs != "" } {
			set status [runjob $cmd ]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}


# Function to call 'lpc topq' to change a print job priority
proc topq_jobs {} {
    global printer_names rhostq localhostname status_length
	set queue [getqueue "Queue to Change Job Priority"]
	if { $queue == "" } { return }
	set result [runjob [ list lpq -P$queue[at_rhostq] ] ]
	if {[lindex $result 0]} {
        set jobs [status_info_dialog "topq_jobs_help" "Move Jobs to Top of Queue" [lindex $result 1]  \
			"Select by clicking on one or more jobs or by entering\nJob Number, User Name, Glob Pattern (matched to Job ID), or 'all'" \
			$queue]
		#puts "jobs '$jobs'"
		set cmd [ list lpc [server_rhostq] topq $queue $jobs ]
		#puts "command '$cmd'"
        if {$jobs != "" } {
			set status [runjob $cmd ]
			if {[lindex $status 0]} {
				popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
			}
		}
	}
}

# Function to call 'lpc printcap' to get the printcap values
proc show_printcap {} {
    global printer_names rhostq font_fixed

    set logdata [runjob [list lpc printcap all[at_rhostq]]]
	if {[ lindex $logdata 0 ]} {
		set logdata [lindex $logdata 1 ]
		popup_info_dialog "
                      Remote Printcap Entries on $rhostq
----------------------------------------------------------------------------------
$logdata
" $font_fixed
	}
}

# Restart LPRng
proc restart_lprng {} {
	set cmd [ list lpc [server_rhostq] reread ]
	set status [runjob $cmd ]
	if {[lindex $status 0]} {
		popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
	}
}


# lpc lpd
proc lpc_lpd {} {
	set cmd [ list lpc [server_rhostq] lpd ]
	set status [runjob $cmd ]
	if {[lindex $status 0]} {
		popup_info_dialog "Results of '[clean_cmd $cmd]':\n[lindex $status 1]"
	}
}

# Run checkpc to verify changes
proc run_checkpc { pr } {
    # Run it twice, once to correct problems, and once to verify problems were
    # fixed and alert us to any that weren't
	set result ""
	set cmd "checkp -f -P $pr"

	if { $pr != "" } {
		set cmd "checkpc -f -P $pr"
		runjob $cmd
		set cmd "checkpc -P $pr"
	} else {
		set cmd "checkpc -f"
		runjob $cmd
		set cmd "checkpc"
	}
	set status [runjob $cmd]
	if { [lindex $status 0 ] } {
		popup_info_dialog "
Results of Configuration Validation using '$cmd'
-----------------------------------------------------
[lindex $status 1]
"
	}
}


# Redisplay the printcap database array to the main menu screen
proc redisplay {} {
    global print_count printer_names printer_type pc_showing printer_status
	global printer_used_by

    .list delete 0 end
	set namelen 20
	if { $pc_showing } {
		for {set i 0} {$i < $print_count} {incr i} {
			set cm [printcap_field $i "cm=" ""]
			if { $cm != "" } { set cm " ($cm)" }
			set line $printer_names($i)$cm
			if { [string length $line] > $namelen } {
				set namelen [string length $line ]
			}
		}
		if { $namelen > 20 } {
			set namelen 40
		}
		for {set i 0} {$i < $print_count} {incr i} {
			set cm [printcap_field $i "cm=" ""]
			if { $cm != "" } { set cm " ($cm)" }
			set msg [$printer_type($i)_summaryline $i]
			set type $printer_used_by($i)
			if { $type == "BOTH" } {
				set type ""
			} else {
				set type "($type only)"
			}
			set out "$msg $type"
			.list insert end [format "%-${namelen}s  %s" $printer_names($i)$cm $out ]
	# Future modification to determine if queues are up and printing
	#	.list insert end [format "%s%s   %-20s  %s" $printable($i) $Queueable($i) \
	#                           $printer_names($i) [$printer_type($i)_summaryline $i] ]
		}
	} else {
		set c "lpq -a -s"
		set result [ runjob $c ]
		#puts "more_detail: result '$result'"
		set print_count 0
		if {[lindex $result 0]} {
			set lines [split [lindex $result 1] "\n" ]
			for {set i 0} {$i < [llength $lines] } {incr i} {
				set line [lindex $lines $i]
				#puts "redisplay: line $line"
				if { [regexp {([^ ]*) +(.*)$} $line dummy name tag] } {
					#puts "redisplay: dummy $dummy"
					#puts "redisplay: name '$name' tag '$tag'"
					set printer_names($print_count) $name
					set printer_status($print_count) $tag
					.list insert end [format "%-${namelen}s  %s" \
						$printer_names($print_count) \
						$printer_status($print_count) ]
					incr print_count
				}
			}
		} else {
			exit 1
		}
	}
}

# Re-read the $printcapfile and re-store everything in it to our arrays
proc reload {} {
    global print_count printer_comments printer_entry printer_used_by printer_force_localhost
    global printer_names printer_type printer_info remote_type
    global version printcapfile lprngtoolconf smb_printer_types
	global remote_type user group font_fixed

    set print_count 0

	#puts "reload: '$printcapfile'"
	if { [ file exists $printcapfile ] == 0 } {
		if {[catch {set fd [open "$printcapfile" w 0644]} stuff] == 1} {
			warnmsg "Printcap file '$printcapfile' not writeable - $stuff."
		} else {
			close $fd
		}
	}
	if {[catch {set fd [open "$printcapfile" r]}] == 1} {
		diemsg "Printcap file '$printcapfile' not readable.\n"
	}
    set print_count 0
    set printer_comments(0) ""
    set printer_entry(0) ""
    set printer_names(0) ""
    set printer_type(0) ""
    set remote_type(0) ""
    set printer_info(0) ""
    set printer_used_by(0) ""
    set printer_force_localhost(0) ""
	set s ""
	set found_header 0
    while { $s != "" || [gets $fd s] != "-1"} {
		# Recognize comments and whitespace lines
		#puts "reload: READ LINE $s"
		set s [string trim $s]
		if { $s == "" } { continue }
		#puts "reload: HEADER READ '$s'"
		if {[regexp "^\#" $s] == 1 } {
			# Is it a special lprngtool description line?
			#puts "reload: COMMENT '$s'"
			if {[regexp "^\#\#LPRNGTOOL\#\#" $s] == 1} {
				set v [lindex $s 1]
				set x [lindex $s 2]
				set w [lrange $s 2 end]
				# deal with older format for test and conversion
				#puts "reload: INFO type '$v' info '$w'"
				if { $v == "LOCAL" } { set v "DEVICE" }
				if { $x == "CLIENT" || $x == "SERVER" } { set w [lrange $s 3 end] }
				#puts "reload: FINAL INFO type '$v' info '$w'"
				set printer_type($print_count) $v
				set printer_info($print_count) $w
				set found_header 1
			} elseif { $found_header } {
				append printer_comments($print_count) $s "\n"
			}
			set s ""
			continue
		}
		set found_header 0

		# If we get to here we have a printer definition
		# Read it in--keep reading until we reach a blank line,
		# EOF, or a line that does not start with :

		set more 0
		if { [regsub {\\$} $s "" s ] } {
			set more 1
		}
		set v "$s"

		#puts "BODY START '$v'"
		while { [gets $fd s] != "-1" } {
			set s [string trim $s]
			#puts "INFO READ '$s'"
			if { $s == "" } { set more 0; continue }
			if { [regexp {^\#\#LPRNGTOOL} $s] == 1 } { break }
			if { [regexp {^#} $s] == 1 } { set more 0; continue }
			if { $more } {
				set more 0
				if { [regsub {\\$} $s "" s ] } {
					set more 1
				}
				append v $s
				continue
			}
			if { ![regexp {^:} $s] } { break }
			if { [regsub {\\$} $s "" s ] } {
				set more 1
			}
			append v ":" $s
		}
		#puts "reload: INPUT '$v'"
		set v [cleanup_printer_entry $v ]
		#puts "reload: FIXED INPUT '$v'"
		set printer_entry($print_count) $v
		set found ""
		set lp ""
		regexp {:lp=([^:]*):} $v found lp
		#puts "reload: found '$found' lp='$lp'"
		if { $lp == "" } {
			set printer_type($print_count) "QUEUE"
		}

		set t 0
		set client "BOTH"
		if { [string first ":client:" $v ] != -1 } {
			incr t
			set client "CLIENT"
		}
		if { [string first ":server:" $v ] != -1 } {
			incr t
			set client "SERVER"
		}
		if { $t == 2 } {
			set client "BOTH"
		}
		set printer_used_by($print_count) $client
		#puts "reload: used_by $client"

		if { [string first ":force_localhost@:" $v ] != -1 } {
			set printer_force_localhost($print_count) "NO"
		}
		if { [string first ":force_localhost:" $v ] != -1 } {
			set printer_force_localhost($print_count) "YES"
		}

		incr print_count
		set printer_comments($print_count) ""
		set printer_entry($print_count) ""
		set printer_names($print_count) ""
		set printer_type($print_count) ""
		set remote_type($print_count) ""
		set printer_info($print_count) ""
		set printer_used_by($print_count) ""
		set printer_force_localhost($print_count) ""
    }

    close $fd

    # NB: the printer_entry and printer_comments arrays have different
    # array bounds.  printer_entry is defined from 0 to $print_count-1.
    # printer_comments is defined from 0 to $print_count.
    # printer_comments($print_count) consists of comments following
    # the last printer.

    # Now go through to examine each entry
	set fixed 0
    for {set i 0} {$i < $print_count} {incr i} {
		# Fix old mx% and mx# stuff
		regsub -all {:(..)[%#]} $printer_entry($i) {:\1=} printer_entry($i)
		set v $printer_entry($i)
		set name [get_printer_name $v ]
		set value [get_printer_value $v ]

		# First field gives the printer names
		if { $name == "" } {
			diemsg "Malformed printcap entry - missing printer name\n'$v'"
		}
		# now we check for the last field - could be a comment field
		# or something horrible
		set lv [string last "|" $name]
		set ncm ""
		if { $lv != -1 } {
			set ncm [string range $name [expr $lv + 1] end]
			#puts "ncm '$ncm'"
			if {[check_printer_name $ncm 1] != ""} {
				# we may have a comment field
				set cm [printcap_field $i "cm=" ""]
				if { $cm != "" } {
					warnmsg "Bad printer name '$ncm', and you have a comment field '$cm'"
					set ncm ""
				} else {
					set res [popup_yesno_dialog "Is '$ncm' a comment?" 0]
					if { $res == 0 } {
						set name [string range $name 0 [expr $lv - 1]]
						set fixed 1
					} else {
						set ncm ""
					}
				}
			} else {
				set ncm ""
			}
		}
		# now we check all printer names
		if { [regexp {[^\-A-Za-z0-9_\.\*|]} $name found ] } {
			set res [popup_yesno_dialog \
			 "Bad printer name or alias '$name' at character '$found', should I fix it?" \
			 0 $font_fixed]
			if { $res == 0 } {
				regsub {[^\-A-Za-z_\.\*|]} $name "_" name
				fixed 1
			} else {
				diemsg "Please fix up printcap manually and retry"
			}
		}

		set printer_entry($i) [cleanup_printer_entry $name$value ]
		set printer_names($i) $name
		#puts "reload: printer_names '$printer_names($i)'"
		set firstname [lindex [split $name |] 0 ]
		if { $ncm != "" } {
			set_printcap_field $i "cm=" $ncm
		}
		#puts "reload: printer_entry '$printer_entry($i)'"
		#puts "reload: printer_type '$printer_type($i)'"

		# Fix printer_type
		if {$printer_type($i) == ""} {
			set printer_type($i) UNKNOWN
			set printer_info($i) {}
		}
		# check to see if in the remote printer list
		set inlist [ lsearch -exact $smb_printer_types $printer_type($i) ]
		if { $inlist != -1 } {
			set remote_type($i) $printer_type($i)
			set printer_type($i) SMB
		}
		# Make sure this type is known
		if {[info procs "$printer_type($i)_summaryline"] == ""} {
			set printer_type($i) UNKNOWN
			set printer_info($i) {}
		}
    }
	if { $fixed } {
		set res [popup_yesno_dialog "Rewrite printcap?" 0]
		if { $res == 0 } {
			write_printcap
		} else {
			diemsg "Please fix up printcap manually and retry"
		}
	}
}

#
# Printer database for printtool
#
#
# FORMAT:
#
# Lines starting with a '#' are considered comments
#
# A printer description is formated as (case-sensitive throughout)
#
# StartEntry and Description must be unique to each other.
#   Change in code may allow Description to be same but
#   currently this is required.
#
# StartEntry:  PrinterEntryName
#   GSDriver: gsdrivername
#   Description: {This should be a one-line description in braces}
#   About: {
#       This is a longer description, giving details for the \
#       user about specific features of this printer entry \
#       It can be many lines long, and a continuation character \
#       is needed in that case. A brace closes the string. \
#       Whitespace and newlines are removed when read in. \
#       The description will be stroed as one long string. \
#       }
#   Resolution: {XDPI} {YDPI} {Optional text comment}
#   Resolution: {XDPI} {YDPI} {Optional text comment}
#   Resolution: {XPDI} {YDPI} {Just keep adding these}
#   Resolution: {XPDI} {YDPI} {No checking if added twice}
#   BitsPerPixel: {BitsPerPixel} {Optional comment}
#   BitsPerPixel: {BitsPerPixel} {Can have arbitrary number}
# or, (for unipriner drivers)
#   BitsPerPixel: {upp_file} {Comment copied from upp file}
# EndEntry
#
# **** Alternative Format *****
#
# StartEntry:  PrinterEntryName
#   GSDriver: gsdrivername
#   Description: This should be a one-line description
#   About:
#       This is a longer description, giving details for the
#       user about specific features of this printer entry
#       It can be many lines long, and a continuation character
#       is needed in that case. A brace closes the string.
#       Whitespace and newlines are removed when read in.
#       The description will be stored as one long string.
#   Resolution: XDPI YDPI {Optional text comment}
#   Resolution: XDPI YDPI {Optional text comment}
#   Resolution: XPDI YDPI {Just keep adding these}
#   Resolution: XPDI YDPI {No checking if added twice}
#   BitsPerPixel: BitsPerPixel {Optional comment}
#   BitsPerPixel: upp_file {Comment copied from upp file}
# EndEntry
#
# Possibly allow other gs params to be input like BitsPerPixel is in future
#
#

proc db_printerdb { msg } {
	global printerdb_debug
	if { $printerdb_debug } { puts $msg }
}

# Re-read the printerdb file with the list of printers LPRng can use
proc reload_printerdb {} {
    global printerdb_entry printerdb_descr printerdb_debug
    global printerdb_about printerdb_GSDriver
    global printerdb_res   printerdb_color
    global printerdb_count
    global printerdb_descr2entry
    global printerdb_curdbline
    global filtersrcdir
    global printerdb_error printerdb
# ifhp support
	global printerdb_model printerdb_ifhpgsdriver printerdb_ifhpgsoptions
	global printerdb_ifhpsupports
	global printerdb_loaded

#
#   counter to let us know where printerdb integrity errors occur
#
	if {![info exists printerdb_loaded]} {
		set printerdb_loaded 0
	}
	if { $printerdb_loaded } { return }

    set printerdb_curdbline 0
    set printerdb_count 0
	set p [ fullpath_or_relative $filtersrcdir $printerdb ]

	db_printerdb "reload_printerdb '$p'"
	if { [ file exists $p ] == 0  } {
		# there is no printerdb
		if { $printerdb_error == 0 } {
			popup_error_dialog "Printer database file '$p' does not exist."
			set printerdb_error 1
		}
		return
	}
    if {[catch {set fd [open "$p" r]}] == 1} {
		# there is no printerdb
		if { $printerdb_error == 0 } {
			popup_error_dialog "Printer database file '$p' not readable."
			set printerdb_error 1
		}
		return
    }

	set printerdb_error 0

    set i 0
	while { 1 == 1} {
		# get next entry, fill in display widgets
		set nextname [string trim [ FindNextEntry $fd ]]
		db_printerdb "FindNextEntry '$nextname'"
		# if we find no more, exit
		if { $nextname == {} } {
			set printerdb_count $i
			if { $printerdb_debug } {
				db_printerdb "reload_printerdb: count $printerdb_count"
				for { set i 0 } { $i < $printerdb_count } { incr i } {
					db_printerdb "\[$i\] entry '$printerdb_entry($i)'"
					set n $printerdb_entry($i)
					db_printerdb "\[$n\] descr '$printerdb_descr($n)'"
					set d $printerdb_descr($n)
					db_printerdb "\[$n\] GS '$printerdb_GSDriver($n)'"
					db_printerdb "\[$n\] about '$printerdb_about($n)'"
					db_printerdb "\[$n\] model '$printerdb_model($n)'"
					db_printerdb "\[$n\] supports '$printerdb_ifhpsupports($n)'"
					db_printerdb "\[$n\] gsdriver '$printerdb_ifhpgsdriver($n)'"
					db_printerdb "\[$n\] gsoptions '$printerdb_ifhpgsoptions($n)'"
					db_printerdb "\[$n\] dbcolor '$printerdb_color($n)'"
					db_printerdb "descr2entry '$printerdb_descr2entry($d)'"
				}
			}
			break
		}
		set printerdb_entry($i) $nextname
		incr i
		set printerdb_descr($nextname) ""
		set printerdb_about($nextname) ""
		set printerdb_res($nextname) ""
		set printerdb_color($nextname) ""
		set printerdb_model($nextname) ""
		set printerdb_ifhpgsdriver($nextname) ""
		set printerdb_ifhpgsoptions($nextname) ""
		set printerdb_ifhpsupports($nextname) ""
		set printerdb_res($nextname) ""
		set printerdb_color($nextname) ""
		set printerdb_GSDriver($nextname) ""

		# find all internal fields
		while { 1 == 1 } {
			set fieldlist [GetNextField $fd ]
			if { [string compare $fieldlist "EndEntry" ] == 0 \
				|| $fieldlist == {} } {
				break;
			}
			set n [ string first : $fieldlist ]
			if { $n == -1 } {
				continue
			}

			# store into associative array of lists indexed by field name
			set fieldname [ string range $fieldlist 0 [expr $n - 1] ]
			set fieldvalue [ string range $fieldlist  [expr $n + 1] end ]
			db_printerdb "FieldList: '$fieldlist'"
			db_printerdb "FieldName: '$fieldname'"
			db_printerdb "Fieldvalue: '$fieldvalue'"

			# g rab ones we really want and store now
			if { [string compare $fieldname "Description" ] == 0 } {
				set printerdb_descr($nextname) $fieldvalue
				set printerdb_descr2entry($printerdb_descr($nextname)) $nextname
			} elseif { [string compare $fieldname "About" ] == 0 } {
				set printerdb_about($nextname) $fieldvalue
			} elseif { [string compare $fieldname "GSDriver" ] == 0 } {
				db_printerdb "GSDriver\($nextname): '$fieldvalue'"
				set printerdb_GSDriver($nextname) $fieldvalue
			} elseif { [string compare $fieldname "Resolution" ] == 0 } {
				append printerdb_res($nextname) [list $fieldvalue] " "
			} elseif { [string compare $fieldname "BitsPerPixel" ] == 0 } {
				append printerdb_color($nextname) [list $fieldvalue ] " "
			} elseif { [string compare $fieldname "IfhpModel" ] == 0 } {
				set printerdb_model($nextname) $fieldvalue
			} elseif { [string compare $fieldname "IfhpGSDriver" ] == 0 } {
				set printerdb_ifhpgsdriver($nextname) $fieldvalue
			} elseif { [string compare $fieldname "IfhpGSOptions" ] == 0 } {
				set printerdb_ifhpgsoptions($nextname) $fieldvalue
			} elseif { [string compare $fieldname "Supports" ] == 0 } {
				set printerdb_ifhpsupports($nextname) $fieldvalue
			}
		}
		# set printdb arrays from entries
	}
	set printerdb_loaded 1
}

proc FindNextEntry { fd } {

    global printerdb_curdbline printerdb_debug

#
# search through file until we find a line marking the start of an entry
#

    while {[gets $fd s] != "-1"} {
		incr printerdb_curdbline

		db_printerdb "FindNextEntry: $s"
		# strip spaces
		set s [string trim $s]

		# is this a comment line?
		if {[regexp "^\#" $s] == 1} {
			continue
		}
		db_printerdb "FindNextEntry: clean '$s'"
		# is this the line we're seeking
		if {[regexp "^StartEntry" $s] == 1} {
			set name [ string trim [lindex [split $s :] 1]]
			if { $name == "" } {
				diemsg \
"Printer Database: line $printerdb_curdbline - missing entry name
$s
"
				continue
			}
			return $name
		}
    }

    # we reached EOF and found no more entries
    return {}
}

#
# GetNextField { fd }
#
# Read the next field from file descriptior fd from inside a printer entry.
set lastline ""

proc GetNextField { fd } {
    global printerdb_curdbline
    global printerdb_debug
	global lastline

    # First we want to find the line marking the start of a new field
    while { 1 } {
		if { $lastline == "" } {
			if { [gets $fd lastline] == "-1"} {
				break;
			}
			incr printerdb_curdbline
		}

		db_printerdb "In GetNextField, read line number $printerdb_curdbline\n       ->   $lastline"

		# strip off continuations
		regsub "\\\\$" $lastline "" lastline
		# strip spaces
		set lastline [string trim $lastline]

		# is this a blank or comment line?
		if { $lastline == "" || [regexp "^\#" $lastline] == 1} {
			set lastline ""
			continue
		}
		# is something messed up?
		if {[regexp "^StartEntry" $lastline] == 1} {
			diemsg \
"Somehow we've hit the start of a new entry.
Printer data base file is corrupt near line $printerdb_curdbline."
		}
		# if it the end of an entry
		if {[regexp "EndEntry" $lastline] == 1} {
			set lastline ""
			return [list EndEntry]
		}
		# Ok, is it the start of a field?
		# We can tell if it has a word followed by a colon
		if { ![ regexp {^([^ ]*):(.*)$} $lastline dummy fieldname fieldparams ] } {
			diemsg \
"Malformed start of entry '$lastline'
Printer data base file is corrupt near line $printerdb_curdbline."
		}
		set fieldparams [string trim $fieldparams]

		db_printerdb "GetNextField: fieldname   -> $fieldname\nGetNextField: fieldparams -> $fieldparams"
		append fieldname ":" [string trim $fieldparams]

		while {[gets $fd lastline] != "-1"} {
			incr printerdb_curdbline

			db_printerdb "Line1: $lastline"
			regsub "\\\\$" $lastline "" lastline
			# strip spaces at both ends
			set lastline [string trim $lastline]

			# is this a comment line?
			if { $lastline == "" || [regexp "^\#" $lastline] == 1} {
				continue
			}
			if { [ regexp {^([^ ]*):(.*)$} $lastline ] \
				 || [regexp "EndEntry" $lastline] } {
				db_printerdb "GetNextField: End of Field"
				break
			}

			# ok, append to current line
			append fieldname " " $lastline
			db_printerdb "GetNextField: Appended: $fieldname"
		}
		# finished reading this param, time to move to next
		db_printerdb "GetNextField: Result $fieldname"
		return $fieldname
    }

    # we reached EOF and found no more entries
    return {}
}

# We return with a printcap entry that ENDS with a :
#

proc cleanup_printer_entry { v } {
	set v "$v:"
	regsub -all "\\\n" $v " " v
	regsub -all "\n" $v ":" v
	#puts "cleanup_printer_entry now $v"
	set l [ split $v : ]
	set nv ""
	#puts "cleanup_printer_entry $l"
	if { [llength $l] > 0 } {
		set nv [ string trim [ lindex $l 0 ] ]
		for { set i 1 } { $i < [llength $l] } { incr i } {
			set val [string trim [ lindex $l $i ]]
			#puts "val '$val'"
			if { $val == "" } { continue }
			if { [ regexp {^([^=@]*)([=@].*)$} $val junk key value ] } {
				#puts "key '$key' value '$value'"
				if { $key != "" } { set pc($key) $value }
			} elseif { [ regexp {^([^=@]*)$} $val junk key value ] } {
				#puts "key '$key' value '$value'"
				if { $key != "" } { set pc($key) $value }
			} else {
				warnmsg "bad value in printcap entry '$val'"
			}
		}
		set nv "[ lindex $l 0 ]:"
		foreach i [ lsort [ array names pc ] ] {
			append nv "$i$pc($i):"
		}
	}
	#puts "nv $nv"
	return $nv
}

# we return with a list entry that STARTS and ENDS with a ,

proc cleanup_list_entry { v } {
	set $v ",$v,"
	regsub -all "\\\n" $v " " v
	regsub -all "\n" $v "," v
	set l [ split $v , ]
	#puts "cleanup_list_entry $l"
	for { set i 0 } { $i < [llength $l] } { incr i } {
		set val [string trim [ lindex $l $i ] ]
		#puts "val '$val'"
		if { $val == "" } { continue }
		if { [ regexp {^([^=@]*)([=@].*)$} $val junk key value ] } {
			#puts "key '$key' value '$value'"
			if { $key != "" } { set pc($key) $value }
		} elseif { [ regexp {^([^=@]*)$} $val junk key value ] } {
			#puts "key '$key' value '$value'"
			if { $key != "" } { set pc($key) $value }
		} else {
			warnmsg "bad value in list entry '$val'"
		}
	}
	set nv ","
	foreach i [ lsort [ array names pc ] ] {
		append nv "$i$pc($i),"
	}
	#puts "nv $nv"
	return $nv
}

# Get the stuff before the options
proc get_printer_name { v } {
	set n [ string first : $v ]
	if { $n != -1 } {
		return [ string range $v 0 [ expr $n - 1] ]
	}
	return $v
}

# Get the options
proc get_printer_value { v } {
	set n [ string first : $v ]
	if { $n != -1 } {
		return [ string range $v $n end ]
	}
	return ""
}

proc remove_fields_in_list { v l } {
	#puts "remove_fields_in_list: before '$v', remove $l"
	set v [ cleanup_list_entry $v ]
	foreach field $l {
		regsub -all ",$field=\[^,\]*," $v "," v
		regsub -all ",$field@\[^,\]*," $v "," v
		regsub -all ",$field," $v "," v
	}
	#puts "remove_fields_in_list: after $v"
	return $v
}

proc remove_field_list { v l } {
	return [ remove_fields_in_list $v [list $l ] ]
}

# remove option values in a list

proc remove_fields_in_printcap { v l } {
	#puts "remove_fields_in_printcap: before '$v', remove $l"
	foreach field $l {
		regsub {=$} $field "" field
		regsub -all ":$field=\[^:\]*:" $v ":" v
		regsub -all ":$field@\[^:\]*:" $v ":" v
		regsub -all ":$field:" $v ":" v
	}
	#puts "remove_fields_in_printcap: after $v"
	return $v
}


# remove the printcap file in the printer entry
proc remove_printcap_field {i field } {
    global printer_entry
	#puts "remove_printcap_field: field '$field', $printer_entry($i)"
	set printer_entry($i) [ remove_fields_in_printcap $printer_entry($i) [list $field ] ]
	#puts "after: field $printer_entry($i)"
}


# select option values in a list
proc get_fields_in_printcap { v l } {
	#puts "get_fields_in_printcap: before '$v', extract $l"
	set found [ string first ":" $v ]
	if { $l != -1 } {
		set nv [ string range $v 0 [expr $found -1] ]
		set tail [ string range $v $found end ]
		#puts "get_fields_in_printcap head '$nv' tail '$tail'"
		foreach field $l {
			if { [ regexp ":$field=\[^:\]*" $tail val ] \
				|| [ regexp ":$field@" $tail val ] \
				|| [ regexp "\(:$field\):" $tail dummy val ] } {
				#puts "found '$val'"
				append nv $val
			}
		}
	} else {
		set nv $v
	}
	#puts "get_fields_in_printcap: after $nv"
	return $nv
}

# Set a printcap switch (eg "sh")
proc set_printcap_switch {i field val} {
	global printer_entry
	#puts "set_printcap_switch: field '$field', val '$val' $printer_entry($i)"
	remove_printcap_field $i $field
    if {$val == 0} {
		append printer_entry($i) "$field@:"
    } else {
		append printer_entry($i) "$field:"
    }
	#puts "added: field $printer_entry($i)"
}

# Set a field in the printcap database
proc set_printcap_field {i field val} {
    global printer_entry

	#puts "set_printcap_field: field '$field', val '$val' $printer_entry($i)"
	remove_printcap_field $i $field
	if { $val != "" } {
  	    append printer_entry($i) $field$val ":"
    }
	#puts "added: field $printer_entry($i)"
}
proc fullpath_or_relative { filterdir pgm } {
	if { [regexp {^/} $pgm ] } {
		return $pgm
	}
	return "$filterdir/$pgm"
}

# set the communications program
proc get_smb_remote_mode { i } {
	global filtersrcdir
	global smb_remote_mode novellkey appletalkkey userkey
	global smbprint ncpprint atalkprint
	set v [ printcap_field $i "lp=" "" ]
	if { $smb_remote_mode == "SMB" } {
		set v "| [fullpath_or_relative $filtersrcdir $smbprint ]"
	} elseif { $smb_remote_mode == $novellkey } {
		set v "| [fullpath_or_relative $filtersrcdir $ncpprint ]"
	} elseif { $smb_remote_mode == $appletalkkey } {
		set v "| [fullpath_or_relative $filtersrcdir $atalkprint ]"
	}
	return $v
}

# update the communications program for menu selection
proc update_smb_remote_mode { i w } {
	global filtersrcdir
	global smb_remote_mode novellkey appletalkkey userkey
	global smbprint ncpprint atalkprint
	set v ""
	if { $smb_remote_mode == "SMB" } {
		set v "| [fullpath_or_relative $filtersrcdir $smbprint ]"
	} elseif { $smb_remote_mode == $novellkey } {
		set v "| [fullpath_or_relative $filtersrcdir $ncpprint ]"
	} elseif { $smb_remote_mode == $appletalkkey } {
		set v "| [fullpath_or_relative $filtersrcdir $atalkprint ]"
	}
	$w config -state normal
	$w delete 0 end
    $w insert 0 $v 
	if { $smb_remote_mode != $userkey } {
		$w config -state disabled
	}
}
# Synchronize the printcap file (write and reload) to ensure changes are kept
proc sync { i } {
    global alwayssync mode printer_names

	write_printcap
	#puts "sync: mode $mode, i $i, client [ printcap_switch $i {client} ]"
	if { $mode == 0 } {
		if { $i != -1 && ![ printcap_switch $i "client" ]  } {
			# get the printer name
			set name $printer_names($i)
			regsub {\|.*} $name "" name
			if { $alwayssync == "yes" || ![ popup_yesno_dialog \
"Do you want to run checkpc to create the spool directory for $name?" 0 ] } {
				run_checkpc $name
			}
		}
		if { ( $alwayssync == "yes" || \
			![ popup_yesno_dialog \
"Do you want to restart the lpd server?                              " 0 ] ) } {
			restart_lprng
		}
	}
    reload
    redisplay
}

proc view_file { i field name log } {
# The real work behind a request to view an accounting file
    global printer_names font_fixed

	if { $log == "" } {
		set log [printcap_field $i $name ""]
	}
	set spool_dir [ resolve_spool_dir [printcap_field $i "sd=" ""] $i ]
	set file "$spool_dir/$log"

	set status ""
	if { $log == "" } {
      popup_error_dialog "No printcap '$field' value"
	} elseif {[catch {set fd [open "$file" r]} status] == 1} {
      popup_error_dialog "Cannot read $field '$file' - $status.  Does this file exist?"
    } else {
      set logdata ""
      while {[gets $fd input] != "-1"} {
		append logdata $input "\n"
      }
      close $fd
      set printer [lindex [split $printer_names($i) '|'] 0]
      popup_info_dialog "$field Data for $printer" "
$field Data for $printer
---------------------------------------------------------------------------

$logdata
" $font_fixed
    }
}

# View the log file of a print queue
proc view_log_file {} {
    set i [get_selected_index]
    if {$i == ""} {
        popup_error_dialog "Please highlight a print queue first"
		return
    }
	view_file $i "Log File" "lf=" ""
}

# View the acct (accounting) file of a print queue
proc view_acct_file {} {
    set i [get_selected_index]
    if {$i == ""} {
        popup_error_dialog "Please highlight a print queue first"
		return
    }
	view_file $i "Accounting File" "af=" ""
}

# View the log file of a print queue
proc view_statushistory {} {
	global printer_names font_fixed
    set i [get_selected_index]
    if {$i == ""} {
        popup_error_dialog "Please highlight a print queue first"
		return
    }

    set printer [lindex [split $printer_names($i) '|'] 0]
	view_file $i "Accounting File" "af=" "status.$printer"
}


# View the current status of a print queue
proc view_queue_info { cmd title detail } {
    global printer_names
    set i [get_selected_index]
    if {$i == ""} {
        popup_error_dialog "Please highlight a print queue first"
		return
    }
    set queue [lindex [split $printer_names($i) '|'] 0]
	regsub {\$queue} $cmd $queue cmd
	#puts "view_queue_info: cmd '$cmd'"

	set c [clean_cmd $cmd]
	status_dialog "$title for $queue \($c\)" \
"                 $title for $queue \($c\)
---------------------------------------------------------------------------
" $detail $cmd
}


# View the current status of a print queue
proc view_queue_jobs {} {
	global status_length
	view_queue_info {lpq {$status_length_flags} -P$queue} "Job Queue" 1
}

# View the current status of a print queue
proc view_queue_status {} {
	global status_length
	view_queue_info {lpc status $queue} "Current Status" 0
}


# Function to call 'lpq' to check the status of jobs in queue
proc job_status { all } {
    global printer_names rhostq status_length

	set cmd ""
	if { $all == "" } {
		set queue [getqueues "Which Queue to show status"]
		if { $queue == "" } { return }
		foreach q $queue {
			if { $cmd != "" } {
				append cmd " ; "
			}
			append cmd [list lpq {$status_length_flags} -P$q[at_rhostq]]
		}
		set q "Queue $queue"
	} else {
		set q "All Queues"
		if { $rhostq == "" } {
			set cmd [list lpq {$status_length_flags} -a ]
			set q "All Queues"
		} else {
			set cmd [list lpq {$status_length_flags} -Pall[at_rhostq] ]
			set q "All Queues on $rhostq"
		}
	}
	set c [clean_cmd $cmd]
    status_dialog "Jobs in $q" \
"              Jobs in $q \($c\)
----------------------------------------------------------------------------------
" 1 $cmd
}

# View the printcap file
proc view_printcap {} {
    global printcapfile font_fixed

	#puts "view_printcap: printcapfile $printcapfile"
    set log "$printcapfile"

    if {[catch {set fd [open "$log" r]}] == 1} {
      popup_error_dialog "Cannot read file '$log'.  Does this file exist?"
    } else {
      set logdata ""
      while {[gets $fd input] != "-1"} {
		append logdata $input "\n"
      }
      close $fd
      popup_info_dialog "
Printcap File (for debugging purposes only)
---------------------------------------------------------------------------
$logdata
" $font_fixed
    }
}

proc make_printer_entry { i } {
    global printer_type remote_type printer_entry printer_info printer_names
	global printer_used_by printer_comments printer_force_localhost
	set type $printer_type($i)
	if { $type == "SMB" } {
		set type $remote_type($i)
	}
	# De-canonicalize entry

	set client $printer_used_by($i)
	#puts "make_printer_entry: client $client"
	set entry ""
	if { $client == "SERVER" } {
		append entry ":server"
	} elseif { $client == "CLIENT" } {
		append entry ":client"
	}

	set localhost $printer_force_localhost($i)
	#puts "make_printer_entry: localhost $localhost"
	if { $localhost == "YES" } {
		append entry ":force_localhost"
	} elseif { $localhost == "NO" } {
		append entry ":force_localhost@"
	}

	append value "\#\#LPRNGTOOL\#\# $type $printer_info($i)\n"
	append value $printer_comments($i)
	append value $printer_names($i)

	#puts "make_printer_entry: printer_entry $printer_entry($i)"

	append entry [get_printer_value $printer_entry($i)]
	regsub {:$} $entry "" entry
	regsub -all ":" $entry "\n:" entry
	append value $entry
	#puts "make_printer_entry: value $value"
	return $value
}

# Write out/update the printcap database to /etc/printcap
proc write_printcap {} {
    global print_count printer_comments printer_entry
    global printer_info printer_type version
    global delete_index printcapfile remote_type user group

	#puts "write_printcap\n"
	if {[catch {set fd [open "$printcapfile" w 0644]} stuff] == 1} {
		warnmsg "Printcap file '$printcapfile' not writeable - $stuff."
		return
	}
	puts $fd \
"# $printcapfile
#
# This printer configuration file was created with lprngtool Version $version
# Any changes made here manually may be lost if lprngtool
# is run later on.
#
# It is NOT advisable to modify this file by hand.  To add, modify, or
# remove printers, please use 'lprngtool' to avoid corruption.

"

	for {set i 0} {$i < $print_count} {incr i} {
		if {$i != $delete_index} {
			puts $fd [make_printer_entry $i ]
			puts $fd "\n"
		}
	}
	close $fd
	set delete_index ""
}

# Read the lprngtoolconf file and configure global defaults.

proc load_configfile {} {
	global lprngtoolconf env printcapfile sysprintcapfile userprintcapfile mode
	set file $lprngtoolconf
	#puts "conf $file"
	if { [ file exists $file ] } {
		loadconf $file
	}
	set file $env(HOME)/.lprngtool
	#puts "userconf $file"
	if { [ file exists $file ] } {
		loadconf $file
	}

	if { $printcapfile != "" } {
		#puts "new printcapfile $printcapfile"
		set sysprintcapfile $printcapfile
	}
#	if { $mode } {
#		set printcapfile $sysprintcapfile
#	} else {
#		set printcapfile $userprintcapfile
#	}
}

proc loadconf { conf } {
	#puts "loadconf $conf"
	if {[catch {set fd [open "$conf" r]}] == 1} {
		warnmsg "Lprngtool config file '$conf' not readable"
		return
	}
	while {[gets $fd cp] != "-1"} {
		# Toss comments and whitespace lines
		set cp [string trim $cp]
		if {[regexp "^\#" $cp] || $cp == "" } {
			continue
		}
		#puts "read '$cp'"

		# Divide line in half, since this should be a variable pair
		#set key [lindex $cp 0]
		#if { $key == "option" } {
		#	set v "option add [lrange $cp 1 end]"
		#puts "option $v"
		#	eval $v
		#	continue
		#}
		set leftside [string trim [lindex [split "$cp" "="] 0] ]
		set rightside [string trim [lindex [split "$cp" "="] 1] ]

		# Do checks to set global variables
		if { $leftside != "" } {
			if {![info exists $leftside] } {
				global $leftside
			}
			set $leftside $rightside
		}
	}
	close $fd
}

# run the id program
proc getuid { user } {
	set uid [lindex [must_runjob "id $user"] 0]
	#puts "getuid: uid '$uid'"
	set val $uid
	if { [regexp {uid=([0-9]+)} $uid junk val] } {
		set uid $val
	}
	#puts "getuid: $uid"
	return $uid
}

#############################################
## Main Program
#############################################

if {[catch {file attribute /tmp} stuff]} {
	diemsg "You must use Tk/Tcl 8.0 or later"
	exit 1
}
set uid [getuid ""]
#puts "main: uid $uid"
#we default to user if not root
set printerdb_debug 0
load_configfile
set_pc_showing
setmode
#reload
if { $test_for_utilities } {
	find_gs
	find_samba
}
# write_printcap
redisplay

