#include "global.h"

#include "build.h"
#include "vp.h"
#include "version.inc"
#include "auto_vararg.h"

#include <stdlib.h>	 /* atoi */
#include <getopt.h>

/* defaults for unset environment variables */
#define DEFAULT_EDITOR	 "vi"
#define DEFAULT_HOME	 "/"   /* no $HOME --> use root directory */
#define DEFAULT_SHELL	 "sh"
#define DEFAULT_LINEFLAG "+%s" /* default: used by vi and emacs */
#define DEFAULT_TMPDIR	 "/tmp"

/* environment variable holders */
char * editor;
char * home;
char * shell;
char * lineflag;
char * tmpdir;
bool lineflagafterfile;

bool  remove_symfile_onexit = false;
bool  onesearch; /* one search only in line mode */
char *reflines;	 /* symbol reference lines file */

/* From a list of envirnment variable names,
 *  return the first valid variable value
 *  or the user given default.
 */
#define coalesce_env(def, ...) _coalesce_env(def, PP_NARG(__VA_ARGS__), __VA_ARGS__)
static inline
char * _coalesce_env(char * mydefault, size_t argc, ...) {
    char * r = mydefault;
    va_list va;
    va_start(va, argc);

    for (int i = 0; i < argc; i++) {
        char * value = va_arg(va, char*);
        value = getenv(value);

        if (value != NULL
        &&  *value != '\0') {
            r = value;
            goto end;
        }
    }

  end:
    va_end(va);
    return r;
}

/* XXX: Add CSOPE_* equivalents while preserving the originals.
 *       DO NOT do it without writting documentation
 */
void readenv(void) {
    editor   = coalesce_env(DEFAULT_EDITOR, "CSCOPE_EDITOR", "VIEWER", "EDITOR");
    home     = coalesce_env(DEFAULT_HOME, "HOME");
    shell    = coalesce_env(DEFAULT_SHELL, "SHELL");
    lineflag = coalesce_env(DEFAULT_LINEFLAG, "CSCOPE_LINEFLAG");
    tmpdir   = coalesce_env(DEFAULT_TMPDIR, "TMPDIR");

    lineflagafterfile = getenv("CSCOPE_LINEFLAG_AFTER_FILE") ? 1 : 0;
}

char **parse_options(int *argc, char **argv) {
	int	  opt;
	int	  longind;
	char  path[PATHLEN + 1]; /* file path */
	char *s;
	int	  argcc = *argc;

	struct option lopts[] = {
		{"help",    0, NULL, 'h'},
		{"version", 0, NULL, 'V'},
		{0,         0,    0,  0 },
	};

	while((opt = getopt_long(argcc,
			   argv,
			   "hVbcCdeF:f:I:i:kLl0:1:2:3:4:5:6:7:8:9:P:p:qRs:TUuvX",
			   lopts,
			   &longind)) != -1) {
		switch(opt) {
			case '?':
				usage();
				myexit(1);
				break;
			case 'X':
				remove_symfile_onexit = true;
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				/* The input fields numbers for line mode operation */
				field = opt - '0';
				if(strlen(optarg) > PATHLEN) {
					postfatal("\
        			cscope: pattern too long, cannot be > \
        			%d characters\n",
						PATLEN);
				}
				strcpy(input_line, optarg);
				break;
			case 'b': /* only build the cross-reference */
				buildonly = true;
				linemode  = true;
				break;
			case 'c': /* ASCII characters only in crossref */
				compress = false;
				break;
			case 'C':					 /* turn on caseless mode for symbol searches */
				caseless = true;
				egrepcaseless(caseless); /* simulate egrep -i flag */
				break;
			case 'd':					 /* consider crossref up-to-date */
				isuptodate = true;
				break;
			case 'e': /* suppress ^E prompt between files */
				editallprompt = false;
				break;
			case 'h':
				longusage();
				myexit(1);
				break;
			case 'k': /* ignore DFLT_INCDIR */
				kernelmode = true;
				break;
			case 'L':
				onesearch = true;
				/* FALLTHROUGH */
			case 'l':
				linemode = true;
				break;
			case 'v':
				verbosemode = true;
				break;
			case 'V':
				fprintf(stderr, PROGRAM_NAME ": version %d%s\n", FILEVERSION, FIXVERSION);
				myexit(0);
				break;
			case 'q': /* quick search */
				invertedindex = true;
				break;
			case 'T': /* truncate symbols to 8 characters */
				trun_syms = true;
				break;
			case 'u': /* unconditionally build the cross-reference */
				unconditional = true;
				break;
			case 'U': /* assume some files have changed */
				fileschanged = true;
				break;
			case 'R':
				recurse_dir = true;
				break;
			case 'f': /* alternate cross-reference file */
				reffile = optarg;
				if(strlen(reffile) > sizeof(path) - 3) {
					postfatal("\
        			cscope: reffile too long, cannot \
        			be > %d characters\n",
						sizeof(path) - 3);
					/* NOTREACHED */
				}
				strcpy(path, reffile);

				s = path + strlen(path);
				strcpy(s, ".in");
				/*coverity[overwrite_var]*/
				invname = strdup(path);
				strcpy(s, ".po");
				/*coverity[overwrite_var]*/
				invpost = strdup(path);
				break;

			case 'F': /* symbol reference lines file */
				reflines = optarg;
				break;
			case 'i': /* file containing file names */
				namefile = optarg;
				break;
			case 'I': /* #include file directory */
				includedir(optarg);
				break;
			case 'p': /* file path components to display */
				dispcomponents = atoi(optarg);
				break;
			case 'P': /* prepend path to file names */
				prependpath = optarg;
				break;
			case 's': /* additional source file directory */
				sourcedir(optarg);
				break;
		}
	}
	/*
	 * This adjusts argv so that we only see the remaining
	 * args. Its ugly, but we need to do it so that the rest
	 * of the main routine doesn't get all confused
	 */
	*argc = *argc - optind;
	return argv + optind;
}