OpenSolaris_b135/cmd/print/scripts/ppdmgr

#!/bin/ksh 
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# Description: Script to generate the Solaris printmgr 'ppdcache' file from the
#              ppd files installed in the given ppd database directory
#
# ppdmgr -a <ppd_filename_path> [ -L <label> ] [-w]
# ppdmgr -g <ppd_filename_path> [ -L <label> ] [ -R <ppd_repository> ]
# ppdmgr -r [ -L <label> ] [ -R <ppd_repository> ]
# ppdmgr -u [ -L <label> ] [ -R <ppd_repository> ]
#
# Options:
#		-a <ppd_filename_path>	- Add a new PPD file to the specified
#					label in the "user" repository, and
#					updates to the "user" repository
#					in the ppdcache.
#		-g <ppd_filename_path>	- Generate a cache file entry
#					for the specified PPD file
#					on standard out.
#		-L <label>		- Label name.  <label>
#					can be any characters from the
#					portable character set, however
#					may not contain a semi-colon (':').
#					The following are the defaults
#					for <label> for each option:
#					OPTION	DEFAULT LABEL
#					------	-------------
#					-a	<label> from <ppd_filename_path>
#						if <ppd_filename_path>
#						is from a known repository,
#						otherwise defaults to "user".
#					-g	<label> from <ppd_filename_path>
#						if <ppd_filename_path>
#						is from a known repository,
#						otherwise defaults to "user".
#					-r	all
#					-u	all
#					The following are reserved labels:
#					caches		- may never be specified
#					ppdcache	- may never be specified
#					manufaliases	- may never be specified
#					all		- applies specified
#							action to all labels
#							in a repository.
#							Can only be specified
#							with -r or -u.
#					SUNW*		- anything starting with
#							SUNW is reserved for
#							use by Sun, but not
#							prohibited.
#		-r			- Rebuild the cache information for the
#					specified label in the specified
#					repository.  Similar to -u, however,
#					the cache file is removed to force an
#					update to the ppdcache.
#		-R <ppd_repository>	- PPD repository name.
#					Defaults to "user".
#					The following are the possible
#					values for <ppd_repository> and
#					location in the system:
#					REP	LOCATION
#					---	--------
#					user	/var/lp/ppd
#					admin	/usr/local/share/ppd
#					vendor	/opt/share/ppd
#					system	/usr/share/ppd
#					all	all repositories
#
#					Note: When specified with the -a option
#					only "user" and "admin" are valid.
#					"vendor", "system", and "all" will be
#					considered reserved.
#		-u			- Update the PPD cache information
#					for the specified label in the specified
#					repository if needed.  If the cache
#					update was required, then the updated
#					cache information is reflected in
#					the ppdcache.
#		-w			- Display full path of where the
#					ppd file is located on the system.
#					Only valid with -a, otherwise the 
#					option is ignored.
#
# If -a, -g, -r, or -u are specified on the command line, only the last action
# specified will be performed.
#
# Cache file entry format:
#	<ModifiedManufacturerName>:<Model>:<NickName>:<1284DeviceIDManufacturer>:<1284DeviceIDModel>:<FullPPDFilePath>
#	HP:HP DeskJet 450:Foomatic/hpijs (recommended):dj450:hp:/usr/share/ppd/HP/HP-DeskJet_450-hpijs.ppd.gz
#

PATH=/bin:/usr/bin:/usr/sbin export PATH
set -o noclobber

TEXTDOMAIN="SUNW_OST_OSCMD"
export TEXTDOMAIN

#
# Generates debug output for calling routine.
# If calling routine's name is passed in, then
# will also generate the name of the calling routine.
#
# $1	- Name of calling routine
debugger()
{
	[[ ${debug} -eq 1 ]] || return 1
	if [[ -n "${1}" ]] ; then
		echo "In ${1}..." 1>&2
	fi
	return 0
}

#
# Set the ownership and permissions on a file.
#
# $1	- Mode
# $2	- Owner:Group
# $3	- Full path to file
#
set_perms()
{
	/bin/chmod -f ${1} "${3}" >/dev/null 2>&1
	/bin/chown -f ${2} "${3}" >/dev/null 2>&1
}

#
# Create administrator repository directories, /usr/local/share/ppd,
# if needed. This is a special case a Solaris doesn't deliver
# /usr/local/share and it has different permissions than the
# user repository.
#	
# $1	- destination repository name
#
create_adminrep_dirs()
{
	if debugger "check_adminrep_dirs" ; then
		set -x
	fi

	# Only create administrator repository directories, if needed.
	[[ "${1}" = "${ADMIN}" ]] || return 0

	# Check /usr/local/share/ppd
	[[ ! -d "${ADMINREP}" ]] || return 0

	# Check /usr/local/share
	admpar=$(/bin/dirname "${ADMINREP}")
	if [[ ! -d "${admpar}" ]] ; then

		# Check /usr/local
		admppar=$(/bin/dirname "${admpar}")
		if [[ ! -d "${admppar}" ]] ; then
			make_dir ${DIRMODE} ${ADMINOWNER} "${admppar}" || \
			    return 1
		fi
		make_dir ${DIRMODE} ${ADMINOWNER} "${admpar}" || return 1
	fi
	make_dir ${DIRMODE} ${ADMINOWNER} ${ADMINREP} || return 1
	return 0
}

