#!/bin/bash
#
# $Id: gmt_make_module_src.sh 16501 2016-06-04 18:21:29Z pwessel $
#
# Copyright (c) 2012-2017
# by P. Wessel, W. H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe
# See LICENSE.TXT file for copying and redistribution conditions.
#
# Below, <TAG> is either core, supplements, or a users custom shared lib tag
#
# This script will find all the C files in the current dir (if core)
# or in subdirs (if supplements) and extract all the THIS_MODULE_PURPOSE
# and other strings from the sources files, then create one file:
# 	gmt_<TAG>_module.h	Function prototypes (required for Win32)
# 	gmt_<TAG>_module.c	Look-up functions
#
# Note: gmt_<TAG>_module.[ch] are in svn.  Only rerun this
# script when there are changes in the code, e.g. a new module.
#
SUPP_DIRS="gshhg|img|meca|mgd77|misc|potential|segy|spotter|x2sys"	# Edit this is new supplements are added
if [ $# -ne 1 ]; then
cat << EOF
usage: gmt_make_module_src.sh [tag]
	tag is the name of the set of modules.
	Choose between core or supplements.
EOF
	exit 0
fi
set -e

LIB=$1
# Make sure we get both upper- and lower-case versions of the tag
U_TAG=`echo $LIB | tr '[a-z]' '[A-Z]'`
L_TAG=`echo $LIB | tr '[A-Z]' '[a-z]'`

if [ "$U_TAG" = "SUPPLEMENTS" ]; then	# Look in directories under the current directory and set LIB_STRING
	grep "#define THIS_MODULE_LIB		" */*.c | egrep "$SUPP" | awk -F: '{print $1}' | sort -u > /tmp/tmp.lis
	LIB_STRING="GMT suppl: The official supplements to the Generic Mapping Tools"
elif [ "$U_TAG" = "CORE" ]; then	# Just look in current dir and set LIB_STRING
	grep "#define THIS_MODULE_LIB		" *.c | grep -v _mt | awk -F: '{print $1}' | sort -u > /tmp/tmp.lis
	LIB_STRING="GMT core: The main modules of the Generic Mapping Tools"
else
	echo "Error: Tag must be either core or supplements"
	exit
fi
rm -f /tmp/NAME.lis /tmp/LIB.lis /tmp/PURPOSE.lis /tmp/KEYS.lis /tmp/all.lis
while read program; do
	grep "#define THIS_MODULE_NAME" $program    | awk '{print $3}' | sed -e 's/"//g' >> /tmp/NAME.lis
	grep "#define THIS_MODULE_LIB" $program     | awk '{print $3}' | sed -e 's/"//g' >> /tmp/LIB.lis
	grep "#define THIS_MODULE_PURPOSE" $program | sed -e 's/#define THIS_MODULE_PURPOSE//g' | awk '{print $0}' >> /tmp/PURPOSE.lis
	grep "#define THIS_MODULE_KEYS" $program    | sed -e 's/#define THIS_MODULE_KEYS//g' | awk '{print $0}' >> /tmp/KEYS.lis
done < /tmp/tmp.lis
# Prepend group+name so we can get a list sorted on group name then individual programs
paste /tmp/LIB.lis /tmp/NAME.lis | awk '{printf "%s%s|%s\n", $1, $2, $2}' > /tmp/SORT.txt
paste /tmp/SORT.txt /tmp/LIB.lis /tmp/PURPOSE.lis /tmp/KEYS.lis | sort -k1 -u > /tmp/SORTED.txt
awk -F"|" '{print $2}' /tmp/SORTED.txt > /tmp/$LIB.txt
#rm -f /tmp/tmp.lis /tmp/NAME.lis /tmp/LIB.lis /tmp/PURPOSE.lis /tmp/SORTED.txt /tmp/SORT.txt /tmp/KEYS.lis

# The output file produced
FILE_GMT_MODULE_C=gmt_${L_TAG}_module.c
FILE_GMT_MODULE_H=gmt_${L_TAG}_module.h
COPY_YEAR=$(date +%Y)

#
# Generate FILE_GMT_MODULE_H
#

cat << EOF > ${FILE_GMT_MODULE_H}
/* \$Id\$
 *
 * Copyright (c) 2012-${COPY_YEAR}
 * by P. Wessel, W. H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe
 * See LICENSE.TXT file for copying and redistribution conditions.
 */

/* gmt_${L_TAG}_module.h declares the prototypes for ${L_TAG} module functions
 * and the array that contains ${L_TAG} GMT module parameters such as name
 * and purpose strings.
 * DO NOT edit this file directly! Regenerate thee file by running
 * 	gmt_make_module_src.sh ${L_TAG}
 */

#pragma once
#ifndef _GMT_${U_TAG}_MODULE_H
#define _GMT_${U_TAG}_MODULE_H

#ifdef __cplusplus /* Basic C++ support */
extern "C" {
#endif

/* Declaration modifiers for DLL support (MSC et al) */
#include "declspec.h"

/* Prototypes of all modules in the GMT ${L_TAG} library */
EOF
gawk '{printf "EXTERN_MSC int GMT_%s (void *API, int mode, void *args);\n", $1;}' /tmp/$LIB.txt >> ${FILE_GMT_MODULE_H}
cat << EOF >> ${FILE_GMT_MODULE_H}

/* Pretty print all modules in the GMT ${L_TAG} library and their purposes */
EXTERN_MSC void gmt_${L_TAG}_module_show_all (void *API);
/* List all modules in the GMT ${L_TAG} library to stdout */
EXTERN_MSC void gmt_${L_TAG}_module_list_all (void *API);
/* Function called by GMT_Encode_Options so developers can get information about a module */
EXTERN_MSC const char * gmt_${L_TAG}_module_info (void *API, char *candidate);

#ifdef __cplusplus
}
#endif

#endif /* !_GMT_${U_TAG}_MODULE_H */
EOF

#
# Generate FILE_GMT_MODULE_C
#

cat << EOF > ${FILE_GMT_MODULE_C}
/* \$Id\$
 *
 * Copyright (c) 2012-${COPY_YEAR}
 * by P. Wessel, W. H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe
 * See LICENSE.TXT file for copying and redistribution conditions.
 */

/* gmt_${L_TAG}_module.c populates the external array of GMT ${L_TAG} with
 * module parameters such as name, group, purpose and keys strings.
 * This file also contains the following convenience functions to
 * display all module purposes or just list their names:
 *
 *   void gmt_${L_TAG}_module_show_all (struct GMTAPI_CTRL *API);
 *   void gmt_${L_TAG}_module_list_all (void *API);
 *
 * These functions may be called by gmt --help and gmt --show-modules
 *
 * Developers of external APIs for accessing GMT modules will use this
 * function indirectly via GMT_Encode_Options to retrieve option keys
 * needed for module arg processing:
 *
 *   char * gmt_${L_TAG}_module_info (void *API, const char *module);
 *
 * DO NOT edit this file directly! Regenerate the file by running
 * 	gmt_make_module_src.sh ${L_TAG}
 */
EOF
if [ "$U_TAG" = "CORE" ]; then
	cat << EOF >> ${FILE_GMT_MODULE_C}
#include "gmt_dev.h"
#ifndef BUILD_SHARED_LIBS
#include "${FILE_GMT_MODULE_H}"
#endif

EOF
else
	cat << EOF >> ${FILE_GMT_MODULE_C}
#include "gmt.h"
#include "gmt_notposix.h"       /* Non-POSIX extensions */
#define gmt_M_unused(x) (void)(x)
#define GMT_LEN256 256
#include "gmt_supplements_module.h"
#include <string.h>
EOF
fi
cat << EOF >> ${FILE_GMT_MODULE_C}

/* Sorted array with information for all GMT ${L_TAG} modules */

/* name, library, and purpose for each module */
struct Gmt_moduleinfo {
	const char *name;             /* Program name */
	const char *component;        /* Component (core, supplement, custom) */
	const char *purpose;          /* Program purpose */
	const char *keys;             /* Program option info for external APIs */
EOF
if [ "$U_TAG" = "CORE" ]; then
	cat << EOF >> ${FILE_GMT_MODULE_C}
#ifndef BUILD_SHARED_LIBS
	/* gmt module function pointer: */
	int (*p_func)(void*, int, void*);
#endif
EOF
fi
cat << EOF >> ${FILE_GMT_MODULE_C}
};
EOF

if [ "$U_TAG" = "CORE" ]; then
	cat << EOF >> ${FILE_GMT_MODULE_C}

static struct Gmt_moduleinfo g_${L_TAG}_module[] = {
#ifdef BUILD_SHARED_LIBS
EOF

# $1 = name, $2 = ${L_TAG}, $3 = tab, $4 = purpose, $5 = tab, $6 = keys
gawk '
	BEGIN {
		FS = "\t";
	}
	{ printf "\t{\"%s\", \"%s\", %s, %s},\n", $1, $2, $4, $6;
}' /tmp/$LIB.txt >> ${FILE_GMT_MODULE_C}

cat << EOF >> ${FILE_GMT_MODULE_C}
	{NULL, NULL, NULL, NULL} /* last element == NULL detects end of array */
#else
EOF
# $1 = name, $2 = core/supplement, $3 = Api_mode, $4 = purpose, $5 = tab, $6 = keys
gawk '
	BEGIN {
		FS = "\t";
	}
	!/^[ \t]*#/ {
		printf "\t{\"%s\", \"%s\", %s, %s, &GMT_%s},\n", $1, $2, $4, $6, $1;
	}' /tmp/$LIB.txt >> ${FILE_GMT_MODULE_C}

cat << EOF >> ${FILE_GMT_MODULE_C}
	{NULL, NULL, NULL, NULL, NULL} /* last element == NULL detects end of array */
#endif
};
EOF
else
	cat << EOF >> ${FILE_GMT_MODULE_C}

