#!/usr/bin/env bash
# Requirements:
for tool in cat find grep mkdir sort xargs  curl git hg perl xqilla; do
	hash "${tool}" 2> /dev/null || { echo "Error: missing tool: ${tool}"; err=1; }
done
[ "${err}" ] && exit 250

# Functions:
function get {
	local url="${1}"
	local target_filename=$(echo "${url}"| md5sum)
	target_filename="${target_filename:0:32}"
	local target_path="${target_filename}.html"
	[ -f "${target_path}" ] || curl -s "${url}" > "${target_path}"
	cat "${target_path}"
}

function list_openresty_modules {
	local github_query_url='https://github.com/orgs/openresty/repositories?language=c&q=nginx-module&sort=name&type=source'
	get "${github_query_url}" | perl -nlE 'say $1 if m#/([^/]+-nginx-module)#' | sort -u
}

download_dir='download'
mkdir -p "${download_dir}"
cd "${download_dir}" || exit 150

# nginx itself:
[ -d 'nginx' ] || hg clone 'http://hg.nginx.org/nginx'
directories=('nginx/src')

# Openresty modules:
while read module; do
	[ -d "${module}" ] || git clone "https://github.com/openresty/${module}.git"
	directories+=("${module}/src")
done < <(list_openresty_modules)

# All nginx (not nginx-plus) directives, along with details on their acceptable scopes and arguments:
find "${directories[@]}" -type f -name '*.c' | perl -nlE '
	open($fh, $_);
	$contents = join(q[], <$fh>);
	while ($contents =~ m/(?x) # Extended mode
		# Part 1: token name
		# Example: { ngx_string("auth_basic"),
		\{
		\s*
		ngx_string\("
		(?<token>
			[a-z_0-9]+
		)
		"\),

		# Part 2: acceptable scopes for the token
		# Example: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
		\s*
		(?<scopes>
			NGX_[A-Z_]+_CONF
			(?:\s*\|NGX_[A-Z_]+_CONF)*
		)

		# Part 3: number of arguments and flag block flags:
		(?<flags>
			(?:\s*\|NGX_CONF_[A-Z0-9]+)*
		)
		,

		# Part 4: handler function:
		# Example: ngx_http_set_complex_value_slot,
		\s*
		(?<handler>
			ngx_[a-z_0-9]+
		)
	/mg) {
		$token = $+{token}; $scopes = $+{scopes}; $flags = $+{flags}; $handler = $+{handler};
		$scopes =~ s#[\s\n\r]##g; $scopes =~ s#NGX_|_CONF##g;
		$flags =~ s#[\s\n\r]##g; $flags=~ s#NGX_CONF_|^\|##g;
		$type = ($handler =~ m#^ngx_conf_set_(.+)_slot$#) ? $1 : q[special];
		if (exists($tokens{$token})) {
			$tokens{$token}{q[scope]} .= q[|] . $scopes;
		} else {
			$tokens{$token} = {q[scope] => $scopes, q[flags] => $flags, q[type] => $type};
		}
	}
	close($fh);
	END {
		while (($token, $token_properties) = each(%tokens)) {
			$scopes = $token_properties->{q[scope]};
			# in practice, DIRECT is but a useless synonym of MAIN:
			$scopes =~ s/\|DIRECT//;
			say sprintf(q["%s": {scope: %s},], $token, $scopes);
		}
	}
' | sort > open-source-directives


# Get all documented nginx directives:
# 1 - fetch the nginx.org source code:
[ -d 'nginx.org' ] || hg clone 'http://hg.nginx.org/nginx.org'

# 2 - adjust ngx_http_api_module_head.xml to make it a complete XML file:
api_module_path='nginx.org/xml/en/docs/http/ngx_http_api_module_head.xml'
missing_tag='</module>'
grep -q "${missing_tag}" "${api_module_path}" || echo "${missing_tag}" >> "${api_module_path}"

# 3 - Extract directives and contexts from the XML files used to generate the nginx documentation:
directive_and_context_xquery='
	for $directive in
		/module/section[@id="directives"]/directive,
		$context in $directive//context
	return fn:concat(data($directive/@name), " ", data($context))'

find 'nginx.org/xml/en/docs' -type f -name '*.xml' | while read xml_filepath; do
	xqilla -i "${xml_filepath}" <(echo "${directive_and_context_xquery}") | while read directive context; do
		# Discard directives already found in source code:
		grep -qP "^\"${directive}\"" open-source-directives && continue
		echo "${directive} ${context} ${xml_filepath}"
	done
done | perl -naE '
	BEGIN {
		$all_known_scopes = {
			q[http] => {
				q[http] => q[HTTP_MAIN],
				q[server] => q[HTTP_SRV],
				q[location] => q[HTTP_LOC],
				q[if] => q[HTTP_SIF|HTTP_LIF],
				q[if in location] => q[HTTP_LIF],
				q[limit_except] => q[HTTP_LMT],
				q[upstream] => q[HTTP_UPS],
			},
			q[mail] => {
				q[mail] => q[MAIL_MAIN],
				q[server] => q[MAIL_SRV],
			},
			q[stream] => {
				q[stream] => q[STREAM_MAIN],
				q[server] => q[STREAM_SRV],
				q[upstream] => q[STREAM_UPS],
			},
		};
		%tokens = ();
	}
	($token, $context, $doc_path) = @F;

	$module_type = q[http];
	$module_type = $1 if ($doc_path =~ m/ngx_(http|mail|stream)/);
	$ngx_scope = $all_known_scopes->{$module_type}->{$context};

	if (exists($tokens{$token})) {
		$tokens{$token}->{q[scope]} .= q[|] . $ngx_scope;
	} else {
		$tokens{$token} = {q[scope] => $ngx_scope};
	}
	END {
		while (($token, $token_properties) = each(%tokens)) {
			$scopes = $token_properties->{q[scope]};
			# in practice, DIRECT is but a useless synonym of MAIN:
			$scopes =~ s/\|DIRECT//;
			say sprintf(q["%s": {scope: %s},], $token, $scopes);
		}
	}
' | sort > nginx-plus-directives

sort open-source-directives nginx-plus-directives

perl -nlE '
	$h{$1} = 1 if m#^== ngx\.([0-9a-z_.]+) ==#;
	$h{$1} = 1 if m#ngx\.([0-9A-Z_]+)#;
	END {
		map { printf(qq["%s", ], $_); } sort(keys(%h));
	}
' lua-nginx-module/doc/HttpLuaModule.wiki | sort -u > lua-directives
fold -s -w 100 lua-directives