#
# Returns full path to PPD file that was added to the system.
#
# $1	- Full path to source PPD file
# $2	- PPD file name
# $3	- Full path to repository
# $4	- Repository name
# $5	- Label name
#
# Return codes:
#	0	- File successfully added
#	1	- Error
#	2	- Duplicate file already exists
#
add_ppd()
{
	if debugger ; then
		set -x
	fi

	verify_ppd_file "${1}"
	if [[ $? -ne 0 ]] ; then
		gettext "invalid PPD file: ${1}" 2>/dev/null
		return 3
	fi

	# The destination path can now be set
	dstlabelpath="${3}/${5}"
	dstmanufpath="${dstlabelpath}/${modmanuf}"
	dstpath="${dstmanufpath}/${2}"

	#
	# If a version (either compressed or not compressed) of the PPD
	# file exists in the destination in the label/repository,
	# then just return as there no work to be done.
	dst_copy_path=$(variant_copy "${1}" "${dstpath}" "${6}" "${ppdfname}")
	ap_rc=$?
	if [[ ${ap_rc} -ne 0 ]] ; then
		echo "${dst_copy_path}"
		return ${ap_rc}
	fi
		
	#
	# Can only add a PPD file to the "user" or "admin" repository.
	# Note: this check is here instead of at the top of this
	# function as we don't want to cause an error if a user
	# specifies the same repository and label as a the specified
	# ppd file and the repository of the specified ppd file
	# exists in a known repository.
	#
	if [[ "${4}" != "${USER}" && "${4}" != "${ADMIN}" ]] ; then
		gettext "invalid PPD file repository name: ${4}" 2>/dev/null
		return 3
	fi

	# Ensure destination directories exist
	if ! create_adminrep_dirs ${4} ${DIRMODE} ${ADMINOWNER} || \
	    ! make_dir ${DIRMODE} ${DIROWNER} "${3}" || \
	    ! make_dir ${DIRMODE} ${DIROWNER} "${dstlabelpath}" || \
	    ! make_dir ${DIRMODE} ${DIROWNER} "${dstmanufpath}" ; then
		gettext "unable to create destination directories" 2>/dev/null
		return 3
	fi

	# Copy source PPD file, and compress if needed, to destination
	if [[ "${ppdfileext}" = "${PEXT}" ]] ; then
		${GZIP} "${1}" >"${dst_copy_path}" 2>/dev/null
		if [[ $? -eq 1 ]] ; then
			gettext "unable to copy PPD file " 2>/dev/null
			gettext "to destination" 2>/dev/null
			return 3
		fi
	else
		/bin/cp -f "${1}" "${dst_copy_path}" >/dev/null 2>&1
		if [[ $? -ne 0 ]] ; then
			gettext "unable to copy PPD file " 2>/dev/null
			gettext "to destination" 2>/dev/null
			return 3
		fi
	fi
	set_perms ${FILEMODE} ${FILEOWNER} "${dst_copy_path}"

	echo "${dst_copy_path}"

	return 0
}

#
# Returns 0 if the cache needs to be modified, otherwise
# returns 1.
#
# $1	- Full path to cache
# $2	- Full path to cache replacement candidate
#
changes_in_cache()
{
	if debugger "changes_in_cache" ; then
		set -x
	fi

	if [[ "${action}" = "${REBUILD}" ]] ; then
		return 0
	fi
	[[ "${2}" -nt "${1}" ]] || return 1
	if $(${CMP} "${1}" "${2}" >/dev/null 2>&1) ; then
		# No differences.  Just update timestamp
		/bin/touch -r "${2}" "${1}" >/dev/null 2>&1
		return 1
	else
		return 0
	fi
}

