Last active
August 29, 2015 13:57
-
-
Save maxdeliso/9442015 to your computer and use it in GitHub Desktop.
recursive functional style opt-parsing in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CC=clang | |
CFLAGS=-Weverything -D_XOPEN_SOURCE -std=c99 -g -O2 | |
OUT=sread | |
.PHONY: clean | |
$(OUT): $(OUT).c | |
$(CC) $(CFLAGS) $< -o $@ | |
$(OUT).S: $(OUT).c | |
$(CC) $(CFLAGS) $< -S -o $@ | |
clean: | |
$(RM) $(OUT) $(OUT).S |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* sread.c | |
* Max DeLiso <[email protected]> | |
* a silly experiment, pay it no heed | |
*/ | |
#include <unistd.h> | |
#include <getopt.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#define MAX_USER_FILE_LEN 1024 | |
typedef unsigned char byte; | |
static char SREAD_OPTSTRING[] = "+:hvVf:"; | |
static byte SREAD_VER_NUMS[] = {0, 0, 1}; | |
static char SREAD_HELPSTRING[] = | |
"sread [OPTIONS] ARGUMENT_LIST\n" | |
" -h prints this help\n" | |
" -v sets verbose mode\n" | |
" -V prints version and exits\n" | |
" -f [FILENAME] reads input from file\n"; | |
/* | |
* if any of the most significant four bits are set, it means normal program execution | |
* should not continue normally; there was a problem. | |
* | |
* the low four bits are switches that affect the running of the program. | |
*/ | |
enum parseOptsResult { | |
PARSE_OPTS_OK = 0x00, /* everything's fine */ | |
PARSE_OPTS_VERB = 0x01, /* be verbose */ | |
PARSE_OPTS_VERS = 0xF2, /* STOP: print the version */ | |
PARSE_OPTS_HELP = 0xF4, /* STOP: print the help string */ | |
PARSE_OPTS_ERR = 0xF8 /* STOP: something's wrong */ | |
}; | |
static const byte PARSE_OPTS_STOP_MASK = 0xF0; | |
static byte parseOpts(int argc, char ** argv, char userFile[MAX_USER_FILE_LEN]); | |
static inline byte lsb4(const byte by) { | |
return by & 0x0F; | |
} | |
int main(int argc, char** argv) { | |
opterr = 0; | |
char userFile[MAX_USER_FILE_LEN] = {'\0'}; | |
const byte runCode = parseOpts(argc, argv, userFile); | |
/* top four bits of run code are set, this is a stop code */ | |
if(runCode & PARSE_OPTS_STOP_MASK) { | |
/* warn on error */ | |
if(lsb4(runCode) & PARSE_OPTS_ERR) { | |
fprintf(stderr, "error: something went wrong during the parse\n"); | |
} | |
/* print version info */ | |
if(lsb4(runCode) & PARSE_OPTS_VERS) { | |
printf("sread v%i.%i.%i\n", | |
SREAD_VER_NUMS[0], | |
SREAD_VER_NUMS[1], | |
SREAD_VER_NUMS[2]); | |
} | |
/* print help info */ | |
if(lsb4(runCode) & PARSE_OPTS_HELP) { | |
printf("%s\n", SREAD_HELPSTRING); | |
} | |
return EXIT_SUCCESS; | |
} | |
const bool verbose = runCode & PARSE_OPTS_VERB; | |
if(verbose) { | |
printf("verbose mode is ON\n"); | |
} | |
if(strlen(userFile) > 0) { | |
printf("in main with f arg: %s\n", userFile); | |
} | |
for(int i = optind; i < argc; ++i) { | |
printf(" opt[%i]: %s\n", i, argv[i]); | |
} | |
/* TODO: something useful */ | |
return EXIT_SUCCESS; | |
} | |
static byte parseOpts(int argc, char ** argv, char userFile[MAX_USER_FILE_LEN]) { | |
const int optChar = getopt(argc, argv, SREAD_OPTSTRING); | |
switch(optChar) { | |
case -1: | |
return PARSE_OPTS_OK; /* base case */ | |
case ':': | |
fprintf(stderr, | |
"command line option \'%c\' is missing argument\n", | |
optopt); | |
return PARSE_OPTS_ERR; /* terminating case */ | |
case '?': | |
fprintf(stderr, | |
"command line option \'%c\' unrecognized\n", | |
optopt); | |
return PARSE_OPTS_ERR; /* terminating case */ | |
case 'h': | |
return PARSE_OPTS_HELP | parseOpts(argc, argv, userFile); /* recursive case */ | |
case 'v': | |
return PARSE_OPTS_VERB | parseOpts(argc, argv, userFile); /* recursive case */ | |
case 'V': | |
return PARSE_OPTS_VERS | parseOpts(argc, argv, userFile); /* recursive case */ | |
case 'f': | |
if(strlen(optarg) >= MAX_USER_FILE_LEN) { | |
fprintf(stderr, | |
"argument to -f is too long. only %i characters allowed.\n", | |
MAX_USER_FILE_LEN - 1); | |
return PARSE_OPTS_ERR; /* terminating case */ | |
} else { | |
strncpy(userFile, optarg, MAX_USER_FILE_LEN); | |
return PARSE_OPTS_OK | parseOpts(argc, argv, userFile); /* recursive case */ | |
} | |
default: | |
return PARSE_OPTS_ERR; /* terminating case */ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment