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