#
# Generate a new golden cache file (/var/lp/ppd/ppdcache),  by
# concatenating and sorting all existing cache files in /var/lp/ppd/caches.  
#
# If there are difference between the newly generated golden cache file and
# the existing one (if it exists) then the newly generated one replaces the
# existing one at /var/lp/ppd/ppdcache.  
#
update_golden_cache()
{

	if debugger "update_golden_cache" ; then
		set -x
	fi

	#
	# Remove any cache files that don't have an associated
	# label.
	#
	for cname in $(/bin/ls ${VARCACHES} 2>/dev/null) ; do
		repname="${cname%%:*}"
		cfile="${cname#*:}"
		checkdir="$(get_rep_path ${repname})/${cfile}"
		remove_unassociated_cache "${checkdir}" "${cname}"
	done

	#
	# Combine the contents of all cache files into a
	# temporary golden cache file.
	#
	tmpgoldencache=$ppdmgrtmpdir/tmpgoldencache

	/bin/sort "${VARCACHES}"/* >>"${tmpgoldencache}" 2>/dev/null

	if [[ ! -s "${tmpgoldencache}" ]] ; then
		# No cache files. Remove golden cache.
		/bin/rm -f "${GOLDCACHE}" >/dev/null 2>&1
		/bin/rm -f "${tmpgoldencache}" >/dev/null 2>&1
	elif [[ -e "${GOLDCACHE}" ]] ; then
		#
		# Use the newly generated "temporary" golden cache file if there
		# differences between the current and newly generated ppdcache
		# or if a rebuild is being performed.
		#
		if [[ "${VARCACHES}" -nt "${GOLDCACHE}" ]] || \
		    changes_in_cache "${GOLDCACHE}" "${tmpgoldencache}" ; then
			set_perms ${FILEMODE} ${FILEOWNER} "${tmpgoldencache}"
			/bin/mv -f "${tmpgoldencache}" \
			    "${GOLDCACHE}" >/dev/null 2>&1
		else
			/bin/rm -f "${tmpgoldencache}" >/dev/null 2>&1
		fi
	else
		# There wasn't an existing ppdcache.  Install the newly
		# generated ppdcache file to the golden ppdcache.
		set_perms ${FILEMODE} ${FILEOWNER} "${tmpgoldencache}"
		/bin/mv -f "${tmpgoldencache}" "${GOLDCACHE}" >/dev/null 2>&1
	fi
}

#
# Returns a list of PPD files that exist.
#
# $1	- Full path to cache file
#
remove_invalid_cache_entries()
{
	if debugger ; then
		set -x
	fi

	[[ -s "${1}" ]] || return

	IFS="$NoSpaceTabIFS"
	for centry in $(/bin/cat "${1}" 2>/dev/null) ; do
		IFS="$SaveIFS"
		#
		# Keep the entry from the ppd cache if it still
		# exists and there haven't been any modifications
		# since the last update to the cache.
		#
		if [[ -n "${centry}" ]] ; then
			ppdfile="${centry##*:}"
			if [[ -n "${ppdfile}" && -e "${ppdfile}"  &&
			    "${1}" -nt "${ppdfile}" ]] ; then
				echo "${centry}"
			fi
		fi
		IFS="$NoSpaceTabIFS"
	done
	IFS="$SaveIFS"
}

#
# Returns 0 if the path to the PPD is as follows:
#	<PPD file repository>/<label>/<manufacturer>/<PPD file>
# otherwise, returns 1
#
# $1	 Full path to PPD file
#
verify_ppd_location()
{
	if debugger ; then
		set -x
	fi

	 #
	 # Strip off what should be <label>/<manufacturer>/<PPD file>
	 # and verify the PPD file repository matches one of the
	 # known PPD file repositories.
	 #
	ppd_file_repository=${1%/*/*/*}
	found=1
	for repository in ${REPOSITORIES} ; do
		if [[ "${repository}" = "${ppd_file_repository}" ]] ; then
			found=0
			break
		fi
	done
	return ${found}
}

#
# Generate, and sort, cache entries for each PPD files in the specified
# list to the specified file.
#
# $1	- List of full paths to PPD files
# $2	- Full path to current cache file
# $3	- Full path to label
# $4	- Full path to new cache file to generate
#
# Return code:
#	0 success
#	1 unsuccessful
#
generate_label_cache_file()
{
	if debugger ; then
		set -x
	fi

	#
	# Generate a cache file containing cache entries for
	# all files in the label.
	#
	ucfile=$ppdmgrtmpdir/unsortedcache

	#
	# Before processing new files, remove any cache entries
	# which may be invalid.
	#
	valid_files=
	if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then
		valid_files=$(remove_invalid_cache_entries "${2}")
		if [[ -n "${valid_files}" ]] ; then
			echo "${valid_files}" >>${ucfile}
		fi
	fi

	# 
	# If there are no valid PPD files in the current cache file,
	# and there are no new PPD files to process, the only thing
	# left to do is to remove the current cache file.
	#
	if [[ -z "${valid_files}" && -z "${1}" ]] ; then
		/bin/rm -f "${2}" >/dev/null 2>&1
		/bin/rm -f "${ucfile}" >/dev/null 2>&1	
		return 0
	fi

	#
	# For each of the label's PPD files, generate
	# a cache file entry and add it to the cache file.
	#
	vpl_rc=0
	vpf_rc=0
	vpl_msg=
	vpf_msg=
	IFS="$NoSpaceTabIFS"
	for fname in ${1} ; do
		IFS="$SaveIFS"
		if [[ -n "${fname}" ]] ; then
			verify_ppd_location "${fname}"
			vpl_rc=$?
			if [[ ${vpl_rc} -ne 0 ]] ; then
				vpl_msg="${vpl_msg}\t${fname}\n"
			fi
				
			verify_ppd_file "${fname}"
			vpf_rc=$?
			if [[ ${vpf_rc} -ne 0 ]] ; then
				vpf_msg="${vpf_msg}\t${fname}\n"
			fi

			if [[ ${vpl_rc} -eq 0 && ${vpf_rc} -eq 0 ]] ; then
				echo "$(generate_cache_file_entry \
				    "${modmanuf}" "${model}" "${nickn}" \
				    "${devidmfg}" "${devidmdl}" "${fname}")"
			fi
		fi
		IFS="$NoSpaceTabIFS"
	done >>"${ucfile}" 
	IFS="$SaveIFS"
	/bin/sort -u "${ucfile}" >>"${4}" 2>/dev/null
	/bin/rm -f "${ucfile}" >/dev/null 2>&1

	[[ -n "${vpl_msg}" || -n "${vpf_msg}" ]] || return 0
	if [[ -n ${vpl_msg} ]] ; then
		gettext "  PPD file(s) not in valid location\n" 2>/dev/null
		gettext \
	    "  (<repository>/<label>/<manufacturer>/<PPD file>):\n" 2>/dev/null
		echo "${vpl_msg}"
	fi
	if [[ -n ${vpf_msg} ]] ; then
		gettext "  invalid PPD file(s):\n" 2>/dev/null
		echo "${vpf_msg}"
	fi
	return 1
}

#
# Update current cache file with candidate cache file if there are
# differences.
#
# $1	- Current cache file
# $2	- Candidate cache file to update
# $3	- Repository name
#
update_current_cache_file()
{
	if debugger "update_current_cache_file" ; then
		set -x
	fi

	if [[ ! -s "${2}" ]] ; then
		#
		# Candidate cache has zero size (label
		# directory with no PPD files under it).
		# Delete the empty candidate cache
		# file and delete the current cache
		# file.
		#
		/bin/rm -f "${1}" >/dev/null 2>&1
		/bin/rm -f "${2}" >/dev/null 2>&1
	elif [[ -e "${1}" ]] ; then
		#
		# If there are differences between the current
		# cache file and the newly generated one, then
		# replace the current one with the new one, and
		# set the flag to update the golden ppdcache
		# file.
		#
		if changes_in_cache "${1}" "${2}" ; then
			set_perms ${FILEMODE} ${FILEOWNER} "${2}"
			/bin/mv -f "${2}" "${1}" >/dev/null 2>&1
		else
			/bin/rm -f "${2}" >/dev/null 2>&1
		fi
	else

		#
		# There is no current cache file.  Move the candidate
		# to the caches directory.
		#
		set_perms ${FILEMODE} ${FILEOWNER} "${2}"
		/bin/mv -f "${2}" "${1}" >/dev/null 2>&1
	fi
}

#
# Returns 0 if there are files in $1 with newer timestamp
# than $2 or if deletions have occurred under $1,
# otherwise returns 1.
#
# $1	- Full path to the destination label
# $2	- Full path to label cache file
#
changes_under_label()
{
	if debugger ; then
		set -x
	fi

	# First check for newer files in the directory
	if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then
		newfiles=$(/bin/find "${1}" -type f -newer "${2}")
	else
		newfiles=$(/bin/find "${1}" -type f)
	fi
	echo "${newfiles}"
	[[ -z "${newfiles}" ]] || return 0

	#
	# Need to detect if PPD files have been deleted by checking
	# timestamps on label and manufacturer directories.
	#
	[[ ! "${1}" -nt "${2}" ]] || return 0
	/bin/find "${1}" -type d -newer "${2}" >/dev/null 2>&1 || return 1
	return 0
}

#
# If -R was specified, or the timestamp on the specified label's
# directory or any of the PPD files under the specified label in
# the specified PPD file respository is newer than the cache file
# associated with the label, then generate a new sorted cache file.
#
# The new cache will replace the existing one (if any) only if there
# are differences.  Note: if -r was specified, then a new cache file
# file will always be installed at
#	/var/lp/ppd/caches/<PPD file repository name>-<label name>
#
# $1	- Full path of the destination PPD file repository
# $2	- Destination PPD file repository name
# $3	- Destination label name
#
update_label_cache()
{
	if debugger ; then
		set -x
	fi

	dstlabelpath="${1}/${3}"
	replabelcachepath="${1}/${CACHES}/${3}"
	varlabelcachepath="${VARCACHES}/${2}${SEP}${3}"

	ulc_rc=0
	if [[ -d "${dstlabelpath}" ]] ; then

		#
		# If the cache doesn't exist for a label,
		# or if there were any changes under a label
		# (i.e., the timestamp on the label directory or any
		# of the PPD files under it is newer than the
		# existing cache file), then generate a new cache file.
		#
		tmpcachepath=$ppdmgrtmpdir/tmpcachepath

		# if this is a system repository, check for a prepopulated cache
		if [[ "${2}" = "${SYSTEM}" && -e ${FOOCACHEDIR}/${3}.cache ]] ; then
			# copy prepopulated cache 
			/bin/cp -f ${FOOCACHEDIR}/${3}.cache ${tmpcachepath}

		else
			newfileslist=$(changes_under_label "${dstlabelpath}" \
			    "${varlabelcachepath}")
			if [[ $? -eq 0 ]] ; then
				err_files=$(generate_label_cache_file \
				    "${newfileslist}" "${varlabelcachepath}" \
				    "${dstlabelpath}" "${tmpcachepath}")
				if [[ $? -ne 0 ]] ; then
					#
					# At least one PPD file was invalid.
					# Don't return yet, as the cache info
					# for the valid PPD files can still be
					# used to generate a cache file.
					#
					echo "${err_files}"
					ulc_rc=1
				fi
			fi
		fi

		if [[ -e "${tmpcachepath}" ]] ; then
			update_current_cache_file \
			    "${varlabelcachepath}" "${tmpcachepath}" "${2}"
			/bin/rm -f "${tmpcachepath}" >/dev/null 2>&1
		fi
	else
		#
		# If there is a cache file in /var/lp/ppd/caches associated
		# with the label which no longer exists, remove it.
		#
		/bin/rm -f "${varlabelcachepath}" >/dev/null 2>&1
	fi
	return ${ulc_rc}
}

#
# Returns the alias for the specified real manufacturer's name.
#
# $1	- Real manufacturer's name
# $2	- File containing list of files that have manufacturers aliases
#
manuf_name_alias()
{
	if debugger ; then
		set -x
	fi
	
	#
	# Found a couple of PPD files which had special characters
	# in the Manufacturer name (i.e, the following is the Manufacturer
	# entry:
	#	*Manufacturer:  "Canon Inc. (Kosugi Offic"
	# We'll only search the alias file for "Canon Inc."
	#
	tmpmanuf="${1% *\(*}"

	# Search alias files for a match on the real manufacturer name
	if [[ -s "${2}" ]] ; then
		#
		# Check the manufacturer aliases file for case
		# insensitive match of the Manufacturer entry
		# from the PPD file.  If a match is found,
		# then modify the manufacturer entry to
		# be that of the specified alias.
		#
		manufaliases=$(/bin/egrep -i \
		    "^${tmpmanuf}:|:${tmpmanuf}:|:${tmpmanuf}$" "${2}")
		if [[ -n "${manufaliases}" ]] ; then
			echo "${manufaliases%%:*}"
			break
		else
			echo "${tmpmanuf}"
		fi
	else
		echo "${tmpmanuf}"
	fi
}

#
# Returns 0 if the extension to the specified PPD file is a known
# extension, otherwise returns 1.
#
# $1	- Full path to PPD file
#
# Set upon return:
#	ppdfileext	- PPD file ext (.ppd or .ppd.gz)
#
verify_file_ext()
{
	if debugger ; then
		set -x
	fi

	if [[ "${1%.gz}".gz = "${1}" ]] ; then
		ppdfileext=${GEXT}
	elif [[ "${1%.ppd}".ppd = "${1}" ]] ; then
		ppdfileext=${PEXT}
	else
		# invalid PPD file name extension
		return 1
	fi

	return 0
}

#
# Return the lines from the specified PPD file matching the specified
# spec items.
#
# $1	- spec entries from PPD file
# $2	- spec item
#
# $1 example - 1 string with substrings separated by newline:
#	*PPD-Adobe: "4.3"
#	*Manufacturer: "HP"
#	*Product:       "(officejet 4200 series)"
#	*ModelName:     "HP OfficeJet 4200"
#	*NickName:      "HP OfficeJet 4200 Foomatic/hpijs (recommended)"
# $2 example:
#	^\*Manufacturer
#
spec_entry()
{
	if debugger ; then
		set -x
	fi

	item=$(echo "${1}" | /bin/grep ${2})
	# Remove everything up to and including the first quote
	item=${item#*\"}
	# Remove the end quote
	echo "${item%\"}"
}

#
# Return the lines from the specified PPD file matching the specified
# spec items.
#
# Note: this is similar to spec_entry() except the tokens in the
# spec entry are different.  
#
# $1	- spec entries from PPD file
# $2	- spec item
#
devid_spec_entry()
{
	if debugger ; then
		set -x
	fi

	item=$(echo "${1}" | /bin/grep ${2})
	# Remove everything up to and including the first semi-colon
	item=${item#*\:}
	# Remove the end quote
	echo ${item%\;}

}

#
# Verifies that the specified PPD file
#	- has a valid extension
#	- has the following required spec file entries:
#		*PPD-Adobe: "4.3"
#		Manufacturer
#		Product
#		ModelName
#		NickName
#
# In addition, the manufacture and model from the IEEE1284 device id
# information will be gathered here, although it's not an error that
# it isn't in the PPD file as many don't contain the IEEE1284 info.
#
# $1	- Full path to PPD file
#
# Return codes:
#	0	success
#	1	invalid PPD file
#
verify_ppd_file()
{
	if debugger ; then
		set -x
	fi

	ADOBESPEC="PPD-Adobe"
	MANUF="Manufacturer"
	PRODUCT="Product"
	MODEL="ModelName"
	NICKNAME="NickName"
	DEVID="1284DeviceID"

	# Verify the PPD file extension
	verify_file_ext "${1}" || return 1

	# Query for the required spec items
	searchentries="^\*${ADOBESPEC}:|^\*${MANUF}:|^\*${PRODUCT}:"
	searchentries="${searchentries}|^\*${MODEL}:|^\*${NICKNAME}:"
	searchentries="${searchentries}|^\*${DEVID}:"
	ppd_info="$(/bin/gzgrep -e "${searchentries}" "${1}")"

	#
	# Process the query results to verify each of the required spec
	# file items appears in the PPD file.
	#
	for spec_item in ${ADOBESPEC} ${MANUF} ${PRODUCT} ${MODEL} \
	    ${NICKNAME} ; do
		entry=$(spec_entry "${ppd_info}" "^\*${spec_item}:")
		[[ ! -z "${entry}" ]] || return 1
		case ${spec_item} in
		${MANUF})
			realmanuf="${entry}"
			;;
		${PRODUCT})
			product="${entry}"
			;;
		${MODEL})
			model="${entry}"
			;;
		${NICKNAME})
			#
			# Remove the model and any commas and spaces
			# which appear before the driver
			#
			nickn="${entry#$model[, ]*}"
			;;
		esac
			
	done

	# Save IEEE1284 device id information
	if $(echo "${ppd_info}" | grep "${DEVID}" >/dev/null 2>&1) ; then
		DMDL="MDL"
		DMFG="MFG"
		devid="$(/bin/gzgrep -e "^[ ]*${DMDL}:|^[ ]*${DMFG}:" "${1}")"
		devidmdl="$(devid_spec_entry "${devid}" "${DMDL}")"
		devidmfg="$(devid_spec_entry "${devid}" "${DMFG}")"
	else
		devidmdl=
		devidmfg=
	fi
	modmanuf=$(manuf_name_alias "${realmanuf}" ${aliasfile})

	return 0
}

#
# generate_cache_file_entry()
#
# Returns a cache file entry for the specified PPD file.
#
# $1	- modmanuf
# $2	- model
# $3	- nickn
# $4	- devidmfg
# $5	- devidmdl
# $6	- Full path to the specified PPD file
#
generate_cache_file_entry()
{
	if debugger "generate_cache_file_entry" ; then
		set -x
	fi

	echo "${1}":"${2}":"${3}":"${4}":"${5}":"${6}"
}

#
# Expand specified file to the full path.
#
# $1	- File path to expand
#
# Return code set to 0 if expanded successfully, otherwise set to 1.
#
ppd_pathname()
{
	if debugger ; then
		set -x
	fi

	if [[ -f "${1}" && -s "${1}" ]] ; then
		(cd "$(/bin/dirname "${1}")" ; \
		    echo "$(/bin/pwd)/$(/bin/basename "${1}")") || return 1
		return 0
	else
		return 1
	fi
}

#
# Returns the PPD repsitory path associated with the specified
# PPD repository name.
#
# $1	- Repository name
#
get_rep_path()
{
	if debugger ; then
		set -x
	fi

	case ${1} in
	${SYSTEM})
		echo "${SYSTEMREP}"
		;;
	${VENDOR})
		echo "${VENDORREP}"
		;;
	${ADMIN})
		echo "${ADMINREP}"
		;;
	${USER})
		echo "${USERREP}"
		;;
	*)
		echo "${UNSET}"
		;;
	esac
}

#
# Returns the PPD respository name from the repository path
#
# $1	- PPD repository path
#
get_rep_name()
{
	if debugger ; then
		set -x
	fi

	case ${1} in
	${SYSTEMREP})
		echo "${SYSTEM}"
		;;
	${VENDORREP})
		echo "${VENDOR}"
		;;
	${ADMINREP})
		echo "${ADMIN}"
		;;
	${USERREP})
		echo "${USER}"
		;;
	"all")
		echo "all"
		;;
	*)
		echo "${UNSET}"
		;;
	esac
}

#
# Returns 0 if a matching label name is found in the specified repository,
# otherwise returns 1.
# 
# $1	- repository path
# $2	- label name
#
label_path_in_repository()
{
	if debugger "label_path_in_repository" ; then
		set -x
	fi

	[[ "${1}" != "" && "${2}" != "" ]] || return 1
	lpir_rc=1
	for repository in ${REPOSITORIES} ; do
		if [[ "${repository}" = "${1}" && -d "${1}/${2}" ]] ; then
			lpir_rc=0
			break
		fi
	done
	return ${lpir_rc}
}

#
# Returns 0 if the source label path is the same
# as the destination label path, otherwise returns 1.
#
# $1	- full path to source PPD file (source label path)
# $2	- destination repository path
# $3	- destination label name
#
label_path_match()
{
	if debugger "label_path_match" ; then
		set -x
	fi

	# dest repository not specified
	if [[ "${2}" = "${UNSET}" ]] ; then
		# dest label not specified
		if [[ "${3}" = "${UNSET}" ]] ; then
			#
			# We've found a match if the label path is in a known
			# repository.
			#
			lpath="${1%/*/*}"
			label_path_in_repository \
			    "${1%/*/*/*}" "${lpath##*/}" || return 1
		else
			# 
			# If the source label path exists in the
			# in a known repository, and the destination
			# label is the same as the source label,
			# then we'll assume the default destination
			# repository is the same as the source
			# destination repository.
			# 
			[[ "${1%/*/*}" = "${1%/*/*/*}/${3}" ]] || return 1
			label_path_in_repository "${1%/*/*/*}" "${3}" || \
			    return 1
		fi

	# dest repository specified, dest label not specified
	elif [[ "${3}" = "${UNSET}" ]] ; then
		#
		# If the destination repository path is the same as the
		# source repository, and if the source label exists in the
		# destination repository path, then we'll assume the default
		# destination label is the same as the source label.
		#
		[[ "${2}" = "${1%/*/*/*}" ]] || return 1
		lpath="${1%/*/*}"
		label_path_in_repository "${2}" "${lpath##*/}" || return 1

	# dest repository and dest label specified.
	else
		#
		# We've found a match if the destination and label
		# match those of the source label path, and the source
		# label path is in a known repository.
		#
		[[ "${1%/*/*}" = "${2}/${3}" ]] || return 1
		label_path_in_repository "${2}" "${3}" || return 1
	fi
	return 0
}

#
# Returns 0 if specified label name is a reserved label, otherwise
# returns 1.
#
# $1	- label name
#
reserved_label()
{
	if debugger ; then
		set -x
	fi

	rl_rc=1
	for labelname in ${RESERVEDLABELS} ; do
		if [[ "${1}" = "${labelname}" ]] ; then
			rl_rc=0
			break
		fi
	done
	return ${rl_rc}
}

#
# Returns a list of all labels that exist in a repository that are
# not reserved labels.
#
# $1	- Full path of repository
# $2	- Repository name
#
get_rep_label_list()
{
	if debugger ; then
		set -x
	fi

	#
	# Get a list of all labels that exist in all of the
	# PPD file repository.
	#
	for lname in $(/bin/ls "${1}" 2>/dev/null) ; do
		if [[ -d "${1}/${lname}" ]] ; then
			if ! reserved_label "${lname}" ; then
				echo "${lname} "
			fi
		fi
	done
}

#
# Returns a valid PPD label.
#
# Verifies the specified PPD label is a valid label.  If the
# label is not set, then it is set to a default value.
#
# Return code set to 0 if the specified PPD label is valid, otherwise 1.
#
# $1	- PPD label
#
valid_specified_label()
{
	if debugger ; then
		set -x
	fi

	# Verify the specified label
	vsl_rc=0
	case "${1}" in
	"all")
		# Reserved label name with -a or -g options
		if [[ "${action}" = "${ADD}" || \
		    "${action}" = "${GENERATEENTRY}" ]] ; then
			print -n "$myprog: " 1>&2
			gettext "reserved PPD label name: ${1}\n" 1>&2
			vsl_rc=1
		else
			echo "${1}"
		fi
		;;

	"ppdcache" | "caches" | "manufaliases")
		# Reserved label names with any option
		print -n "$myprog: " 1>&2
		gettext "reserved PPD label name: ${1}\n" 1>&2
		vsl_rc=1
		;;

	"" | "${UNSET}")	
		# Label name not specified.  Set the default label name.
		# For -g and -a, default is "user", otherwise, default
		# is "all".
		if [[ "${action}" = "${ADD}" || \
		    "${action}" = "${GENERATEENTRY}" ]] ; then
			echo "${USER}"
		else
			echo "all"
		fi
		;;

	*)
		# label cannot be "." or ".."
		if [[ "${1}" = "." || "${1}" = ".." ]] ; then
			print -n "$myprog: " 1>&2
			gettext "PPD label name cannot be " 1>&2
			gettext "\".\" or \"..\"\n" 1>&2
			vsl_rc=1
		fi

		# Label name cannot contain special characters
		echo "${1}" | /bin/egrep "${SPECIALCHARS}" >/dev/null
		if [[ $? -eq 0 ]] ; then
			print -n "$myprog: " 1>&2
			gettext "PPD label name contains " 1>&2
			gettext "an invalid character: ${1}\n" 1>&2
			vsl_rc=1
		else
			echo "${1}"
		fi
		;;
	esac
	return ${vsl_rc}
}

