xref: /illumos-gate/usr/src/cmd/print/scripts/ppdmgr (revision 24dd95375a09d43fe4f6b55f6d5188f4bac4f68d)
1#!/bin/ksh
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23# Use is subject to license terms.
24#
25# ident	"%Z%%M%	%I%	%E% SMI"
26#
27
28#
29# Description: Script to generate the Solaris printmgr 'ppdcache' file from the
30#              ppd files installed in the given ppd database directory
31#
32# ppdmgr -a <ppd_filename_path> [ -L <label> ] [-w]
33# ppdmgr -g <ppd_filename_path> [ -L <label> ] [ -R <ppd_repository> ]
34# ppdmgr -r [ -L <label> ] [ -R <ppd_repository> ]
35# ppdmgr -u [ -L <label> ] [ -R <ppd_repository> ]
36#
37# Options:
38#		-a <ppd_filename_path>	- Add a new PPD file to the specified
39#					label in the "user" repository, and
40#					updates to the "user" repository
41#					in the ppdcache.
42#		-g <ppd_filename_path>	- Generate a cache file entry
43#					for the specified PPD file
44#					on standard out.
45#		-L <label>		- Label name.  <label>
46#					can be any characters from the
47#					portable character set, however
48#					may not contain a semi-colon (':').
49#					The following are the defaults
50#					for <label> for each option:
51#					OPTION	DEFAULT LABEL
52#					------	-------------
53#					-a	<label> from <ppd_filename_path>
54#						if <ppd_filename_path>
55#						is from a known repository,
56#						otherwise defaults to "user".
57#					-g	<label> from <ppd_filename_path>
58#						if <ppd_filename_path>
59#						is from a known repository,
60#						otherwise defaults to "user".
61#					-r	all
62#					-u	all
63#					The following are reserved labels:
64#					caches		- may never be specified
65#					ppdcache	- may never be specified
66#					manufaliases	- may never be specified
67#					all		- applies specified
68#							action to all labels
69#							in a repository.
70#							Can only be specified
71#							with -r or -u.
72#					SUNW*		- anything starting with
73#							SUNW is reserved for
74#							use by Sun, but not
75#							prohibited.
76#		-r			- Rebuild the cache information for the
77#					specified label in the specified
78#					repository.  Similar to -u, however,
79#					the cache file is removed to force an
80#					update to the ppdcache.
81#		-R <ppd_repository>	- PPD repository name.
82#					Defaults to "user".
83#					The following are the possible
84#					values for <ppd_repository> and
85#					location in the system:
86#					REP	LOCATION
87#					---	--------
88#					user	/var/lp/ppd
89#					admin	/usr/local/share/ppd
90#					vendor	/opt/share/ppd
91#					system	/usr/share/ppd
92#					all	all repositories
93#
94#					Note: When specified with the -a option
95#					only "user" and "admin" are valid.
96#					"vendor", "system", and "all" will be
97#					considered reserved.
98#		-u			- Update the PPD cache information
99#					for the specified label in the specified
100#					repository if needed.  If the cache
101#					update was required, then the updated
102#					cache information is reflected in
103#					the ppdcache.
104#		-w			- Display full path of where the
105#					ppd file is located on the system.
106#					Only valid with -a, otherwise the
107#					option is ignored.
108#
109# If -a, -g, -r, or -u are specified on the command line, only the last action
110# specified will be performed.
111#
112# Cache file entry format:
113#	<ModifiedManufacturerName>:<Model>:<NickName>:<1284DeviceIDManufacturer>:<1284DeviceIDModel>:<FullPPDFilePath>
114#	HP:HP DeskJet 450:Foomatic/hpijs (recommended):dj450:hp:/usr/share/ppd/HP/HP-DeskJet_450-hpijs.ppd.gz
115#
116
117PATH=/bin:/usr/bin:/usr/sbin export PATH
118set -o noclobber
119
120TEXTDOMAIN="SUNW_OST_OSCMD"
121export TEXTDOMAIN
122
123#
124# Generates debug output for calling routine.
125# If calling routine's name is passed in, then
126# will also generate the name of the calling routine.
127#
128# $1	- Name of calling routine
129debugger()
130{
131	[[ ${debug} -eq 1 ]] || return 1
132	if [[ -n "${1}" ]] ; then
133		echo "In ${1}..." 1>&2
134	fi
135	return 0
136}
137
138#
139# Set the ownership and permissions on a file.
140#
141# $1	- Mode
142# $2	- Owner:Group
143# $3	- Full path to file
144#
145set_perms()
146{
147	/bin/chmod -f ${1} "${3}" >/dev/null 2>&1
148	/bin/chown -f ${2} "${3}" >/dev/null 2>&1
149}
150
151#
152# Create administrator repository directories, /usr/local/share/ppd,
153# if needed. This is a special case a Solaris doesn't deliver
154# /usr/local/share and it has different permissions than the
155# user repository.
156#
157# $1	- destination repository name
158#
159create_adminrep_dirs()
160{
161	if debugger "check_adminrep_dirs" ; then
162		set -x
163	fi
164
165	# Only create administrator repository directories, if needed.
166	[[ "${1}" = "${ADMIN}" ]] || return 0
167
168	# Check /usr/local/share/ppd
169	[[ ! -d "${ADMINREP}" ]] || return 0
170
171	# Check /usr/local/share
172	admpar=$(/bin/dirname "${ADMINREP}")
173	if [[ ! -d "${admpar}" ]] ; then
174
175		# Check /usr/local
176		admppar=$(/bin/dirname "${admpar}")
177		if [[ ! -d "${admppar}" ]] ; then
178			make_dir ${DIRMODE} ${ADMINOWNER} "${admppar}" || \
179			    return 1
180		fi
181		make_dir ${DIRMODE} ${ADMINOWNER} "${admpar}" || return 1
182	fi
183	make_dir ${DIRMODE} ${ADMINOWNER} ${ADMINREP} || return 1
184	return 0
185}
186
187#
188# Returns full path to PPD file that was added to the system.
189#
190# $1	- Full path to source PPD file
191# $2	- PPD file name
192# $3	- Full path to repository
193# $4	- Repository name
194# $5	- Label name
195#
196# Return codes:
197#	0	- File successfully added
198#	1	- Error
199#	2	- Duplicate file already exists
200#
201add_ppd()
202{
203	if debugger ; then
204		set -x
205	fi
206
207	verify_ppd_file "${1}"
208	if [[ $? -ne 0 ]] ; then
209		gettext "invalid PPD file: ${1}" 2>/dev/null
210		return 3
211	fi
212
213	# The destination path can now be set
214	dstlabelpath="${3}/${5}"
215	dstmanufpath="${dstlabelpath}/${modmanuf}"
216	dstpath="${dstmanufpath}/${2}"
217
218	#
219	# If a version (either compressed or not compressed) of the PPD
220	# file exists in the destination in the label/repository,
221	# then just return as there no work to be done.
222	dst_copy_path=$(variant_copy "${1}" "${dstpath}" "${6}" "${ppdfname}")
223	ap_rc=$?
224	if [[ ${ap_rc} -ne 0 ]] ; then
225		echo "${dst_copy_path}"
226		return ${ap_rc}
227	fi
228
229	#
230	# Can only add a PPD file to the "user" or "admin" repository.
231	# Note: this check is here instead of at the top of this
232	# function as we don't want to cause an error if a user
233	# specifies the same repository and label as a the specified
234	# ppd file and the repository of the specified ppd file
235	# exists in a known repository.
236	#
237	if [[ "${4}" != "${USER}" && "${4}" != "${ADMIN}" ]] ; then
238		gettext "invalid PPD file repository name: ${4}" 2>/dev/null
239		return 3
240	fi
241
242	# Ensure destination directories exist
243	if ! create_adminrep_dirs ${4} ${DIRMODE} ${ADMINOWNER} || \
244	    ! make_dir ${DIRMODE} ${DIROWNER} "${3}" || \
245	    ! make_dir ${DIRMODE} ${DIROWNER} "${dstlabelpath}" || \
246	    ! make_dir ${DIRMODE} ${DIROWNER} "${dstmanufpath}" ; then
247		gettext "unable to create destination directories" 2>/dev/null
248		return 3
249	fi
250
251	# Copy source PPD file, and compress if needed, to destination
252	if [[ "${ppdfileext}" = "${PEXT}" ]] ; then
253		${GZIP} "${1}" >"${dst_copy_path}" 2>/dev/null
254		if [[ $? -eq 1 ]] ; then
255			gettext "unable to copy PPD file " 2>/dev/null
256			gettext "to destination" 2>/dev/null
257			return 3
258		fi
259	else
260		/bin/cp -f "${1}" "${dst_copy_path}" >/dev/null 2>&1
261		if [[ $? -ne 0 ]] ; then
262			gettext "unable to copy PPD file " 2>/dev/null
263			gettext "to destination" 2>/dev/null
264			return 3
265		fi
266	fi
267	set_perms ${FILEMODE} ${FILEOWNER} "${dst_copy_path}"
268
269	echo "${dst_copy_path}"
270
271	return 0
272}
273
274#
275# Returns 0 if the cache needs to be modified, otherwise
276# returns 1.
277#
278# $1	- Full path to cache
279# $2	- Full path to cache replacement candidate
280#
281changes_in_cache()
282{
283	if debugger "changes_in_cache" ; then
284		set -x
285	fi
286
287	if [[ "${action}" = "${REBUILD}" ]] ; then
288		return 0
289	fi
290	[[ "${2}" -nt "${1}" ]] || return 1
291	if $(${CMP} "${1}" "${2}" >/dev/null 2>&1) ; then
292		# No differences.  Just update timestamp
293		/bin/touch -r "${2}" "${1}" >/dev/null 2>&1
294		return 1
295	else
296		return 0
297	fi
298}
299
300#
301# Generate a new golden cache file (/var/lp/ppd/ppdcache),  by
302# concatenating and sorting all existing cache files in /var/lp/ppd/caches.
303#
304# If there are difference between the newly generated golden cache file and
305# the existing one (if it exists) then the newly generated one replaces the
306# existing one at /var/lp/ppd/ppdcache.
307#
308update_golden_cache()
309{
310
311	if debugger "update_golden_cache" ; then
312		set -x
313	fi
314
315	#
316	# Remove any cache files that don't have an associated
317	# label.
318	#
319	for cname in $(/bin/ls ${VARCACHES} 2>/dev/null) ; do
320		repname="${cname%%:*}"
321		cfile="${cname#*:}"
322		checkdir="$(get_rep_path ${repname})/${cfile}"
323		remove_unassociated_cache "${checkdir}" "${cname}"
324	done
325
326	#
327	# Combine the contents of all cache files into a
328	# temporary golden cache file.
329	#
330	tmpgoldencache=$(/bin/mktemp -p "${ppdmgrtmpdir}" \
331	    tmpgoldencache.XXXXXX 2>/dev/null)
332	/bin/sort "${VARCACHES}"/* >>"${tmpgoldencache}" 2>/dev/null
333
334	if [[ ! -s "${tmpgoldencache}" ]] ; then
335		# No cache files. Remove golden cache.
336		/bin/rm -f "${GOLDCACHE}" >/dev/null 2>&1
337		/bin/rm -f "${tmpgoldencache}" >/dev/null 2>&1
338	elif [[ -e "${GOLDCACHE}" ]] ; then
339		#
340		# Use the newly generated "temporary" golden cache file if there
341		# differences between the current and newly generated ppdcache
342		# or if a rebuild is being performed.
343		#
344		if [[ "${VARCACHES}" -nt "${GOLDCACHE}" ]] || \
345		    changes_in_cache "${GOLDCACHE}" "${tmpgoldencache}" ; then
346			set_perms ${FILEMODE} ${FILEOWNER} "${tmpgoldencache}"
347			/bin/mv -f "${tmpgoldencache}" \
348			    "${GOLDCACHE}" >/dev/null 2>&1
349		else
350			/bin/rm -f "${tmpgoldencache}" >/dev/null 2>&1
351		fi
352	else
353		# There wasn't an existing ppdcache.  Install the newly
354		# generated ppdcache file to the golden ppdcache.
355		set_perms ${FILEMODE} ${FILEOWNER} "${tmpgoldencache}"
356		/bin/mv -f "${tmpgoldencache}" "${GOLDCACHE}" >/dev/null 2>&1
357	fi
358}
359
360#
361# Returns a list of PPD files that exist.
362#
363# $1	- Full path to cache file
364#
365remove_invalid_cache_entries()
366{
367	if debugger ; then
368		set -x
369	fi
370
371	[[ -s "${1}" ]] || return
372
373	IFS="$NoSpaceTabIFS"
374	for centry in $(/bin/cat "${1}" 2>/dev/null) ; do
375		IFS="$SaveIFS"
376		#
377		# Keep the entry from the ppd cache if it still
378		# exists and there haven't been any modifications
379		# since the last update to the cache.
380		#
381		if [[ -n "${centry}" ]] ; then
382			ppdfile="${centry##*:}"
383			if [[ -n "${ppdfile}" && -e "${ppdfile}"  &&
384			    "${1}" -nt "${ppdfile}" ]] ; then
385				echo "${centry}"
386			fi
387		fi
388		IFS="$NoSpaceTabIFS"
389	done
390	IFS="$SaveIFS"
391}
392
393#
394# Returns 0 if the path to the PPD is as follows:
395#	<PPD file repository>/<label>/<manufacturer>/<PPD file>
396# otherwise, returns 1
397#
398# $1	 Full path to PPD file
399#
400verify_ppd_location()
401{
402	if debugger ; then
403		set -x
404	fi
405
406	 #
407	 # Strip off what should be <label>/<manufacturer>/<PPD file>
408	 # and verify the PPD file repository matches one of the
409	 # known PPD file repositories.
410	 #
411	ppd_file_repository=${1%/*/*/*}
412	found=1
413	for repository in ${REPOSITORIES} ; do
414		if [[ "${repository}" = "${ppd_file_repository}" ]] ; then
415			found=0
416			break
417		fi
418	done
419	return ${found}
420}
421
422#
423# Generate, and sort, cache entries for each PPD files in the specified
424# list to the specified file.
425#
426# $1	- List of full paths to PPD files
427# $2	- Full path to current cache file
428# $3	- Full path to label
429# $4	- Full path to new cache file to generate
430#
431# Return code:
432#	0 success
433#	1 unsuccessful
434#
435generate_label_cache_file()
436{
437	if debugger ; then
438		set -x
439	fi
440
441	#
442	# Generate a cache file containing cache entries for
443	# all files in the label.
444	#
445	ucfile=$(/bin/mktemp -p "${ppdmgrtmpdir}" \
446	    unsortedcache.XXXXXX 2>/dev/null)
447
448	#
449	# Before processing new files, remove any cache entries
450	# which may be invalid.
451	#
452	valid_files=
453	if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then
454		valid_files=$(remove_invalid_cache_entries "${2}")
455		if [[ -n "${valid_files}" ]] ; then
456			echo "${valid_files}" >>${ucfile}
457		fi
458	fi
459
460	#
461	# If there are no valid PPD files in the current cache file,
462	# and there are no new PPD files to process, the only thing
463	# left to do is to remove the current cache file.
464	#
465	if [[ -z "${valid_files}" && -z "${1}" ]] ; then
466		/bin/rm -f "${2}" >/dev/null 2>&1
467		/bin/rm -f "${ucfile}" >/dev/null 2>&1
468		return 0
469	fi
470
471	#
472	# For each of the label's PPD files, generate
473	# a cache file entry and add it to the cache file.
474	#
475	vpl_rc=0
476	vpf_rc=0
477	vpl_msg=
478	vpf_msg=
479	IFS="$NoSpaceTabIFS"
480	for fname in ${1} ; do
481		IFS="$SaveIFS"
482		if [[ -n "${fname}" ]] ; then
483			verify_ppd_location "${fname}"
484			vpl_rc=$?
485			if [[ ${vpl_rc} -ne 0 ]] ; then
486				vpl_msg="${vpl_msg}\t${fname}\n"
487			fi
488
489			verify_ppd_file "${fname}"
490			vpf_rc=$?
491			if [[ ${vpf_rc} -ne 0 ]] ; then
492				vpf_msg="${vpf_msg}\t${fname}\n"
493			fi
494
495			if [[ ${vpl_rc} -eq 0 && ${vpf_rc} -eq 0 ]] ; then
496				echo "$(generate_cache_file_entry \
497				    "${modmanuf}" "${model}" "${nickn}" \
498				    "${devidmfg}" "${devidmdl}" "${fname}")"
499			fi
500		fi
501		IFS="$NoSpaceTabIFS"
502	done >>"${ucfile}"
503	IFS="$SaveIFS"
504	/bin/sort -u "${ucfile}" >>"${4}" 2>/dev/null
505	/bin/rm -f "${ucfile}" >/dev/null 2>&1
506
507	[[ -n "${vpl_msg}" || -n "${vpf_msg}" ]] || return 0
508	if [[ -n ${vpl_msg} ]] ; then
509		gettext "  PPD file(s) not in valid location\n" 2>/dev/null
510		gettext \
511	    "  (<repository>/<label>/<manufacturer>/<PPD file>):\n" 2>/dev/null
512		echo "${vpl_msg}"
513	fi
514	if [[ -n ${vpf_msg} ]] ; then
515		gettext "  invalid PPD file(s):\n" 2>/dev/null
516		echo "${vpf_msg}"
517	fi
518	return 1
519}
520
521#
522# Update current cache file with candidate cache file if there are
523# differences.
524#
525# $1	- Current cache file
526# $2	- Candidate cache file to update
527# $3	- Repository name
528#
529update_current_cache_file()
530{
531	if debugger "update_current_cache_file" ; then
532		set -x
533	fi
534
535	if [[ ! -s "${2}" ]] ; then
536		#
537		# Candidate cache has zero size (label
538		# directory with no PPD files under it).
539		# Delete the empty candidate cache
540		# file and delete the current cache
541		# file.
542		#
543		/bin/rm -f "${1}" >/dev/null 2>&1
544		/bin/rm -f "${2}" >/dev/null 2>&1
545	elif [[ -e "${1}" ]] ; then
546		#
547		# If there are differences between the current
548		# cache file and the newly generated one, then
549		# replace the current one with the new one, and
550		# set the flag to update the golden ppdcache
551		# file.
552		#
553		if changes_in_cache "${1}" "${2}" ; then
554			set_perms ${FILEMODE} ${FILEOWNER} "${2}"
555			/bin/mv -f "${2}" "${1}" >/dev/null 2>&1
556		else
557			/bin/rm -f "${2}" >/dev/null 2>&1
558		fi
559	else
560
561		#
562		# There is no current cache file.  Move the candidate
563		# to the caches directory.
564		#
565		set_perms ${FILEMODE} ${FILEOWNER} "${2}"
566		/bin/mv -f "${2}" "${1}" >/dev/null 2>&1
567	fi
568}
569
570#
571# Returns 0 if there are files in $1 with newer timestamp
572# than $2 or if deletions have occurred under $1,
573# otherwise returns 1.
574#
575# $1	- Full path to the destination label
576# $2	- Full path to label cache file
577#
578changes_under_label()
579{
580	if debugger ; then
581		set -x
582	fi
583
584	# First check for newer files in the directory
585	if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then
586		newfiles=$(/bin/find "${1}" -type f -newer "${2}")
587	else
588		newfiles=$(/bin/find "${1}" -type f)
589	fi
590	echo "${newfiles}"
591	[[ -z "${newfiles}" ]] || return 0
592
593	#
594	# Need to detect if PPD files have been deleted by checking
595	# timestamps on label and manufacturer directories.
596	#
597	[[ ! "${1}" -nt "${2}" ]] || return 0
598	/bin/find "${1}" -type d -newer "${2}" >/dev/null 2>&1 || return 1
599	return 0
600}
601
602#
603# If -R was specified, or the timestamp on the specified label's
604# directory or any of the PPD files under the specified label in
605# the specified PPD file respository is newer than the cache file
606# associated with the label, then generate a new sorted cache file.
607#
608# The new cache will replace the existing one (if any) only if there
609# are differences.  Note: if -r was specified, then a new cache file
610# file will always be installed at
611#	/var/lp/ppd/caches/<PPD file repository name>-<label name>
612#
613# $1	- Full path of the destination PPD file repository
614# $2	- Destination PPD file repository name
615# $3	- Destination label name
616#
617update_label_cache()
618{
619	if debugger ; then
620		set -x
621	fi
622
623	dstlabelpath="${1}/${3}"
624	replabelcachepath="${1}/${CACHES}/${3}"
625	varlabelcachepath="${VARCACHES}/${2}${SEP}${3}"
626
627	ulc_rc=0
628	if [[ -d "${dstlabelpath}" ]] ; then
629
630		#
631		# If the cache doesn't exist for a label,
632		# or if there were any changes under a label
633		# (i.e., the timestamp on the label directory or any
634		# of the PPD files under it is newer than the
635		# existing cache file), then generate a new cache file.
636		#
637		tmpcachepath=$(/bin/mktemp -p "${ppdmgrtmpdir}" \
638		    tmpcachepath.XXXXXX 2>/dev/null)
639		newfileslist=$(changes_under_label "${dstlabelpath}" \
640		    "${varlabelcachepath}")
641		if [[ $? -eq 0 ]] ; then
642			err_files=$(generate_label_cache_file \
643			    "${newfileslist}" "${varlabelcachepath}" \
644			    "${dstlabelpath}" "${tmpcachepath}")
645			if [[ $? -ne 0 ]] ; then
646				#
647				# At least one PPD file was invalid.
648				# Don't return yet, as the cache info
649				# for the valid PPD files can still be
650				# used to generate a cache file.
651				#
652				echo "${err_files}"
653				ulc_rc=1
654			fi
655		fi
656
657		if [[ -e "${tmpcachepath}" ]] ; then
658			update_current_cache_file \
659			    "${varlabelcachepath}" "${tmpcachepath}" "${2}"
660			/bin/rm -f "${tmpcachepath}" >/dev/null 2>&1
661		fi
662	else
663		#
664		# If there is a cache file in /var/lp/ppd/caches associated
665		# with the label which no longer exists, remove it.
666		#
667		/bin/rm -f "${varlabelcachepath}" >/dev/null 2>&1
668	fi
669	return ${ulc_rc}
670}
671
672#
673# Returns the alias for the specified real manufacturer's name.
674#
675# $1	- Real manufacturer's name
676# $2	- File containing list of files that have manufacturers aliases
677#
678manuf_name_alias()
679{
680	if debugger ; then
681		set -x
682	fi
683
684	#
685	# Found a couple of PPD files which had special characters
686	# in the Manufacturer name (i.e, the following is the Manufacturer
687	# entry:
688	#	*Manufacturer:  "Canon Inc. (Kosugi Offic"
689	# We'll only search the alias file for "Canon Inc."
690	#
691	tmpmanuf="${1% *\(*}"
692
693	# Search alias files for a match on the real manufacturer name
694	if [[ -s "${2}" ]] ; then
695		#
696		# Check the manufacturer aliases file for case
697		# insensitive match of the Manufacturer entry
698		# from the PPD file.  If a match is found,
699		# then modify the manufacturer entry to
700		# be that of the specified alias.
701		#
702		manufaliases=$(/bin/egrep -i \
703		    "^${tmpmanuf}:|:${tmpmanuf}:|:${tmpmanuf}$" "${2}")
704		if [[ -n "${manufaliases}" ]] ; then
705			echo "${manufaliases%%:*}"
706			break
707		else
708			echo "${tmpmanuf}"
709		fi
710	else
711		echo "${tmpmanuf}"
712	fi
713}
714
715#
716# Returns 0 if the extension to the specified PPD file is a known
717# extension, otherwise returns 1.
718#
719# $1	- Full path to PPD file
720#
721# Set upon return:
722#	ppdfileext	- PPD file ext (.ppd or .ppd.gz)
723#
724verify_file_ext()
725{
726	if debugger ; then
727		set -x
728	fi
729
730	if [[ "${1%.gz}".gz = "${1}" ]] ; then
731		ppdfileext=${GEXT}
732	elif [[ "${1%.ppd}".ppd = "${1}" ]] ; then
733		ppdfileext=${PEXT}
734	else
735		# invalid PPD file name extension
736		return 1
737	fi
738
739	return 0
740}
741
742#
743# Return the lines from the specified PPD file matching the specified
744# spec items.
745#
746# $1	- spec entries from PPD file
747# $2	- spec item
748#
749# $1 example - 1 string with substrings separated by newline:
750#	*PPD-Adobe: "4.3"
751#	*Manufacturer: "HP"
752#	*Product:       "(officejet 4200 series)"
753#	*ModelName:     "HP OfficeJet 4200"
754#	*NickName:      "HP OfficeJet 4200 Foomatic/hpijs (recommended)"
755# $2 example:
756#	^\*Manufacturer
757#
758spec_entry()
759{
760	if debugger ; then
761		set -x
762	fi
763
764	item=$(echo "${1}" | /bin/grep ${2})
765	# Remove everything up to and including the first quote
766	item=${item#*\"}
767	# Remove the end quote
768	echo "${item%\"}"
769}
770
771#
772# Return the lines from the specified PPD file matching the specified
773# spec items.
774#
775# Note: this is similar to spec_entry() except the tokens in the
776# spec entry are different.
777#
778# $1	- spec entries from PPD file
779# $2	- spec item
780#
781devid_spec_entry()
782{
783	if debugger ; then
784		set -x
785	fi
786
787	item=$(echo "${1}" | /bin/grep ${2})
788	# Remove everything up to and including the first semi-colon
789	item=${item#*\:}
790	# Remove the end quote
791	echo ${item%\;}
792
793}
794
795#
796# Verifies that the specified PPD file
797#	- has a valid extension
798#	- has the following required spec file entries:
799#		*PPD-Adobe: "4.3"
800#		Manufacturer
801#		Product
802#		ModelName
803#		NickName
804#
805# In addition, the manufacture and model from the IEEE1284 device id
806# information will be gathered here, although it's not an error that
807# it isn't in the PPD file as many don't contain the IEEE1284 info.
808#
809# $1	- Full path to PPD file
810#
811# Return codes:
812#	0	success
813#	1	invalid PPD file
814#
815verify_ppd_file()
816{
817	if debugger ; then
818		set -x
819	fi
820
821	ADOBESPEC="PPD-Adobe"
822	MANUF="Manufacturer"
823	PRODUCT="Product"
824	MODEL="ModelName"
825	NICKNAME="NickName"
826	DEVID="1284DeviceID"
827
828	# Verify the PPD file extension
829	verify_file_ext "${1}" || return 1
830
831	# Query for the required spec items
832	searchentries="^\*${ADOBESPEC}:|^\*${MANUF}:|^\*${PRODUCT}:"
833	searchentries="${searchentries}|^\*${MODEL}:|^\*${NICKNAME}:"
834	searchentries="${searchentries}|^\*${DEVID}:"
835	ppd_info="$(/bin/gzgrep -e "${searchentries}" "${1}")"
836
837	#
838	# Process the query results to verify each of the required spec
839	# file items appears in the PPD file.
840	#
841	for spec_item in ${ADOBESPEC} ${MANUF} ${PRODUCT} ${MODEL} \
842	    ${NICKNAME} ; do
843		entry=$(spec_entry "${ppd_info}" "^\*${spec_item}:")
844		[[ ! -z "${entry}" ]] || return 1
845		case ${spec_item} in
846		${MANUF})
847			realmanuf="${entry}"
848			;;
849		${PRODUCT})
850			product="${entry}"
851			;;
852		${MODEL})
853			model="${entry}"
854			;;
855		${NICKNAME})
856			#
857			# Remove the model and any commas and spaces
858			# which appear before the driver
859			#
860			nickn="${entry#$model[, ]*}"
861			;;
862		esac
863
864	done
865
866	# Save IEEE1284 device id information
867	if $(echo "${ppd_info}" | grep "${DEVID}" >/dev/null 2>&1) ; then
868		DMDL="MDL"
869		DMFG="MFG"
870		devid="$(/bin/gzgrep -e "^[ ]*${DMDL}:|^[ ]*${DMFG}:" "${1}")"
871		devidmdl="$(devid_spec_entry "${devid}" "${DMDL}")"
872		devidmfg="$(devid_spec_entry "${devid}" "${DMFG}")"
873	else
874		devidmdl=
875		devidmfg=
876	fi
877	modmanuf=$(manuf_name_alias "${realmanuf}" ${aliasfile})
878
879	return 0
880}
881
882#
883# generate_cache_file_entry()
884#
885# Returns a cache file entry for the specified PPD file.
886#
887# $1	- modmanuf
888# $2	- model
889# $3	- nickn
890# $4	- devidmfg
891# $5	- devidmdl
892# $6	- Full path to the specified PPD file
893#
894generate_cache_file_entry()
895{
896	if debugger "generate_cache_file_entry" ; then
897		set -x
898	fi
899
900	echo "${1}":"${2}":"${3}":"${4}":"${5}":"${6}"
901}
902
903#
904# Expand specified file to the full path.
905#
906# $1	- File path to expand
907#
908# Return code set to 0 if expanded successfully, otherwise set to 1.
909#
910ppd_pathname()
911{
912	if debugger ; then
913		set -x
914	fi
915
916	if [[ -f "${1}" && -s "${1}" ]] ; then
917		(cd "$(/bin/dirname "${1}")" ; \
918		    echo "$(/bin/pwd)/$(/bin/basename "${1}")") || return 1
919		return 0
920	else
921		return 1
922	fi
923}
924
925#
926# Returns the PPD repsitory path associated with the specified
927# PPD repository name.
928#
929# $1	- Repository name
930#
931get_rep_path()
932{
933	if debugger ; then
934		set -x
935	fi
936
937	case ${1} in
938	${SYSTEM})
939		echo "${SYSTEMREP}"
940		;;
941	${VENDOR})
942		echo "${VENDORREP}"
943		;;
944	${ADMIN})
945		echo "${ADMINREP}"
946		;;
947	${USER})
948		echo "${USERREP}"
949		;;
950	*)
951		echo "${UNSET}"
952		;;
953	esac
954}
955
956#
957# Returns the PPD respository name from the repository path
958#
959# $1	- PPD repository path
960#
961get_rep_name()
962{
963	if debugger ; then
964		set -x
965	fi
966
967	case ${1} in
968	${SYSTEMREP})
969		echo "${SYSTEM}"
970		;;
971	${VENDORREP})
972		echo "${VENDOR}"
973		;;
974	${ADMINREP})
975		echo "${ADMIN}"
976		;;
977	${USERREP})
978		echo "${USER}"
979		;;
980	"all")
981		echo "all"
982		;;
983	*)
984		echo "${UNSET}"
985		;;
986	esac
987}
988
989#
990# Returns 0 if a matching label name is found in the specified repository,
991# otherwise returns 1.
992#
993# $1	- repository path
994# $2	- label name
995#
996label_path_in_repository()
997{
998	if debugger "label_path_in_repository" ; then
999		set -x
1000	fi
1001
1002	[[ "${1}" != "" && "${2}" != "" ]] || return 1
1003	lpir_rc=1
1004	for repository in ${REPOSITORIES} ; do
1005		if [[ "${repository}" = "${1}" && -d "${1}/${2}" ]] ; then
1006			lpir_rc=0
1007			break
1008		fi
1009	done
1010	return ${lpir_rc}
1011}
1012
1013#
1014# Returns 0 if the source label path is the same
1015# as the destination label path, otherwise returns 1.
1016#
1017# $1	- full path to source PPD file (source label path)
1018# $2	- destination repository path
1019# $3	- destination label name
1020#
1021label_path_match()
1022{
1023	if debugger "label_path_match" ; then
1024		set -x
1025	fi
1026
1027	# dest repository not specified
1028	if [[ "${2}" = "${UNSET}" ]] ; then
1029		# dest label not specified
1030		if [[ "${3}" = "${UNSET}" ]] ; then
1031			#
1032			# We've found a match if the label path is in a known
1033			# repository.
1034			#
1035			lpath="${1%/*/*}"
1036			label_path_in_repository \
1037			    "${1%/*/*/*}" "${lpath##*/}" || return 1
1038		else
1039			#
1040			# If the source label path exists in the
1041			# in a known repository, and the destination
1042			# label is the same as the source label,
1043			# then we'll assume the default destination
1044			# repository is the same as the source
1045			# destination repository.
1046			#
1047			[[ "${1%/*/*}" = "${1%/*/*/*}/${3}" ]] || return 1
1048			label_path_in_repository "${1%/*/*/*}" "${3}" || \
1049			    return 1
1050		fi
1051
1052	# dest repository specified, dest label not specified
1053	elif [[ "${3}" = "${UNSET}" ]] ; then
1054		#
1055		# If the destination repository path is the same as the
1056		# source repository, and if the source label exists in the
1057		# destination repository path, then we'll assume the default
1058		# destination label is the same as the source label.
1059		#
1060		[[ "${2}" = "${1%/*/*/*}" ]] || return 1
1061		lpath="${1%/*/*}"
1062		label_path_in_repository "${2}" "${lpath##*/}" || return 1
1063
1064	# dest repository and dest label specified.
1065	else
1066		#
1067		# We've found a match if the destination and label
1068		# match those of the source label path, and the source
1069		# label path is in a known repository.
1070		#
1071		[[ "${1%/*/*}" = "${2}/${3}" ]] || return 1
1072		label_path_in_repository "${2}" "${3}" || return 1
1073	fi
1074	return 0
1075}
1076
1077#
1078# Returns 0 if specified label name is a reserved label, otherwise
1079# returns 1.
1080#
1081# $1	- label name
1082#
1083reserved_label()
1084{
1085	if debugger ; then
1086		set -x
1087	fi
1088
1089	rl_rc=1
1090	for labelname in ${RESERVEDLABELS} ; do
1091		if [[ "${1}" = "${labelname}" ]] ; then
1092			rl_rc=0
1093			break
1094		fi
1095	done
1096	return ${rl_rc}
1097}
1098
1099#
1100# Returns a list of all labels that exist in a repository that are
1101# not reserved labels.
1102#
1103# $1	- Full path of repository
1104# $2	- Repository name
1105#
1106get_rep_label_list()
1107{
1108	if debugger ; then
1109		set -x
1110	fi
1111
1112	#
1113	# Get a list of all labels that exist in all of the
1114	# PPD file repository.
1115	#
1116	for lname in $(/bin/ls "${1}" 2>/dev/null) ; do
1117		if [[ -d "${1}/${lname}" ]] ; then
1118			if ! reserved_label "${lname}" ; then
1119				echo "${lname} "
1120			fi
1121		fi
1122	done
1123}
1124
1125#
1126# Returns a valid PPD label.
1127#
1128# Verifies the specified PPD label is a valid label.  If the
1129# label is not set, then it is set to a default value.
1130#
1131# Return code set to 0 if the specified PPD label is valid, otherwise 1.
1132#
1133# $1	- PPD label
1134#
1135valid_specified_label()
1136{
1137	if debugger ; then
1138		set -x
1139	fi
1140
1141	# Verify the specified label
1142	vsl_rc=0
1143	case "${1}" in
1144	"all")
1145		# Reserved label name with -a or -g options
1146		if [[ "${action}" = "${ADD}" || \
1147		    "${action}" = "${GENERATEENTRY}" ]] ; then
1148			print -n "$myprog: " 1>&2
1149			gettext "reserved PPD label name: ${1}\n" 1>&2
1150			vsl_rc=1
1151		else
1152			echo "${1}"
1153		fi
1154		;;
1155
1156	"ppdcache" | "caches" | "manufaliases")
1157		# Reserved label names with any option
1158		print -n "$myprog: " 1>&2
1159		gettext "reserved PPD label name: ${1}\n" 1>&2
1160		vsl_rc=1
1161		;;
1162
1163	"" | "${UNSET}")
1164		# Label name not specified.  Set the default label name.
1165		# For -g and -a, default is "user", otherwise, default
1166		# is "all".
1167		if [[ "${action}" = "${ADD}" || \
1168		    "${action}" = "${GENERATEENTRY}" ]] ; then
1169			echo "${USER}"
1170		else
1171			echo "all"
1172		fi
1173		;;
1174
1175	*)
1176		# label cannot be "." or ".."
1177		if [[ "${1}" = "." || "${1}" = ".." ]] ; then
1178			print -n "$myprog: " 1>&2
1179			gettext "PPD label name cannot be " 1>&2
1180			gettext "\".\" or \"..\"\n" 1>&2
1181			vsl_rc=1
1182		fi
1183
1184		# Label name cannot contain special characters
1185		echo "${1}" | /bin/egrep "${SPECIALCHARS}" >/dev/null
1186		if [[ $? -eq 0 ]] ; then
1187			print -n "$myprog: " 1>&2
1188			gettext "PPD label name contains " 1>&2
1189			gettext "an invalid character: ${1}\n" 1>&2
1190			vsl_rc=1
1191		else
1192			echo "${1}"
1193		fi
1194		;;
1195	esac
1196	return ${vsl_rc}
1197}
1198
1199#
1200# Returns the full path of any variant copy of the source file in
1201# the destination label/repository.
1202#
1203# $1	- Full path to source PPD file
1204# $2	- Full path to destination PPD file
1205#
1206# Return code set to
1207#	0	- Copy doesn't exist
1208#	1	- Duplicate copy exists
1209#	2	- Variant copy exists
1210#
1211variant_copy()
1212{
1213	if debugger ; then
1214		set -x
1215	fi
1216
1217	#
1218	# First make sure there is not a .ppd and a .ppd.gz version
1219	# of the destination file; users should know not to do this.
1220	#
1221	if [[ -e "${2%.gz}" && -e "${2%.gz}.gz" ]] ; then
1222		/bin/rm -f "${2%.gz}" >/dev/null 2>&1
1223	fi
1224
1225	# Use gzcmp to compare PPD files as it can deal with
1226	# gzipped or regular files.
1227	if $(${GZCMP} "${1}" "${2}"* >/dev/null 2>&1) ; then
1228		echo "${2}"*
1229		return 1
1230	elif [[ -e "${2%.gz}" ]] ; then
1231		echo "${2%.gz}"
1232		return 2
1233	elif [[ -e "${2%.gz}.gz" ]] ; then
1234		echo "${2%.gz}.gz"
1235		return 2
1236	else
1237		#
1238		# A PPD file doesn't exist in the destination
1239		# repository under the destination label.
1240		# Just display the source PPD file, ensuring
1241		# it has a gzip extension as we will always
1242		# try to gzip the copy in the destination.
1243		#
1244		if [[ "${1#*.ppd}" = ".gz" ]] ; then
1245			echo "${2}"
1246		else
1247			echo "${2}.gz"
1248		fi
1249		return 0
1250	fi
1251}
1252
1253#
1254# $1	- Directory mode
1255# $2	- Directory owner (i.e., root:lp)
1256# $3	- Directory to create
1257#
1258make_dir()
1259{
1260	if debugger "make_dir" ; then
1261		set -x
1262	fi
1263
1264	[[ ! -d "${3}" ]] || return 0
1265	/bin/mkdir "${3}" >/dev/null 2>&1 || return 1
1266	set_perms ${1} ${2} "${3}"
1267	return 0
1268}
1269
1270#
1271# Remove a ppdmgr generated cache (in /var/lp/ppd/cache)
1272# if it doesn't have an associated label in the repository.
1273#
1274# $1	- Full path to label
1275# $2	- Cache name
1276#
1277remove_unassociated_cache()
1278{
1279	if debugger "remove_unassociated_cache" ; then
1280		set -x
1281	fi
1282
1283	if [[ "${1}" != "${UNSET}" ]] ; then
1284		if [[ -n "${1}" && ! -d "${1}" ]] ; then
1285			#
1286			# The label doesn't exist, so delete
1287			# the associated cache file.
1288			#
1289			/bin/rm -f "${VARCACHES}/${2}" >/dev/null 2>&1
1290		fi
1291	fi
1292}
1293
1294#
1295# Sorted copies of cache files for each label in each PPD repository
1296# are maintained in /var/lp/ppd/caches/<PPD respository>-<label>.
1297# This is done so that changes in delivered cache files can be
1298# detected.  If a difference in cache files is detected, or a
1299# cache file is either added or removed, then we know that
1300# the ppdcache file needs to be updated.
1301#
1302# Get a list of all cache files and compare against the list
1303# of labels in all of the PPD file repositories.  They should
1304# be the same.  If there is a label in one of the PPD file
1305# repositories that doesn't have an associated cache file, then
1306# we don't worry about it now, as that will be resolved when
1307# we update the cache for that label.  However, if there is
1308# a cache file associated with a label that no longer exists, then
1309# remove the cache file.
1310#
1311# $1	- Full path to repository (or "all")
1312# $2	- Label name
1313#
1314update_cache()
1315{
1316	if debugger ; then
1317		set -x
1318	fi
1319
1320	#
1321	# Determine which labels in which PPD repository the
1322	# cache file will be updated for.
1323	#
1324	if [[ "${1}" = "all" ]] ; then
1325		rname="${REPOSITORIES}"
1326	else
1327		rname="${1}"
1328	fi
1329
1330	uc_rc=0
1331	for dstreppath in ${rname} ; do
1332		labellist=
1333		if [[ "${2}" = "all" ]] ; then
1334			dstrepname=$(get_rep_name "${dstreppath}")
1335			labellist=$(get_rep_label_list "${dstreppath}" \
1336			    "${dstrepname}")
1337		else
1338
1339			# Ensure the label exists in the PPD file repository.
1340			if [[ -d "${dstreppath}/${2}" ]] ; then
1341				labellist="${2}"
1342			fi
1343		fi
1344
1345		#
1346		# Update the cache for each label in the PPD repository
1347		#
1348		for dstlabel in ${labellist} ; do
1349			ulc_msg=$(update_label_cache "${dstreppath}" \
1350			    "${dstrepname}" "${dstlabel}")
1351			if [[ $? -ne 0 ]] ; then
1352				echo "${ulc_msg}"
1353				uc_rc=1
1354			fi
1355		done
1356	done
1357
1358	# Update the golden cache file.
1359	update_golden_cache
1360	return ${uc_rc}
1361}
1362
1363# $1	- exit status
1364ppdmgr_exit()
1365{
1366	if debugger "ppdmgr_exit" ; then
1367		set -x
1368	fi
1369
1370	/bin/rm -rf "${ppdmgrtmpdir}" >/dev/null 2>&1
1371	exit ${1}
1372}
1373
1374
1375usage()
1376{
1377	gettext "usage:\n" 1>&2
1378	print -n "\t$myprog: " 1>&2
1379	gettext "-a <ppd_filename_path> [ -L <label> ]\n" 1>&2
1380	gettext "\t\t[ -R <ppd_repository> ] [-w]\n" 1>&2
1381	print -n "\t$myprog: " 1>&2
1382	gettext "-r [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2
1383	print -n "\t$myprog: " 1>&2
1384	gettext "-u [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2
1385
1386	ppdmgr_exit ${FAIL}
1387}
1388
1389##########################################################################
1390# main
1391##########################################################################
1392
1393myprog=$(/bin/basename $0)
1394
1395SaveIFS="$IFS"
1396NoSpaceTabIFS='
1397'
1398
1399# Updatable PPD repository
1400VARDIR=/var/lp/ppd
1401
1402# Delivered PPD respository
1403SYSTEMREP=/usr/share/ppd
1404ADMINREP=/usr/local/share/ppd
1405VENDORREP=/opt/share/ppd
1406USERREP=${VARDIR}
1407
1408RESERVEDREPS="${SYSTEMREP} ${ADMINREP} ${VENDORREP}"
1409REPOSITORIES="${USERREP} ${RESERVEDREPS}"
1410RESERVEDLABELS="all caches ppdcache manufaliases"
1411
1412# Deliveries
1413SYSTEM=system
1414VENDOR=vendor
1415ADMIN=admin
1416USER=user
1417
1418# Sytem PPD cache name used by printmgr
1419GOLDCACHE=${USERREP}/ppdcache
1420
1421# Delivered caches directory
1422CACHES=caches
1423MANUFALIASES=manufaliases
1424
1425# Updated caches directory
1426VARCACHES=${VARDIR}/${CACHES}
1427
1428# valid PPD file name extensions
1429PEXT=ppd
1430GEXT=gz
1431FILEEXTS=".${PEXT} .${PEXT}.${GEXT}"
1432
1433# Default modes and owners
1434DIRMODE=755
1435DIROWNER=root:lp
1436ADMINOWNER=root:root
1437FILEMODE=444
1438FILEOWNER=root:lp
1439
1440# ppdmgr actions
1441ADD=add
1442GENERATEENTRY=generateentry
1443UPDATE=update
1444REBUILD=rebuild
1445
1446SUCCESS=0
1447FAIL=1
1448WARN=2
1449
1450MAXLABELNAME=256
1451GZIP="/bin/gzip -c"
1452GZCMP="/bin/gzcmp -s"
1453CMP="/bin/cmp -s"
1454SPECIALCHARS=":"
1455SEP=":"
1456
1457debug=0
1458wflag=0
1459status=${SUCCESS}
1460
1461UNSET=""
1462ppdlabel=${UNSET}
1463ppdrepname=${UNSET}
1464ppdreppath=${UNSET}
1465modmanuf=
1466model=
1467nickn=
1468devidmdl=
1469devidmfg=
1470
1471ppdmgrtmpdir=/tmp/ppdmgr.$$
1472/bin/mkdir "${ppdmgrtmpdir}" >/dev/null 2>&1
1473set_perms ${DIRMODE} ${DIROWNER} "${ppdmgrtmpdir}"
1474
1475aliasfile=${USERREP}/manufaliases
1476tmpfilepath=
1477
1478
1479OPTS=a:g:L:rR:uwZ
1480while getopts "$OPTS" arg ; do
1481	case ${arg} in
1482	a)	# add PPD file
1483		action=${ADD}
1484		origsrcppdpath=${OPTARG}
1485		;;
1486
1487	g)	# create cache entry
1488		action=${GENERATEENTRY}
1489		origsrcppdpath=${OPTARG}
1490		;;
1491
1492	L)	# PPD label name
1493		ppdlabel=${OPTARG}
1494		;;
1495
1496	r)	# rebuild cache
1497		action=${REBUILD}
1498		;;
1499
1500	R)	# PPD file repository to use
1501		ppdrepname=${OPTARG}
1502		;;
1503
1504	u)	# update cache
1505		action=${UPDATE}
1506		;;
1507
1508	w)	# display PPD file path
1509		wflag=1
1510		;;
1511
1512	Z)	# debug
1513		debug=1
1514		;;
1515
1516	?)
1517		usage
1518		;;
1519	esac
1520done
1521
1522if debugger "Main" ; then
1523	set -x
1524fi
1525
1526if [[ $# -lt 1 || -z "${action}" ]] ; then
1527	usage
1528fi
1529
1530# ignore wflag unless specified with -a
1531if [[ ${wflag} -eq 1 && "${action}" != ${ADD} ]] ; then
1532	wflag=0
1533fi
1534
1535#
1536# Ensure the destination PPD repository directory is set
1537# to match the specified repository.  If the
1538# destination PPD file repository was specified, then
1539# it must be one of the following:
1540# 	"user"
1541#	"admin"
1542#	"vendor"
1543#	"system"
1544#	"all"
1545#
1546case "${ppdrepname}" in
1547"${SYSTEM}")
1548	ppdreppath="${SYSTEMREP}"
1549	;;
1550"${ADMIN}")
1551	ppdreppath="${ADMINREP}"
1552	;;
1553"${VENDOR}")
1554	ppdreppath="${VENDORREP}"
1555	;;
1556"${USER}")
1557	ppdreppath="${USERREP}"
1558	;;
1559"all")
1560	if [[ "${action}" = "${ADD}" || \
1561	    "${action}" = "${GENERATEENTRY}" ]] ; then
1562		print -n "$myprog: " 1>&2
1563		gettext "reserved PPD repository name: " 1>&2
1564		gettext "${ppdrepname}\n" 1>&2
1565		ppdmgr_exit ${FAIL}
1566	fi
1567	ppdreppath="all"
1568	;;
1569"${UNSET}"|"")
1570	ppdreppath="${UNSET}"
1571	;;
1572
1573*)
1574	print -n "$myprog: " 1>&2
1575	gettext "invalid PPD repository name: ${ppdrepname}\n" 1>&2
1576	ppdmgr_exit ${FAIL}
1577	;;
1578esac
1579
1580#
1581# When a source PPD file's path is from a known repository, the
1582# destination repository and desination label are assumed to be the
1583# same as the source PPD file's unless a differing repository or label
1584# was specified.
1585#
1586if [[ "${action}" = "${ADD}" || "${action}" = "${GENERATEENTRY}" ]] ; then
1587
1588	srcppdpath=$(ppd_pathname "${origsrcppdpath}")
1589	ppd_pathname_rc=$?
1590	if [[ ${ppd_pathname_rc} -ne 0 ]] ; then
1591		print -n "$myprog: " 1>&2
1592		gettext "invalid PPD file: ${origsrcppdpath}\n" 1>&2
1593		ppdmgr_exit ${ppd_pathname_rc}
1594	fi
1595
1596	# Path cannot contain special characters
1597	echo "${srcppdpath}" | /bin/egrep  "${SPECIALCHARS}" >/dev/null
1598	if [[ $? -eq 0 ]] ; then
1599		print -n "$myprog: " 1>&2
1600		gettext "PPD path contains " 1>&2
1601		gettext "an invalid character: ${ppd_pathname}\n" 1>&2
1602		ppdmgr_exit ${FAIL}
1603	fi
1604	ppdfname=$(/bin/basename "${origsrcppdpath}")
1605
1606	#
1607	# Check to see if there's any work to be done.  If the source file
1608	# is already in the destination repository under the destination
1609	# label, then there's nothing left to do.  We exit rather than
1610	# going on to do an update on the label in the repository as
1611	# it could possible take a long time to update.  If an add was
1612	# requested, it could have come from an application, so we want
1613	# to return quickly.
1614	#
1615	if label_path_match "${srcppdpath}" "${ppdreppath}" "${ppdlabel}" ; then
1616		if [[ ${wflag} -eq 1 || \
1617		    "${action}" = "${GENERATEENTRY}" ]] ; then
1618			echo "${srcppdpath}"
1619		fi
1620		ppdmgr_exit ${SUCCESS}
1621	fi
1622fi
1623
1624ppdlabel=$(valid_specified_label "${ppdlabel}")
1625if [[ $? -ne 0 ]] ; then
1626	ppdmgr_exit ${FAIL}
1627fi
1628
1629if [[ "${ppdreppath}" = "${UNSET}" ]] ; then
1630	ppdreppath="${USERREP}"
1631fi
1632
1633dstrepname=$(get_rep_name "${ppdreppath}")
1634
1635case "${action}" in
1636"${ADD}")
1637	#
1638	# Attempt to add the PPD file to the repository under the
1639	# specified label.  If any errors occur, final_dst_ppd_path
1640	# will contain the error message rather than the path to the
1641	# PPD file.
1642	#
1643	final_dst_ppd_path=$(add_ppd "${srcppdpath}" "${ppdfname}" \
1644	    "${ppdreppath}" "${dstrepname}" "${ppdlabel}")
1645	add_ppd_rc=$?
1646	case ${add_ppd_rc} in
1647	0)	#
1648		# The PPD file was added.  Update the specified
1649		# cache associated with the label if the PPD file
1650		# was added successfully and was not a duplicate.
1651		# Ensure any changes are also reflected in the
1652		# golden cache.
1653		#
1654		add_ppd_msg=$(update_label_cache "${ppdreppath}" \
1655		    "${dstrepname}" "${ppdlabel}")
1656		apm_rc=$?
1657
1658		echo "${add_ppd_msg}" | /bin/grep "${final_dst_ppd_path}"
1659		path_in_msg=$?
1660
1661		#
1662		# Only report cache update errors if the file that was
1663		# added was one that was reported as not being added
1664		# to the cache.  This really should happen as the file
1665		# was verified during the add.
1666		#
1667		if [[ ${apm_rc} -ne 0 && ${path_in_msg} -eq 0 ]] ; then
1668			print -n "$myprog: " 1>&2
1669			gettext "printer information does not reflect " 1>&2
1670			gettext "the\nfollowing PPD file(s):\n" 1>&2
1671			print "${add_ppd_msg}" 1>&2
1672			status=${FAIL}
1673		else
1674			update_golden_cache
1675
1676			#
1677			# Display the full path to the added PPD file,
1678			# if requested (-w).
1679			#
1680			if [[ ${wflag} -eq 1 ]] ; then
1681				print "${final_dst_ppd_path}"
1682			fi
1683		fi
1684		;;
1685
1686	1)	# Duplicate copy exists
1687		if [[ ${wflag} -eq 1 ]] ; then
1688			print "${final_dst_ppd_path}"
1689		fi
1690		;;
1691
1692	2)	# Varying copy exists
1693		print -n "$myprog: " 1>&2
1694		gettext "differing variant of source PPD file " 1>&2
1695		gettext "already exists at\n" 1>&2
1696		gettext "${final_dst_ppd_path}\n" 1>&2
1697		status=${FAIL}
1698		;;
1699	*)	# The PPD file was not added as a problem occurred.
1700		# Display the error message.
1701		print -n "$myprog: " 1>&2
1702		print "${final_dst_ppd_path}" 1>&2
1703		status=${FAIL}
1704		;;
1705
1706	esac
1707	;;
1708
1709"${GENERATEENTRY}")
1710	#
1711	# Create a cache file entry for the specified PPD file and
1712	# display it on standard out.
1713	#
1714	verify_ppd_file "${srcppdpath}"
1715	if [[ $? -eq 0 ]] ; then
1716		dstdir="${ppdreppath}/${ppdlabel}/${modmanuf}"
1717		final_dst_path="${dstdir}/$(/bin/basename ${srcppdpath})"
1718		verify_ppd_location "${final_dst_path}"
1719		if [[ $? -eq 0 ]] ; then
1720			# Generate the cache file entry
1721			print "$(generate_cache_file_entry "${modmanuf}" \
1722			    "${model}" "${nickn}" "${devidmfg}" "${devidmdl}" \
1723			    "${final_dst_path}")"
1724		else
1725			print -n "$myprog: " 1>&2
1726			gettext "PPD file not in valid location\n" 1>&2
1727			gettext \
1728	    "(<repository>/<label>/<manufacturer>/<PPD file>):\n\t${1}\n" 1>&2
1729			status=${FAIL}
1730		fi
1731
1732	else
1733		print -n "$myprog: " 1>&2
1734		gettext "invalid PPD file: ${1}\n" 1>&2
1735		status=${FAIL}
1736	fi
1737	;;
1738
1739"${REBUILD}" | "${UPDATE}")
1740	update_msg=$(update_cache "${ppdreppath}" "${ppdlabel}")
1741	if [[ $? -ne 0 ]] ; then
1742		print -n "$myprog: " 1>&2
1743		gettext "printer information does not reflect " 1>&2
1744		gettext "the\nfollowing PPD file(s):\n" 1>&2
1745		print "${update_msg}" 1>&2
1746		status=${WARN}
1747	fi
1748	;;
1749
1750*)
1751	usage
1752	;;
1753esac
1754
1755ppdmgr_exit ${status}
1756