twiboot/linux/optarg.c

194 lines
5.5 KiB
C

/***************************************************************************
* Copyright (C) 10/2010 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* 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; version 2 of the License, *
* *
* 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. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include "list.h"
#include "optarg.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
struct optarg_entry {
struct list_head list;
const struct option *opts;
int count;
int (* parser_cb)(int val, const char *arg, void *privdata);
void *privdata;
};
static LIST_HEAD(option_list);
int optarg_register(const struct option *opts, int count,
int (* parser_cb)(int val, const char *arg, void *privdata),
void *privdata)
{
struct optarg_entry *entry;
entry = malloc(sizeof(struct optarg_entry));
if (entry == NULL)
return -1;
entry->opts = opts; /* TODO: copy? */
entry->count = count;
entry->parser_cb = parser_cb;
entry->privdata = privdata;
list_add_tail(&entry->list, &option_list);
return 0;
}
void optarg_free(void)
{
struct optarg_entry *entry, *entry_tmp;
list_for_each_entry_safe(entry, entry_tmp, &option_list, list) {
list_del(&entry->list);
free(entry);
}
}
static void optarg_getsize(int *opt_count, int *optstring_len)
{
int count = 0;
int length = 0;
struct optarg_entry *entry;
list_for_each_entry(entry, &option_list, list) {
count += entry->count;
int i;
for (i = 0; i < entry->count; i++) {
switch (entry->opts[i].has_arg) {
case 0: /* no arguments */
case 1: /* has argument */
case 2: /* optional argument */
length += entry->opts[i].has_arg +1;
break;
default:
break;
}
}
}
*opt_count = count +1;
*optstring_len = length +1;
}
static void optarg_copy(struct option *opts, char *optstring)
{
struct optarg_entry *entry;
list_for_each_entry(entry, &option_list, list) {
memcpy(opts, entry->opts, sizeof(struct option) * entry->count);
opts += entry->count;
int i;
for (i = 0; i < entry->count; i++) {
switch (entry->opts[i].has_arg) {
case 0: /* no arguments */
*optstring++ = (char)entry->opts[i].val;
break;
case 1: /* has argument */
*optstring++ = (char)entry->opts[i].val;
*optstring++ = ':';
break;
case 2: /* optional argument */
*optstring++ = (char)entry->opts[i].val;
*optstring++ = ':';
*optstring++ = ':';
break;
default:
break;
}
}
}
memset(opts++, 0x00, sizeof(struct option));
*optstring++ = '\0';
}
int optarg_parse(int argc, char * const argv[])
{
struct option *longopts;
char *optstring;
int opt_count;
int optstring_len;
optarg_getsize(&opt_count, &optstring_len);
longopts = malloc(sizeof(struct option) * opt_count);
if (longopts == NULL)
return -1;
optstring = malloc(optstring_len);
if (optstring == NULL) {
free(longopts);
return -1;
}
optarg_copy(longopts, optstring);
int retval = 0;
int val = 0;
while (val != -1 && retval == 0) {
opterr = 1; /* print error message to stderr */
val = getopt_long(argc, argv, optstring, longopts, NULL);
if (val == 0x00) /* variable assigned (not supported) */
continue;
struct optarg_entry *entry;
list_for_each_entry(entry, &option_list, list) {
int ret = entry->parser_cb(val, optarg, entry->privdata);
/* option recognized, with error */
if (ret < 0) {
retval = ret;
break;
/* option recognized, no error */
} else if (ret == 0) {
break;
}
}
if (val == -1) /* parsing completed */
break;
if (val == '?') { /* parsing error */
retval = 1;
break;
}
}
free(optstring);
free(longopts);
return retval;
}