summaryrefslogtreecommitdiff
blob: 58efcf1aa2b6683eacc4051bbbd77398ad15c5ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
# Copyright 1999-2015 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$

# @ECLASS: cvs.eclass
# @MAINTAINER:
# vapier@gentoo.org (and anyone who wants to help)
# @BLURB: This eclass provides generic cvs fetching functions
# @DESCRIPTION:
# This eclass provides the generic cvs fetching functions. To use this from an
# ebuild, set the ECLASS VARIABLES as specified below in your ebuild before
# inheriting. Then either leave the default src_unpack or extend over
# cvs_src_unpack. If you find that you need to call the cvs_* functions
# directly, I'd be interested to hear about it.

if [[ -z ${_CVS_ECLASS} ]]; then
_CVS_ECLASS=1

inherit eutils

# TODO:

# Implement more auth types (gserver?, kserver?)

# Support additional remote shells with `ext' authentication (does
# anyone actually need to use it with anything other than SSH?)


# Users shouldn't change these settings!  The ebuild/eclass inheriting
# this eclass will take care of that.  If you want to set the global
# KDE cvs ebuilds' settings, see the comments in kde-source.eclass.

# @ECLASS-VARIABLE: ECVS_CVS_COMPRESS
# @DESCRIPTION:
# Set the default compression level.  Has no effect when ECVS_CVS_COMMAND
# is defined by ebuild/user.
: ${ECVS_CVS_COMPRESS:=-z1}

# @ECLASS-VARIABLE: ECVS_CVS_OPTIONS
# @DESCRIPTION:
# Additional options to the cvs commands.  Has no effect when ECVS_CVS_COMMAND
# is defined by ebuild/user.
: ${ECVS_CVS_OPTIONS:=-q -f}

# @ECLASS-VARIABLE: ECVS_CVS_COMMAND
# @DESCRIPTION:
# CVS command to run
#
# You can set, for example, "cvs -t" for extensive debug information
# on the cvs connection.  The default of "cvs -q -f -z4" means to be
# quiet, to disregard the ~/.cvsrc config file and to use maximum
# compression.
: ${ECVS_CVS_COMMAND:=cvs ${ECVS_CVS_OPTIONS} ${ECVS_CVS_COMPRESS}}

# @ECLASS-VARIABLE: ECVS_UP_OPTS
# @DESCRIPTION:
# CVS options given after the cvs update command. Don't remove "-dP" or things
# won't work.
: ${ECVS_UP_OPTS:=-dP}

# @ECLASS-VARIABLE: ECVS_CO_OPTS
# @DEFAULT_UNSET
# @DESCRIPTION:
# CVS options given after the cvs checkout command.

# @ECLASS-VARIABLE: ECVS_OFFLINE
# @DESCRIPTION:
# Set this variable to a non-empty value to disable the automatic updating of
# a CVS source tree. This is intended to be set outside the cvs source
# tree by users.
: ${ECVS_OFFLINE:=${EVCS_OFFLINE}}

# @ECLASS-VARIABLE: ECVS_LOCAL
# @DEFAULT_UNSET
# @DESCRIPTION:
# If this is set, the CVS module will be fetched non-recursively.
# Refer to the information in the CVS man page regarding the -l
# command option (not the -l global option).

# @ECLASS-VARIABLE: ECVS_LOCALNAME
# @DEFAULT_UNSET
# @DESCRIPTION:
# Local name of checkout directory
#
# This is useful if the module on the server is called something
# common like 'driver' or is nested deep in a tree, and you don't like
# useless empty directories.
#
# WARNING: Set this only from within ebuilds!  If set in your shell or
# some such, things will break because the ebuild won't expect it and
# have e.g. a wrong $S setting.

# @ECLASS-VARIABLE: ECVS_TOP_DIR
# @DESCRIPTION:
# The directory under which CVS modules are checked out.
: ${ECVS_TOP_DIR:="${PORTAGE_ACTUAL_DISTDIR-${DISTDIR}}/cvs-src"}

# @ECLASS-VARIABLE: ECVS_SERVER
# @DESCRIPTION:
# CVS path
#
# The format is "server:/dir", e.g. "anoncvs.kde.org:/home/kde".
# Remove the other parts of the full CVSROOT, which might look like
# ":pserver:anonymous@anoncvs.kde.org:/home/kde"; this is generated
# using other settings also.
#
# Set this to "offline" to disable fetching (i.e. to assume the module
# is already checked out in ECVS_TOP_DIR).
: ${ECVS_SERVER:="offline"}

# @ECLASS-VARIABLE: ECVS_MODULE
# @REQUIRED
# @DESCRIPTION:
# The name of the CVS module to be fetched
#
# This must be set when cvs_src_unpack is called.  This can include
# several directory levels, i.e. "foo/bar/baz"
#[[ -z ${ECVS_MODULE} ]] && die "$ECLASS: error: ECVS_MODULE not set, cannot continue"

# @ECLASS-VARIABLE: ECVS_DATE
# @DEFAULT_UNSET
# @DESCRIPTION:
# The date of the checkout.  See the -D date_spec option in the cvs
# man page for more details.

# @ECLASS-VARIABLE: ECVS_BRANCH
# @DEFAULT_UNSET
# @DESCRIPTION:
# The name of the branch/tag to use
#
# The default is "HEAD".  The following default _will_ reset your
# branch checkout to head if used.
#: ${ECVS_BRANCH:="HEAD"}

# @ECLASS-VARIABLE: ECVS_AUTH
# @DESCRIPTION:
# Authentication method to use
#
# Possible values are "pserver" and "ext".  If `ext' authentication is
# used, the remote shell to use can be specified in CVS_RSH (SSH is
# used by default).  Currently, the only supported remote shell for
# `ext' authentication is SSH.
#
# Armando Di Cianno <fafhrd@gentoo.org> 2004/09/27
# - Added "no" as a server type, which uses no AUTH method, nor
#    does it login
#  e.g.
#   "cvs -danoncvs@savannah.gnu.org:/cvsroot/backbone co System"
#   ( from gnustep-apps/textedit )
: ${ECVS_AUTH:="pserver"}

# @ECLASS-VARIABLE: ECVS_USER
# @DESCRIPTION:
# Username to use for authentication on the remote server.
: ${ECVS_USER:="anonymous"}

# @ECLASS-VARIABLE: ECVS_PASS
# @DEFAULT_UNSET
# @DESCRIPTION:
# Password to use for authentication on the remote server

# @ECLASS-VARIABLE: ECVS_SSH_HOST_KEY
# @DEFAULT_UNSET
# @DESCRIPTION:
# If SSH is used for `ext' authentication, use this variable to
# specify the host key of the remote server.  The format of the value
# should be the same format that is used for the SSH known hosts file.
#
# WARNING: If a SSH host key is not specified using this variable, the
# remote host key will not be verified.

# @ECLASS-VARIABLE: ECVS_CLEAN
# @DEFAULT_UNSET
# @DESCRIPTION:
# Set this to get a clean copy when updating (passes the
# -C option to cvs update)

# @ECLASS-VARIABLE: ECVS_RUNAS
# @DEFAULT_UNSET
# @DESCRIPTION:
# Specifies an alternate (non-root) user to use to run cvs.  Currently
# b0rked and wouldn't work with portage userpriv anyway without
# special magic.

# : ${ECVS_RUNAS:=$(whoami)}

# add cvs to deps
# ssh is used for ext auth
# sudo is used to run as a specified user
DEPEND="dev-vcs/cvs"

[[ -n ${ECVS_RUNAS} ]] && DEPEND+=" app-admin/sudo"

if [[ ${ECVS_AUTH} == "ext" ]] ; then
	#default to ssh
	[[ -z ${CVS_RSH} ]] && export CVS_RSH="ssh"
	if [[ ${CVS_RSH} != "ssh" ]] ; then
		die "Support for ext auth with clients other than ssh has not been implemented yet"
	fi
	DEPEND+=" net-misc/openssh"
fi