#
# Returns the full path of any variant copy of the source file in
# the destination label/repository.  
# 
# $1	- Full path to source PPD file
# $2	- Full path to destination PPD file
#
# Return code set to
#	0	- Copy doesn't exist
#	1	- Duplicate copy exists
#	2	- Variant copy exists
#
variant_copy()
{
	if debugger ; then
		set -x
	fi

	#
	# First make sure there is not a .ppd and a .ppd.gz version
	# of the destination file; users should know not to do this.
	#
	if [[ -e "${2%.gz}" && -e "${2%.gz}.gz" ]] ; then
		/bin/rm -f "${2%.gz}" >/dev/null 2>&1
	fi

	# Use gzcmp to compare PPD files as it can deal with
	# gzipped or regular files.
	if $(${GZCMP} "${1}" "${2}"* >/dev/null 2>&1) ; then
		echo "${2}"*
		return 1
	elif [[ -e "${2%.gz}" ]] ; then
		echo "${2%.gz}"
		return 2
	elif [[ -e "${2%.gz}.gz" ]] ; then
		echo "${2%.gz}.gz"
		return 2
	else
		#
		# A PPD file doesn't exist in the destination
		# repository under the destination label.
		# Just display the source PPD file, ensuring
		# it has a gzip extension as we will always
		# try to gzip the copy in the destination.
		#
		if [[ "${1#*.ppd}" = ".gz" ]] ; then
			echo "${2}"
		else
			echo "${2}.gz"
		fi
		return 0
	fi
}

