
COREPATH=../../../src

#include $(COREPATH)/Makefile.defs

CFG2TXT=../../scripts/cdefs2doc/dump_rpcs.pl
CFG2DOCBOOK=../../scripts/cdefs2doc/dump_rpcs.pl

# output directory for generated txt files
txt_output_dir=.
# output directory for generated docbook xml files
docbook_output_dir=docbook

# list of files containing rpc defs in the following format:
# <filename>:<cfg_grp_name>
# can be easily updated by adding the output of:
#   make diff-list   (which obeys grp_exclude and file_exclude)
# or completely regenerated by replacing files_list with the output of:
#   make gen-files-list
# NOTE: suffix duplicated modules located in modules_s with '_s' to
# avoid file naming conflicts
#
files_list= \
		$(COREPATH)/core/core_cmd.c:core \
		$(COREPATH)/modules/app_jsdt/app_jsdt_api.c:app_jsdt \
		$(COREPATH)/modules/app_lua/app_lua_mod.c:app_lua \
		$(COREPATH)/modules/app_perl/app_perl_mod.c:app_perl \
		$(COREPATH)/modules/app_sqlang/app_sqlang_api.c:app_sqlang \
		$(COREPATH)/modules/auth_ephemeral/auth_ephemeral_mod.c:auth_ephemeral \
		$(COREPATH)/modules/auth_xkeys/auth_xkeys.c:auth_xkeys \
		$(COREPATH)/modules/benchmark/benchmark.c:benchmark \
		$(COREPATH)/modules/call_obj/call_obj_mod.c:call_obj \
		$(COREPATH)/modules/carrierroute/carrierroute.c:carrierroute \
		$(COREPATH)/modules/cdp/cdp_rpc.c:cdp \
		$(COREPATH)/modules/cfg_rpc/cfg_rpc.c:cfg_rpc \
		$(COREPATH)/modules/cfgt/cfgt_int.c:cfgt \
		$(COREPATH)/modules/cfgutils/cfgutils.c:cfgutils \
		$(COREPATH)/modules/cnxcc/cnxcc_mod.c:cnxcc \
		$(COREPATH)/modules/corex/corex_rpc.c:corex \
		$(COREPATH)/modules/counters/counters.c:counters \
		$(COREPATH)/modules/cplc/cpl_loader.c:cplc \
		$(COREPATH)/modules/ctl/ctl.c:ctl \
		$(COREPATH)/modules/db_berkeley/db_berkeley.c:db_berkeley \
		$(COREPATH)/modules/db_flatstore/flat_rpc.c:db_flatstore \
		$(COREPATH)/modules/db_flatstore/km_flatstore_mod.c:db_flatstore \
		$(COREPATH)/modules/db_text/db_text.c:db_text \
		$(COREPATH)/modules/debugger/debugger_api.c:debugger \
		$(COREPATH)/modules/dialog/dialog.c:dialog \
		$(COREPATH)/modules/dialplan/dialplan.c:dialplan \
		$(COREPATH)/modules/dispatcher/dispatcher.c:dispatcher \
		$(COREPATH)/modules/dmq/dmq.c:dmq \
		$(COREPATH)/modules/domain/domain_mod.c:domain \
		$(COREPATH)/modules/drouting/drouting.c:drouting \
		$(COREPATH)/modules/htable/htable.c:htable \
		$(COREPATH)/modules/http_client/curlrpc.c:http_client \
		$(COREPATH)/modules/imc/imc.c:imc \
		$(COREPATH)/modules/ims_dialog/ims_dialog.c:ims_dialog \
		$(COREPATH)/modules/ims_registrar_scscf/reg_rpc.c:ims_registrar_scscf \
		$(COREPATH)/modules/ims_usrloc_pcscf/ul_rpc.c:ims_usrloc_pcscf \
		$(COREPATH)/modules/ims_usrloc_scscf/ul_rpc.c:ims_usrloc_scscf \
		$(COREPATH)/modules/jsonrpcs/jsonrpcs_mod.c:jsonrpcs \
		$(COREPATH)/modules/keepalive/keepalive_rpc.c:keepalive \
		$(COREPATH)/modules/kex/core_stats.c:kex \
		$(COREPATH)/modules/kex/mod_stats.c:kex \
		$(COREPATH)/modules/kex/pkg_stats.c:kex \
		$(COREPATH)/modules/lcr/lcr_rpc.c:lcr \
		$(COREPATH)/modules/malloc_test/malloc_test.c:malloc_test \
		$(COREPATH)/modules/matrix/matrix.c:matrix \
		$(COREPATH)/modules/mqueue/mqueue_mod.c:mqueue \
		$(COREPATH)/modules/msrp/msrp_cmap.c:msrp \
		$(COREPATH)/modules/mtree/mtree_mod.c:mtree \
		$(COREPATH)/modules/nathelper/nathelper.c:nathelper \
		$(COREPATH)/modules/pdb/pdb.c:pdb \
		$(COREPATH)/modules/pdt/pdt.c:pdt \
		$(COREPATH)/modules/permissions/permissions.c:permissions \
		$(COREPATH)/modules/pike/pike_rpc.c:pike \
		$(COREPATH)/modules/pipelimit/pipelimit.c:pipelimit \
		$(COREPATH)/modules/prefix_route/pr_rpc.c:prefix_route \
		$(COREPATH)/modules/presence/presence.c:presence \
		$(COREPATH)/modules/pua/pua.c:pua \
		$(COREPATH)/modules/pua_rpc/pua_rpc.c:pua_rpc \
		$(COREPATH)/modules/pv/pv.c:pv \
		$(COREPATH)/modules/ratelimit/ratelimit.c:ratelimit \
		$(COREPATH)/modules/regex/regex_mod.c:regex \
		$(COREPATH)/modules/rls/rls.c:rls \
		$(COREPATH)/modules/rtpengine/rtpengine.c:rtpengine \
		$(COREPATH)/modules/rtpproxy/rtpproxy.c:rtpproxy \
		$(COREPATH)/modules/sca/sca.c:sca \
		$(COREPATH)/modules/sctp/sctp_rpc.c:sctp \
		$(COREPATH)/modules/sipcapture/sipcapture.c:sipcapture \
		$(COREPATH)/modules/siptrace/siptrace.c:siptrace \
		$(COREPATH)/modules/sl/sl_stats.c:sl \
		$(COREPATH)/modules/statsc/statsc_mod.c:statsc \
		$(COREPATH)/modules/tls/tls_rpc.c:tls \
		$(COREPATH)/modules/tm/tm.c:tm \
		$(COREPATH)/modules/tsilo/ts_rpc.c:tsilo \
		$(COREPATH)/modules/uac/uac_reg.c:uac \
		$(COREPATH)/modules/uid_domain/domain_rpc.c:uid_domain \
		$(COREPATH)/modules/uid_gflags/uid_gflags.c:uid_gflags \
		$(COREPATH)/modules/userblacklist/userblacklist.c:userblacklist \
		$(COREPATH)/modules/usrloc/ul_rpc.c:usrloc \
		$(COREPATH)/modules/websocket/websocket.c:websocket \
		$(COREPATH)/modules/xcap_client/xcap_client.c:xcap_client \
		$(COREPATH)/modules/xhttp_pi/xhttp_pi.c:xhttp_pi