# called from cvs_src_unpack
cvs_fetch() {
	has "${EAPI:-0}" 0 1 2 && ! use prefix && EPREFIX=

	# Make these options local variables so that the global values are
	# not affected by modifications in this function.

	local ECVS_COMMAND=${ECVS_COMMAND}
	local ECVS_UP_OPTS=${ECVS_UP_OPTS}
	local ECVS_CO_OPTS=${ECVS_CO_OPTS}

	debug-print-function ${FUNCNAME} "$@"

	# Update variables that are modified by ebuild parameters, which
	# should be effective every time cvs_fetch is called, and not just
	# every time cvs.eclass is inherited

	# Handle parameter for local (non-recursive) fetching

	if [[ -n ${ECVS_LOCAL} ]] ; then
		ECVS_UP_OPTS+=" -l"
		ECVS_CO_OPTS+=" -l"
	fi

	# Handle ECVS_BRANCH option
	#
	# Because CVS auto-switches branches, we just have to pass the
	# correct -rBRANCH option when updating.

	if [[ -n ${ECVS_BRANCH} ]] ; then
		ECVS_UP_OPTS+=" -r${ECVS_BRANCH}"
		ECVS_CO_OPTS+=" -r${ECVS_BRANCH}"
	fi

	# Handle ECVS_LOCALNAME, which specifies the local directory name
	# to use.  Note that the -d command option is not equivalent to
	# the global -d option.

	if [[ ${ECVS_LOCALNAME} != "${ECVS_MODULE}" ]] ; then
		ECVS_CO_OPTS+=" -d ${ECVS_LOCALNAME}"
	fi

	if [[ -n ${ECVS_CLEAN} ]] ; then
		ECVS_UP_OPTS+=" -C"
	fi

	if [[ -n ${ECVS_DATE} ]] ; then
		ECVS_CO_OPTS+=" -D ${ECVS_DATE}"
		ECVS_UP_OPTS+=" -D ${ECVS_DATE}"
	fi

	# It would be easiest to always be in "run-as mode", logic-wise,
	# if sudo didn't ask for a password even when sudo'ing to `whoami`.

	if [[ -z ${ECVS_RUNAS} ]] ; then
		run=""
	else
		run="sudo -u ${ECVS_RUNAS}"
	fi

	# Create the top dir if needed

	if [[ ! -d ${ECVS_TOP_DIR} ]] ; then
		# Note that the addwrite statements in this block are only
		# there to allow creating ECVS_TOP_DIR; we allow writing
		# inside it separately.

		# This is because it's simpler than trying to find out the
		# parent path of the directory, which would need to be the
		# real path and not a symlink for things to work (so we can't
		# just remove the last path element in the string)

		debug-print "${FUNCNAME}: checkout mode. creating cvs directory"
		addwrite /foobar
		addwrite /
		${run} mkdir -p "/${ECVS_TOP_DIR}"
		export SANDBOX_WRITE="${SANDBOX_WRITE//:\/foobar:\/}"
	fi

	# In case ECVS_TOP_DIR is a symlink to a dir, get the real path,
	# otherwise addwrite() doesn't work.

	cd -P "${ECVS_TOP_DIR}" >/dev/null
	ECVS_TOP_DIR=$(pwd)

	# Disable the sandbox for this dir
	addwrite "${ECVS_TOP_DIR}"

	# Chown the directory and all of its contents
	if [[ -n ${ECVS_RUNAS} ]] ; then
		${run} chown -R "${ECVS_RUNAS}" "/${ECVS_TOP_DIR}"
	fi

	# Determine the CVS command mode (checkout or update)
	if [[ ! -d ${ECVS_TOP_DIR}/${ECVS_LOCALNAME}/CVS ]] ; then
		mode=checkout
	else
		mode=update
	fi

	# Our server string (i.e. CVSROOT) without the password so it can
	# be put in Root
	local connection="${ECVS_AUTH}"
	if [[ ${ECVS_AUTH} == "no" ]] ; then
		local server="${ECVS_USER}@${ECVS_SERVER}"
	else
		[[ -n ${ECVS_PROXY} ]] && connection+=";proxy=${ECVS_PROXY}"
		[[ -n ${ECVS_PROXY_PORT} ]] && connection+=";proxyport=${ECVS_PROXY_PORT}"
		local server=":${connection}:${ECVS_USER}@${ECVS_SERVER}"
	fi

	# Switch servers automagically if needed
	if [[ ${mode} == "update" ]] ; then
		cd "/${ECVS_TOP_DIR}/${ECVS_LOCALNAME}"
		local oldserver=$(${run} cat CVS/Root)
		if [[ ${server} != "${oldserver}" ]] ; then
			einfo "Changing the CVS server from ${oldserver} to ${server}:"
			debug-print "${FUNCNAME}: Changing the CVS server from ${oldserver} to ${server}:"

			einfo "Searching for CVS directories ..."
			local cvsdirs=$(${run} find . -iname CVS -print)
			debug-print "${FUNCNAME}: CVS directories found:"
			debug-print "${cvsdirs}"

			einfo "Modifying CVS directories ..."
			local x
			for x in ${cvsdirs} ; do
				debug-print "In ${x}"
				${run} echo "${server}" > "${x}/Root"
			done
		fi
	fi

	# Prepare a cvspass file just for this session, we don't want to
	# mess with ~/.cvspass
	touch "${T}/cvspass"
	export CVS_PASSFILE="${T}/cvspass"
	if [[ -n ${ECVS_RUNAS} ]] ; then
		chown "${ECVS_RUNAS}" "${T}/cvspass"
	fi

	# The server string with the password in it, for login (only used for pserver)
	cvsroot_pass=":${connection}:${ECVS_USER}:${ECVS_PASS}@${ECVS_SERVER}"

	# Ditto without the password, for checkout/update after login, so
	# that the CVS/Root files don't contain the password in plaintext
	if [[ ${ECVS_AUTH} == "no" ]] ; then
		cvsroot_nopass="${ECVS_USER}@${ECVS_SERVER}"
	else
		cvsroot_nopass=":${connection}:${ECVS_USER}@${ECVS_SERVER}"
	fi

	# Commands to run
	cmdlogin="${run} ${ECVS_CVS_COMMAND} -d \"${cvsroot_pass}\" login"
	cmdupdate="${run} ${ECVS_CVS_COMMAND} -d \"${cvsroot_nopass}\" update ${ECVS_UP_OPTS} ${ECVS_LOCALNAME}"
	cmdcheckout="${run} ${ECVS_CVS_COMMAND} -d \"${cvsroot_nopass}\" checkout ${ECVS_CO_OPTS} ${ECVS_MODULE}"

	# Execute commands

	cd "${ECVS_TOP_DIR}"
	if [[ ${ECVS_AUTH} == "pserver" ]] ; then
		einfo "Running ${cmdlogin}"
		eval ${cmdlogin} || die "cvs login command failed"
		if [[ ${mode} == "update" ]] ; then
			einfo "Running ${cmdupdate}"
			eval ${cmdupdate} || die "cvs update command failed"
		elif [[ ${mode} == "checkout" ]] ; then
			einfo "Running ${cmdcheckout}"
			eval ${cmdcheckout} || die "cvs checkout command failed"
		fi
	elif [[ ${ECVS_AUTH} == "ext" || ${ECVS_AUTH} == "no" ]] ; then
		# Hack to support SSH password authentication

		# Backup environment variable values
		local CVS_ECLASS_ORIG_CVS_RSH="${CVS_RSH}"

		if [[ ${SSH_ASKPASS+set} == "set" ]] ; then
			local CVS_ECLASS_ORIG_SSH_ASKPASS="${SSH_ASKPASS}"
		else
			unset CVS_ECLASS_ORIG_SSH_ASKPASS
		fi

		if [[ ${DISPLAY+set} == "set" ]] ; then
			local CVS_ECLASS_ORIG_DISPLAY="${DISPLAY}"
		else
			unset CVS_ECLASS_ORIG_DISPLAY
		fi

		if [[ ${CVS_RSH} == "ssh" ]] ; then
			# Force SSH to use SSH_ASKPASS by creating python wrapper

			export CVS_RSH="${T}/cvs_sshwrapper"
			cat > "${CVS_RSH}"<<EOF
#!${EPREFIX}/usr/bin/python
import fcntl
import os
import sys
try:
	fd = os.open('/dev/tty', 2)
	TIOCNOTTY=0x5422
	try:
		fcntl.ioctl(fd, TIOCNOTTY)
	except:
		pass
	os.close(fd)
except:
	pass
newarglist = sys.argv[:]
EOF

			# disable X11 forwarding which causes .xauth access violations
			# - 20041205 Armando Di Cianno <fafhrd@gentoo.org>
			echo "newarglist.insert(1, '-oClearAllForwardings=yes')" \
				>> "${CVS_RSH}"
			echo "newarglist.insert(1, '-oForwardX11=no')" \
				>> "${CVS_RSH}"

			# Handle SSH host key checking

			local CVS_ECLASS_KNOWN_HOSTS="${T}/cvs_ssh_known_hosts"
			echo "newarglist.insert(1, '-oUserKnownHostsFile=${CVS_ECLASS_KNOWN_HOSTS}')" \
				>> "${CVS_RSH}"

			if [[ -z ${ECVS_SSH_HOST_KEY} ]] ; then
				ewarn "Warning: The SSH host key of the remote server will not be verified."
				einfo "A temporary known hosts list will be used."
				local CVS_ECLASS_STRICT_HOST_CHECKING="no"
				touch "${CVS_ECLASS_KNOWN_HOSTS}"
			else
				local CVS_ECLASS_STRICT_HOST_CHECKING="yes"
				echo "${ECVS_SSH_HOST_KEY}" > "${CVS_ECLASS_KNOWN_HOSTS}"
			fi

			echo -n "newarglist.insert(1, '-oStrictHostKeyChecking=" \
				>> "${CVS_RSH}"
			echo "${CVS_ECLASS_STRICT_HOST_CHECKING}')" \
				>> "${CVS_RSH}"
			echo "os.execv('${EPREFIX}/usr/bin/ssh', newarglist)" \
				>> "${CVS_RSH}"

			chmod a+x "${CVS_RSH}"

			# Make sure DISPLAY is set (SSH will not use SSH_ASKPASS
			# if DISPLAY is not set)

			: ${DISPLAY:="DISPLAY"}
			export DISPLAY

			# Create a dummy executable to echo ${ECVS_PASS}

			export SSH_ASKPASS="${T}/cvs_sshechopass"
			if [[ ${ECVS_AUTH} != "no" ]] ; then
				echo -en "#!/bin/bash\necho \"${ECVS_PASS}\"\n" \
					> "${SSH_ASKPASS}"
			else
				echo -en "#!/bin/bash\nreturn\n" \
					> "${SSH_ASKPASS}"
			fi
			chmod a+x "${SSH_ASKPASS}"
		fi

		if [[ ${mode} == "update" ]] ; then
			einfo "Running ${cmdupdate}"
			eval ${cmdupdate} || die "cvs update command failed"
		elif [[ ${mode} == "checkout" ]] ; then
			einfo "Running ${cmdcheckout}"
			eval ${cmdcheckout} || die "cvs checkout command failed"
		fi

		# Restore environment variable values
		export CVS_RSH="${CVS_ECLASS_ORIG_CVS_RSH}"
		if [[ ${CVS_ECLASS_ORIG_SSH_ASKPASS+set} == "set" ]] ; then
			export SSH_ASKPASS="${CVS_ECLASS_ORIG_SSH_ASKPASS}"
		else
			unset SSH_ASKPASS
		fi

		if [[ ${CVS_ECLASS_ORIG_DISPLAY+set} == "set" ]] ; then
			export DISPLAY="${CVS_ECLASS_ORIG_DISPLAY}"
		else
			unset DISPLAY
		fi
	fi

	# Restore ownership.  Not sure why this is needed, but someone
	# added it in the orig ECVS_RUNAS stuff.
	if [[ -n ${ECVS_RUNAS} ]] ; then
		chown $(whoami) "${T}/cvspass"
	fi

}

