Skip to content

Instantly share code, notes, and snippets.

@maxdeliso
Last active August 29, 2015 13:57
Show Gist options
  • Save maxdeliso/9442015 to your computer and use it in GitHub Desktop.
Save maxdeliso/9442015 to your computer and use it in GitHub Desktop.
recursive functional style opt-parsing in C
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
/*
* 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