xref: /illumos-gate/usr/src/cmd/print/scripts/ppdmgr (revision d0b12b66)
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 2009 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=$ppdmgrtmpdir/tmpgoldencache
329
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=$ppdmgrtmpdir/unsortedcache
444
445	#
446	# Before processing new files, remove any cache entries
447	# which may be invalid.
448	#
449	valid_files=
450	if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then
451		valid_files=$(remove_invalid_cache_entries "${2}")
452		if [[ -n "${valid_files}" ]] ; then
453			echo "${valid_files}" >>${ucfile}
454		fi
455	fi
456
457	#
458	# If there are no valid PPD files in the current cache file,
459	# and there are no new PPD files to process, the only thing
460	# left to do is to remove the current cache file.
461	#
462	if [[ -z "${valid_files}" && -z "${1}" ]] ; then
463		/bin/rm -f "${2}" >/dev/null 2>&1
464		/bin/rm -f "${ucfile}" >/dev/null 2>&1
465		return 0
466	fi
467
468	#
469	# For each of the label's PPD files, generate
470	# a cache file entry and add it to the cache file.
471	#
472	vpl_rc=0
473	vpf_rc=0
474	vpl_msg=
475	vpf_msg=
476	IFS="$NoSpaceTabIFS"
477	for fname in ${1} ; do
478		IFS="$SaveIFS"
479		if [[ -n "${fname}" ]] ; then
480			verify_ppd_location "${fname}"
481			vpl_rc=$?
482			if [[ ${vpl_rc} -ne 0 ]] ; then
483				vpl_msg="${vpl_msg}\t${fname}\n"
484			fi
485
486			verify_ppd_file "${fname}"
487			vpf_rc=$?
488			if [[ ${vpf_rc} -ne 0 ]] ; then
489				vpf_msg="${vpf_msg}\t${fname}\n"
490			fi
491
492			if [[ ${vpl_rc} -eq 0 && ${vpf_rc} -eq 0 ]] ; then
493				echo "$(generate_cache_file_entry \
494				    "${modmanuf}" "${model}" "${nickn}" \
495				    "${devidmfg}" "${devidmdl}" "${fname}")"
496			fi
497		fi
498		IFS="$NoSpaceTabIFS"
499	done >>"${ucfile}"
500	IFS="$SaveIFS"
501	/bin/sort -u "${ucfile}" >>"${4}" 2>/dev/null
502	/bin/rm -f "${ucfile}" >/dev/null 2>&1
503
504	[[ -n "${vpl_msg}" || -n "${vpf_msg}" ]] || return 0
505	if [[ -n ${vpl_msg} ]] ; then
506		gettext "  PPD file(s) not in valid location\n" 2>/dev/null
507		gettext \
508	    "  (<repository>/<label>/<manufacturer>/<PPD file>):\n" 2>/dev/null
509		echo "${vpl_msg}"
510	fi
511	if [[ -n ${vpf_msg} ]] ; then
512		gettext "  invalid PPD file(s):\n" 2>/dev/null
513		echo "${vpf_msg}"
514	fi
515	return 1
516}
517
518#
519# Update current cache file with candidate cache file if there are
520# differences.
521#
522# $1	- Current cache file
523# $2	- Candidate cache file to update
524# $3	- Repository name
525#
526update_current_cache_file()
527{
528	if debugger "update_current_cache_file" ; then
529		set -x
530	fi
531
532	if [[ ! -s "${2}" ]] ; then
533		#
534		# Candidate cache has zero size (label
535		# directory with no PPD files under it).
536		# Delete the empty candidate cache
537		# file and delete the current cache
538		# file.
539		#
540		/bin/rm -f "${1}" >/dev/null 2>&1
541		/bin/rm -f "${2}" >/dev/null 2>&1
542	elif [[ -e "${1}" ]] ; then
543		#
544		# If there are differences between the current
545		# cache file and the newly generated one, then
546		# replace the current one with the new one, and
547		# set the flag to update the golden ppdcache
548		# file.
549		#
550		if changes_in_cache "${1}" "${2}" ; then
551			set_perms ${FILEMODE} ${FILEOWNER} "${2}"
552			/bin/mv -f "${2}" "${1}" >/dev/null 2>&1
553		else
554			/bin/rm -f "${2}" >/dev/null 2>&1
555		fi
556	else
557
558		#
559		# There is no current cache file.  Move the candidate
560		# to the caches directory.
561		#
562		set_perms ${FILEMODE} ${FILEOWNER} "${2}"
563		/bin/mv -f "${2}" "${1}" >/dev/null 2>&1
564	fi
565}
566
567#
568# Returns 0 if there are files in $1 with newer timestamp
569# than $2 or if deletions have occurred under $1,
570# otherwise returns 1.
571#
572# $1	- Full path to the destination label
573# $2	- Full path to label cache file
574#
575changes_under_label()
576{
577	if debugger ; then
578		set -x
579	fi
580
581	# First check for newer files in the directory
582	if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then
583		newfiles=$(/bin/find "${1}" -type f -newer "${2}")
584	else
585		newfiles=$(/bin/find "${1}" -type f)
586	fi
587	echo "${newfiles}"
588	[[ -z "${newfiles}" ]] || return 0
589
590	#
591	# Need to detect if PPD files have been deleted by checking
592	# timestamps on label and manufacturer directories.
593	#
594	[[ ! "${1}" -nt "${2}" ]] || return 0
595	/bin/find "${1}" -type d -newer "${2}" >/dev/null 2>&1 || return 1
596	return 0
597}
598
599#
600# If -R was specified, or the timestamp on the specified label's
601# directory or any of the PPD files under the specified label in
602# the specified PPD file respository is newer than the cache file
603# associated with the label, then generate a new sorted cache file.
604#
605# The new cache will replace the existing one (if any) only if there
606# are differences.  Note: if -r was specified, then a new cache file
607# file will always be installed at
608#	/var/lp/ppd/caches/<PPD file repository name>-<label name>
609#
610# $1	- Full path of the destination PPD file repository
611# $2	- Destination PPD file repository name
612# $3	- Destination label name
613#
614update_label_cache()
615{
616	if debugger ; then
617		set -x
618	fi
619
620	dstlabelpath="${1}/${3}"
621	replabelcachepath="${1}/${CACHES}/${3}"
622	varlabelcachepath="${VARCACHES}/${2}${SEP}${3}"
623
624	ulc_rc=0
625	if [[ -d "${dstlabelpath}" ]] ; then
626
627		#
628		# If the cache doesn't exist for a label,
629		# or if there were any changes under a label
630		# (i.e., the timestamp on the label directory or any
631		# of the PPD files under it is newer than the
632		# existing cache file), then generate a new cache file.
633		#
634		tmpcachepath=$ppdmgrtmpdir/tmpcachepath
635
636		# if this is a system repository, check for a prepopulated cache
637		if [[ "${2}" = "${SYSTEM}" && -e ${FOOCACHEDIR}/${3}.cache ]] ; then
638			# copy prepopulated cache
639			/bin/cp -f ${FOOCACHEDIR}/${3}.cache ${tmpcachepath}
640
641		else
642			newfileslist=$(changes_under_label "${dstlabelpath}" \
643			    "${varlabelcachepath}")
644			if [[ $? -eq 0 ]] ; then
645				err_files=$(generate_label_cache_file \
646				    "${newfileslist}" "${varlabelcachepath}" \
647				    "${dstlabelpath}" "${tmpcachepath}")
648				if [[ $? -ne 0 ]] ; then
649					#
650					# At least one PPD file was invalid.
651					# Don't return yet, as the cache info
652					# for the valid PPD files can still be
653					# used to generate a cache file.
654					#
655					echo "${err_files}"
656					ulc_rc=1
657				fi
658			fi
659		fi
660
661		if [[ -e "${tmpcachepath}" ]] ; then
662			update_current_cache_file \
663			    "${varlabelcachepath}" "${tmpcachepath}" "${2}"
664			/bin/rm -f "${tmpcachepath}" >/dev/null 2>&1
665		fi
666	else
667		#
668		# If there is a cache file in /var/lp/ppd/caches associated
669		# with the label which no longer exists, remove it.
670		#
671		/bin/rm -f "${varlabelcachepath}" >/dev/null 2>&1
672	fi
673	return ${ulc_rc}
674}
675
676#
677# Returns the alias for the specified real manufacturer's name.
678#
679# $1	- Real manufacturer's name
680# $2	- File containing list of files that have manufacturers aliases
681#
682manuf_name_alias()
683{
684	if debugger ; then
685		set -x
686	fi
687
688	#
689	# Found a couple of PPD files which had special characters
690	# in the Manufacturer name (i.e, the following is the Manufacturer
691	# entry:
692	#	*Manufacturer:  "Canon Inc. (Kosugi Offic"
693	# We'll only search the alias file for "Canon Inc."
694	#
695	tmpmanuf="${1% *\(*}"
696
697	# Search alias files for a match on the real manufacturer name
698	if [[ -s "${2}" ]] ; then
699		#
700		# Check the manufacturer aliases file for case
701		# insensitive match of the Manufacturer entry
702		# from the PPD file.  If a match is found,
703		# then modify the manufacturer entry to
704		# be that of the specified alias.
705		#
706		manufaliases=$(/bin/egrep -i \
707		    "^${tmpmanuf}:|:${tmpmanuf}:|:${tmpmanuf}$" "${2}")
708		if [[ -n "${manufaliases}" ]] ; then
709			echo "${manufaliases%%:*}"
710			break
711		else
712			echo "${tmpmanuf}"
713		fi
714	else
715		echo "${tmpmanuf}"
716	fi
717}
718
719#
720# Returns 0 if the extension to the specified PPD file is a known
721# extension, otherwise returns 1.
722#
723# $1	- Full path to PPD file
724#
725# Set upon return:
726#	ppdfileext	- PPD file ext (.ppd or .ppd.gz)
727#
728verify_file_ext()
729{
730	if debugger ; then
731		set -x
732	fi
733
734	if [[ "${1%.gz}".gz = "${1}" ]] ; then
735		ppdfileext=${GEXT}
736	elif [[ "${1%.ppd}".ppd = "${1}" ]] ; then
737		ppdfileext=${PEXT}
738	else
739		# invalid PPD file name extension
740		return 1
741	fi
742
743	return 0
744}
745
746#
747# Return the lines from the specified PPD file matching the specified
748# spec items.
749#
750# $1	- spec entries from PPD file
751# $2	- spec item
752#
753# $1 example - 1 string with substrings separated by newline:
754#	*PPD-Adobe: "4.3"
755#	*Manufacturer: "HP"
756#	*Product:       "(officejet 4200 series)"
757#	*ModelName:     "HP OfficeJet 4200"
758#	*NickName:      "HP OfficeJet 4200 Foomatic/hpijs (recommended)"
759# $2 example:
760#	^\*Manufacturer
761#
762spec_entry()
763{
764	if debugger ; then
765		set -x
766	fi
767
768	item=$(echo "${1}" | /bin/grep ${2})
769	# Remove everything up to and including the first quote
770	item=${item#*\"}
771	# Remove the end quote
772	echo "${item%\"}"
773}
774
775#
776# Return the lines from the specified PPD file matching the specified
777# spec items.
778#
779# Note: this is similar to spec_entry() except the tokens in the
780# spec entry are different.
781#
782# $1	- spec entries from PPD file
783# $2	- spec item
784#
785devid_spec_entry()
786{
787	if debugger ; then
788		set -x
789	fi
790
791	item=$(echo "${1}" | /bin/grep ${2})
792	# Remove everything up to and including the first semi-colon
793	item=${item#*\:}
794	# Remove the end quote
795	echo ${item%\;}
796
797}
798
799#
800# Verifies that the specified PPD file
801#	- has a valid extension
802#	- has the following required spec file entries:
803#		*PPD-Adobe: "4.3"
804#		Manufacturer
805#		Product
806#		ModelName
807#		NickName
808#
809# In addition, the manufacture and model from the IEEE1284 device id
810# information will be gathered here, although it's not an error that
811# it isn't in the PPD file as many don't contain the IEEE1284 info.
812#
813# $1	- Full path to PPD file
814#
815# Return codes:
816#	0	success
817#	1	invalid PPD file
818#
819verify_ppd_file()
820{
821	if debugger ; then
822		set -x
823	fi
824
825	ADOBESPEC="PPD-Adobe"
826	MANUF="Manufacturer"
827	PRODUCT="Product"
828	MODEL="ModelName"
829	NICKNAME="NickName"
830	DEVID="1284DeviceID"
831
832	# Verify the PPD file extension
833	verify_file_ext "${1}" || return 1
834
835	# Query for the required spec items
836	searchentries="^\*${ADOBESPEC}:|^\*${MANUF}:|^\*${PRODUCT}:"
837	searchentries="${searchentries}|^\*${MODEL}:|^\*${NICKNAME}:"
838	searchentries="${searchentries}|^\*${DEVID}:"
839	ppd_info="$(/bin/gzgrep -e "${searchentries}" "${1}")"
840
841	#
842	# Process the query results to verify each of the required spec
843	# file items appears in the PPD file.
844	#
845	for spec_item in ${ADOBESPEC} ${MANUF} ${PRODUCT} ${MODEL} \
846	    ${NICKNAME} ; do
847		entry=$(spec_entry "${ppd_info}" "^\*${spec_item}:")
848		[[ ! -z "${entry}" ]] || return 1
849		case ${spec_item} in
850		${MANUF})
851			realmanuf="${entry}"
852			;;
853		${PRODUCT})
854			product="${entry}"
855			;;
856		${MODEL})
857			model="${entry}"
858			;;
859		${NICKNAME})
860			#
861			# Remove the model and any commas and spaces
862			# which appear before the driver
863			#
864			nickn="${entry#$model[, ]*}"
865			;;
866		esac
867
868	done
869
870	# Save IEEE1284 device id information
871	if $(echo "${ppd_info}" | grep "${DEVID}" >/dev/null 2>&1) ; then
872		DMDL="MDL"
873		DMFG="MFG"
874		devid="$(/bin/gzgrep -e "^[ ]*${DMDL}:|^[ ]*${DMFG}:" "${1}")"
875		devidmdl="$(devid_spec_entry "${devid}" "${DMDL}")"
876		devidmfg="$(devid_spec_entry "${devid}" "${DMFG}")"
877	else
878		devidmdl=
879		devidmfg=
880	fi
881	modmanuf=$(manuf_name_alias "${realmanuf}" ${aliasfile})
882
883	return 0
884}
885
886#
887# generate_cache_file_entry()
888#
889# Returns a cache file entry for the specified PPD file.
890#
891# $1	- modmanuf
892# $2	- model
893# $3	- nickn
894# $4	- devidmfg
895# $5	- devidmdl
896# $6	- Full path to the specified PPD file
897#
898generate_cache_file_entry()
899{
900	if debugger "generate_cache_file_entry" ; then
901		set -x
902	fi
903
904	echo "${1}":"${2}":"${3}":"${4}":"${5}":"${6}"
905}
906
907#
908# Expand specified file to the full path.
909#
910# $1	- File path to expand
911#
912# Return code set to 0 if expanded successfully, otherwise set to 1.
913#
914ppd_pathname()
915{
916	if debugger ; then
917		set -x
918	fi
919
920	if [[ -f "${1}" && -s "${1}" ]] ; then
921		(cd "$(/bin/dirname "${1}")" ; \
922		    echo "$(/bin/pwd)/$(/bin/basename "${1}")") || return 1
923		return 0
924	else
925		return 1
926	fi
927}
928
929#
930# Returns the PPD repsitory path associated with the specified
931# PPD repository name.
932#
933# $1	- Repository name
934#
935get_rep_path()
936{
937	if debugger ; then
938		set -x
939	fi
940
941	case ${1} in
942	${SYSTEM})
943		echo "${SYSTEMREP}"
944		;;
945	${VENDOR})
946		echo "${VENDORREP}"
947		;;
948	${ADMIN})
949		echo "${ADMINREP}"
950		;;
951	${USER})
952		echo "${USERREP}"
953		;;
954	*)
955		echo "${UNSET}"
956		;;
957	esac
958}
959
960#
961# Returns the PPD respository name from the repository path
962#
963# $1	- PPD repository path
964#
965get_rep_name()
966{
967	if debugger ; then
968		set -x
969	fi
970
971	case ${1} in
972	${SYSTEMREP})
973		echo "${SYSTEM}"
974		;;
975	${VENDORREP})
976		echo "${VENDOR}"
977		;;
978	${ADMINREP})
979		echo "${ADMIN}"
980		;;
981	${USERREP})
982		echo "${USER}"
983		;;
984	"all")
985		echo "all"
986		;;
987	*)
988		echo "${UNSET}"
989		;;
990	esac
991}
992
993#
994# Returns 0 if a matching label name is found in the specified repository,
995# otherwise returns 1.
996#
997# $1	- repository path
998# $2	- label name
999#
1000label_path_in_repository()
1001{
1002	if debugger "label_path_in_repository" ; then
1003		set -x
1004	fi
1005
1006	[[ "${1}" != "" && "${2}" != "" ]] || return 1
1007	lpir_rc=1
1008	for repository in ${REPOSITORIES} ; do
1009		if [[ "${repository}" = "${1}" && -d "${1}/${2}" ]] ; then
1010			lpir_rc=0
1011			break
1012		fi
1013	done
1014	return ${lpir_rc}
1015}
1016
1017#
1018# Returns 0 if the source label path is the same
1019# as the destination label path, otherwise returns 1.
1020#
1021# $1	- full path to source PPD file (source label path)
1022# $2	- destination repository path
1023# $3	- destination label name
1024#
1025label_path_match()
1026{
1027	if debugger "label_path_match" ; then
1028		set -x
1029	fi
1030
1031	# dest repository not specified
1032	if [[ "${2}" = "${UNSET}" ]] ; then
1033		# dest label not specified
1034		if [[ "${3}" = "${UNSET}" ]] ; then
1035			#
1036			# We've found a match if the label path is in a known
1037			# repository.
1038			#
1039			lpath="${1%/*/*}"
1040			label_path_in_repository \
1041			    "${1%/*/*/*}" "${lpath##*/}" || return 1
1042		else
1043			#
1044			# If the source label path exists in the
1045			# in a known repository, and the destination
1046			# label is the same as the source label,
1047			# then we'll assume the default destination
1048			# repository is the same as the source
1049			# destination repository.
1050			#
1051			[[ "${1%/*/*}" = "${1%/*/*/*}/${3}" ]] || return 1
1052			label_path_in_repository "${1%/*/*/*}" "${3}" || \
1053			    return 1
1054		fi
1055
1056	# dest repository specified, dest label not specified
1057	elif [[ "${3}" = "${UNSET}" ]] ; then
1058		#
1059		# If the destination repository path is the same as the
1060		# source repository, and if the source label exists in the
1061		# destination repository path, then we'll assume the default
1062		# destination label is the same as the source label.
1063		#
1064		[[ "${2}" = "${1%/*/*/*}" ]] || return 1
1065		lpath="${1%/*/*}"
1066		label_path_in_repository "${2}" "${lpath##*/}" || return 1
1067
1068	# dest repository and dest label specified.
1069	else
1070		#
1071		# We've found a match if the destination and label
1072		# match those of the source label path, and the source
1073		# label path is in a known repository.
1074		#
1075		[[ "${1%/*/*}" = "${2}/${3}" ]] || return 1
1076		label_path_in_repository "${2}" "${3}" || return 1
1077	fi
1078	return 0
1079}
1080
1081#
1082# Returns 0 if specified label name is a reserved label, otherwise
1083# returns 1.
1084#
1085# $1	- label name
1086#
1087reserved_label()
1088{
1089	if debugger ; then
1090		set -x
1091	fi
1092
1093	rl_rc=1
1094	for labelname in ${RESERVEDLABELS} ; do
1095		if [[ "${1}" = "${labelname}" ]] ; then
1096			rl_rc=0
1097			break
1098		fi
1099	done
1100	return ${rl_rc}
1101}
1102
1103#
1104# Returns a list of all labels that exist in a repository that are
1105# not reserved labels.
1106#
1107# $1	- Full path of repository
1108# $2	- Repository name
1109#
1110get_rep_label_list()
1111{
1112	if debugger ; then
1113		set -x
1114	fi
1115
1116	#
1117	# Get a list of all labels that exist in all of the
1118	# PPD file repository.
1119	#
1120	for lname in $(/bin/ls "${1}" 2>/dev/null) ; do
1121		if [[ -d "${1}/${lname}" ]] ; then
1122			if ! reserved_label "${lname}" ; then
1123				echo "${lname} "
1124			fi
1125		fi
1126	done
1127}
1128
1129#
1130# Returns a valid PPD label.
1131#
1132# Verifies the specified PPD label is a valid label.  If the
1133# label is not set, then it is set to a default value.
1134#
1135# Return code set to 0 if the specified PPD label is valid, otherwise 1.
1136#
1137# $1	- PPD label
1138#
1139valid_specified_label()
1140{
1141	if debugger ; then
1142		set -x
1143	fi
1144
1145	# Verify the specified label
1146	vsl_rc=0
1147	case "${1}" in
1148	"all")
1149		# Reserved label name with -a or -g options
1150		if [[ "${action}" = "${ADD}" || \
1151		    "${action}" = "${GENERATEENTRY}" ]] ; then
1152			print -n "$myprog: " 1>&2
1153			gettext "reserved PPD label name: ${1}\n" 1>&2
1154			vsl_rc=1
1155		else
1156			echo "${1}"
1157		fi
1158		;;
1159
1160	"ppdcache" | "caches" | "manufaliases")
1161		# Reserved label names with any option
1162		print -n "$myprog: " 1>&2
1163		gettext "reserved PPD label name: ${1}\n" 1>&2
1164		vsl_rc=1
1165		;;
1166
1167	"" | "${UNSET}")
1168		# Label name not specified.  Set the default label name.
1169		# For -g and -a, default is "user", otherwise, default
1170		# is "all".
1171		if [[ "${action}" = "${ADD}" || \
1172		    "${action}" = "${GENERATEENTRY}" ]] ; then
1173			echo "${USER}"
1174		else
1175			echo "all"
1176		fi
1177		;;
1178
1179	*)
1180		# label cannot be "." or ".."
1181		if [[ "${1}" = "." || "${1}" = ".." ]] ; then
1182			print -n "$myprog: " 1>&2
1183			gettext "PPD label name cannot be " 1>&2
1184			gettext "\".\" or \"..\"\n" 1>&2
1185			vsl_rc=1
1186		fi
1187
1188		# Label name cannot contain special characters
1189		echo "${1}" | /bin/egrep "${SPECIALCHARS}" >/dev/null
1190		if [[ $? -eq 0 ]] ; then
1191			print -n "$myprog: " 1>&2
1192			gettext "PPD label name contains " 1>&2
1193			gettext "an invalid character: ${1}\n" 1>&2
1194			vsl_rc=1
1195		else
1196			echo "${1}"
1197		fi
1198		;;
1199	esac
1200	return ${vsl_rc}
1201}
1202
1203#
1204# Returns the full path of any variant copy of the source file in
1205# the destination label/repository.
1206#
1207# $1	- Full path to source PPD file
1208# $2	- Full path to destination PPD file
1209#
1210# Return code set to
1211#	0	- Copy doesn't exist
1212#	1	- Duplicate copy exists
1213#	2	- Variant copy exists
1214#
1215variant_copy()
1216{
1217	if debugger ; then
1218		set -x
1219	fi
1220
1221	#
1222	# First make sure there is not a .ppd and a .ppd.gz version
1223	# of the destination file; users should know not to do this.
1224	#
1225	if [[ -e "${2%.gz}" && -e "${2%.gz}.gz" ]] ; then
1226		/bin/rm -f "${2%.gz}" >/dev/null 2>&1
1227	fi
1228
1229	# Use gzcmp to compare PPD files as it can deal with
1230	# gzipped or regular files.
1231	if $(${GZCMP} "${1}" "${2}"* >/dev/null 2>&1) ; then
1232		echo "${2}"*
1233		return 1
1234	elif [[ -e "${2%.gz}" ]] ; then
1235		echo "${2%.gz}"
1236		return 2
1237	elif [[ -e "${2%.gz}.gz" ]] ; then
1238		echo "${2%.gz}.gz"
1239		return 2
1240	else
1241		#
1242		# A PPD file doesn't exist in the destination
1243		# repository under the destination label.
1244		# Just display the source PPD file, ensuring
1245		# it has a gzip extension as we will always
1246		# try to gzip the copy in the destination.
1247		#
1248		if [[ "${1#*.ppd}" = ".gz" ]] ; then
1249			echo "${2}"
1250		else
1251			echo "${2}.gz"
1252		fi
1253		return 0
1254	fi
1255}
1256
1257#
1258# $1	- Directory mode
1259# $2	- Directory owner (i.e., root:lp)
1260# $3	- Directory to create
1261#
1262make_dir()
1263{
1264	if debugger "make_dir" ; then
1265		set -x
1266	fi
1267
1268	[[ ! -d "${3}" ]] || return 0
1269	/bin/mkdir "${3}" >/dev/null 2>&1 || return 1
1270	set_perms ${1} ${2} "${3}"
1271	return 0
1272}
1273
1274#
1275# Remove a ppdmgr generated cache (in /var/lp/ppd/cache)
1276# if it doesn't have an associated label in the repository.
1277#
1278# $1	- Full path to label
1279# $2	- Cache name
1280#
1281remove_unassociated_cache()
1282{
1283	if debugger "remove_unassociated_cache" ; then
1284		set -x
1285	fi
1286
1287	if [[ "${1}" != "${UNSET}" ]] ; then
1288		if [[ -n "${1}" && ! -d "${1}" ]] ; then
1289			#
1290			# The label doesn't exist, so delete
1291			# the associated cache file.
1292			#
1293			/bin/rm -f "${VARCACHES}/${2}" >/dev/null 2>&1
1294		fi
1295	fi
1296}
1297
1298#
1299# Sorted copies of cache files for each label in each PPD repository
1300# are maintained in /var/lp/ppd/caches/<PPD respository>-<label>.
1301# This is done so that changes in delivered cache files can be
1302# detected.  If a difference in cache files is detected, or a
1303# cache file is either added or removed, then we know that
1304# the ppdcache file needs to be updated.
1305#
1306# Get a list of all cache files and compare against the list
1307# of labels in all of the PPD file repositories.  They should
1308# be the same.  If there is a label in one of the PPD file
1309# repositories that doesn't have an associated cache file, then
1310# we don't worry about it now, as that will be resolved when
1311# we update the cache for that label.  However, if there is
1312# a cache file associated with a label that no longer exists, then
1313# remove the cache file.
1314#
1315# $1	- Full path to repository (or "all")
1316# $2	- Label name
1317#
1318update_cache()
1319{
1320	if debugger ; then
1321		set -x
1322	fi
1323
1324	#
1325	# Determine which labels in which PPD repository the
1326	# cache file will be updated for.
1327	#
1328	if [[ "${1}" = "all" ]] ; then
1329		rname="${REPOSITORIES}"
1330	else
1331		rname="${1}"
1332	fi
1333
1334	uc_rc=0
1335	for dstreppath in ${rname} ; do
1336		labellist=
1337		if [[ "${2}" = "all" ]] ; then
1338			dstrepname=$(get_rep_name "${dstreppath}")
1339			labellist=$(get_rep_label_list "${dstreppath}" \
1340			    "${dstrepname}")
1341		else
1342
1343			# Ensure the label exists in the PPD file repository.
1344			if [[ -d "${dstreppath}/${2}" ]] ; then
1345				labellist="${2}"
1346			fi
1347		fi
1348
1349		#
1350		# Update the cache for each label in the PPD repository
1351		#
1352		for dstlabel in ${labellist} ; do
1353			ulc_msg=$(update_label_cache "${dstreppath}" \
1354			    "${dstrepname}" "${dstlabel}")
1355			if [[ $? -ne 0 ]] ; then
1356				echo "${ulc_msg}"
1357				uc_rc=1
1358			fi
1359		done
1360	done
1361
1362	# Update the golden cache file.
1363	update_golden_cache
1364	return ${uc_rc}
1365}
1366
1367# $1	- exit status
1368ppdmgr_exit()
1369{
1370	if debugger "ppdmgr_exit" ; then
1371		set -x
1372	fi
1373
1374	/bin/rm -rf "${ppdmgrtmpdir}" >/dev/null 2>&1
1375	exit ${1}
1376}
1377
1378
1379usage()
1380{
1381	gettext "usage:\n" 1>&2
1382	print -n "\t$myprog: " 1>&2
1383	gettext "-a <ppd_filename_path> [ -L <label> ]\n" 1>&2
1384	gettext "\t\t[ -R <ppd_repository> ] [-w]\n" 1>&2
1385	print -n "\t$myprog: " 1>&2
1386	gettext "-r [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2
1387	print -n "\t$myprog: " 1>&2
1388	gettext "-u [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2
1389
1390	ppdmgr_exit ${FAIL}
1391}
1392
1393##########################################################################
1394# main
1395##########################################################################
1396
1397myprog=$(/bin/basename $0)
1398
1399SaveIFS="$IFS"
1400NoSpaceTabIFS='
1401'
1402
1403# Updatable PPD repository
1404VARDIR=/var/lp/ppd
1405
1406# Delivered PPD respository
1407SYSTEMREP=/usr/share/ppd
1408ADMINREP=/usr/local/share/ppd
1409VENDORREP=/opt/share/ppd
1410USERREP=${VARDIR}
1411
1412RESERVEDREPS="${SYSTEMREP} ${ADMINREP} ${VENDORREP}"
1413REPOSITORIES="${USERREP} ${RESERVEDREPS}"
1414RESERVEDLABELS="all caches ppdcache manufaliases"
1415
1416# Directory where system:SUNWfoomatic is delivered
1417FOOCACHEDIR=/usr/lib/lp/caches
1418
1419# Deliveries
1420SYSTEM=system
1421VENDOR=vendor
1422ADMIN=admin
1423USER=user
1424
1425# System PPD cache name used by printmgr
1426GOLDCACHE=${USERREP}/ppdcache
1427
1428# Delivered caches directory
1429CACHES=caches
1430MANUFALIASES=manufaliases
1431
1432# Updated caches directory
1433VARCACHES=${VARDIR}/${CACHES}
1434
1435# valid PPD file name extensions
1436PEXT=ppd
1437GEXT=gz
1438FILEEXTS=".${PEXT} .${PEXT}.${GEXT}"
1439
1440# Default modes and owners
1441DIRMODE=755
1442DIROWNER=root:lp
1443ADMINOWNER=root:root
1444FILEMODE=444
1445FILEOWNER=root:lp
1446
1447# ppdmgr actions
1448ADD=add
1449GENERATEENTRY=generateentry
1450UPDATE=update
1451REBUILD=rebuild
1452
1453SUCCESS=0
1454FAIL=1
1455WARN=2
1456
1457MAXLABELNAME=256
1458GZIP="/bin/gzip -c"
1459GZCMP="/bin/gzcmp -s"
1460CMP="/bin/cmp -s"
1461SPECIALCHARS=":"
1462SEP=":"
1463
1464debug=0
1465wflag=0
1466status=${SUCCESS}
1467
1468UNSET=""
1469ppdlabel=${UNSET}
1470ppdrepname=${UNSET}
1471ppdreppath=${UNSET}
1472modmanuf=
1473model=
1474nickn=
1475devidmdl=
1476devidmfg=
1477
1478ppdmgrtmpdir=$(/usr/bin/mktemp -t -d ppdmgr.XXXXXX)
1479if [ -z "$ppdmgrtmpdir" ] ; then
1480	print -n "$myprog: " 1>&2
1481	gettext "Fatal error: could not create temporary directory\n" 1>&2
1482	exit 1
1483fi
1484
1485aliasfile=${USERREP}/manufaliases
1486tmpfilepath=
1487
1488
1489OPTS=a:g:L:rR:uwZ
1490while getopts "$OPTS" arg ; do
1491	case ${arg} in
1492	a)	# add PPD file
1493		action=${ADD}
1494		origsrcppdpath=${OPTARG}
1495		;;
1496
1497	g)	# create cache entry
1498		action=${GENERATEENTRY}
1499		origsrcppdpath=${OPTARG}
1500		;;
1501
1502	L)	# PPD label name
1503		ppdlabel=${OPTARG}
1504		;;
1505
1506	r)	# rebuild cache
1507		action=${REBUILD}
1508		;;
1509
1510	R)	# PPD file repository to use
1511		ppdrepname=${OPTARG}
1512		;;
1513
1514	u)	# update cache
1515		action=${UPDATE}
1516		;;
1517
1518	w)	# display PPD file path
1519		wflag=1
1520		;;
1521
1522	Z)	# debug
1523		debug=1
1524		;;
1525
1526	?)
1527		usage
1528		;;
1529	esac
1530done
1531
1532if debugger "Main" ; then
1533	set -x
1534fi
1535
1536if [[ $# -lt 1 || -z "${action}" ]] ; then
1537	usage
1538fi
1539
1540# ignore wflag unless specified with -a
1541if [[ ${wflag} -eq 1 && "${action}" != ${ADD} ]] ; then
1542	wflag=0
1543fi
1544
1545#
1546# Ensure the destination PPD repository directory is set
1547# to match the specified repository.  If the
1548# destination PPD file repository was specified, then
1549# it must be one of the following:
1550# 	"user"
1551#	"admin"
1552#	"vendor"
1553#	"system"
1554#	"all"
1555#
1556case "${ppdrepname}" in
1557"${SYSTEM}")
1558	ppdreppath="${SYSTEMREP}"
1559	;;
1560"${ADMIN}")
1561	ppdreppath="${ADMINREP}"
1562	;;
1563"${VENDOR}")
1564	ppdreppath="${VENDORREP}"
1565	;;
1566"${USER}")
1567	ppdreppath="${USERREP}"
1568	;;
1569"all")
1570	if [[ "${action}" = "${ADD}" || \
1571	    "${action}" = "${GENERATEENTRY}" ]] ; then
1572		print -n "$myprog: " 1>&2
1573		gettext "reserved PPD repository name: " 1>&2
1574		gettext "${ppdrepname}\n" 1>&2
1575		ppdmgr_exit ${FAIL}
1576	fi
1577	ppdreppath="all"
1578	;;
1579"${UNSET}"|"")
1580	ppdreppath="${UNSET}"
1581	;;
1582
1583*)
1584	print -n "$myprog: " 1>&2
1585	gettext "invalid PPD repository name: ${ppdrepname}\n" 1>&2
1586	ppdmgr_exit ${FAIL}
1587	;;
1588esac
1589
1590#
1591# When a source PPD file's path is from a known repository, the
1592# destination repository and desination label are assumed to be the
1593# same as the source PPD file's unless a differing repository or label
1594# was specified.
1595#
1596if [[ "${action}" = "${ADD}" || "${action}" = "${GENERATEENTRY}" ]] ; then
1597
1598	srcppdpath=$(ppd_pathname "${origsrcppdpath}")
1599	ppd_pathname_rc=$?
1600	if [[ ${ppd_pathname_rc} -ne 0 ]] ; then
1601		print -n "$myprog: " 1>&2
1602		gettext "invalid PPD file: ${origsrcppdpath}\n" 1>&2
1603		ppdmgr_exit ${ppd_pathname_rc}
1604	fi
1605
1606	# Path cannot contain special characters
1607	echo "${srcppdpath}" | /bin/egrep  "${SPECIALCHARS}" >/dev/null
1608	if [[ $? -eq 0 ]] ; then
1609		print -n "$myprog: " 1>&2
1610		gettext "PPD path contains " 1>&2
1611		gettext "an invalid character: ${ppd_pathname}\n" 1>&2
1612		ppdmgr_exit ${FAIL}
1613	fi
1614	ppdfname=$(/bin/basename "${origsrcppdpath}")
1615
1616	#
1617	# Check to see if there's any work to be done.  If the source file
1618	# is already in the destination repository under the destination
1619	# label, then there's nothing left to do.  We exit rather than
1620	# going on to do an update on the label in the repository as
1621	# it could possible take a long time to update.  If an add was
1622	# requested, it could have come from an application, so we want
1623	# to return quickly.
1624	#
1625	if label_path_match "${srcppdpath}" "${ppdreppath}" "${ppdlabel}" ; then
1626		if [[ ${wflag} -eq 1 || \
1627		    "${action}" = "${GENERATEENTRY}" ]] ; then
1628			echo "${srcppdpath}"
1629		fi
1630		ppdmgr_exit ${SUCCESS}
1631	fi
1632fi
1633
1634ppdlabel=$(valid_specified_label "${ppdlabel}")
1635if [[ $? -ne 0 ]] ; then
1636	ppdmgr_exit ${FAIL}
1637fi
1638
1639if [[ "${ppdreppath}" = "${UNSET}" ]] ; then
1640	ppdreppath="${USERREP}"
1641fi
1642
1643dstrepname=$(get_rep_name "${ppdreppath}")
1644
1645case "${action}" in
1646"${ADD}")
1647	#
1648	# Attempt to add the PPD file to the repository under the
1649	# specified label.  If any errors occur, final_dst_ppd_path
1650	# will contain the error message rather than the path to the
1651	# PPD file.
1652	#
1653	final_dst_ppd_path=$(add_ppd "${srcppdpath}" "${ppdfname}" \
1654	    "${ppdreppath}" "${dstrepname}" "${ppdlabel}")
1655	add_ppd_rc=$?
1656	case ${add_ppd_rc} in
1657	0)	#
1658		# The PPD file was added.  Update the specified
1659		# cache associated with the label if the PPD file
1660		# was added successfully and was not a duplicate.
1661		# Ensure any changes are also reflected in the
1662		# golden cache.
1663		#
1664		add_ppd_msg=$(update_label_cache "${ppdreppath}" \
1665		    "${dstrepname}" "${ppdlabel}")
1666		apm_rc=$?
1667
1668		echo "${add_ppd_msg}" | /bin/grep "${final_dst_ppd_path}"
1669		path_in_msg=$?
1670
1671		#
1672		# Only report cache update errors if the file that was
1673		# added was one that was reported as not being added
1674		# to the cache.  This really should happen as the file
1675		# was verified during the add.
1676		#
1677		if [[ ${apm_rc} -ne 0 && ${path_in_msg} -eq 0 ]] ; then
1678			print -n "$myprog: " 1>&2
1679			gettext "printer information does not reflect " 1>&2
1680			gettext "the\nfollowing PPD file(s):\n" 1>&2
1681			print "${add_ppd_msg}" 1>&2
1682			status=${FAIL}
1683		else
1684			update_golden_cache
1685
1686			#
1687			# Display the full path to the added PPD file,
1688			# if requested (-w).
1689			#
1690			if [[ ${wflag} -eq 1 ]] ; then
1691				print "${final_dst_ppd_path}"
1692			fi
1693		fi
1694		;;
1695
1696	1)	# Duplicate copy exists
1697		if [[ ${wflag} -eq 1 ]] ; then
1698			print "${final_dst_ppd_path}"
1699		fi
1700		;;
1701
1702	2)	# Varying copy exists
1703		print -n "$myprog: " 1>&2
1704		gettext "differing variant of source PPD file " 1>&2
1705		gettext "already exists at\n" 1>&2
1706		gettext "${final_dst_ppd_path}\n" 1>&2
1707		status=${FAIL}
1708		;;
1709	*)	# The PPD file was not added as a problem occurred.
1710		# Display the error message.
1711		print -n "$myprog: " 1>&2
1712		print "${final_dst_ppd_path}" 1>&2
1713		status=${FAIL}
1714		;;
1715
1716	esac
1717	;;
1718
1719"${GENERATEENTRY}")
1720	#
1721	# Create a cache file entry for the specified PPD file and
1722	# display it on standard out.
1723	#
1724	verify_ppd_file "${srcppdpath}"
1725	if [[ $? -eq 0 ]] ; then
1726		dstdir="${ppdreppath}/${ppdlabel}/${modmanuf}"
1727		final_dst_path="${dstdir}/$(/bin/basename ${srcppdpath})"
1728		verify_ppd_location "${final_dst_path}"
1729		if [[ $? -eq 0 ]] ; then
1730			# Generate the cache file entry
1731			print "$(generate_cache_file_entry "${modmanuf}" \
1732			    "${model}" "${nickn}" "${devidmfg}" "${devidmdl}" \
1733			    "${final_dst_path}")"
1734		else
1735			print -n "$myprog: " 1>&2
1736			gettext "PPD file not in valid location\n" 1>&2
1737			gettext \
1738	    "(<repository>/<label>/<manufacturer>/<PPD file>):\n\t${1}\n" 1>&2
1739			status=${FAIL}
1740		fi
1741
1742	else
1743		print -n "$myprog: " 1>&2
1744		gettext "invalid PPD file: ${1}\n" 1>&2
1745		status=${FAIL}
1746	fi
1747	;;
1748
1749"${REBUILD}" | "${UPDATE}")
1750	update_msg=$(update_cache "${ppdreppath}" "${ppdlabel}")
1751	if [[ $? -ne 0 ]] ; then
1752		print -n "$myprog: " 1>&2
1753		gettext "printer information does not reflect " 1>&2
1754		gettext "the\nfollowing PPD file(s):\n" 1>&2
1755		print "${update_msg}" 1>&2
1756		status=${WARN}
1757	fi
1758	;;
1759
1760*)
1761	usage
1762	;;
1763esac
1764
1765ppdmgr_exit ${status}
1766