# @FUNCTION: cvs_src_unpack
# @DESCRIPTION:
# The cvs src_unpack function, which will be exported
cvs_src_unpack() {

	debug-print-function ${FUNCNAME} "$@"

	debug-print "${FUNCNAME}: init:
	ECVS_CVS_COMMAND=${ECVS_CVS_COMMAND}
	ECVS_UP_OPTS=${ECVS_UP_OPTS}
	ECVS_CO_OPTS=${ECVS_CO_OPTS}
	ECVS_TOP_DIR=${ECVS_TOP_DIR}
	ECVS_SERVER=${ECVS_SERVER}
	ECVS_USER=${ECVS_USER}
	ECVS_PASS=${ECVS_PASS}
	ECVS_MODULE=${ECVS_MODULE}
	ECVS_LOCAL=${ECVS_LOCAL}
	ECVS_RUNAS=${ECVS_RUNAS}
	ECVS_LOCALNAME=${ECVS_LOCALNAME}"

	[[ -z ${ECVS_MODULE} ]] && die "ERROR: CVS module not set, cannot continue."

	local ECVS_LOCALNAME=${ECVS_LOCALNAME:-${ECVS_MODULE}}

	local sanitized_pn=$(echo "${PN}" | LC_ALL=C sed -e 's:[^A-Za-z0-9_]:_:g')
	local offline_pkg_var="ECVS_OFFLINE_${sanitized_pn}"
	if [[ -n ${!offline_pkg_var}${ECVS_OFFLINE} ]] || [[ ${ECVS_SERVER} == "offline" ]] ; then
		# We're not required to fetch anything; the module already
		# exists and shouldn't be updated.
		if [[ -d ${ECVS_TOP_DIR}/${ECVS_LOCALNAME} ]] ; then
			debug-print "${FUNCNAME}: offline mode"
		else
			debug-print "${FUNCNAME}: Offline mode specified but directory ${ECVS_TOP_DIR}/${ECVS_LOCALNAME} not found, exiting with error"
			die "ERROR: Offline mode specified, but directory ${ECVS_TOP_DIR}/${ECVS_LOCALNAME} not found. Aborting."
		fi
	elif [[ -n ${ECVS_SERVER} ]] ; then # ECVS_SERVER!=offline --> real fetching mode
		einfo "Fetching CVS module ${ECVS_MODULE} into ${ECVS_TOP_DIR} ..."
		cvs_fetch
	else # ECVS_SERVER not set
		die "ERROR: CVS server not specified, cannot continue."
	fi

	einfo "Copying ${ECVS_MODULE} from ${ECVS_TOP_DIR} ..."
	debug-print "Copying module ${ECVS_MODULE} local_mode=${ECVS_LOCAL} from ${ECVS_TOP_DIR} ..."

	# This is probably redundant, but best to make sure.
	mkdir -p "${WORKDIR}/${ECVS_LOCALNAME}"

	if [[ -n ${ECVS_LOCAL} ]] ; then
		cp -f "${ECVS_TOP_DIR}/${ECVS_LOCALNAME}"/* "${WORKDIR}/${ECVS_LOCALNAME}"
	else
		cp -Rf "${ECVS_TOP_DIR}/${ECVS_LOCALNAME}" "${WORKDIR}/${ECVS_LOCALNAME}/.."
	fi

	# Not exactly perfect, but should be pretty close #333773
	export ECVS_VERSION=$(
		find "${ECVS_TOP_DIR}/${ECVS_LOCALNAME}/" -ipath '*/CVS/Entries' -exec cat {} + | \
			LC_ALL=C sort | \
			sha1sum | \
			awk '{print $1}'
	)

	# If the directory is empty, remove it; empty directories cannot
	# exist in cvs.  This happens when, for example, kde-source
	# requests module/doc/subdir which doesn't exist.  Still create
	# the empty directory in workdir though.
	if [[ $(ls -A "${ECVS_TOP_DIR}/${ECVS_LOCALNAME}") == "CVS" ]] ; then
		debug-print "${FUNCNAME}: removing empty CVS directory ${ECVS_LOCALNAME}"
		rm -rf "${ECVS_TOP_DIR}/${ECVS_LOCALNAME}"
	fi

	# Implement some of base_src_unpack's functionality; note however
	# that base.eclass may not have been inherited!
	if [[ -n ${PATCHES} ]] ; then
		debug-print "${FUNCNAME}: PATCHES=${PATCHES}, S=${S}, autopatching"
		cd "${S}"
		epatch ${PATCHES}
		# Make sure we don't try to apply patches more than once,
		# since cvs_src_unpack is usually called several times from
		# e.g. kde-source_src_unpack
		export PATCHES=""
	fi

	einfo "CVS module ${ECVS_MODULE} is now in ${WORKDIR}"
}

EXPORT_FUNCTIONS src_unpack

fi