-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
393 additions
and
169 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#include "case.h" | ||
|
||
#include <jansson.h> | ||
#include <stdbool.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
void case_clear(Case *c) { | ||
if (c != NULL) { | ||
free(c->label); | ||
free(c->opts); | ||
free(c->lopts); | ||
if (c->argv != NULL) { | ||
for (int i = 0; i < c->argc; i++) { | ||
free(c->argv[i]); | ||
} | ||
free(c->argv); | ||
} | ||
|
||
c->label = NULL; | ||
c->opts = NULL; | ||
c->lopts = NULL; | ||
c->argv = NULL; | ||
c->argc = 0; | ||
} | ||
} | ||
|
||
bool read_case(Case *dest, FILE *src) { | ||
size_t flags = JSON_DISABLE_EOF_CHECK; | ||
json_t *json_value; | ||
json_error_t json_err; | ||
|
||
json_value = json_loadf(src, flags, &json_err); | ||
if (!json_value) { | ||
fprintf(stderr, "JSON parsing error: %s\n", json_err.text); | ||
return true; | ||
} | ||
|
||
json_t *args; | ||
const char *label, *opts, *lopts; | ||
int err = json_unpack_ex(json_value, &json_err, 0, "{s:s, s:o, s:s, s:s}", | ||
"label", &label, | ||
"args", &args, | ||
"opts", &opts, | ||
"lopts", &lopts); | ||
|
||
if (err != 0) { | ||
fprintf(stderr, "JSON parsing error: %s %s\n", json_err.text, json_err.source); | ||
json_decref(json_value); | ||
return true; | ||
} | ||
|
||
if (!json_is_array(args)) { | ||
fprintf(stderr, "expected args to be an array\n"); | ||
json_decref(json_value); | ||
return true; | ||
} | ||
|
||
dest->label = strdup(label); | ||
dest->opts = strdup(opts); | ||
dest->lopts = strdup(lopts); | ||
|
||
if (!dest->label || !dest->opts || !dest->lopts) { | ||
fprintf(stderr, "Memory allocation error\n"); | ||
free(dest->label); | ||
free(dest->opts); | ||
free(dest->lopts); | ||
json_decref(json_value); | ||
return true; | ||
} | ||
|
||
dest->argc = json_array_size(args); | ||
dest->argv = malloc(dest->argc * sizeof(char *)); | ||
if (dest->argv == NULL) { | ||
fprintf(stderr, "Memory allocation error for args\n"); | ||
free(dest->label); | ||
free(dest->opts); | ||
free(dest->lopts); | ||
json_decref(json_value); | ||
return true; | ||
} | ||
|
||
for (int i = 0; i < dest->argc; i++) { | ||
json_t *el = json_array_get(args, i); | ||
if (!json_is_string(el)) { | ||
fprintf(stderr, "expected args element to be a string\n"); | ||
for (int j = 0; j < i; j++) { | ||
free(dest->argv[j]); | ||
} | ||
free(dest->argv); | ||
free(dest->label); | ||
free(dest->opts); | ||
free(dest->lopts); | ||
json_decref(json_value); | ||
return true; | ||
} | ||
dest->argv[i] = strdup(json_string_value(el)); | ||
if (dest->argv[i] == NULL) { | ||
fprintf(stderr, "Memory allocation error for arg %d\n", i); | ||
for (int j = 0; j < i; j++) { | ||
free(dest->argv[j]); | ||
} | ||
free(dest->argv); | ||
free(dest->label); | ||
free(dest->opts); | ||
free(dest->lopts); | ||
json_decref(json_value); | ||
return true; | ||
} | ||
} | ||
|
||
json_decref(json_value); | ||
return false; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#ifndef CASE_H | ||
#define CASE_H | ||
|
||
#include <stdbool.h> | ||
#include <stdio.h> | ||
|
||
typedef struct Case { | ||
char *label; | ||
char *opts; | ||
char *lopts; | ||
char **argv; | ||
int argc; | ||
} Case; | ||
|
||
bool read_case(Case *dest, FILE *src); | ||
void case_clear(Case *c); | ||
|
||
#endif |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
|
||
#include "config.h" | ||
|
||
#include <errno.h> | ||
#include <getopt.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
static void print_usage(const char *name); | ||
|
||
Config *create_config(int argc, char *argv[]) { | ||
Config *cfg = calloc(1, sizeof(Config)); | ||
if (cfg == NULL) { | ||
fprintf(stderr, "error allocating config: %s\n", strerror(errno)); | ||
return NULL; | ||
} | ||
|
||
int opt; | ||
while ((opt = getopt(argc, argv, ":o:")) != -1) { | ||
switch (opt) { | ||
case 'o': | ||
cfg->outpath = strdup(optarg); | ||
if (cfg->outpath == NULL) { | ||
fprintf(stderr, "error allocating config: %s\n", strerror(errno)); | ||
config_destroy(cfg); | ||
return NULL; | ||
} | ||
break; | ||
case '?': | ||
fprintf(stderr, "error: Unknown option \"%c\"\n", optopt); | ||
print_usage(argv[0]); | ||
config_destroy(cfg); | ||
return NULL; | ||
case ':': | ||
fprintf(stderr, "error: Option \"%c\" requires an argument\n", optopt); | ||
print_usage(argv[0]); | ||
config_destroy(cfg); | ||
return NULL; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
if (cfg->outpath == NULL) { | ||
fprintf(stderr, "error: Option -o is required\n"); | ||
print_usage(argv[0]); | ||
config_destroy(cfg); | ||
return NULL; | ||
} | ||
|
||
if (optind < argc) { | ||
cfg->inpath = strdup(argv[optind]); | ||
if (cfg->inpath == NULL) { | ||
fprintf(stderr, "error allocating config: %s\n", strerror(errno)); | ||
config_destroy(cfg); | ||
return NULL; | ||
} | ||
} else { | ||
fprintf(stderr, "error: missing required infile parameter\n"); | ||
print_usage(argv[optind]); | ||
config_destroy(cfg); | ||
return NULL; | ||
} | ||
|
||
return cfg; | ||
} | ||
|
||
void config_destroy(Config *cfg) { | ||
if (cfg != NULL) { | ||
free((char *)cfg->outpath); | ||
free((char *)cfg->inpath); | ||
free(cfg); | ||
} | ||
} | ||
|
||
static void print_usage(const char *name) { | ||
fprintf(stderr, "usage: %s -o <outfile> <infile>\n", name); | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#ifndef CONFIG_H | ||
#define CONFIG_H | ||
|
||
typedef struct Config { | ||
const char *inpath; | ||
const char *outpath; | ||
} Config; | ||
|
||
Config *create_config(int argc, char *argv[]); | ||
void config_destroy(Config *cfg); | ||
|
||
#endif |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#ifndef GENERATE_H | ||
#define GENERATE_H | ||
|
||
#endif |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#include "iterate.h" | ||
|
||
#include <ctype.h> | ||
#include <errno.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include "case.h" | ||
#include "config.h" | ||
#include "stdbool.h" | ||
|
||
static bool seek_array_start(FILE *f); | ||
static enum IteratorStatus seek_next_element(FILE *f); | ||
static FILE *prepare_infile(const Config *cfg); | ||
|
||
Iterator *create_iterator(Config *cfg) { | ||
Iterator *iter = calloc(1, sizeof(Iterator)); | ||
if (iter == NULL) { | ||
fprintf(stderr, "error allocating iterator: %s\n", strerror(errno)); | ||
return NULL; | ||
} | ||
|
||
iter->src = prepare_infile(cfg); | ||
if (iter->src == NULL) { | ||
free(iter); | ||
return NULL; | ||
} | ||
|
||
iter->status = ITER_OK; | ||
iter->index = -1; | ||
|
||
return iter; | ||
} | ||
|
||
void iterator_destroy(Iterator *iter) { | ||
if (iter != NULL) { | ||
if (iter->src != NULL) { | ||
fclose(iter->src); | ||
} | ||
case_clear(&iter->current); | ||
free(iter); | ||
} | ||
} | ||
|
||
void iterator_next(Iterator *iter) { | ||
if (iter->status != ITER_OK) { | ||
return; | ||
} | ||
|
||
if (iter->index != -1) { | ||
iter->status = seek_next_element(iter->src); | ||
if (iter->status != ITER_OK) { | ||
return; | ||
} | ||
} | ||
|
||
case_clear(&iter->current); | ||
if (read_case(&iter->current, iter->src)) { | ||
iter->status = ITER_ERROR; | ||
return; | ||
} | ||
|
||
iter->index++; | ||
} | ||
|
||
static bool seek_array_start(FILE *f) { | ||
for (;;) { | ||
int c = fgetc(f); | ||
if (c == EOF || !isspace(c)) { | ||
if (c != '[') { | ||
fprintf(stderr, "error: Input is not a valid json array\n"); | ||
return true; | ||
} | ||
break; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
static enum IteratorStatus seek_next_element(FILE *f) { | ||
if (f == NULL) { | ||
fprintf(stderr, "error: Invalid file pointer\n"); | ||
return ITER_ERROR; | ||
} | ||
|
||
int c; | ||
while ((c = fgetc(f)) != EOF) { | ||
switch (c) { | ||
case ']': | ||
return ITER_DONE; | ||
case ',': | ||
return ITER_OK; | ||
default: | ||
if (!isspace(c)) { | ||
fprintf(stderr, "error: Input is not a valid json array\n"); | ||
return ITER_ERROR; | ||
} | ||
} | ||
} | ||
fprintf(stderr, "error: Unexpected EOF\n"); | ||
return ITER_ERROR; | ||
} | ||
|
||
static FILE *prepare_infile(const Config *cfg) { | ||
FILE *f = fopen(cfg->inpath, "r"); | ||
if (f == NULL) { | ||
fprintf(stderr, "error opening iterator source %s: %s\n", cfg->inpath, strerror(errno)); | ||
return NULL; | ||
} | ||
|
||
if (seek_array_start(f) != 0) { | ||
return NULL; | ||
} | ||
|
||
return f; | ||
} |
Oops, something went wrong.