#
# $1	- Directory mode
# $2	- Directory owner (i.e., root:lp)
# $3	- Directory to create
#
make_dir() 
{
	if debugger "make_dir" ; then
		set -x
	fi

	[[ ! -d "${3}" ]] || return 0
	/bin/mkdir "${3}" >/dev/null 2>&1 || return 1
	set_perms ${1} ${2} "${3}"
	return 0
}

#
# Remove a ppdmgr generated cache (in /var/lp/ppd/cache)
# if it doesn't have an associated label in the repository.
# 
# $1	- Full path to label
# $2	- Cache name
#
remove_unassociated_cache()
{
	if debugger "remove_unassociated_cache" ; then
		set -x
	fi

	if [[ "${1}" != "${UNSET}" ]] ; then
		if [[ -n "${1}" && ! -d "${1}" ]] ; then
			#
			# The label doesn't exist, so delete
			# the associated cache file.
			#
			/bin/rm -f "${VARCACHES}/${2}" >/dev/null 2>&1
		fi
	fi
}

#
# Sorted copies of cache files for each label in each PPD repository
# are maintained in /var/lp/ppd/caches/<PPD respository>-<label>.
# This is done so that changes in delivered cache files can be
# detected.  If a difference in cache files is detected, or a
# cache file is either added or removed, then we know that
# the ppdcache file needs to be updated.
#
# Get a list of all cache files and compare against the list
# of labels in all of the PPD file repositories.  They should
# be the same.  If there is a label in one of the PPD file
# repositories that doesn't have an associated cache file, then
# we don't worry about it now, as that will be resolved when
# we update the cache for that label.  However, if there is
# a cache file associated with a label that no longer exists, then
# remove the cache file.
#
# $1	- Full path to repository (or "all")
# $2	- Label name
#
update_cache()
{
	if debugger ; then
		set -x
	fi

	#
	# Determine which labels in which PPD repository the
	# cache file will be updated for.
	#
	if [[ "${1}" = "all" ]] ; then
		rname="${REPOSITORIES}"
	else
		rname="${1}"
	fi

	uc_rc=0
	for dstreppath in ${rname} ; do
		labellist=
		if [[ "${2}" = "all" ]] ; then
			dstrepname=$(get_rep_name "${dstreppath}")
			labellist=$(get_rep_label_list "${dstreppath}" \
			    "${dstrepname}")
		else

			# Ensure the label exists in the PPD file repository.
			if [[ -d "${dstreppath}/${2}" ]] ; then
				labellist="${2}"
			fi
		fi

		#
		# Update the cache for each label in the PPD repository
		#
		for dstlabel in ${labellist} ; do
			ulc_msg=$(update_label_cache "${dstreppath}" \
			    "${dstrepname}" "${dstlabel}")
			if [[ $? -ne 0 ]] ; then
				echo "${ulc_msg}"
				uc_rc=1
			fi
		done
	done

	# Update the golden cache file.
	update_golden_cache
	return ${uc_rc}
}

# $1	- exit status
ppdmgr_exit()
{
	if debugger "ppdmgr_exit" ; then
		set -x
	fi

	/bin/rm -rf "${ppdmgrtmpdir}" >/dev/null 2>&1
	exit ${1}
}


usage()
{
	gettext "usage:\n" 1>&2
	print -n "\t$myprog: " 1>&2
	gettext "-a <ppd_filename_path> [ -L <label> ]\n" 1>&2
	gettext "\t\t[ -R <ppd_repository> ] [-w]\n" 1>&2
	print -n "\t$myprog: " 1>&2
	gettext "-r [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2
	print -n "\t$myprog: " 1>&2
	gettext "-u [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2

	ppdmgr_exit ${FAIL}
}

##########################################################################
# main
##########################################################################

myprog=$(/bin/basename $0)

SaveIFS="$IFS"
NoSpaceTabIFS='
'

# Updatable PPD repository
VARDIR=/var/lp/ppd

# Delivered PPD respository
SYSTEMREP=/usr/share/ppd
ADMINREP=/usr/local/share/ppd
VENDORREP=/opt/share/ppd
USERREP=${VARDIR}

RESERVEDREPS="${SYSTEMREP} ${ADMINREP} ${VENDORREP}"
REPOSITORIES="${USERREP} ${RESERVEDREPS}"
RESERVEDLABELS="all caches ppdcache manufaliases"
 
# Directory where system:SUNWfoomatic is delivered
FOOCACHEDIR=/usr/lib/lp/caches

# Deliveries
SYSTEM=system
VENDOR=vendor
ADMIN=admin
USER=user

# Sytem PPD cache name used by printmgr
GOLDCACHE=${USERREP}/ppdcache

# Delivered caches directory
CACHES=caches
MANUFALIASES=manufaliases

# Updated caches directory
VARCACHES=${VARDIR}/${CACHES}

# valid PPD file name extensions
PEXT=ppd
GEXT=gz
FILEEXTS=".${PEXT} .${PEXT}.${GEXT}"

# Default modes and owners
DIRMODE=755
DIROWNER=root:lp
ADMINOWNER=root:root
FILEMODE=444
FILEOWNER=root:lp

# ppdmgr actions
ADD=add
GENERATEENTRY=generateentry
UPDATE=update
REBUILD=rebuild

SUCCESS=0
FAIL=1
WARN=2

MAXLABELNAME=256
GZIP="/bin/gzip -c"
GZCMP="/bin/gzcmp -s"
CMP="/bin/cmp -s"
SPECIALCHARS=":"
SEP=":"

debug=0
wflag=0
status=${SUCCESS}

UNSET=""
ppdlabel=${UNSET}
ppdrepname=${UNSET}
ppdreppath=${UNSET}
modmanuf=
model=
nickn=
devidmdl=
devidmfg=

ppdmgrtmpdir=$(/usr/bin/mktemp -t -d ppdmgr.XXXXXX)
if [ -z "$ppdmgrtmpdir" ] ; then
	print -n "$myprog: " 1>&2
	gettext "Fatal error: could not create temporary directory\n" 1>&2
	exit 1
fi

aliasfile=${USERREP}/manufaliases
tmpfilepath=


OPTS=a:g:L:rR:uwZ
while getopts "$OPTS" arg ; do
	case ${arg} in
	a)	# add PPD file
		action=${ADD}
		origsrcppdpath=${OPTARG}
		;;

	g)	# create cache entry
		action=${GENERATEENTRY}
		origsrcppdpath=${OPTARG}
		;;

	L)	# PPD label name
		ppdlabel=${OPTARG}
		;;

	r)	# rebuild cache
		action=${REBUILD}
		;;

	R)	# PPD file repository to use
		ppdrepname=${OPTARG}
		;;

	u)	# update cache
		action=${UPDATE}
		;;

	w)	# display PPD file path
		wflag=1
		;;

	Z)	# debug
		debug=1
		;;

	?)
		usage
		;;
	esac
done

if debugger "Main" ; then
	set -x
fi

if [[ $# -lt 1 || -z "${action}" ]] ; then
	usage
fi

# ignore wflag unless specified with -a
if [[ ${wflag} -eq 1 && "${action}" != ${ADD} ]] ; then
	wflag=0
fi

#
# Ensure the destination PPD repository directory is set
# to match the specified repository.  If the
# destination PPD file repository was specified, then
# it must be one of the following:
# 	"user"
#	"admin"
#	"vendor"
#	"system"
#	"all"
#
case "${ppdrepname}" in
"${SYSTEM}")
	ppdreppath="${SYSTEMREP}"
	;;
"${ADMIN}")
	ppdreppath="${ADMINREP}"
	;;
"${VENDOR}")
	ppdreppath="${VENDORREP}"
	;;
"${USER}")
	ppdreppath="${USERREP}"
	;;
"all")
	if [[ "${action}" = "${ADD}" || \
	    "${action}" = "${GENERATEENTRY}" ]] ; then
		print -n "$myprog: " 1>&2
		gettext "reserved PPD repository name: " 1>&2
		gettext "${ppdrepname}\n" 1>&2
		ppdmgr_exit ${FAIL}
	fi
	ppdreppath="all"
	;;
"${UNSET}"|"")
	ppdreppath="${UNSET}"
	;;