# list of excluded groups
grp_exclude=pa
# list of file prefixes to exclude (full path needed)
file_exclude=$(COREPATH)/obsolete

# special per file group overrides
# format= grp_filename=... ,where filename does not contain the extension
# e.g.:
#     grp_f_tcp_options=tcp
#     grp_f_sctp_options=sctp

# special per group group name overrides
# e.g.:
#      grp_g_maxfwd=mf

# override auto-detected group if set to 1 (else the group is used inside the
# file only if it cannot be aut-odetected)
ifeq ($(group_override),1)
override force_grp=force-
else
override force_grp=
endif

# command used for gcc (contains extra includes)
gcc=gcc
#-I$(COREPATH)/lib -I$(COREPATH) -I/usr/include/libxml2

# defines used by gcc
c_defsX=-DNAME='\"kamailio\"' -DVERSION='\"5.1.0-dev3\"' -DARCH='\"x86_64\"' \
	   -DOS='linux_' -DOS_QUOTED='\"linux\"' -DCOMPILER='\"gcc 4.9.2\"' \
	   -D__CPU_x86_64 -D__OS_linux -DSER_VER=5001000 \
	   -DCFG_DIR='\"/usr/local/etc/kamailio/\"' \
	   -DRUN_DIR='\"/run/kamailio/\"' -DPKG_MALLOC -DSHM_MEM -DSHM_MMAP \
	   -DDNS_IP_HACK -DUSE_MCAST -DUSE_TCP -DDISABLE_NAGLE -DHAVE_RESOLV_RES \
	   -DUSE_DNS_CACHE -DUSE_DNS_FAILOVER -DUSE_DST_BLOCKLIST -DUSE_NAPTR \
	   -DMEM_JOIN_FREE -DF_MALLOC -DQ_MALLOC -DTLSF_MALLOC \
	   -DDBG_SR_MEMORY -DUSE_TLS -DTLS_HOOKS -DUSE_CORE_STATS -DSTATISTICS \
	   -DMALLOC_STATS -DWITH_AS_SUPPORT -DUSE_SCTP -DFAST_LOCK -DADAPTIVE_WAIT \
	   -DADAPTIVE_WAIT_LOOPS=1024 -DCC_GCC_LIKE_ASM -DHAVE_GETHOSTBYNAME2 \
	   -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD -DHAVE_MSG_NOSIGNAL \
	   -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H -DHAVE_TIMEGM \
	   -DHAVE_SCHED_SETSCHEDULER -DHAVE_IP_MREQN -DHAVE_EPOLL -DHAVE_SIGIO_RT \
	   -DSIGINFO64_WORKAROUND -DUSE_FUTEX -DHAVE_SELECT