static struct Gmt_moduleinfo g_${L_TAG}_module[] = {
EOF

# $1 = name, $2 = ${L_TAG}, $3 = tab, $4 = purpose, $5 = tab, $6 = keys
gawk '
	BEGIN {
		FS = "\t";
	}
	{ printf "\t{\"%s\", \"%s\", %s, %s},\n", $1, $2, $4, $6;
}' /tmp/$LIB.txt >> ${FILE_GMT_MODULE_C}

cat << EOF >> ${FILE_GMT_MODULE_C}
	{NULL, NULL, NULL, NULL} /* last element == NULL detects end of array */
};
EOF
fi
cat << EOF >> ${FILE_GMT_MODULE_C}

/* Pretty print all GMT ${L_TAG} module names and their purposes for gmt --help */
void gmt_${L_TAG}_module_show_all (void *V_API) {
	unsigned int module_id = 0;
	char message[GMT_LEN256];
EOF
if [ "$U_TAG" = "CORE" ]; then
	cat << EOF >> ${FILE_GMT_MODULE_C}
	struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
EOF
fi
cat << EOF >> ${FILE_GMT_MODULE_C}
	GMT_Message (V_API, GMT_TIME_NONE, "\n===  $LIB_STRING  ===\n");
	while (g_${L_TAG}_module[module_id].name != NULL) {
		if (module_id == 0 || strcmp (g_${L_TAG}_module[module_id-1].component, g_${L_TAG}_module[module_id].component)) {
			/* Start of new supplemental group */
			snprintf (message, GMT_LEN256, "\nModule name:     Purpose of %s module:\n", g_${L_TAG}_module[module_id].component);
			GMT_Message (V_API, GMT_TIME_NONE, message);
			GMT_Message (V_API, GMT_TIME_NONE, "----------------------------------------------------------------\n");
		}
EOF
if [ "$U_TAG" = "CORE" ]; then
		cat << EOF >> ${FILE_GMT_MODULE_C}
		if (API->external || (strcmp (g_${L_TAG}_module[module_id].name, "gmtread") && strcmp (g_${L_TAG}_module[module_id].name, "gmtwrite"))) {
			snprintf (message, GMT_LEN256, "%-16s %s\n",
				g_${L_TAG}_module[module_id].name, g_${L_TAG}_module[module_id].purpose);
				GMT_Message (V_API, GMT_TIME_NONE, message);
		}
EOF
else
		cat << EOF >> ${FILE_GMT_MODULE_C}
		snprintf (message, GMT_LEN256, "%-16s %s\n",
			g_${L_TAG}_module[module_id].name, g_${L_TAG}_module[module_id].purpose);
			GMT_Message (V_API, GMT_TIME_NONE, message);
EOF
fi
cat << EOF >> ${FILE_GMT_MODULE_C}
		++module_id;
	}
}

