/*
 * Copyright (c) 1997, 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include <mom/compiler.h>
#include <mom/pres_c.h>
#include <mom/c/pbe.hh>
#include <mom/c/be/be_state.hh>

extern char *progname;

/*
 * `BE_FLAGS' is the number of flags ``hardcoded'' in this file --- yuck!
 * These flags include:
 *
 * `-h' / `--header'
 *		to specify the name of the output `.h' file
 * `-p' / `--prefix'
 *		to specify the path prefix for #includes that refer to the
 *		generated header file
 * `-s' / `--system_header'
 *		to specify that the generated header file should be #included
 *		as a system header (i.e., using <>'s)
 *
 * `-n' / `--inline'
 *		to specify the name of the output inline file
 *
 * `--no_timestamp'
 *		to suppress the timestamp information that is otherwise output
 *		into the generated `.h' and `.c' files; useful when doing
 *		regression testing on the generated output
 *
 * `-i' / `--no_included_implementations'
 *		to suppress client stubs and server funcs that were generated
 *		from IDL files that were #included by the root IDL file
 * `-d' / `--no_included_declarations'
 *              to suppress client stubs and server func declarations that were
 *              generated from IDL files that were #include by the root IDL
 *              file
 *
 * `-m' / `--all_mu_stubs'
 *		to generate ALL marshal and unmarshal stubs (rather than only
 *		those that are necessary).
 *
 * `-P' / `--presentation_implementation'
 *		to use the named SCML file as the presentation implementation
 * `--nostdinc'
 *		to remove the standard directories from the SCML search path
 * `-I' / `--include'
 *		to add a directory to the list of directories searched for SCML
 *		files
 *
 * `-f' / `--source_include_file'
 *		to `#include' a specific file in the generated source file
 * `-F' / `--header_include_file'
 *		to `#include' a specific file in the generated header file
 *
 * `-D' / `--define'
 *		Define an SCML variable
 */
#define BE_FLAGS (14)

/* Forward declaration of `be_args' helper function. */
static const char **
pres_impl_dir_list(
	flag_value_seq *dir_seq,
	const char **std_list,
	const char *pres_impl);

/*
 * Parse the command line arguments to a Flick back end.
 */
be_flags
be_state::be_args(int argc, char **argv, const be_flags &def_flags,
		  const char *info)
{
	flags_in in[STD_FLAGS + BE_FLAGS];
	flags_out out;
	be_flags res;
	
	int nostdinc;
	
	int flags_index;
	
	/* Initialize the array of command line options. */
	
	set_def_flags(in);
	flags_index = STD_FLAGS;
	
	in[flags_index].sng = 'h';
	in[flags_index].dbl = "header";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = 1;
	in[flags_index].dfault.string = def_flags.header;
	in[flags_index].explain
		= "Set the name of the output header file to <string>";
	++flags_index;
	
	in[flags_index].sng = 'p';
	in[flags_index].dbl = "prefix";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = 1;
	in[flags_index].dfault.string = def_flags.prefix;
	in[flags_index].explain
		= "Set the prefix for the path to the header to <string>";
	++flags_index;
	
	in[flags_index].sng = 's';
	in[flags_index].dbl = "system_header";
	in[flags_index].kind = fk_FLAG;
	in[flags_index].max_occur = FLAG_UNLIMITED_USE_LAST;
	in[flags_index].dfault.flag = def_flags.system_header;
	in[flags_index].explain
		= "When including the header, use <>, not \"\"";
	++flags_index;
	
	in[flags_index].sng = 'n';
	in[flags_index].dbl = "inline";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = 1;
	in[flags_index].dfault.string = def_flags.inline_file;
	in[flags_index].explain
		= "Set the name of the output inline file to <string>";
	++flags_index;
	
	in[flags_index].sng = 0;
	in[flags_index].dbl = "no_timestamp";
	in[flags_index].kind = fk_FLAG;
	in[flags_index].max_occur = FLAG_UNLIMITED_USE_LAST;
	in[flags_index].dfault.flag = def_flags.no_timestamp;
	in[flags_index].explain
		= ("Don't output the timestamp comment block into generated "
		   "files");
	++flags_index;
	
	in[flags_index].sng = 'i';
	in[flags_index].dbl = "no_included_implementations";
	in[flags_index].kind = fk_FLAG;
	in[flags_index].max_occur = FLAG_UNLIMITED_USE_LAST;
	in[flags_index].dfault.flag = def_flags.no_included_implementations;
	in[flags_index].explain
		= ("Don't define stubs for interfaces that were `#include'd");
	++flags_index;
	
	in[flags_index].sng = 'd';
	in[flags_index].dbl = "no_included_declarations";
	in[flags_index].kind = fk_FLAG;
	in[flags_index].max_occur = FLAG_UNLIMITED_USE_LAST;
	in[flags_index].dfault.flag = def_flags.no_included_declarations;
	in[flags_index].explain
		= ("Don't declare stubs for interfaces that were `#include'd");
	++flags_index;
	
	in[flags_index].sng = 'm';
	in[flags_index].dbl = "all_mu_stubs";
	in[flags_index].kind = fk_FLAG;
	in[flags_index].max_occur = FLAG_UNLIMITED_USE_LAST;
	in[flags_index].dfault.flag = def_flags.all_mu_stubs;
	in[flags_index].explain
		= ("Generate ALL marshal and unmarshal stubs");
	++flags_index;
	
	in[flags_index].sng = 'P';
	in[flags_index].dbl = "presentation_implementation";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = 1;
	in[flags_index].dfault.string = def_flags.pres_impl;
	in[flags_index].explain
		= ("Use the named SCML file as the presentation "
		   "implementation");
	++flags_index;
	
	in[flags_index].sng = 0;
	in[flags_index].dbl = "nostdinc";
	in[flags_index].kind = fk_FLAG;
	in[flags_index].max_occur = FLAG_UNLIMITED_USE_LAST;
	in[flags_index].dfault.flag = 0;
	in[flags_index].explain
		= ("Do not search the standard directories for SCML files");
	++flags_index;
	
	/*
	 * `-I' must come after `-P' and after `--nostdinc'; see how
	 * `res.pres_impl_dirs' is set at the end of this function.
	 */
	in[flags_index].sng = 'I';
	in[flags_index].dbl = "include";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = FLAG_UNLIMITED_LIST;
	in[flags_index].dfault.string = 0;
	in[flags_index].explain
		= ("Search the named directory for SCML files");
	++flags_index;
	
	in[flags_index].sng = 'f';
	in[flags_index].dbl = "source_include_file";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = FLAG_UNLIMITED_LIST;
	in[flags_index].dfault.string = 0;
	in[flags_index].explain
		= ("`#include \"...\"' a file within the generated source "
		   "code file");
	++flags_index;
	
	in[flags_index].sng = 'F';
	in[flags_index].dbl = "header_include_file";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = FLAG_UNLIMITED_LIST;
	in[flags_index].dfault.string = 0;
	in[flags_index].explain
		= ("`#include \"...\"' a file within the generated header "
		   "file");
	++flags_index;
	
	in[flags_index].sng = 'D';
	in[flags_index].dbl = "define";
	in[flags_index].kind = fk_STRING;
	in[flags_index].max_occur = FLAG_UNLIMITED_LIST;
	in[flags_index].dfault.string = 0;
	in[flags_index].explain
		= ("Define an SCML variable");
	++flags_index;
	
	assert(flags_index == (STD_FLAGS + BE_FLAGS));
	
	/* Parse the actual command line arguments. */
	
	out = parse_args(argc, argv, (STD_FLAGS + BE_FLAGS), in);
	std_handler(out,
		    (STD_FLAGS + BE_FLAGS), in,
		    "<optional input filename>", info);
	
	res.output = out.flag_seqs[OUTPUT_FILE_FLAG].values[0].string;
	if (out.other_count == 1)
		res.input = *out.other;
	else if (out.other_count) {
		print_args_usage(out.progname,
				 (STD_FLAGS + BE_FLAGS), in,
				 "<optional input filename>", info);
		exit(1);
	} else
		res.input = 0;
	progname = out.progname;
	
	/* These had better line up with the list above! */
	flags_index = STD_FLAGS;
	
	res.header         = out.flag_seqs[flags_index++].values[0].string;
	res.prefix         = out.flag_seqs[flags_index++].values[0].string;
	res.system_header  = out.flag_seqs[flags_index++].values[0].flag;
	res.inline_file    = out.flag_seqs[flags_index++].values[0].string;
	res.no_timestamp   = out.flag_seqs[flags_index++].values[0].flag;
	res.no_included_implementations
		= out.flag_seqs[flags_index++].values[0].flag;
	res.no_included_declarations
		= out.flag_seqs[flags_index++].values[0].flag;
	res.all_mu_stubs   = out.flag_seqs[flags_index++].values[0].flag;
	res.pres_impl      = out.flag_seqs[flags_index++].values[0].string;
	nostdinc           = out.flag_seqs[flags_index++].values[0].flag;
	res.pres_impl_dirs
		= pres_impl_dir_list(&(out.flag_seqs[flags_index++]),
				     (nostdinc ?
				      0 :
				      def_flags.pres_impl_dirs),
				     res.pres_impl);
	res.src_includes   = out.flag_seqs[flags_index++]; /* struct copy */
	res.hdr_includes   = out.flag_seqs[flags_index++]; /* struct copy */
	res.scml_defs      = out.flag_seqs[flags_index++]; /* struct copy */
	
	assert(flags_index == (STD_FLAGS + BE_FLAGS));
	
	/*
	 * If no special source includes were specified on the command line,
	 * the default (null) string was stored into the value list of
	 * `src_includes'.  We don't want it, so take it out now.  Do the same
	 * for `hdr_includes' as well.
	 */
	if ((res.src_includes.len == 1) && !res.src_includes.values[0].string)
		res.src_includes.len = 0;
	if ((res.hdr_includes.len == 1) && !res.hdr_includes.values[0].string)
		res.hdr_includes.len = 0;
	if ((res.scml_defs.len == 1) && !res.scml_defs.values[0].string)
		res.scml_defs.len = 0;
	
	tag_item *ti;
	
	if( !(ti = find_tag(this->root_scope->get_values(), "system_info")) ) {
		tag_list *tl;
		
		tl = create_tag_list(0);
		ti = add_tag(this->root_scope->get_values(), "system_info",
			     TAG_TAG_LIST, tl);
	}
	add_tag(ti->data.tag_data_u.tl, "flags", TAG_TAG_LIST,
		flags_to_tag_list(in, &out, STD_FLAGS + BE_FLAGS));
	return res;
}

/*****************************************************************************/

/*
 * A helper function for `be_args': convert the `flag_value_seq' representation
 * of the list of presentation implementation search directories into a null-
 * terminated array of strings.  Additionally, append the standard search
 * directories to the search path, and if `pres_impl' contains a directory
 * component, add that directory to the search path as well.
 */
static const char**
pres_impl_dir_list(
	flag_value_seq *dir_seq,
	const char **std_list,
	const char *pres_impl)
{
	unsigned int	dir_seq_len;
	unsigned int	std_list_len;
	
	char *		pres_impl_slash;
	char *		pres_impl_dir;
	int		pres_impl_dir_strlen;
	
	const char **	dir_list;
	unsigned int	dir_list_len;
	unsigned int	dir_list_index;
	unsigned int	i;
	
	/*****/
	
	/* Determine the number of strings in `dir_seq'. */
	if ((dir_seq->len == 1) && !dir_seq->values[0].string)
		/* Special case: discard the default null string. */
		dir_seq_len = 0;
	else
		dir_seq_len = dir_seq->len;
	
	/* Determine the number of strings in `std_list'. */
	if (!std_list)
		/* Special case: ignore a null `std_list'. */
		std_list_len = 0;
	else {
		for (std_list_len = 0; std_list[std_list_len]; ++std_list_len)
			/* No body required. */
			;
	}
	
	/* Does `pres_impl' contain a directory component?  If so, get it. */
	pres_impl_slash = (pres_impl ? strrchr(pres_impl, '/') : 0);
	if (pres_impl_slash) {
		if (pres_impl_slash == pres_impl)
			/* `pres_impl' is in `/'. */
			++pres_impl_slash;
		pres_impl_dir_strlen = pres_impl_slash - pres_impl;
		pres_impl_dir = (char *)
				mustmalloc((pres_impl_dir_strlen + 1)
					   * sizeof(char));
		strncpy(pres_impl_dir, pres_impl, pres_impl_dir_strlen);
		pres_impl_dir[pres_impl_dir_strlen] = 0;
		
	} else
		pres_impl_dir = 0;
	
	/* Create and fill the `dir_list'. */
	dir_list_len   = dir_seq_len
			 + (pres_impl_dir ? 1 : 0)
			 + std_list_len;
	dir_list       = (const char **)
			 mustmalloc((dir_list_len + 1)
				    * sizeof(const char *));
	dir_list_index = 0;
	
	for (i = 0; i < dir_seq_len; ++i)
		dir_list[dir_list_index++] = dir_seq->values[i].string;
	if (pres_impl_dir)
		dir_list[dir_list_index++] = pres_impl_dir;
	for (i = 0; i < std_list_len; ++i)
		dir_list[dir_list_index++] = std_list[i];
	dir_list[dir_list_len] = 0;
	
	return dir_list;
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1