c_defs=$(subst ^^,='\",$(subst ",\"',$(subst =",^^,$(shell $(MAKE) -s -C ../../../src printcdefs))))

c_defs+= -DMOD_NAME='\"rpcgen\"'
c_defs+= -I$(COREPATH)/modules/app_sqlang/squirrel/include

# common makefile vars used in defs
LOCALBASE=/usr/local
SYSBASE=/usr

filter_files=$(filter-out $(addsuffix %,$(file_exclude)),\
				$(filter-out $(addprefix %:,$(grp_exclude)),$(1)))

#filtered files list
flist=$(call filter_files,$(files_list))

# throws an error if input is not in the format filename:grp
check_fname_grp=$(if $(filter-out 2,$(words $(subst :, ,$(1)))),\
	$(error bad format "$(1)", it should be filename:grp))

# get prereq from file:grp (get_prereq(file:grp) => file)
get_prereq=$(firstword $(subst :, ,$(1)))

# get grp from file:grp (get_grp(file:grp) => grp)
get_listed_grp=$(word 2, $(subst :, ,$(1)))

# get module interface define
get_modiface=$(word 3, $(subst :, ,$(1)))

find_modiface=$(if $(findstring modules,$(1)),$(shell $(MAKE) -s -C $(dir $(1)) printmiface),-DNONE)

# get base file name from file:grp: get_bname(file:grp)
# => basename(file) without extension (e.g. get_bname(foo/bar.c:x) => bar)
#
get_bname=$(basename $(notdir $(call get_prereq,$(1))))

#get grp from file:grp, using the overrides
get_grp=$(strip $(if $(grp_f_$(call get_bname,$(1))), \
					$(grp_f_$(call get_bname,$(1))),\
					$(if $(grp_g_$(call get_listed_grp,$(1))),\
						$(grp_g_$(call get_listed_grp,$(1))),\
						$(call get_listed_grp,$(1))) ) )

# get target from file:grp (get_target(file:grp) => rpc_grp.txt)
get_target=rpc_$(call get_grp,$(1))


# $(LF) definition (do not remove)
define LF


endef

# get all the lines containing DEFS or INCLUDES definitions from the Makefile.
# WARNING: does not work with all sed implementation (tested with GNU sed).
# It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are
# replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF.
get_make_idefs=$(subst ^LF^,$(LF),$(shell sed \
 -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*[^\]$$/H'\
 -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/H'\
 -ne '$${g;s/\n/^LF^/g;p}'\
< $(1)/Makefile ))


# get all the lines from the makefile containing variable definitions.
# It will also return conditionals and try to filter out possible rules.
# WARNING: does not work with all sed implementation (tested with GNU sed).
# It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are
# replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF.
get_make_vars=$(subst ^LF^,$(LF),$(shell sed -n \
 -e ': start' \
 -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]*\($$\|.*[^\]$$\)/{H;b end}' \
 -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]\+.*[\]$$/,/[^\]$$/{H;b end}' \
 -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \
 -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*[^\]$$/{H;b end}' \
 -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/{H;b end}' \
 -e ': end' \
 -e '$${g;s/\n/^LF^/g;p}'\
 -e 'b' \
 -e ': eat_rule' \
 -e '$$b end' \
 -e 'n' \
 -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \
 -e '/^[\t]/b eat_rule' \
 -e 'b start' \
< $(1)/Makefile ))


define  mk_rules

$(call check_fname_grp, $(1))

#$$(info generating rpc_$$(call get_grp,$(1)).txt: $$(call get_prereq,$(1)))


DEFS:=
INCLUDES:=
# extract all the includes and defs from the module makefile and
# evaluate them
$$(eval $$(call get_make_vars,$$(dir $$(call get_prereq,$(1)))))
# override COREPATH (we know better)
COREPATH=../../../src
# save the result in a per group e_idefs_<grp_name> var
$$(eval e_idefs_$$(call get_grp,$(1)):=$$(DEFS) $$(INCLUDES))

# debugging:
#$$(info eval: $$(call get_make_vars,$$(dir $$(call get_prereq,$(1)))))
#$$(info e_idefs_$$(call get_grp,$(1))=$$(e_idefs_$$(call get_grp,$(1))))

$(txt_output_dir)/$$(call get_target,$(1)).txt: \
								$$(call get_prereq,$(1)) Makefile $(CFG2TXT)
	$(CFG2TXT) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \
		--gcc="$(gcc)" --txt \
		--defs="$(c_defs) $$(call get_modiface,$(1)) $$(e_idefs_$$(call get_grp,$(1)))" \
		> "$$@" || (rm -f "$$@"; exit 1)

$(docbook_output_dir)/$$(call get_target,$(1)).xml: \
								$$(call get_prereq,$(1)) Makefile $(CFG2TXT)
	$(CFG2DOCBOOK) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \
		--gcc="$(gcc)" --docbook \
		--defs="$(c_defs) $$(e_idefs_$$(call get_grp,$(1)))" \
		> "$$@" || (rm -f "$$@"; exit 1)


clean_$$(call get_target,$(1)).txt:
	rm -f "$(txt_output_dir)/$$(call get_target,$(1)).txt"

clean_$$(call get_target,$(1)).xml:
	rm -f "$(docbook_output_dir)/$$(call get_target,$(1)).xml"

txt: $(txt_output_dir)/$$(call get_target,$(1)).txt

docbook: $(docbook_output_dir)/$$(call get_target,$(1)).xml

clean_txt: clean_$$(call get_target,$(1)).txt

clean_docbook: clean_$$(call get_target,$(1)).xml


endef

find_rpc_files_cmd= find $(COREPATH) -type f -name "*.c" \
		-exec grep "rpc_export_t[	 ][a-zA-Z0-9_]*\[\][	 ]*=" /dev/null {} \; \
		| cut -d: -f1

# shell command to generate a file:grp list from a list of files
# grp will be the modulename if the file is in a module directory or
# the file name with the extension and _cmd, cmd_ or _rpc stripped out of
# it.
# output: list of  " "filename":"grpname
gen_file_grp_cmd=\
sed -e "s!\(.*/modules[^/]*/\([^/][^/]*\)/.*\)! \1:\2!" \
    -e "s!^\([^ ].*/\([^/.]*\)[^/]*$$\)!\1:\2!" \
    -e "s!^\([^ :]*\):\(.*\)_cmd[_]*!\1:\2!" \
    -e "s!^\([^ :]*\):\(.*\)cmd[_]*!\1:\2!" \
    -e "s!^\([^ :]*\):\(.*\)_rpc[_]*!\1:\2!"

# special vars for generating the list of files or updates
found_lst=$(shell $(find_rpc_files_cmd) | $(gen_file_grp_cmd))
# filtered found lst
f_found_lst=$(call filter_files,$(found_lst))
diff_lst=$(filter-out $(flist),$(f_found_lst))


get_core_files=$(filter-out $(COREPATH)/modules% $(COREPATH)/lib%,$(1))
sort_files=$(sort $(call get_core_files,$(1)))\
			$(sort $(filter-out $(call get_core_files,$(1)),$(1)))

# replace $(COREPATH) with the text "$(COREPATH)"
subst_corepath=$(patsubst $(patsubst %/,%,$(COREPATH))/%,\$$(COREPATH)/%,$(1))


# help will be the default rule (on-purpose since without having a patched
# GCC:TranslationUnit module, make all won't work)
.PHONY: help
help:
	@echo "To regenerate $(foreach f,$(flist),$(call get_target,$f).{txt,xml})"
	@echo "type: $(MAKE) all ."
	@echo "or to regenerate all the rpc lists by searching all"
	@echo " the source files for definitions, type: $(MAKE) autogen ."
	@echo "NOTE: you need the GCC:TranslationUnit perl module with an "
	@echo "extra patch applied (see $(CFG2TXT) --patch)."

.PHONY: txt
txt:

.PHONY: docbook
docbook:

.PHONY: clean_txt
clean_txt:

.PHONY: clean_docbook
clean_docbook:


.PHONY: all
all: txt $(docbook_output_dir)/rpc_list.xml

.PHONY: clean
clean: clean_txt clean_docbook
	@rm -f $(docbook_output_dir)/rpc_list.xml

.PHONY: proper
proper:
	@rm -f $(txt_output_dir)/rpc_*.txt
	@rm -f $(docbook_output_dir)/rpc_*.xml

repo_ver="Kamailio "\
	"git-$(shell  git rev-parse --verify --short=6 HEAD 2>/dev/null)"
ifeq ($(repo_ver),git-)
repo_ver="Kamailio unknown"
endif

$(docbook_output_dir)/rpc_list.xml: Makefile \
		$(foreach f,$(flist),$(docbook_output_dir)/$(call get_target,$f).xml)
	@echo '<?xml version="1.0" encoding="UTF-8"?>' >$@
	@echo '<!-- this file is autogenerated, do not edit! -->' >>$@
	@echo '<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"' >>$@
	@echo '	"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"' >>$@
	@echo '	[ <!ENTITY % local.common.attrib' >>$@
	@echo "	 \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2001/XInclude'\">]">>$@
	@echo '>' >>$@
	@echo '<book id="rpc_list"'\
		'xmlns:xi="http://www.w3.org/2001/XInclude">' >>$@
	@echo '	<title>RPC Commands List</title>' >>$@
	@echo '	<bookinfo>' >>$@
	@echo '   <productname class="trade">kamailio.org</productname>' >>$@
	@echo '   <authorgroup>' >>$@
	@echo '    <author>' >>$@
	@echo '      <firstname>Kamailio</firstname>' >>$@
	@echo '      <surname>Development Team</surname>' >>$@
	@echo '      <affiliation><orgname>https://www.kamailio.org</orgname></affiliation>' >>$@
	@echo '      <address>sr-dev@lists.kamailio.org</address>' >>$@
	@echo '    </author>' >>$@
	@echo '   </authorgroup>' >>$@
	@echo '   <copyright>' >>$@
	@echo '    <year>2008-2017</year>' >>$@
	@echo '    <holder>Kamailio Project</holder>' >>$@
	@echo '   </copyright>' >>$@
	@echo '	</bookinfo>' >>$@
	@$(foreach f,$(flist),\
		echo '		<xi:include'\
			'href="'$(call get_target,$f).xml'"/>' \
			>>$@ ; )
	@echo '</book>' >>$@

# finds all the files containing cfg defs
.PHONY: find
find:
	@$(find_rpc_files_cmd)

# print the list of the autogenerated files
.PHONY: print-lst
print-lst:
	@$(find_rpc_files_cmd) | $(gen_file_grp_cmd)

#
.PHONY: gen-file-list
.PHONY: gen-files_list
.PHONY: gen_files_list
gen-file-list gen-files-list gen_files_list:
	@$(foreach f,$(call subst_corepath,$(call sort_files,$(f_found_lst))),\
		echo "$f \\";)

print-modifaces:
	@$(foreach f,$(call sort_files,$(f_found_lst)),\
		echo "$(call subst_corepath,$(f)):$(call find_modiface, $(f)) \\";)

.PHONY: check-list
.PHONY: update-list
.PHONY: diff-list
check-list update-list diff-list:
	@$(foreach f,$(call subst_corepath,$(call sort_files,$(diff_lst))),\
		echo "$f \\";)


# try to generate the docs from all the sources
.PHONY: autogen
autogen:
	@$(MAKE) all files_list="$(call sort_files,$(f_found_lst))"

$(foreach f,$(flist),$(eval $(call mk_rules,$(f))))