*)
	print -n "$myprog: " 1>&2
	gettext "invalid PPD repository name: ${ppdrepname}\n" 1>&2
	ppdmgr_exit ${FAIL}
	;;
esac

#
# When a source PPD file's path is from a known repository, the
# destination repository and desination label are assumed to be the
# same as the source PPD file's unless a differing repository or label
# was specified.
#
if [[ "${action}" = "${ADD}" || "${action}" = "${GENERATEENTRY}" ]] ; then

	srcppdpath=$(ppd_pathname "${origsrcppdpath}")
	ppd_pathname_rc=$?
	if [[ ${ppd_pathname_rc} -ne 0 ]] ; then
		print -n "$myprog: " 1>&2
		gettext "invalid PPD file: ${origsrcppdpath}\n" 1>&2
		ppdmgr_exit ${ppd_pathname_rc}
	fi

	# Path cannot contain special characters
	echo "${srcppdpath}" | /bin/egrep  "${SPECIALCHARS}" >/dev/null
	if [[ $? -eq 0 ]] ; then
		print -n "$myprog: " 1>&2
		gettext "PPD path contains " 1>&2
		gettext "an invalid character: ${ppd_pathname}\n" 1>&2
		ppdmgr_exit ${FAIL}
	fi
	ppdfname=$(/bin/basename "${origsrcppdpath}")

	#
	# Check to see if there's any work to be done.  If the source file
	# is already in the destination repository under the destination
	# label, then there's nothing left to do.  We exit rather than
	# going on to do an update on the label in the repository as
	# it could possible take a long time to update.  If an add was
	# requested, it could have come from an application, so we want
	# to return quickly.
	#
	if label_path_match "${srcppdpath}" "${ppdreppath}" "${ppdlabel}" ; then
		if [[ ${wflag} -eq 1 || \
		    "${action}" = "${GENERATEENTRY}" ]] ; then
			echo "${srcppdpath}"
		fi
		ppdmgr_exit ${SUCCESS}
	fi
fi

ppdlabel=$(valid_specified_label "${ppdlabel}")
if [[ $? -ne 0 ]] ; then
	ppdmgr_exit ${FAIL}
fi

if [[ "${ppdreppath}" = "${UNSET}" ]] ; then
	ppdreppath="${USERREP}"
fi

dstrepname=$(get_rep_name "${ppdreppath}")

case "${action}" in
"${ADD}")
	#
	# Attempt to add the PPD file to the repository under the
	# specified label.  If any errors occur, final_dst_ppd_path
	# will contain the error message rather than the path to the
	# PPD file.
	#
	final_dst_ppd_path=$(add_ppd "${srcppdpath}" "${ppdfname}" \
	    "${ppdreppath}" "${dstrepname}" "${ppdlabel}")
	add_ppd_rc=$?
	case ${add_ppd_rc} in
	0)	#
		# The PPD file was added.  Update the specified
		# cache associated with the label if the PPD file
		# was added successfully and was not a duplicate.
		# Ensure any changes are also reflected in the
		# golden cache.
		#
		add_ppd_msg=$(update_label_cache "${ppdreppath}" \
		    "${dstrepname}" "${ppdlabel}")
		apm_rc=$?

		echo "${add_ppd_msg}" | /bin/grep "${final_dst_ppd_path}"
		path_in_msg=$?

		#
		# Only report cache update errors if the file that was
		# added was one that was reported as not being added
		# to the cache.  This really should happen as the file
		# was verified during the add.
		#
		if [[ ${apm_rc} -ne 0 && ${path_in_msg} -eq 0 ]] ; then
			print -n "$myprog: " 1>&2
			gettext "printer information does not reflect " 1>&2
			gettext "the\nfollowing PPD file(s):\n" 1>&2
			print "${add_ppd_msg}" 1>&2
			status=${FAIL}
		else
			update_golden_cache

			#
			# Display the full path to the added PPD file,
			# if requested (-w).
			#
			if [[ ${wflag} -eq 1 ]] ; then
				print "${final_dst_ppd_path}"
			fi
		fi
		;;

	1)	# Duplicate copy exists
		if [[ ${wflag} -eq 1 ]] ; then
			print "${final_dst_ppd_path}"
		fi
		;;

	2)	# Varying copy exists
		print -n "$myprog: " 1>&2
		gettext "differing variant of source PPD file " 1>&2
		gettext "already exists at\n" 1>&2
		gettext "${final_dst_ppd_path}\n" 1>&2
		status=${FAIL}
		;;
	*)	# The PPD file was not added as a problem occurred.
		# Display the error message.
		print -n "$myprog: " 1>&2
		print "${final_dst_ppd_path}" 1>&2
		status=${FAIL}
		;;

	esac
	;;

"${GENERATEENTRY}")
	#
	# Create a cache file entry for the specified PPD file and
	# display it on standard out.
	#
	verify_ppd_file "${srcppdpath}"
	if [[ $? -eq 0 ]] ; then
		dstdir="${ppdreppath}/${ppdlabel}/${modmanuf}"
		final_dst_path="${dstdir}/$(/bin/basename ${srcppdpath})"
		verify_ppd_location "${final_dst_path}"
		if [[ $? -eq 0 ]] ; then
			# Generate the cache file entry
			print "$(generate_cache_file_entry "${modmanuf}" \
			    "${model}" "${nickn}" "${devidmfg}" "${devidmdl}" \
			    "${final_dst_path}")"
		else
			print -n "$myprog: " 1>&2
			gettext "PPD file not in valid location\n" 1>&2
			gettext \
	    "(<repository>/<label>/<manufacturer>/<PPD file>):\n\t${1}\n" 1>&2
			status=${FAIL}
		fi

	else
		print -n "$myprog: " 1>&2
		gettext "invalid PPD file: ${1}\n" 1>&2
		status=${FAIL}
	fi
	;;
	
"${REBUILD}" | "${UPDATE}")
	update_msg=$(update_cache "${ppdreppath}" "${ppdlabel}")
	if [[ $? -ne 0 ]] ; then
		print -n "$myprog: " 1>&2
		gettext "printer information does not reflect " 1>&2
		gettext "the\nfollowing PPD file(s):\n" 1>&2
		print "${update_msg}" 1>&2
		status=${WARN}
	fi
	;;

*)
	usage
	;;
esac

ppdmgr_exit ${status}