1#!@INSTALLSHELLPATH@
2# Copyright (C) 2018, 2021 g10 Code GmbH
3#
4# This file is free software; as a special exception the author gives
5# unlimited permission to copy and/or distribute it, with or without
6# modifications, as long as this notice is preserved.
7#
8# This file is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
10# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# SPDX-License-Identifier: FSFULLR
12
13#### start of functions for this script
14
15#
16# Bourne shell functions for config file in pkg-config style, so that
17# we can share such a config file between pkg-config and script
18#
19
20#
21# get_var: Get the variable value of NAME
22#
23# Variables are recorded in the shell variables named "VAR_<NAME>"
24#
25get_var () {
26    ___name=$1
27
28    eval echo \$VAR_$___name
29}
30
31#
32# get_attr: Get the attribute value of KEY
33#
34# Attributes are recorded in the shell variables named "ATTR_<KEY>"
35#
36get_attr () {
37    ___name=$1
38
39    eval echo \$ATTR_$___name
40}
41
42# variant of get_attr for list (separated by ',')
43get_attr_l () {
44    (IFS=', '; for x in "$(get_attr $1)"; do echo $x; done)
45}
46
47# Remove ${varname} part in the beginning of a string.
48remove_var_expr () {
49    ___varname=$1
50    shift
51
52    expr "$*" : "\${$___varname}\\(.*\\)"
53}
54
55# Given a string, substitute variables.
56substitute_vars () {
57    __string="$1"
58    __varname=""
59    __result=""
60
61    while [ -n "$__string" ]; do
62	case "$__string" in
63	    \$\$*)
64		__result="$__result\$"
65		__string="${__string#\$\$}"
66		;;
67	    \${*}*)
68		__varname="${__string#\$\{}"
69		__varname="${__varname%%\}*}"
70		__result="$__result$(get_var $__varname)"
71		__string=$(remove_var_expr $__varname $__string)
72		;;
73	    *)
74		__result="$__result$(printf %c "$__string")"
75		__string="${__string#$(printf %c "$__string")}"
76		;;
77	esac
78    done
79
80    echo "$__result"
81}
82
83#
84# Read a config from stdin
85#
86# Variables:
87# For VAR=VALUE, value is stored in the shell variable VAR_*.
88#
89# Attributes:
90# For KEY: VALUE, value is stored in the shell variable ATTR_*.
91#
92read_config_from_stdin () {
93    _filename=$1
94    _line=""
95    _varname=""
96    _value=""
97    _key=""
98    _reading_attrs=""
99
100    while read _line; do
101	if [ -z "$_line" ]; then
102	    _reading_attrs=yes
103	    continue
104	elif [ -z "$_reading_attrs" ]; then
105	    case "$_line" in
106		*=*)
107		    _varname="${_line%%=*}"
108		    _value="${_line#*=}"
109		    VAR_list="$VAR_list${VAR_list:+ }VAR_$_varname"
110		    read VAR_$_varname <<EOF1
111$(substitute_vars "$_value")
112EOF1
113		    continue
114		    ;;
115		*) _reading_attrs=yes ;;
116	    esac
117	fi
118	if [ -n "$_reading_attrs" ]; then
119	    case "$_line" in
120		*:\ *)
121		    _key="${_line%%:\ *}"
122		    _value="${_line#*:\ }"
123		    if expr "$_key" : ".*\..*" >/dev/null; then
124			_key="${_key%.*}_${_key#*.}"
125		    fi
126		    ATTR_list="$ATTR_list${ATTR_list:+ }ATTR_$_key"
127		    read ATTR_$_key <<EOF2
128$(substitute_vars "$_value")
129EOF2
130		    ;;
131		*:|*:\ ) ;;
132		*)
133		    echo "Error reading $_filename: $_line" 1>&2
134		    exit 1
135		    ;;
136	    esac
137	fi
138    done
139}
140
141
142find_file_in_path () {
143    _f=$1
144    _p=$2
145    _saved_IFS="$IFS"
146    _arg=""
147    IFS=":"		# On Windows it should be ";"???
148
149    for _arg in $_p; do
150	if [ -r $_arg/$_f ]; then
151	    RESULT="$_arg/$_f"
152	    IFS="$_saved_IFS"
153	    return 0
154	fi
155    done
156    IFS="$_saved_IFS"
157    RESULT=""
158    return 1
159}
160
161read_config_file () {
162    if ! find_file_in_path $1.pc $2; then
163	if [ -z "$want_exists" ]; then
164	    echo "Can't find $1.pc" 1>&2
165	fi
166	exit 1
167    fi
168    read_config_from_stdin $RESULT < $RESULT
169}
170
171cleanup_vars_attrs () {
172    eval unset $VAR_list VAR_list
173    eval unset $ATTR_list ATTR_list
174}
175
176not_listed_yet () {
177    ___m=$1
178    ___arg=""
179    shift
180
181    for ___arg; do
182	if [ $___m = $___arg ]; then
183	    return 1
184	fi
185    done
186
187    return 0
188}
189
190list_only_once () {
191    __result=""
192    __arg=""
193
194    for __arg; do
195	if not_listed_yet $__arg $__result; then
196	    __result="$__result${__result:+ }$__arg"
197	fi
198    done
199
200    echo $__result
201}
202
203list_only_once_for_libs () {
204    __result=""
205    __rev_list=""
206    __arg=""
207
208    # Scan the list and eliminate duplicates for non-"-lxxx"
209    # the resulted list is in reverse order
210    for __arg; do
211	case "$__arg" in
212	    -l*)
213		# As-is
214		__rev_list="$__arg${__rev_list:+ }$__rev_list"
215		;;
216	    *)
217		if not_listed_yet $__arg $__rev_list; then
218		    __rev_list="$__arg${__rev_list:+ }$__rev_list"
219		fi
220		;;
221	esac
222    done
223
224    # Scan again
225    for __arg in $__rev_list; do
226	case "$__arg" in
227	    -l*)
228		if not_listed_yet $__arg $__result; then
229		    __result="$__arg${__result:+ }$__result"
230		fi
231		;;
232	    *)
233		# As-is
234		__result="$__arg${__result:+ }$__result"
235		;;
236	esac
237    done
238
239    echo $__result
240}
241
242arg1_is_same () {
243    [ "$1" = "=" -o "$1" = ">=" -o "$1" = "<=" ]
244}
245
246arg1_is_less () {
247    [ "$1" = "!=" -o "$1" = "<" -o "$1" = "<=" ]
248}
249
250arg1_is_great () {
251    [ "$1" = "!=" -o "$1" = ">" -o "$1" = ">=" ]
252}
253
254#
255# Evaluate comparison between versions in RPM way
256#
257eval_compare_version () {
258    ___str1="$1"
259    ___cmp="$2"
260    ___str2="$3"
261    ___char1=""
262    ___char2=""
263    ___chunk1=""
264    ___chunk2=""
265
266    while [ -n "$___str1" -a -n "$___str2" ]; do
267	# Trim anything that's not alnum or tilde from the front
268	___str1="$(expr "$___str1" : '[^0-9A-Za-z~]*\(.*\)')"
269	___str2="$(expr "$___str2" : '[^0-9A-Za-z~]*\(.*\)')"
270
271	# Get the first character
272	___char1=${___str1%${___str1#?}}
273	___char2=${___str2%${___str2#?}}
274
275	if [ "$___char1" = ~ -o "$___char2" = ~ ]; then
276	    if [ "$___char1" != ~ ]; then
277		arg1_is_great $___cmp
278		return
279	    fi
280	    if [ "$___char2" != ~ ]; then
281		arg1_is_less $___cmp
282		return
283	    fi
284	    ___str1=${___str1#~}
285	    ___str2=${___str2#~}
286	    continue
287	fi
288
289	if [ -z "$___char1" -o -z "$___char2" ]; then
290	    break
291	fi
292
293	case "$___char1$___char2" in
294	    [0-9][A-Za-z])
295		arg1_is_great $___cmp
296		return
297		;;
298	    [A-Za-z][0-9])
299		arg1_is_less $___cmp
300		return
301		;;
302	    [0-9][0-9])
303		___chunk1="$(expr "$___str1" : '\([0-9]*\)')"
304		___chunk2="$(expr "$___str2" : '\([0-9]*\)')"
305		;;
306	    [A-Za-z][A-Za-z])
307		___chunk1="$(expr "$___str1" : '\([A-Za-z]*\)')"
308		___chunk2="$(expr "$___str2" : '\([A-Za-z]*\)')"
309		;;
310	esac
311
312	# Compare chunks numerically if digits, or lexicographically
313	if expr "$___chunk1" "!=" "$___chunk2" >/dev/null; then
314	    if expr "$___chunk1" ">" "$___chunk2" >/dev/null; then
315		arg1_is_great $___cmp
316		return
317	    else
318		arg1_is_less $___cmp
319		return
320	    fi
321	fi
322
323	# Remove the chunk
324	___str1="${___str1#$___chunk1}"
325	___str2="${___str2#$___chunk2}"
326    done
327
328    # Either STR1, STR2 or both is empty here
329    if [ -n "$___str1" ]; then
330	case "$___str1" in
331	    ~*) arg1_is_less $___cmp ;;
332	    *)  arg1_is_great $___cmp ;;
333	esac
334    elif [ -n "$___str2" ]; then
335	case "$___str2" in
336	    ~*) arg1_is_great $___cmp ;;
337	    *)  arg1_is_less $___cmp ;;
338	esac
339    else
340	arg1_is_same $___cmp
341    fi
342}
343
344#
345# Recursively solve package dependencies
346#
347# Result is in the PKG_LIST variable
348#
349all_required_config_files () {
350    all_list=""
351    new_list=""
352    p=""
353    pkg=""
354    cmp=""
355
356    list=$*
357    while [ -n "$list" ]; do
358	for p in $list; do
359	    if [ -z "$pkg" ]; then
360		pkg=$p
361	    elif [ -z "$cmp" ]; then
362		case "$p" in
363		    "="|"!="|"<"|">"|"<="|">=") cmp=$p ;;
364		    *)
365			read_config_file $pkg $PKG_CONFIG_PATH
366			all_list="$all_list${all_list:+ }$pkg"
367			new_list="$new_list${new_list:+ }$(get_attr_l Requires)"
368			if [ -n "$enable_static" ]; then
369			    new_list="$new_list${new_list:+ }$(get_attr_l Requires_private)"
370			fi
371			cleanup_vars_attrs
372			pkg=$p
373			;;
374		esac
375	    else
376		read_config_file $pkg $PKG_CONFIG_PATH
377		if ! eval_compare_version "$(get_attr Version)" $cmp $p; then
378		    echo "Version mismatch for $pkg $cmp $p: $(get_attr Version)" 1>&2
379		    exit 1
380		fi
381		all_list="$all_list${all_list:+ }$pkg"
382		new_list="$new_list${new_list:+ }$(get_attr_l Requires)"
383		if [ -n "$enable_static" ]; then
384		    new_list="$new_list${new_list:+ }$(get_attr_l Requires_private)"
385		fi
386		cleanup_vars_attrs
387		pkg=""
388		cmp=""
389	    fi
390	done
391	if [ -n "$cmp" ]; then
392	    echo "No version after comparison operator ($cmp): $pkg" 1>&2
393	    exit 1
394	elif [ -n "$pkg" ]; then
395	    read_config_file $pkg $PKG_CONFIG_PATH
396	    all_list="$all_list${all_list:+ }$pkg"
397	    new_list="$new_list${new_list:+ }$(get_attr_l Requires)"
398	    if [ -n "$enable_static" ]; then
399		new_list="$new_list${new_list:+ }$(get_attr_l Requires_private)"
400	    fi
401	    cleanup_vars_attrs
402	fi
403
404	list="$new_list"
405	new_list=""
406    done
407
408    PKG_LIST=$(list_only_once $all_list)
409}
410
411#
412# Modify -I or -L by PKG_CONFIG_SYSROOT_DIR variable
413#
414sysroot () {
415    _opt="$1"
416    _result=""
417    shift
418
419    while [ $# -gt 0 ]; do
420	if [ $1 = $_opt ]; then
421	    _result="$_result${_result:+ }$_opt"
422	    shift
423	    _result="$_result $PKG_CONFIG_SYSROOT_DIR$1"
424	elif expr "x$1" : "^x$_opt" >/dev/null; then
425	    _result="$_result${_result:+ }$_opt$PKG_CONFIG_SYSROOT_DIR$(expr "x$1" : "^x$_opt\(.*\)")"
426	else
427	    _result="$_result${_result:+ }$1"
428	fi
429	shift
430    done
431    echo "$_result"
432}
433
434# Show usage
435usage () {
436    cat <<EOF
437Usage: gpgrt-config [--libdir=LIBDIR] [OPTIONS] MODULES
438Options:
439	[--exists]
440	[--modversion]
441	[--libs]
442	[--cflags]
443	[--static]
444	[--variable=VARNAME]
445EOF
446    exit $1
447}
448#### end of functions for this script
449
450myname=${0##*/}
451if [ $myname = gpgrt-config ]; then
452  default_module="gpg-error"
453else
454  default_module=${myname%-config}
455fi
456
457# First stage to process --libdir option
458
459libdir=""
460while test $# -gt 0; do
461    case $1 in
462	--libdir=*)
463	    libdir=${1#--libdir=}
464	    shift
465	    ;;
466	*)
467	    break
468	    ;;
469    esac
470done
471
472if [ x"${PKG_CONFIG_LIBDIR:+set}" = xset -a -z "$PKG_CONFIG_LIBDIR" ]; then
473  # The variable set as empty, we use PKG_CONFIG_PATH in this case,
474  # ignoring --libdir option
475  if [ -z "$PKG_CONFIG_PATH"  ]; then
476    echo "Please have valid PKG_CONFIG_PATH if PKG_CONFIG_LIBDIR is empty" 1>&2
477    exit 1
478  fi
479else
480  if [ -n "$libdir" ]; then
481    # --libdir option is available, it overrides existing PKG_CONFIG_LIBDIR
482    PKG_CONFIG_LIBDIR=$libdir/pkgconfig
483  fi
484  if [ -z "$PKG_CONFIG_LIBDIR" ]; then
485    if [ -z "$PKG_CONFIG_PATH" ]; then
486      echo "Please use --libdir=LIBDIR option or set PKG_CONFIG_LIBDIR" 1>&2
487      echo "Or set PKG_CONFIG_PATH" 1>&2
488      exit 1
489    fi
490  else
491    # PKG_CONFIG_LIBDIR is available here
492    # Modify PKG_CONFIG_PATH, prepending PKG_CONFIG_LIBDIR
493    PKG_CONFIG_PATH="$PKG_CONFIG_LIBDIR${PKG_CONFIG_PATH:+:}$PKG_CONFIG_PATH"
494  fi
495fi
496# PKG_CONFIG_PATH is ready here
497
498#
499
500if test $# -eq 0; then
501    usage 1 1>&2
502fi
503
504
505# Second stage to do the main functionality
506
507module_list=""
508want_var=""
509want_attr=""
510want_cflags=""
511want_libs=""
512want_exists=""
513enable_static=""
514
515cflags=""
516libs=""
517mtcflags=""
518mtlibs=""
519
520output=""
521
522mt="no"
523
524VAR_list=VAR_pc_sysrootdir
525if [ -z "$PKG_CONFIG_SYSROOT_DIR" ]; then
526    VAR_pc_sysrootdir="/"
527else
528    VAR_pc_sysrootdir="$PKG_CONFIG_SYSROOT_DIR"
529fi
530
531while test $# -gt 0; do
532    case $1 in
533	#### pkg-config incompatible options: begin
534	--prefix)
535	    # In future, use --variable=prefix instead.
536	    want_var=prefix
537	    ;;
538	--exec-prefix)
539	    # In future, use --variable=exec_prefix instead.
540	    want_var=exec_prefix
541	    ;;
542	--version)
543	    # In future, use --modversion instead.
544	    want_attr=Version
545	    ;;
546	--api-version)
547	    # In future, use --variable=api_version instead.
548	    want_var=api_version
549	    ;;
550	--host)
551	    # In future, use --variable=host instead.
552	    want_var=host
553	    ;;
554	--mt)
555	    # In future, use --variable=mtcflags or --variable=mtlibs.
556	    mt=yes
557	    ;;
558	#### pkg-config incompatible options: end
559	--modversion)
560	    want_attr=Version
561	    ;;
562	--exists)
563	    want_exists=yes
564	    ;;
565	--cflags)
566	    want_cflags=yes
567	    ;;
568	--libs)
569	    want_libs=yes
570	    ;;
571	--static)
572	    enable_static=yes
573	    ;;
574	--variable=*)
575	    want_var=${1#*=}
576	    ;;
577	--help)
578	    usage 0
579	    ;;
580	--*)
581	    usage 1 1>&2
582	    ;;
583	*)
584	    # Modules
585	    module_list="$module_list${module_list:+ }$1"
586	    ;;
587    esac
588
589    shift
590done
591
592
593if [ -z "$module_list" ]; then
594    module_list=$default_module
595elif expr "$module_list" : "=\|!=\|<\|>\|<=\|>=" >/dev/null; then
596    module_list="$default_module $module_list"
597fi
598
599all_required_config_files $module_list
600
601for p in $PKG_LIST; do
602    read_config_file $p $PKG_CONFIG_PATH
603    # For want_var or want_attr, get it from the first package
604    if [ -n "$want_var" ]; then
605	output="$(get_var $want_var)"
606	break
607    elif [ -n "$want_attr" ]; then
608	output="$(get_attr $want_attr)"
609	break
610    else
611	cflags="$cflags${cflags:+ }$(get_attr Cflags)"
612	libs="$libs${libs:+ }$(get_attr Libs)"
613	if [ -n "$enable_static" ]; then
614	    libs="$libs${libs:+ }$(get_attr Libs_private)"
615	fi
616
617	if [ $p = "gpg-error" ]; then
618	    mtcflags="$(get_var mtcflags)"
619	    mtlibs="$(get_var mtlibs)"
620	fi
621    fi
622    cleanup_vars_attrs
623done
624
625if [ -z "$want_var" -a -z "$want_attr" ]; then
626    if [ -n "$want_cflags" ]; then
627	output="$output${output:+ }$(sysroot -I $(list_only_once $cflags))"
628	# Backward compatibility to old gpg-error-config
629	if [ $mt = yes -a -n "$mtcflags" ]; then
630	    output="$output${output:+ }$mtcflags"
631	fi
632    fi
633    if [ -n "$want_libs" ]; then
634	output="$output${output:+ }$(sysroot -L $(list_only_once_for_libs $libs))"
635	# Backward compatibility to old gpg-error-config
636	if [ $mt = yes -a -n "$mtlibs" ]; then
637	    output="$output${output:+ }$mtlibs"
638	fi
639    fi
640fi
641
642if [ -z "$want_exists" ]; then
643  echo "$output"
644fi
645
646exit 0
647