#!/usr/bin/env bash

# Make the list sourse files for split to a one file from obj-files
#
# This program parse .o-files and does:
# 1) get exported symbols: `nm --defined-only -g $*`
# 2) get undefined symbols: `nm -u $*`
# 3) skip absolutely undefined
# 4) find a obj-file with all defined symbols and show
#    with changed the file's suffix
# 5) find minimal forward declarations, show the need declare symbol from source
# 6) loop 4-6
#
#  Copyright (C) 2017-2022 by Vladimir Oleynik <dzo@simtreas.ru>
#  may safely be consumed by a GPL license.

usage() {
	[[ $1 -eq 0 ]] && \
	echo "Make the list sourse files for split to a one file from obj-files"
	echo $'\n'"Usage:"$'\n'"$0 [-d] [-s suffix] [-v] obj-files"
	echo $'\t'"-d    - do not show forward declarations"
	echo $'\t'"-s s  - change suffix .o to the 's' for output, default: -s .c"
	echo $'\t'"-v    - verbose mode (output to stderr)"
	[[ $1 -eq 0 ]] && \
	echo $'\n'"For example:"$'\n'"$0 -s .c++ *.o"
	exit $1
}

SUFFIX=.c D=1 v=
while getopts "ds:v" o; do
    case "$o" in
    d) D=0 ;;
    s) SUFFIX=$OPTARG ;;
    v) v=1 ;;
    *) usage 2 >&2 ;;
    esac
done
shift $((OPTIND-1))

[[ $# -eq 0 ]] && usage 0


declare -a OBJ DL UL C
declare -ai U_S U_C
declare -i i=0 j=0 k m max_s
declare -A U
declare -Ai DJ

echov() {
	[[ $v ]] && echo "$1" >&2
}

for o ; do
	while read a u s; do
		DJ[$s]=j
		DL[j]+=" $s"
	done < <(nm --defined-only -g "$o")

	U_S[j]=i

	while read u s; do
		U[$s]+=" $i"
		UL[i++]=$s
	done < <(nm -u "$o")

	U_C[j]=i-U_S[j]
	OBJ[j++]=${o%.o}${SUFFIX}
	echov "$o"
done
max_s=i+1

# remove absolutely undefined
for u in ${!U[*]}; do
	[[ ${DJ[$u]} ]] && continue
	for i in ${U[$u]}; do
		UL[i]=
	done
done
echov

all_defined() {
	echo "${OBJ[j]}"
	unset 'OBJ[j]'
	for s in ${DL[j]}; do
		for i in ${U[$s]}; do
			UL[i]=
		done
	done
}

while [[ ${#OBJ[*]} -ne 0 ]]; do

  m=max_s
  for j in ${!OBJ[*]}; do
	C=(${UL[*]:U_S[j]:U_C[j]})
	i=${#C[*]}
	[[ i -ge m ]] && continue
	if [[ i -eq 0 ]]; then
		all_defined
		continue 2
	fi
	k=j m=i
  done
  j=k

  echov $'\n'"${OBJ[j]} need forward decalarations $m symbols"
  for u in ${UL[@]:U_S[j]:U_C[j]}; do
	[[ D -ne 0 ]] && echo " $u: ${OBJ[DJ[$u]]}"
	for i in ${U[$u]}; do
		UL[i]=
	done
  done
  all_defined

done
