/*
#
# DESCRIPTION
#
# $Id: sysconf.c,v 1.1.1.1 2003/12/13 11:17:03 nemies Exp $
#
# Copyright (C) 2003 Kees Cook
# kees@outflux.net, http://outflux.net/
# 
# This program 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.
# 
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# http://www.gnu.org/copyleft/gpl.html
#
*/

#include "sysconf.h"
#include <errno.h>
#include <getopt.h>

struct conf_list_t {
	int	cfvar;
	char *	name;
	int	category;
	int	rettype;
};

#define CONF(a,t)	{ (a) , #a, (t) }
#define STR(a)		CONF(a,RET_STR)
#define NUM(a)		CONF(a,RET_NUM)


/* grab values for pathconf, sysconf, and confstr */
struct conf_list_t conf_list[]={
#include "defs.h"
	{ -1, NULL, -1, -1 },
};

/* copyright info */
char * copyright="Copyright 2003 Kees Cook <kees@outflux.net>";
char * gpl="\
This program is free software; you may redistribute it under the terms of\n\
the GNU General Public License.  This program has absolutely no warranty.";


/* global settings */
int show_many=0;
int show_all=0;
char * use_path=NULL;


void Usage(char * title)
{
    printf("Usage: %s [OPTIONS] [VARIABLE ...]\n\
\n\
  -h, --help         This help\n\
  -v, --verbose      Display variable name along with value\n\
  -V, --version      Return program version\n\
  -p, --path FILE    Use FILE as the path argument to 'pathconf' variables\n\
  -l, --all          Show all configuration variables (may need --path)\n\
\n", title);

    exit(1);
}

void handle_args(int argc, char *argv[])
{
    static struct option long_options[] = {
        { "help",    0, NULL, 'h' },
        { "verbose", 0, NULL, 'v' },
        { "version", 0, NULL, 'V' },
        { "path",    1, NULL, 'p' },
        { "all",     0, NULL, 'l' },
        { 0,         0, 0,    0 }
    };

    /* do our option handling */
    while (1)
    {
        int c;
        int index;

        c = getopt_long(argc, argv, "hvVp:l", long_options, &index);

        if (c == -1)
            break; // done with args

        switch (c)
        {
            case 0:
                // got a matching long argument (with no short equiv)
                switch (index)
                {
                    default:
                        fprintf(stderr,
                                "Unknown long argument index %d found!\n",
                                index);
                }
                break;
            case 'h':
                Usage(PACKAGE_NAME);
                break;
            case 'v':
                show_many=1;
                break;
            case 'l':
                show_all=1;
                show_many=1;
                break;
            case 'V':
                printf("%s version %s\n%s\n%s\n",
                        PACKAGE,PACKAGE_VERSION,copyright,gpl);
                exit(0);
            case 'p':
                use_path=optarg;
                break;
            default:
                fprintf(stderr, "Unknown short argument '%c' found?!\n", c);
                /* fall through to the Usage ... */
            case '?':
                Usage(argv[0]);
                break;
        }
    }

}

/* 0 on success, -1 if something bad happens */
int report_variable(int index, char * path)
{
		// for RET_STR
		size_t n=0;
		size_t len;
		char *pathbuf=NULL;
        char *category=NULL;

		// for RET_NUM
		long   result;

        // our option
		char * name  = conf_list[index].name;
		int    cfvar = conf_list[index].cfvar;

		/* decide if we want to deal with this config variable */
		

		switch (conf_list[index].rettype) {
		case RET_STR:
			n = confstr(cfvar,NULL,(size_t)0);
			if (n == 0) {
				fprintf(stderr,"confstr: ");
				fprintf(stderr,
				        "%s: bad configuration variable\n",
					name);
				break;
			}
			if ((pathbuf = malloc(n)) == NULL) {
				perror("malloc");
				return -1;
			}
			if ((len=confstr(cfvar, pathbuf, n))<1 ||
			     len!=n) {
				fprintf(stderr,"confstr: ");
				perror(name);
				return -1;
			}
			else {
				if (show_many) printf("%s: ",name);
				printf("%s\n",pathbuf);
				free(pathbuf);
			}
			break;
		case RET_NUM:
			errno=0;
            switch (conf_list[index].category) {
            case SYSCONF:
                category="sysconf";
			    result=sysconf(cfvar);
                break;
            case PATHCONF:
                category="pathconf";
                if (!use_path) {
                    fprintf(stderr,"%s: requires a path (use --path)\n",name);
                    /* only an error on a specific request */
                    return (show_all ? 0 : -1);
                }
                result=pathconf(use_path,cfvar);
                break;
            default:
                category="unknown";
                fprintf(stderr,"%s: I don't know how to fetch this!?\n",name);
                return -1;
            }
			if (result==-1 && errno!=0) {
				fprintf(stderr,"%s: ",category);
				perror(name);
                return -1;
			}
			else {
				if (show_many) printf("%s: ",name);
				printf("%ld\n",result);
			}
			break;
		default:
			fprintf(stderr,"Cannot handle '%s' type %d\n",
				       name,conf_list[index].rettype);
            return -1;
		}

        return 0;
}

/* returns 1 on match, 0 on failure */
int match_name(int index, char * name)
{
    char * var_name = conf_list[index].name;
    /* skip past the leading "_" if they didn't include it */
    if (name && name[0]!='_' && var_name[0]=='_') var_name++;
    /* let's do this without case sensitivity */
    return (!strcasecmp(var_name,name));
}



/*
 * Usage methods:
 *   report a single value (most common)
 *   report multiple values (one per line?)
 *     show name on line? with colon? "batch" mode?
 *   show all possible conf names
 *     show only nums
 *     show only strs
 *   show all values for all conf names (part of "multi" above)
 */
int main(int argc, char *argv[])
{
	int i,opt;
	int exitcode=0;
    int var_count;
    int found;

    /* handle our command line args */
    handle_args(argc, argv);

    /* with many args, turn on show_many */
    if (optind <= argc) {
        var_count = (argc - optind);
        if (var_count > 1) show_many=1;
    }

    /* need either "show_all" or a variable to display */
    if (var_count == 0 && !show_all) Usage(PACKAGE_NAME);

    if (show_all) {
        for (i=0;conf_list[i].name;i++) {
                if (report_variable(i,use_path)<0)
                    exitcode=1;
        }
    }
    else {
        for (opt=optind;opt<argc;opt++) {
            found=0;
            for (i=0;conf_list[i].name;i++) {
                if (match_name(i,argv[opt])) {
                    if (report_variable(i,use_path)<0)
                        exitcode=1;
                    found=1;
                }
            }
            if (!found) {
                fprintf(stderr,"%s: not a recognized variable\n",argv[opt]);
                exitcode=1;
            }
        }
    }

    return exitcode;
}


/* vi:set ai ts=4 sw=4 expandtab: */