/* Produce single list on stdout of all GMT ${L_TAG} module names for gmt --show-modules */
void gmt_${L_TAG}_module_list_all (void *V_API) {
	unsigned int module_id = 0;
EOF
if [ "$U_TAG" = "CORE" ]; then
	cat << EOF >> ${FILE_GMT_MODULE_C}
	struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
EOF
else
	cat << EOF >> ${FILE_GMT_MODULE_C}
	gmt_M_unused(V_API);
EOF
fi
cat << EOF >> ${FILE_GMT_MODULE_C}
	while (g_${L_TAG}_module[module_id].name != NULL) {
EOF
if [ "$U_TAG" = "CORE" ]; then
		cat << EOF >> ${FILE_GMT_MODULE_C}
		if (API->external || (strcmp (g_${L_TAG}_module[module_id].name, "gmtread") && strcmp (g_${L_TAG}_module[module_id].name, "gmtwrite")))
			printf ("%s\n", g_${L_TAG}_module[module_id].name);
EOF
else
		cat << EOF >> ${FILE_GMT_MODULE_C}
		printf ("%s\n", g_${L_TAG}_module[module_id].name);
EOF
fi
cat << EOF >> ${FILE_GMT_MODULE_C}
		++module_id;
	}
}

/* Lookup module id by name, return option keys pointer (for external API developers) */
const char *gmt_${L_TAG}_module_info (void *API, char *candidate) {
	int module_id = 0;
	gmt_M_unused(API);

	/* Match actual_name against g_module[module_id].name */
	while (g_${L_TAG}_module[module_id].name != NULL &&
	       strcmp (candidate, g_${L_TAG}_module[module_id].name))
		++module_id;

	/* Return Module keys or NULL */
	return (g_${L_TAG}_module[module_id].keys);
}
EOF

if [ "$U_TAG" = "CORE" ]; then
	cat << EOF >> ${FILE_GMT_MODULE_C}
	
#ifndef BUILD_SHARED_LIBS
/* Lookup static module id by name, return function pointer */
void *gmt_${L_TAG}_module_lookup (void *API, const char *candidate) {
	int module_id = 0;
	size_t len = strlen (candidate);
	gmt_M_unused(API);

	if (len < 4) return NULL;	/* All candidates should start with GMT_ */
	/* Match actual_name against g_module[module_id].name */
	while (g_${L_TAG}_module[module_id].name != NULL &&
	       strcmp (&candidate[4], g_${L_TAG}_module[module_id].name))
		++module_id;

	/* Return Module function or NULL */
	return (g_${L_TAG}_module[module_id].p_func);
}
#endif
EOF
fi
#rm -f /tmp/$LIB.txt
exit 0

# vim: set ft=c:
