1#!/bin/sh
2#
3# This script is used to derive compiler flags and filesystem paths
4# necessary to utilize Lua, LuaJIT, and particular versions thereof in both
5# simple and mixed installation environments.
6#
7# For usage help information use the -h switch.
8#
9# This script attempts to adhere strictly to POSIX shell specifications. The
10# known non-POSIX features used are the path of the shell at the very first
11# line of this script, the default compiler command name of `cc' instead of
12# `c99', and the use of /dev/urandom for generating a random sandbox
13# directory suffix. All of these can be override. For any other issues
14# please contact the author.
15#
16# WARNING: When searching for a Lua interpreter this script may execute
17# various utilities in an attempt to deduce their fitness and release
18# version. By default this script will search for and execute utilities
19# using the glob patterns luac* and lua*. But this script CANNOT GUARANTEE
20# that executing such utilities, or any other utilities, either wittingly or
21# unwittingly, will not result in your COMPUTER EXPLODING. You have been
22# warned.
23#
24# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
25#
26# Changelog:
27#
28# * 2013-08-02 - Published. Derived from an earlier script, lua.path,
29# 	written for the cqueues project.
30#
31# * 2013-08-05 - Redirect stdin from /dev/null when probing so we don't
32# 	freeze if a utility tries to read from stdin.
33#
34# 	chdir to a read-only directory by default to try to prevent creation
35# 	of temporary files. These features address the issues of LuaTeX
36# 	reading from stdin and creating a luatex.out file in the current
37# 	working directory. By default a directory with a random suffix
38# 	generated from /dev/urandom is placed in TMPDIR and removed on exit.
39#
40# 	If CPPFLAGS is empty and no -I options directly specified then set
41# 	INCDIRS to "/usr/include:/usr/local/include".
42#
43# * 2013-08-07 - Add pkg-config support and refactor header probing to delay
44# 	recursive searching.
45#
46# * 2013-09-09 - NetBSD's sh gets upset over the noclobber option and
47# 	redirection to /dev/null, so use append operator. And check $#
48# 	before iterating over a null parameter set with `do X; ...  done`
49# 	when `set -u` is enabled--it complains about $@ being unset.
50#
51# * 2013-10-22 - Initial ldflags detection.
52#
53# * 2014-01-26 - Migrate CC vendor detection from external script.
54#
55# * 2014-09-29 - Add ldir and cdir modes which print install path by parsing
56# 	package.path and package.cpath.
57#
58# * 2014-12-18 - Add -e GLOB option.
59#
60# 	Deprecate ldir and cdir modes.
61#
62# 	Add package.path and package.cpath to replace ldir and dir modes.
63# 	Optional arguments to the new modes are preferred install paths,
64# 	rather than globs for finding the lua utility path (use the new -e
65# 	option, instead).
66#
67# * 2014-12-19 - Fix pkg-config version matching. The --modversion of
68# 	the lua package might be stale. For example, it's 5.2.0 on Ubuntu
69# 	14.04 even though the Lua release is 5.2.3.
70#
71# 	Use the interpreter path as a reference point when searching for
72# 	headers. $(dirname ${LUA_PATH})/../include is a very likely location
73# 	as bindir and includedir have the same prefix in most installations.
74#
75# * 2015-01-15 - Quote more command names and arguments. Still need to
76# 	handle space characters in code that employs command substitution. I
77# 	think we could handle all whitespace characters, including newlines,
78# 	by using a control character in IFS and using --exec printf "%s\1" {}
79# 	rather than -print with find(1).
80#
81# * 2015-01-19 - Add fix for LuaJIT's default package.cpath, which tends to
82# 	hardcode /usr/local/lib/lua/5.1, ordered before the LuaJIT
83# 	installation prefix.
84#
85# * 2015-07-14 - Add recursive glob function implemented in shell code
86# 	and use instead of find(1).
87#
88# * 2016-03-18 - Fix bug in tryluac where a continue statement was used
89# 	instead of return 0.
90#
91# * 2016-03-25 - Support ${CC} values with trailing flags, which invoke
92# 	the compiler through env(1), or which otherwise are intended to
93# 	expand as multiple words.
94#
95# 	OpenBSD 5.8 sh does not suppress strict errors within an eval
96# 	invoked from an if condition compound-list. Workaround by changing
97# 	trylua to return 0 on matching failure, like tryluainclude and
98# 	tryluac do.
99#
100# 	Undeprecate ldir and cdir. The names are more intuitive and
101# 	convenient as evidenced by the fact that I keep using them instead
102# 	of package.path and package.cpath. Try to maintain backwards
103# 	compatibility by using a simple heuristic to differentiate lua
104# 	interpreter glob patterns from preferred install directory
105# 	string.match expressions.
106#
107# * 2016-10-10 - Fix issue with passing empty CPPFLAGS to ${CC}. /usr/bin/cc
108# 	in NetBSD 7.0.1 does not tolerate an empty string argument. This
109# 	exposed a bug in NetBSD's and FreeBSD's /bin/sh, triggered by how we
110# 	pass CPPFLAGS (see evalmacro and runcc routines, below).
111#
112# 	Some Ash variants (confirmed /bin/sh in NetBSD 7.0.1 and FreeBSD
113# 	10.1) will expand unquoted ${UNSET-} and ${UNSET:-} as an empty
114# 	string rather than eliding it during argument processing. That is,
115#
116# 		nargs() { printf "%d\n" "$#"; }
117# 		nargs ${UNSET} 2 3
118# 		nargs ${UNSET-} 2 3
119#
120# 	prints "2" and "3", whereas every other shell tested prints "2" and
121# 	"2" (confirmed dash in Ubuntu Xenial; bash 4.3 in Ubuntu Xenial;
122# 	pdksh in FreeBSD 10.1, NetBSD 7.0, OS X 10.1, OpenBSD 6.0; ksh93 in
123# 	Solaris 11.3 and AIX 7.1; ksh88 in AIX 7.1).
124#
125# 	A workaround in set -u mode (where unbound variable expansion aborts
126# 	execution) is to substitute a known empty value. E.g.
127#
128# 		EMPTY=
129# 		nargs ${UNSET-$EMPTY}
130#
131# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
132#
133# Copyright (C) 2012-2016 William Ahern
134#
135# Permission is hereby granted, free of charge, to any person obtaining a
136# copy of this software and associated documentation files (the "Software"),
137# to deal in the Software without restriction, including without limitation
138# the rights to use, copy, modify, merge, publish, distribute, sublicense,
139# and/or sell copies of the Software, and to permit persons to whom the
140# Software is furnished to do so, subject to the following conditions:
141#
142# The above copyright notice and this permission notice shall be included in
143# all copies or substantial portions of the Software.
144#
145# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
146# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
147# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
148# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
149# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
150# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
151# DEALINGS IN THE SOFTWARE.
152#
153set -e # strict errors
154set -u # don't expand unbound variables
155set -f # disable pathname expansion
156set -C # noclobber
157\unalias -a # no command surprises
158export LC_ALL=C # no locale headaches
159unset IFS # no field splitting surprises
160: ${TMPDIR:=/tmp} # sane TMPDIR
161: ${CC:=cc}
162unset LUA_PATH || true # interferes search for module install directory
163unset LUA_CPATH || true
164
165MYVERSION=20161010
166MYVENDOR="william@25thandClement.com"
167
168
169EMPTY= # empty string for parameter expansion workaround for Ash bug
170DEVRANDOM=/dev/urandom
171SANDBOX="${TMPDIR}/${0##*/}-"
172
173CPPDIRS=    # -I directories from CPPFLAGS
174INCDIRS=
175LDDIRS=     # -L directories from LDFLAGS
176LIBDIRS=
177BINDIRS=
178RECURSE=no
179MAXDEPTH=5  # maximum recursion depth
180SHORTEST=   # continue searching until shortest pathname found
181PKGCONFIG=  # path to pkg-config, found by `command -v` when -k option invoked
182GLOB=       # -e GLOB expression for lua, luac, ldir, and cdir
183
184GLOB_LUA="lua:lua[5-9]*:lua-[5-9]*:luajit*"
185GLOB_LUAC="luac:luac[5-9]*:luac-[5-9]*"
186
187API_MIN=500
188API_MAX=999
189API_VER=
190API_DIR=
191
192JIT_REQ=
193JIT_MIN=20000
194JIT_MAX=99999
195JIT_VER=
196JIT_DIR=
197
198LIBLUA_VER=
199LIBLUA_DIR=
200LIBLUA_LIB=
201
202LIBJIT_VER=
203LIBJIT_DIR=
204LIBJIT_LIB=
205
206LUAC_PATH=
207LUAC_VER=
208
209LUA_PATH=
210LUA_VER=
211
212
213#
214# warn FORMAT [...]
215#
216# Print message to original stderr.
217#
218exec 9>&2
219warn() {
220	printf "%s: %.0s${1}\n" "${0##*/}" "$@" >&9
221}
222
223#
224# panic FORMAT [...]
225#
226# Print message to original stderr, then exit with failure.
227#
228panic() {
229	warn "$@"
230	exit 1
231}
232
233
234#
235# parse CPPFLAGS -I or LDFLAGS -L directories
236#
237xdirs() {
238	OPTC="${1:-I}"
239	DIRS=
240
241	set -- ${2:-}
242
243	while [ $# -gt 0 ]; do
244		case "${1}" in
245		-${OPTC})
246			shift
247
248			if [ -n "${1:-}" ]; then
249				DIRS="${DIRS}${DIRS:+:}${1}"
250			fi
251
252			;;
253		-${OPTC}*)
254			if [ "${1}" != "-${OPTC}" ]; then
255				DIRS="${DIRS}${DIRS:+:}${1#-${OPTC}}"
256			fi
257
258			;;
259		esac
260
261		shift
262	done
263
264	printf -- "${DIRS}"
265}
266
267idirs() {
268	xdirs "I" "${1:-}"
269}
270
271ldirs() {
272	xdirs "L" "${1:-}"
273}
274
275# count ":"-delimited substrings
276count() {
277	IFS=:
278	set -- ${1:-}
279	unset IFS
280
281	printf "$#"
282}
283
284# append to ":"-delimited string variable
285append() {
286	NAME=${1}
287	eval VALUE="\${${NAME}}"
288	shift
289
290	IFS=:
291	TMP="$*"
292
293	IFS="\n"
294	read -r "${NAME}" <<-EOF
295		${VALUE:-}${VALUE:+:}${TMP}
296	EOF
297	unset IFS
298}
299
300#
301# glob PATTERN [MAXDEPTH] [EXEC-COMMAND] [INTERNAL:GLOB-COUNT]
302#
303glob() {
304	glob_N="${4:-0}"
305
306	IFS=
307	set +f
308	for F in ${1}; do
309		[ -e "${F}" ] || continue
310		if eval "${3:-printf '%s\\n'} \"\${F}\""; then
311			glob_N=$((${glob_N} + 1))
312		fi
313	done
314	set -f
315	unset IFS
316
317	if [ "${2-0}" -gt 0 ]; then
318		glob "${1%/*}/*/${1##*/}" "$((${2} - 1))" "${3:-}" "${glob_N}" || :
319	fi
320
321	[ "${glob_N}" -gt 0 ]
322} # glob
323
324
325#
326# runcc [...]
327#
328# Wrapper for invoking ${CC}. Some build system include flags in ${CC},
329# invoke the compiler through env(1), or employ other hacks.
330#
331# TODO: Optionally handle unescaping of words in a manner similar to how
332# ${CC} would be evaluated from a make rule--typically by being passed
333# through system(3).
334#
335runcc() {
336	(unset IFS; exec ${CC} "$@")
337}
338
339
340#
341# evalmacro PATH MACRO [REGEX] [SUBST]
342#
343# 	PATH   Header identifier--#include <PATH>
344# 	MACRO  Macro identifier
345# 	REGEX  Optional regex pattern to match macro evaluation result
346# 	SUBST  Optional replacement expression
347#
348evalmacro() {
349	printf "#include <$1>\n[===[$2]===]\n" \
350	| runcc ${CPPFLAGS:-${EMPTY}} -E - 2>>/dev/null \
351	| sed -ne "
352		s/^.*\\[===\\[ *\\(${3:-.*}\\) *\\]===\\].*$/${4:-\\1}/
353		t Found
354		d
355		:Found
356		p
357		q
358	"
359}
360
361
362#
363# testsym PATH NAME
364#
365# Test whether global symbol NAME exists in object file at PATH. Exits with
366# 0 (true) when found, non-0 (false) otherwise.
367#
368testsym() {
369	# NOTE: No -P for OpenBSD nm(1), but the default output format is
370	# close enough. Section types always have a leading and trailing
371	# space. U section type means undefined. On AIX [VWZ] are weak
372	# global symbols. Solaris and OS X have additional symbol types
373	# beyond the canonical POSIX/BSD types, all of which are uppercase
374	# and within [A-T].
375	(nm -Pg ${1} 2>>/dev/null || nm -g 2>>/dev/null) \
376	| sed -ne '/ [A-T] /p' \
377	| grep -q "${2}"
378}
379
380
381tryluainclude() {
382	V="$(evalmacro "${1}" LUA_VERSION_NUM '[0123456789][0123456789]*')"
383	: ${V:=0}
384
385	if [ "${1%/*}" != "${1}" ]; then
386		D="${1%/*}"
387
388		# cleanup after Solaris directory prune trick
389		if [ "${D##*/./}" != "${D}" ]; then
390			D="${D%%/./*}/${D##*/./}"
391		else
392			D="${D%/.}"
393		fi
394	else
395		D=
396	fi
397
398	[ "$V" -gt 0 -a "$V" -ge "${API_VER:-0}" ] || return 0
399
400	[ "$V" -gt "${API_VER:-0}" -o "${#D}" -lt "${#API_DIR}" -o \( "${JIT_REQ}" = "yes" -a "${JIT_VER:-0}" -lt "${JIT_MAX}" \) ] || return 0
401
402	[ "$V" -ge "${API_MIN}" -a "$V" -le "${API_MAX}" ] || return 0
403
404	if [ -n "${JIT_REQ}" ]; then
405		J="$(evalmacro "${1%%lua.h}luajit.h" LUAJIT_VERSION_NUM '[0123456789][0123456789]*')"
406		: ${J:=0}
407
408		if [ "${JIT_REQ}" = "skip" ]; then
409			[ "${J}" -eq 0 ] || return 0
410		elif [ "${JIT_REQ}" = "yes" ]; then
411			[ "$J" -ge "${JIT_VER:-0}" ] || return 0
412			[ "$J" -gt "${JIT_VER:-0}" -o "${#D}" -lt "${#JIT_DIR}" ] || return 0
413			[ "$J" -ge ${JIT_MIN} ] || return 0
414			[ "$J" -le "${JIT_MAX}" ] || return 0
415
416			JIT_VER="$J"
417			JIT_DIR="$D"
418		fi
419	fi
420
421	API_VER="$V"
422	API_DIR="$D"
423}
424
425
426#
427# foundversion
428#
429# true if found the best (maximum) possible version, false otherwise
430#
431foundversion() {
432	if [ "${API_VER:-0}" -lt "${API_MAX}" ]; then
433		return 1
434	fi
435
436	if [ "${JIT_REQ}" = "yes" -a "${JIT_VER:-0}" -lt "${JIT_MAX}" ]; then
437		return 1
438	fi
439
440	if [ "${SHORTEST}" = "yes" ]; then
441		return 1
442	fi
443
444	return 0
445}
446
447
448#
449# luapc
450#
451# wrapper around `pkg-config ... LIB`, where LIB is derived by
452# searching for all libraries with "lua" in the name that have a
453# --modversion equal to the release version printed by ${LUA_PATH} -v.
454#
455LUAPC_LIB=
456
457luapc() {
458	[ -n "${LUA_PATH}" ] || return 0
459
460	[ -n "${PKGCONFIG}" ] || return 0
461
462	# find pkg-config library name
463	if [ -z "${LUAPC_LIB}" ]; then
464		V="$("${LUA_PATH}" -v </dev/null 2>&1 | head -n1 | sed -ne 's/^Lua[^ ]* \([0123456789][0123456789]*\(\.[0123456789][0123456789]*\)*\).*/\1/p')"
465
466		[ -n "${V}" ] || return 0
467
468		V_N=$(mmp2num "${V}")
469
470		for LIB in $("${PKGCONFIG}" --list-all </dev/null 2>>/dev/null | sed -ne 's/^\(lua[^ 	]*\).*/\1/p'); do
471			M="$("${PKGCONFIG}" --modversion ${LIB} || true)"
472
473			# break immediately on exact match
474			if [ "${V}" = "${M}" ]; then
475				LUAPC_LIB="${LIB}"
476
477				break
478			fi
479
480			#
481			# NOTE: On Ubuntu 14.04 pkg-config --modversion
482			# lua5.2 prints 5.2.0 even though the release
483			# version is 5.2.3 (what lua5.2 -v prints).
484			#
485			# If the major.minor components match, then
486			# tentatively use that package name.
487			#
488			M_N=$(mmp2num "${M}" 0 0 0)
489
490			if [ "$((${V_N} / 100))" -eq "$((${M_N} / 100))" ]; then
491				LUAPC_LIB="${LIB}"
492			fi
493		done
494
495		[ -n "${LUAPC_LIB}" ] || return 0
496	fi
497
498	${PKGCONFIG} "$@" "${LUAPC_LIB}" </dev/null 2>>/dev/null || true
499}
500
501
502#
503# findinstalldir  package.path|package.cpath [preferred-path ...]
504#
505findinstalldir() {
506	V_DIR=$((${LUA_VER} / 100 % 100)).$((${LUA_VER} % 100))
507
508	if [ "${1}" = "package.cpath" -o "${1}" = "cdir" ]; then
509		ARRAY="package.cpath"
510
511		DIR="$(luapc --variable INSTALL_CMOD)"
512		[ -n "${DIR}" ] && set -- "$@" "${DIR}"
513
514		DIR="$(luapc --variable INSTALL_LIB)"
515		[ -n "${DIR}" ] && set -- "$@" "${DIR}/lua/${V_DIR}"
516
517		DIR="$(luapc --variable libdir)"
518		[ -n "${DIR}" ] && set -- "$@" "${DIR}/lua/${V_DIR}"
519
520		DIR="$(luapc --variable prefix)"
521		[ -n "${DIR}" ] && set -- "$@" "${DIR}/lib/lua/${V_DIR}"
522
523		# LuaJIT installations tend to include
524		# /usr/local/lib/lua/5.1 as one of the first paths, ordered
525		# before the LuaJIT installation prefix, and regardless of
526		# whether there exists a /usr/local/lib/lua/5.1.
527		set -- "$@" "${LUA_PATH}/../../lib/lua/${V_DIR}"
528		set -- "$@" "${LUA_PATH}/../../lib/*/lua/${V_DIR}" # e.g. lib/x86_64-linux-gnu
529	else
530		ARRAY="package.path"
531
532		DIR="$(luapc --variable INSTALL_LMOD)"
533		[ -n "${DIR}" ] && set -- "$@" "${DIR}"
534
535		DIR="$(luapc --variable prefix)"
536		[ -n "${DIR}" ] && set -- "$@" "${DIR}/share/lua/${V_DIR}"
537
538		# See above LuaJIT note. Although the built-in package.path
539		# usually orders the LuaJIT installation prefix first.
540		set -- "$@" "${LUA_PATH}/../../share/lua/${V_DIR}"
541	fi
542
543	shift
544
545	if [ $# -eq 0 ]; then
546		set -- "/nonexistent" # cannot expand empty $@ on some implementations
547	fi
548
549	"${LUA_PATH}" - "$@" <<-EOF
550		--
551		-- actual pkg-config variable on Ubuntu 14.04
552		--
553		-- 	/usr//share/lua/5.1
554		--
555		local function fixpath(path)
556			local stack = { path:match"^/" and "" or "." }
557
558			for ent in path:gmatch"([^/]+)" do
559				if ent == ".." and #stack > 1 then
560					stack[#stack] = nil
561				elseif ent ~= "." then
562					stack[#stack + 1] = ent
563				end
564			end
565
566			return table.concat(stack, "/")
567		end
568
569		local function topattern(path)
570			if string.match(path, "*") then
571				path = string.gsub(path, "%%", "%%")
572				return string.gsub(path, "*", "[^/]+")
573			end
574		end
575
576		local dirs = { }
577
578		for dir in ${ARRAY}:gmatch"([^;?]+)/" do
579			dir = fixpath(dir)
580
581			if dir ~= "." then
582				dirs[#dirs + 1] = dir
583			end
584		end
585
586		for _, arg in ipairs{ ... } do
587			arg = fixpath(arg)
588
589			local pat = topattern(arg)
590
591			for _, dir in ipairs(dirs) do
592				if arg == dir then
593					print(dir)
594					os.exit(0)
595				elseif pat and string.match(dir, pat) then
596					print(dir)
597					os.exit(0)
598				end
599			end
600		end
601
602		if dirs[1] then
603			print(dirs[1])
604			os.exit(0)
605		else
606			os.exit(1)
607		end
608	EOF
609}
610
611
612#
613# findversion
614#
615findversion() {
616	tryluainclude "lua.h"
617
618	if foundversion; then
619		return 0
620	fi
621
622
623	# iterate through CPPFLAGS to probe different precedence
624	if [ "${API_VER:-0}" -lt "${API_MAX}" ]; then
625		IFS=:
626		set -- ${CPPDIRS}
627		unset IFS
628
629		if [ $# -gt 0 ]; then
630			for D; do
631				tryluainclude "${D}/lua.h"
632
633				if foundversion; then
634					return 0
635				fi
636			done
637		fi
638	fi
639
640
641	if [ -n "${PKGCONFIG}" ]; then
642		PKGFLAGS="$("${PKGCONFIG}" --list-all </dev/null 2>>/dev/null | sed -ne 's/^\(lua[^ 	]*\).*/\1/p' | xargs -- ${PKGCONFIG} --cflags 2>>/dev/null | cat)"
643		PKGDIRS="$(idirs "${PKGFLAGS}")"
644
645		IFS=:
646		set -- ${PKGDIRS}
647		unset IFS
648
649		if [ $# -gt 0 ]; then
650			for D; do
651				tryluainclude "${D}/lua.h"
652
653				if foundversion; then
654					return 0
655				fi
656			done
657		fi
658	fi
659
660
661	IFS=:
662	set -- ${INCDIRS}
663	unset IFS
664
665	if [ $# -gt 0 ]; then
666		for D; do
667			tryluainclude "${D}/lua.h"
668
669			if foundversion; then
670				return 0
671			fi
672		done
673	fi
674
675
676	if [ "${RECURSE}" != "yes" ]; then
677		[ "${API_VER:-0}" -gt 0 ]
678		return $?
679	fi
680
681
682	# recurse into CPPDIRS
683	IFS=:
684	set -- ${CPPDIRS}
685	unset IFS
686
687	if [ $# -gt 0 ]; then
688		for D; do
689			glob "${D}/lua.h" "${MAXDEPTH}" tryluainclude || :
690
691			if foundversion; then
692				return 0
693			fi
694		done
695	fi
696
697
698	# recurse into INCDIRS
699	IFS=:
700	set -- ${INCDIRS}
701	unset IFS
702
703	if [ $# -gt 0 ]; then
704		for D; do
705			glob "${D}/lua.h" "${MAXDEPTH}" tryluainclude || :
706
707			if foundversion; then
708				return 0
709			fi
710		done
711	fi
712
713
714	# if we can find the lua interpreter, use it as a reference for
715	# header locations.
716	if findlua; then
717		D="${LUA_PATH%/*}"
718		D="${D%/*}/include"
719
720		if [ -d "${D}" ]; then
721			glob "${D}/lua.h" "${MAXDEPTH}" tryluainclude || :
722
723			if foundversion; then
724				return 0
725			fi
726		fi
727	fi
728
729	[ "${API_VER:-0}" -gt 0 ]
730}
731
732
733#
734# Unlike API version checking, this is less likely to be accurately forward
735# compatible.
736#
737trylib() {
738	testsym "${1}" "lua_newstate" || return 1
739
740	# exclude C++
741	[ "${1#*++}" = "${1}" ] || return 1
742
743	V=0
744	J=0
745	D=
746	F="${1##*/}"
747	L=
748
749	if [ "${1%/*}" != "${1}" ]; then
750		D="${1%/*}"
751
752		# cleanup after Solaris directory prune trick
753		if [ "${D##*/./}" != "${D}" ]; then
754			D="${D%%/./*}/${D##*/./}"
755		else
756			D="${D%/.}"
757		fi
758	fi
759
760	L="${F#lib}"
761	L="${L%.so}"
762	L="${L%.a}"
763	L="${L%.dylib}"
764
765
766	# FIXME: need more versioning tests
767	if testsym "${1}" "lua_getfenv"; then
768		V=501
769	elif testsym "${1}" "lua_yieldk"; then
770		if testsym "${1}" "lua_getctx"; then
771			V=502
772		else
773			V=503
774		fi
775	else
776		return 1
777	fi
778
779	[ "$V" -gt 0 -a "$V" -ge "${LIBLUA_VER:-0}" ] || return 1
780
781	[ "$V" -gt "${LIBLUA_VER:-0}" -o "${#D}" -lt "${#LIBLUA_DIR}" -o \( "${JIT_REQ}" = "yes" -a "${LIBJIT_VER:-0}" -lt "${JIT_MAX}" \) ] || return 1
782
783	[ "$V" -ge "${API_MIN}" -a "$V" -le "${API_MAX}" ] || return 1
784
785
786	if [ -n "${JIT_REQ}" ]; then
787		# FIXME: need more versioning tests
788		if testsym "${1}" "luaopen_jit"; then
789			J=20000
790		fi
791
792		if [ "${JIT_REQ}" = "skip" ]; then
793			[ "${J}" -eq 0 ] || return 1
794		elif [ "${JIT_REQ}" = "yes" ]; then
795			[ "$J" -ge "${LIBJIT_VER:-0}" ] || return 1
796			[ "$J" -gt "${LIBJIT_VER:-0}" -o "${#D}" -lt "${#LIBJIT_DIR}" ] || return 1
797			[ "$J" -ge ${JIT_MIN} ] || return 1
798			[ "$J" -le "${JIT_MAX}" ] || return 1
799
800			LIBJIT_VER="$J"
801			LIBJIT_DIR="$D"
802			LIBJIT_LIB="$L"
803		fi
804	fi
805
806	LIBLUA_VER="$V"
807	LIBLUA_DIR="$D"
808	LIBLUA_LIB="$L"
809}
810
811
812#
813# foundlib
814#
815# true if found the best (maximum) possible version, false otherwise
816#
817foundlib() {
818	if [ "${LIBLUA_VER:-0}" -lt "${API_MAX}" ]; then
819		return 1
820	fi
821
822	if [ "${JIT_REQ}" = "yes" -a "${LIBJIT_VER:-0}" -lt "${JIT_MAX}" ]; then
823		return 1
824	fi
825
826	if [ "${SHORTEST}" = "yes" ]; then
827		return 1
828	fi
829
830	return 0
831}
832
833
834findlib() {
835	if [ -n "${PKGCONFIG}" ]; then
836		PKGFLAGS="$("${PKGCONFIG}" --list-all </dev/null 2>>/dev/null | sed -ne 's/^\(lua[^ 	]*\).*/\1/p' | xargs -- ${PKGCONFIG} --libs 2>>/dev/null | cat)"
837		PKGDIRS="$(ldirs "${PKGFLAGS}")"
838		PKGDIRS="${PKGDIRS}${PKGDIRS:+:}/lib:/usr/lib:/usr/local/lib"
839		NUMDIRS="$(count "${PKGDIRS}")"
840		PKGLIBS="$(xdirs "l" "${PKGFLAGS}")"
841		NUMLIBS="$(count "${PKGLIBS}")"
842		ALLDIRS="${PKGDIRS}${PKGLIBS:+:}${PKGLIBS}"
843
844		IFS=:
845		set -- ${ALLDIRS}
846		unset IFS
847
848		I=1
849		while [ $I -le ${NUMDIRS} ]; do
850			K=$((1 + ${NUMDIRS}))
851			while [ $K -le $# ]; do
852				findlib_L=$(eval "printf \${$I}")
853				findlib_l=$(eval "printf \${$K}")
854
855				#printf -- "I=$I K=$K $findlib_L/lib$findlib_l*.*\n"
856
857				glob "${findlib_L}/lib${findlib_l}*.*" 0 trylib || :
858
859				if foundlib; then
860					return 0;
861				fi
862
863				glob "${findlib_L}/lib${findlib_l}*.*" ${MAXDEPTH} trylib || :
864
865				if foundlib; then
866					return 0;
867				fi
868
869				K=$(($K + 1))
870			done
871			I=$(($I + 1))
872		done
873	fi
874
875	ALLDIRS="${LDDIRS}${LDDIRS:+:}${LIBDIRS}"
876
877	IFS=:
878	set -- ${ALLDIRS}
879	unset IFS
880
881	for findlib_D; do
882		glob "${findlib_D}/liblua*.*" "${MAXDEPTH}" trylib || :
883
884		if foundlib; then
885			return 0
886		fi
887	done
888
889	# if we can find the lua interpreter, use it as a reference for
890	# library locations.
891	if findlua; then
892		findlib_D="${LUA_PATH%/*}"
893		findlib_D="${findlib_D%/*}/lib"
894
895		if [ -d "${findlib_D}" ]; then
896			glob "${findlib_D}/liblua*.*" "${MAXDEPTH}" trylib || :
897
898			if foundlib; then
899				return 0
900			fi
901		fi
902	fi
903}
904
905
906# check setuid and setgid mode
907safeperm() {
908	[ -f "$1" -a ! -u "$1" -a ! -g "$1" ]
909}
910
911
912tryluac() {
913	tryluac_F="${1}"
914
915	[ -x "${tryluac_F}" ] && safeperm "${tryluac_F}" || return 0
916
917	tryluac_V="$("${tryluac_F}" -v </dev/null 2>&1 | head -n1 | sed -ne 's/^Lua \([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')"
918	: ${tryluac_V:=0}
919	tryluac_V="$((${tryluac_V%%.*} * 100 + ${tryluac_V##*.} % 100))"
920
921	[ "${tryluac_V}" -gt 0 -a "${tryluac_V}" -ge "${LUAC_VER:-0}" ] || return 0
922
923	[ "${tryluac_V}" -gt "${LUAC_VER:-0}" -o "${#tryluac_F}" -lt "${#LUAC_PATH}" ] || return 0
924
925	[ "${tryluac_V}" -ge "${API_MIN}" -a "${tryluac_V}" -le "${API_MAX}" ] || return 0
926
927	printf "return true" 2>>/dev/null | ${tryluac_F} -p - </dev/null >>/dev/null 2>&1 || return 0
928
929	LUAC_PATH="${tryluac_F}"
930	LUAC_VER="${tryluac_V}"
931}
932
933#
934# foundluac
935#
936# true if found the best (maximum) possible version, false otherwise
937#
938foundluac() {
939	if [ "${LUAC_VER:-0}" -lt "${API_MAX}" ]; then
940		return 1
941	fi
942
943	if [ "${SHORTEST}" = "yes" ]; then
944		return 1
945	fi
946
947	return 0
948}
949
950findluac() {
951	if [ $# -eq 0 ]; then
952		IFS=:
953		set -- ${GLOB:-${GLOB_LUAC}}
954		unset IFS
955	fi
956
957	for findluac_G; do
958		IFS=:
959		for findluac_D in ${PATH}; do
960			unset IFS
961
962			glob "${findluac_D}/${findluac_G}" 0 tryluac || :
963
964			if foundluac; then
965				return 0
966			fi
967		done
968
969		IFS=:
970		for findluac_D in ${BINDIRS}; do
971			unset IFS
972
973			glob "${findluac_D}/${findluac_G}" "${MAXDEPTH}" tryluac || :
974
975			if foundluac; then
976				return 0
977			fi
978		done
979
980		unset IFS
981	done
982
983	[ "${LUAC_VER:-0}" -gt 0 ] && [ "${#LUAC_PATH}" -gt 0 ]
984}
985
986
987isinteger() {
988	I="${1}"
989
990	[ "${#I}" -gt 0 ] || return 1
991
992	while [ "${#I}" -gt 0 ]; do
993		if [ "${I##[0123456789]}" = "${I}" ]; then
994			return 1
995		fi
996
997		I=${I##[0123456789]}
998	done
999
1000	return 0
1001}
1002
1003
1004checkints() {
1005	while [ $# -gt 0 ]; do
1006		if ! isinteger "${1}"; then
1007			warn "%s: not a number" "${1}"
1008			return 1
1009		fi
1010
1011		shift
1012	done
1013}
1014
1015
1016# Only major.minor for matching LUA_VERSION_NUM in lua.h. Also, _VERSION
1017# only includes major.minor.
1018lua2num() {
1019	M=0
1020	m="${2:-0}"
1021
1022	IFS=.
1023	set -- ${1}
1024	unset IFS
1025
1026	M=${1:-${M}}
1027	m=${2:-${m}}
1028
1029	checkints $M $m
1030
1031	printf "$((${M} * 100 + ${m}))\n"
1032}
1033
1034
1035# All major.minor.patch for matching LUAJIT_VERSION_NUM in luajit.h.
1036jit2num() {
1037	M=0
1038	m="${2:-0}"
1039	p="${3:-0}"
1040
1041	IFS=.
1042	set -- ${1}
1043	unset IFS
1044
1045	M=${1:-${M}}
1046	m=${2:-${m}}
1047	p=${3:-${p}}
1048
1049	checkints $M $m $p
1050
1051	printf "$((${M} * 10000 + ${m} * 100 + ${p}))\n"
1052}
1053
1054
1055mmp2num() {
1056	M="${2:-0}"
1057	m="${3:-0}"
1058	p="${4:-0}"
1059
1060	IFS=".+-_"
1061	set -- ${1}
1062	unset IFS
1063
1064	if isinteger "${1:-}"; then
1065		M=${1}
1066	fi
1067
1068	if isinteger "${2:-}"; then
1069		m=${2}
1070	fi
1071
1072	if isinteger "${3:-}"; then
1073		p=${3}
1074	fi
1075
1076	checkints $M $m $p
1077
1078	printf "$((${M} * 10000 + ${m} * 100 + ${p}))\n"
1079}
1080
1081
1082trylua() {
1083	trylua_F="${1}"
1084	[ -x "${trylua_F}" ] && safeperm "${trylua_F}" || return 0
1085
1086	trylua_V="$("${trylua_F}" -e 'print(string.match(_VERSION, [[[%d.]+]]))' </dev/null 2>>/dev/null | head -n1 | sed -ne 's/^\([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')"
1087	: ${trylua_V:=0}
1088	trylua_V="$((${trylua_V%%.*} * 100 + ${trylua_V##*.} % 100))"
1089
1090	[ "${trylua_V}" -gt 0 -a "${trylua_V}" -ge "${LUA_VER:-0}" ] || return 0
1091
1092	[ "${trylua_V}" -gt "${LUA_VER:-0}" -o "${#trylua_F}" -lt "${#LUA_PATH}" ] || return 0
1093
1094	[ "${trylua_V}" -ge "${API_MIN}" -a "${trylua_V}" -le "${API_MAX}" ] || return 0
1095
1096	if [ -n "${JIT_REQ}" ]; then
1097		J="$("${trylua_F}" -v </dev/null 2>&1 | head -n1 | sed -ne 's/^LuaJIT \([0123456789][0123456789]*\.[0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')"
1098		J="$(jit2num ${J:-0})"
1099
1100		if [ "${JIT_REQ}" = "skip" ]; then
1101			[ "${J}" -eq 0 ] || return 0
1102		elif [ "${JIT_REQ}" = "yes" ]; then
1103			[ "${J}" -gt 0 ] || return 0
1104			[ "${J}" -ge "${JIT_MIN}" ] || return 0
1105			[ "${J}" -le "${JIT_MAX}" ] || return 0
1106		fi
1107	fi
1108
1109	LUA_PATH="${trylua_F}"
1110	LUA_VER="${trylua_V}"
1111}
1112
1113#
1114# foundlua
1115#
1116# true if found the best (maximum) possible version, false otherwise
1117#
1118foundlua() {
1119	if [ "${LUA_VER:-0}" -lt "${API_MAX}" ]; then
1120		return 1
1121	fi
1122
1123	if [ "${SHORTEST}" = "yes" ]; then
1124		return 1
1125	fi
1126
1127	return 0
1128}
1129
1130findlua() {
1131	if [ $# -eq 0 ]; then
1132		IFS=:
1133		set -- ${GLOB:-${GLOB_LUA}}
1134		unset IFS
1135	fi
1136
1137	for findlua_G; do
1138		IFS=:
1139		for findlua_D in ${PATH}; do
1140			unset IFS
1141
1142			glob "${findlua_D}/${findlua_G}" 0 trylua || :
1143
1144			if foundlua; then
1145				return 0
1146			fi
1147		done
1148
1149		IFS=:
1150		for findlua_D in ${BINDIRS}; do
1151			unset IFS
1152
1153			glob "${findlua_D}/${findlua_G}" "${MAXDEPTH}" trylua || :
1154
1155			if foundlua; then
1156				return 0
1157			fi
1158		done
1159
1160		unset IFS
1161	done
1162
1163	[ "${LUA_VER:-0}" -gt 0 ] && [ "${#LUA_PATH}" -gt 0 ]
1164}
1165
1166
1167ccname() {
1168	runcc -E - <<-EOF | awk '/sunpro/||/clang/||/gcc/||/other/{ print $1; exit; }'
1169		#if defined __SUNPRO_C
1170		sunpro
1171		#elif defined __clang__
1172		clang
1173		#elif defined __GNUC__
1174		gcc
1175		#else
1176		other
1177		#endif
1178	EOF
1179}
1180
1181
1182usage() {
1183	cat <<-EOF
1184	usage: ${0##*/} [-I:L:P:d:De:krm:xsv:j:JVh] cppflags|version|lua|luac|...
1185	  -I PATH      additional search directory for includes
1186	  -L PATH      additional search directory for libraries
1187	  -P PATH      additional search directory for binaries
1188	  -d PATH      use PATH as sandbox directory; a random 16 byte suffix is
1189	               generated from /dev/urandom and the directory removed on exit
1190	               unless a trailing "/" is present
1191	               (default sandbox is \$TMPDIR/${0##*/}-XXXXXXXXXXXXXXXX)
1192	  -D           do not create a sandbox
1193	  -e GLOB      glob pattern for finding utilities (lua, luac, etc)
1194	  -k           query pkg-config if available
1195	  -r           recursively search directories
1196	  -m MAXDEPTH  limit recursion to MAXDEPTH
1197	  -s           find shortest pathname, otherwise print first best match
1198	  -v VERSION   require specific Lua version or range
1199	               (e.g. "5.1" or "5.1-5.2")
1200	  -j VERSION   require specific LuaJIT version or range
1201	               (e.g. "2.0.1"; empty ranges like "-" force any LuaJIT version)
1202	  -J           skip LuaJIT if encountered
1203	  -V           print this script's version information
1204	  -h           print this usage message
1205
1206	  cppflags       print derived additional CPPFLAGS necessary
1207	  version        print derived Lua API version from cppflags discovery
1208	  ldflags        print derived additional LDFLAGS necessary (TODO)
1209	  libs	         print derived additional LIBS necessary (TODO)
1210	  libversion     print derived Lua API version from ldflags/libs discovery
1211	  luac           print path to luac utility ($(printf "${GLOB_LUA}" | tr ':' ' '))
1212	  lua            print path to lua interpreter ($(printf "${GLOB_LUAC}" | tr ':' ' '))
1213	  package.path   print preferred module install path
1214	  package.cpath  print preferred C module install path
1215	  ccname         print CC name (e.g. sunpro, clang, gcc)
1216	  evalmacro      run internal macro evaluator for debugging
1217	  testsym        run internal library symbol reader for debugging
1218
1219	This utility is used to derive compiler flags and filesystem paths
1220	necessary to utilize Lua, LuaJIT, and particular versions thereof.
1221	On success it prints the requested information and exits with 0,
1222	otherwise it fails with an exit status of 1.
1223
1224	Note that cppflags may not print anything if no additional flags are
1225	required to compile against the requested API version.
1226
1227	When searching, the highest Lua version is preferred. Searching
1228	stops once the highest version in the allowable range is found
1229	unless the -s flag is specified.
1230
1231	LuaJIT is treated like any other Lua installation. If an explicit
1232	LuaJIT version or range is specified, then only LuaJIT installations
1233	will match. To exclude LuaJIT entirely use the -J switch.
1234
1235	This utility processes the environment variables CC, CPPFLAGS,
1236	LDFLAGS, and PATH if present. If recursion is requested, then
1237	directories specified in CPPFLAGS, LDFLAGS, and PATH are also
1238	recursed.
1239
1240	If the environment variable CPPFLAGS is empty and no -I options are
1241	specified directly, then /usr/include and /usr/local/include are
1242	used when probing for cppflags and API version.
1243
1244	Report bugs to <william@25thandClement.com>
1245	EOF
1246}
1247
1248
1249version() {
1250	cat <<-EOF
1251	luapath $MYVERSION
1252	vendor  $MYVENDOR
1253	release $MYVERSION
1254	EOF
1255}
1256
1257
1258while getopts I:L:P:d:De:krm:xsv:j:JVh OPT; do
1259	case "${OPT}" in
1260	I)
1261		INCDIRS="${INCDIRS:-}${INCDIRS:+:}${OPTARG}"
1262		;;
1263	L)
1264		LIBDIRS="${LIBDIRS:-}${LIBDIRS:+:}${OPTARG}"
1265		;;
1266	P)
1267		BINDIRS="${BINDIRS:-}${BINDIRS:+:}${OPTARG}"
1268		;;
1269	d)
1270		SANDBOX="${OPTARG}"
1271		;;
1272	D)
1273		SANDBOX=
1274		;;
1275	e)
1276		GLOB="${GLOB:-}${GLOB:+:}${OPTARG}"
1277		;;
1278	k)
1279		PKGCONFIG="$(command -v pkg-config || true)"
1280		;;
1281	r)
1282		RECURSE=yes
1283		;;
1284	m)
1285		if [ "${#OPTARG}" -eq 0 -o -n "${OPTARG##[0123456789]}" ]; then
1286			panic "%s: invalid maxdepth" "${OPTARG}"
1287		fi
1288
1289		MAXDEPTH="${OPTARG}"
1290		;;
1291	x)
1292		#
1293		# NOTE: This option was
1294		#
1295		# 	-x  do not cross device mounts when recursing
1296		#
1297		# but is currently unsupported as our built-in glob function
1298		# does not implement this functionality. Previously this
1299		# option caused -xdev to be added to invocations of find(1).
1300		;;
1301	s)
1302		SHORTEST=yes
1303		;;
1304	v)
1305		MIN=${OPTARG%%[,:-]*}
1306		MAX=${OPTARG##*[,:-]}
1307
1308		API_MIN="$(lua2num ${MIN:-0} 0)"
1309		API_MAX="$(lua2num ${MAX:-99} 99)"
1310
1311		if [ "${API_MIN}" -gt "${API_MAX}" ]; then
1312			panic "%s: invalid version range" "${OPTARG}"
1313		fi
1314
1315		;;
1316	j)
1317		MIN=${OPTARG%%[,:-]*}
1318		MAX=${OPTARG##*[,:-]}
1319
1320		JIT_MIN="$(jit2num ${MIN:-0} 0 0)"
1321		JIT_MAX="$(jit2num ${MAX:-99} 99 99)"
1322
1323		if [ "${JIT_MIN}" -gt "${JIT_MAX}" ]; then
1324			panic "%s: invalid version range" "${OPTARG}"
1325		fi
1326
1327		JIT_REQ=yes
1328		;;
1329	J)
1330		JIT_REQ=skip
1331		;;
1332	V)
1333		version
1334		exit 0
1335		;;
1336	h)
1337		usage
1338		exit 0
1339		;;
1340	*)
1341		usage >&2
1342		exit 1
1343		;;
1344	esac
1345done
1346
1347shift $(($OPTIND - 1))
1348
1349
1350[ "${RECURSE}" = "yes" ] || MAXDEPTH=0
1351
1352
1353for U in "${CC}" grep od rm rmdir sed xargs; do
1354	! command -v "${U}" >>/dev/null 2>&1 || continue
1355
1356	# ${CC} might have trailing flags or invoke the compiler through env
1357	! command -v "${U%% *}" >>/dev/null 2>&1 || continue
1358
1359	warn "%s: command not found" "${U}"
1360done
1361
1362
1363if [ -n "${SANDBOX}" ]; then
1364	if [ "${SANDBOX}" = "${SANDBOX%/}" ]; then
1365		if [ ! -c "${DEVRANDOM}" ]; then
1366			# TODO: expand DEVRANDOM into set of different possibilities to check
1367			panic "%s: no character random device available" "${DEVRANDOM}"
1368		fi
1369
1370		TMP="${SANDBOX}$(od -An -N8 -tx1 < ${DEVRANDOM} 2>>/dev/null | tr -d ' 	')"
1371
1372		if [ ${#TMP} -ne $((${#SANDBOX} + 16)) ]; then
1373			panic "%s: unable to generate random suffix" "${SANDBOX}"
1374		fi
1375
1376		SANDBOX="${TMP}"
1377
1378		trap "cd .. && rm -f -- ${SANDBOX}/* && rmdir -- ${SANDBOX}" EXIT
1379	fi
1380
1381	if [ ! -d "${SANDBOX}" ]; then
1382		OMASK="$(umask)"
1383		umask 0777
1384		mkdir -m0550 -- "${SANDBOX}" || exit 1
1385		umask ${OMASK}
1386	fi
1387
1388	cd ${SANDBOX}
1389fi
1390
1391
1392CPPDIRS="$(idirs "${CPPFLAGS:-}")"
1393
1394if [ -z "${CPPDIRS}" -a -z "${INCDIRS}" ]; then
1395	INCDIRS="/usr/include:/usr/local/include"
1396fi
1397
1398
1399LDDIRS="$(ldirs "${LDFLAGS:-}")"
1400
1401if [ -z "${LDDIRS}" -a -z "${LIBDIRS}" ]; then
1402	LIBDIRS="/lib:/usr/lib:/usr/local/lib"
1403fi
1404
1405
1406case "${1:-}" in
1407cppflags)
1408	findversion || exit 1
1409
1410	[ "${API_VER:-0}" -gt 0 ] || exit 1
1411
1412	[ -z "${API_DIR:-}" ] || printf -- "-I${API_DIR}\n"
1413
1414	;;
1415version)
1416	findversion || exit 1
1417
1418	printf "$(((${API_VER} / 100) % 100)).$((($API_VER) % 100))\n"
1419
1420	;;
1421ldflags)
1422	findlib
1423
1424	[ "${LIBLUA_VER:-0}" -gt 0 ] || exit 1
1425
1426	if [ "${#LIBLUA_DIR}" -gt 0 ]; then
1427		printf -- "-L%s\n" "${LIBLUA_DIR}"
1428	fi
1429
1430	;;
1431libs)
1432	findlib
1433
1434	[ "${LIBLUA_VER:-0}" -gt 0 ] || exit 1
1435
1436	printf -- "-l%s\n" "${LIBLUA_LIB}"
1437
1438	;;
1439libv*)
1440	findlib
1441
1442	[ "${LIBLUA_VER:-0}" -gt 0 ] || exit 1
1443
1444	printf "$(((${LIBLUA_VER} / 100) % 100)).$((($LIBLUA_VER) % 100))\n"
1445
1446	;;
1447luac)
1448	shift
1449
1450	if [ $# -gt 0 ]; then
1451		append GLOB $*
1452	fi
1453
1454	findluac || exit 1
1455
1456	printf -- "${LUAC_PATH}\n"
1457
1458	;;
1459lua)
1460	shift
1461
1462	if [ $# -gt 0 ]; then
1463		append GLOB $*
1464	fi
1465
1466	findlua || exit 1
1467
1468	printf -- "${LUA_PATH}\n"
1469
1470	;;
1471ldir|cdir)
1472	#
1473	# ldir and cdir were deprecated on 2014-12-18. On 2016-03-25 they
1474	# were revived because their names are more intuitive than
1475	# package.path and package.cpath. For now try to support the
1476	# semantics of both by assuming interpreter glob patterns only match
1477	# file names, while preferred install directory string.match
1478	# expressions have directory components.
1479	#
1480	if true; then
1481		MODE="${1}"
1482
1483		# move command to end; rotates to ${1} after loop
1484		set -- "$@" "${1}"
1485		shift
1486
1487		cdir_I=0
1488		cdir_N="$(($# - 1))"
1489		while [ "${cdir_I}" -lt "${cdir_N}" ]; do
1490			if [ "${1#*/}" = "${1}" ]; then
1491				append GLOB "${1}"
1492				warn "%s: passing glob patterns to %s is deprecated" "${1}" "${MODE}"
1493			else
1494				set -- "$@" "${1}"
1495			fi
1496			shift
1497			cdir_I=$((${cdir_I} + 1))
1498		done
1499	fi
1500
1501	findlua || exit 1
1502
1503	findinstalldir "$@" || exit 1
1504
1505	;;
1506package.path|package.cpath)
1507	findlua || exit 1
1508
1509	findinstalldir "$@" || exit 1
1510
1511	;;
1512ccname)
1513	ccname
1514
1515	;;
1516evalmacro)
1517	shift
1518
1519	evalmacro  $*
1520	;;
1521testsym)
1522	shift
1523
1524	if testsym $*; then
1525		printf "found\n"
1526		exit 0
1527	else
1528		printf "not found\n"
1529		exit 1
1530	fi
1531	;;
1532*)
1533	if [ -n "${1:-}" ]; then
1534		warn "%s: unknown command" "${1}"
1535	else
1536		warn "no command specified"
1537	fi
1538
1539	exit 1
1540	;;
1541esac
1542
1543exit 0
1544