Saturday, April 19, 2008

arglib

Satisfied with the result of PerlArg, I decided to rewrite it for use on my CSE303 homework and any other C applications I happen to write.

arglib.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct {
const char * key;
const char * value;
} hashnode;

typedef struct {
hashnode * Long, * LongSynoms, * ShortSynoms;
const char * help;
char ** Anonymous;
size_t LongLen, LongSynomsLen, ShortSynomsLen, AnonymousLen;
} argparser;

argparser * argparser_new(const char ** Long, const char ** LongSynoms, const char ** ShortSynoms, const char * help);
void argparser_delete(argparser * self);
const char * argparser_getValue(argparser * self, const char * key);
const char * argparser_getAnonymous(argparser * self, const int idx);
int argparser_getAnonymousLen(argparser * self);
int argparser_parseArgs(argparser * self, int argc, char ** argv);

arglib.c

#include "arglib.h"
/*
typedef struct {
const char * key;
const char * value;
} hashnode;

typedef struct {
hashnode * Long, * LongSynoms, * ShortSynoms;
const char * help;
char ** Anonymous;
size_t LongLen, LongSynomsLen, ShortSynomsLen, AnonymousLen;
} argparser;
*/

size_t argparser_getLength(const char ** src);
hashnode * argparser_allocateNodes(size_t len, const char ** src);
int strpcmp(char * str1, char * str2, const char * addr);
int fputs_until(FILE * fp, char * str, const char * end);

argparser * argparser_new(const char ** Long, const char ** LongSynoms, const char ** ShortSynoms, const char * help) {
argparser * self = malloc(sizeof(argparser));
const char * help_alloc[] = {"help", "Help", NULL};
if(!self) {
fputs("Error: Unable to allocate memory\n", stderr);
return NULL;
}
memset(self, '\0', sizeof(argparser));
self->LongLen = argparser_getLength(Long);
self->AnonymousLen = 0;
self->Long = NULL; self->LongSynoms = NULL; self->ShortSynoms = NULL; self->help = NULL; self->Anonymous = NULL;
if(LongSynoms)
self->LongSynomsLen = argparser_getLength(LongSynoms);
if(ShortSynoms)
self->ShortSynomsLen = argparser_getLength(ShortSynoms);
if(self->LongLen % 2 || self->LongSynomsLen % 2 || self->ShortSynomsLen % 2) {
fputs("Error: Non-even array provided.\n", stderr);
free(self);
return NULL;
}
self->LongLen /= 2; self->Long = argparser_allocateNodes(self->LongLen, Long);
if(LongSynoms) {
self->LongSynomsLen /= 2;
self->LongSynoms = argparser_allocateNodes(self->LongSynomsLen, LongSynoms);
if(!self->LongSynoms) {
fputs("Error: Unable to allocate memory\n", stderr);
free(self);
return NULL;
}
}
if(help) {
self->help = help;
if(!LongSynoms) {
self->LongSynoms = argparser_allocateNodes(self->LongSynomsLen, help_alloc);
}
else {
self->LongSynoms = (hashnode *)realloc(self->LongSynoms, (self->LongSynomsLen + 1) * sizeof(hashnode));
if(!self->LongSynoms) {
fputs("Error: Unable to allocate memory\n", stderr);
free(self);
return NULL;
}
self->LongSynoms[self->LongSynomsLen].key = help_alloc[0];
self->LongSynoms[self->LongSynomsLen++].value = help_alloc[1];
}
}
if(ShortSynoms) {
self->ShortSynomsLen /= 2;
self->ShortSynoms = argparser_allocateNodes(self->ShortSynomsLen, ShortSynoms);
if(!self->ShortSynoms) {
fputs("Error: Unable to allocate memory\n", stderr);
free(self);
return NULL;
}
}
if(help)
self->help = help;
self->AnonymousLen = 0;
return self;
}
void argparser_delete(argparser * self) {
if(self) {
if(self->Long)
free(self->Long);
if(self->LongSynoms)
free(self->LongSynoms);
if(self->ShortSynoms)
free(self->ShortSynoms);
free(self);
}
}
const char * argparser_getValue(argparser * self, const char * key) {
size_t i;
if(key && self && self->Long) {
for(i = 0; i < self->LongLen; i++)
if(!strcmp(key, self->Long[i].key))
return self->Long[i].value;
}
return NULL;
}
int argparser_parseArgs(argparser * self, int argc, char ** argv) {
int i, j, k;
bool opting = true, found, found2;
char * eq = NULL, * value = NULL;
char * nodashes = NULL;
if(self->Anonymous) {
self->AnonymousLen = 0;
free(self->Anonymous);
self->Anonymous = NULL;
}
for(i = 1; i < argc; i++) {
if(opting && !strcmp("--", argv[i])) // Disables arguments
opting = false;
else if(opting && strstr(argv[i], "--") == argv[i]) { // Gets --long and --long=value
nodashes = argv[i] + 2;
if((eq = strchr(nodashes, '=')) != NULL) { // Gets --long=value
found = false;
for(j = 0; !found && j < self->LongLen; j++) {
if(!strpcmp(nodashes, self->Long[j].key, eq)) {
self->Long[j].value = eq + 1;
found = true;
}
}
if(!found) {
fputs("Unknown argument: ", stderr); fputs_until(stderr, nodashes, eq); fputc('\n', stderr);
return -1;
}
}
else { // Gets --long
found = false;
for(j = 0; !found && j < self->LongSynomsLen; j++) {
if(!strcmp(nodashes, self->LongSynoms[j].key)) {
value = self->LongSynoms[j].value;
if((eq = strchr(value, '=')) != NULL) { // Handles longsynom: long=value
found2 = false;
for(k = 0; !found && k < self->LongLen; k++) {
if(!strpcmp(value, self->Long[k].key, eq)) {
self->Long[k].value = eq + 1;
found2 = true;
}
}
if(!found2) {
fprintf(stderr, "Unknown equivalent: %s\n", value);
return -2;
}
}
else if(self->help && !strcmp("Help", value)) {
fputs(self->help, stderr);
return -4;
}
else if(++i < argc) { // Handles longsynom: long
found2 = false;
for(k = 0; !found && k < self->LongLen; k++) {
if(!strcmp(value, self->Long[k].key)) {
self->Long[k].value = argv[i];
found2 = true;
}
}
if(!found2) {
fprintf(stderr, "Unknown equivalent: %s\n", value);
return -2;
}
}
else {
fprintf(stderr, "Needs a value: %s\n", nodashes);
return -2;
}
found = true;
}
}
if(!found) {
fprintf(stderr, "Unknown argument: %s\n", nodashes);
return -1;
}
}
}
else if(opting && strchr(argv[i], '-') == argv[i]) { // Gets -s
nodashes = argv[i] + 1;
while(*nodashes) {
found = false;
for(j = 0; !found && j < self->ShortSynomsLen; j++) {
if(*nodashes == self->ShortSynoms[j].key[0]) {
value = self->ShortSynoms[j].value;
if((eq = strchr(value, '=')) != NULL) { // Handles shortsynom: long=value
found2 = false;
for(k = 0; !found && k < self->LongLen; k++) {
if(!strpcmp(value, self->Long[k].key, eq)) {
self->Long[k].value = eq + 1;
found2 = true;
}
}
if(!found2) {
fprintf(stderr, "Unknown equivalent: %s\n", value);
return -2;
}
}
else if(*(nodashes + 1) == '\0' && ++i < argc) { // Handles shortsynom: long
found2 = false;
for(k = 0; !found && k < self->LongLen; k++) {
if(!strcmp(value, self->Long[k].key)) {
self->Long[k].value = argv[i];
found2 = true;
}
}
if(!found2) {
fprintf(stderr, "Unknown equivalent: %s\n", value);
return -2;
}
}
else {
fprintf(stderr, "Needs a value: %c\n", *nodashes);
return -2;
}
found = true;
}
}
if(!found) {
fprintf(stderr, "Unknown argument: %c\n", *nodashes);
return -1;
}
nodashes++;
}
}
else { // Gets everything else
self->Anonymous = (char **)realloc(self->Anonymous, (self->AnonymousLen + 1) * sizeof(char *));
if(!self->Anonymous) {
fputs("Error: Unable to allocate memory\n", stderr);
return -1;
}
self->Anonymous[self->AnonymousLen++] = argv[i];
}
}
return 0;
}


const char * argparser_getAnonymous(argparser * self, const int idx) {
if(!self || idx < 0 || idx >= self->AnonymousLen)
return NULL;
return self->Anonymous[idx];
}
int argparser_getAnonymousLen(argparser * self) {
return self->AnonymousLen;
}








// HELPER METHODS
size_t argparser_getLength(const char ** src) {
size_t i;
if(!src)
return 0;
for(i = 0; src[i]; i++);
return i;
}
hashnode * argparser_allocateNodes(size_t len, const char ** src) {
size_t i;
if(!src)
return NULL;
hashnode * ret = (hashnode*)calloc(len, sizeof(hashnode));
if(!ret)
return NULL;
for(i = 0; i < len; i++) {
ret[i].key = src[2 * i];
ret[i].value = src[2 * i + 1];
}
return ret;
}
int strpcmp(char * str1, char * str2, const char * addr) {
while(str1 != addr && str2 != addr) {
if(*str1 > *str2)
return 1;
else if(*str1 < *str2)
return -1;
str1++; str2++;
}
return 0;
}
int fputs_until(FILE * fp, char * str, const char * end) {
int printed = 0;
while(str && str != end) {
fputc(*str, fp);
str++;
end++;
}
return printed;
}

And of course, an example program:

arglibtest.c

#include "arglib.h"


int main(int argc, char ** argv) {
const char * Long[] = {"cow", "say", "foo", "bar", NULL};
const char * LongSynoms[] = {"foo", "foo=FOFOFOFOFO", "car", "cow", NULL};
const char * ShortSynoms[] = {"f", "foo", "k", "cow=ohrecee=eoea", NULL};
const char help[1024];
sprintf(help, "Usage: %s [ --cow=value | --foo=value | --foo | --car=value | -f value -k ]\n", argv[0]);
argparser * argtest = argparser_new(Long, LongSynoms, ShortSynoms, help);
int i, len;
if(argtest) {
if(argparser_parseArgs(argtest, argc, argv) < 0) {
argparser_delete(argtest);
return 1;
}
printf("cow:%s\n", argparser_getValue(argtest, "cow"));
printf("foo:%s\n", argparser_getValue(argtest, "foo"));

len = argparser_getAnonymousLen(argtest);
for(i = 0; i < len; i++)
puts(argparser_getAnonymous(argtest, i));


argparser_delete(argtest);
argtest = NULL;
}
return 0;
}
% ./arglibtest --help
Usage: ./arglibtest [ --cow=value | --foo=value | --foo | --car=value | -f value -k ]
% ./arglibtest --cow=bar --foo -- --car 10 -fk 100
cow:bar
foo:FOFOFOFOFO
--car
10
-fk
100

Aside from being written in C, it essentially has the same result as the Perl version.

No comments: