mirror of
https://github.com/sigmasternchen/gosh
synced 2025-03-15 07:48:55 +00:00
first working version
This commit is contained in:
parent
cc30f18a3f
commit
44a51013ed
12 changed files with 595 additions and 0 deletions
71
src/cli.c
Normal file
71
src/cli.c
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <editline/readline.h>
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "cli.h"
|
||||||
|
#include "interpreter/interpreter.h"
|
||||||
|
|
||||||
|
settings_t* settings;
|
||||||
|
|
||||||
|
void cli_init(settings_t* set) {
|
||||||
|
settings = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HOSTNAME_MAX_LENGTH 1024
|
||||||
|
#define CWD_MAX_LENGTH 1024
|
||||||
|
char* cli_getPrompt(state_t* state) {
|
||||||
|
int length = 1;
|
||||||
|
const char* username = getlogin();
|
||||||
|
if (username == NULL)
|
||||||
|
username = "[unknown]";
|
||||||
|
|
||||||
|
char hostname[HOSTNAME_MAX_LENGTH];
|
||||||
|
if (gethostname(hostname, HOSTNAME_MAX_LENGTH) != 0)
|
||||||
|
bailOut(EXIT_FAILURE, "cli_getPrompt", "gethostname");
|
||||||
|
hostname[HOSTNAME_MAX_LENGTH - 1] = '\0'; // for safety
|
||||||
|
|
||||||
|
char* cwd = getcwd(NULL, CWD_MAX_LENGTH);
|
||||||
|
|
||||||
|
char* sep = "$";
|
||||||
|
if (getuid() == 0)
|
||||||
|
sep = "#";
|
||||||
|
|
||||||
|
length += strlen(username) + 1 + strlen(hostname) + 1 + strlen(cwd) + 1 + 1;
|
||||||
|
|
||||||
|
char* prompt = malloc(length);
|
||||||
|
if (prompt == NULL)
|
||||||
|
bailOut(EXIT_FAILURE, "cli_getPromp", "malloc");
|
||||||
|
|
||||||
|
(void) strcpy(prompt, username);
|
||||||
|
(void) strcat(prompt, "@");
|
||||||
|
(void) strcat(prompt, hostname);
|
||||||
|
(void) strcat(prompt, ":");
|
||||||
|
(void) strcat(prompt, cwd);
|
||||||
|
(void) strcat(prompt, sep);
|
||||||
|
(void) strcat(prompt, " ");
|
||||||
|
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* cli_getLine(state_t* state) {
|
||||||
|
char* input = NULL;
|
||||||
|
char* prompt = cli_getPrompt(state);
|
||||||
|
|
||||||
|
input = readline(prompt);
|
||||||
|
|
||||||
|
free(prompt);
|
||||||
|
|
||||||
|
if (input == NULL)
|
||||||
|
bailOut(EXIT_FAILURE, "cli_getLine", NULL);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_afterLine(char* input) {
|
||||||
|
free(input);
|
||||||
|
}
|
13
src/cli.h
Normal file
13
src/cli.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef CLI_H
|
||||||
|
#define CLI_H
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "interpreter/interpreter.h"
|
||||||
|
|
||||||
|
void cli_init(settings_t*);
|
||||||
|
|
||||||
|
char* cli_getLine(state_t*);
|
||||||
|
|
||||||
|
void cli_afterLine(char*);
|
||||||
|
|
||||||
|
#endif
|
88
src/global.c
Normal file
88
src/global.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
bool showShutdownDetails = false;
|
||||||
|
|
||||||
|
#define HANDLER_ARRAY_RESIZE_STEP 5
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
exit_handler_t handler;
|
||||||
|
} handler_t;
|
||||||
|
|
||||||
|
struct cleanUpData {
|
||||||
|
int handlerCount;
|
||||||
|
int handlerArraySize;
|
||||||
|
handler_t* handlers;
|
||||||
|
} cleanUpData;
|
||||||
|
|
||||||
|
int setup() {
|
||||||
|
cleanUpData = (struct cleanUpData) {0};
|
||||||
|
|
||||||
|
return 0; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
int addExitHandler(exit_handler_t handler, const char* name) {
|
||||||
|
if (cleanUpData.handlerCount <= cleanUpData.handlerArraySize) {
|
||||||
|
void* tmp = realloc(cleanUpData.handlers, (cleanUpData.handlerArraySize + HANDLER_ARRAY_RESIZE_STEP) * sizeof (handler_t));
|
||||||
|
if (tmp == NULL) {
|
||||||
|
bailOut(EXIT_SETUP_FAILED, "addExitHandler", name);
|
||||||
|
}
|
||||||
|
cleanUpData.handlers = tmp;
|
||||||
|
cleanUpData.handlerArraySize += HANDLER_ARRAY_RESIZE_STEP;
|
||||||
|
}
|
||||||
|
cleanUpData.handlers[cleanUpData.handlerCount] = (handler_t) {
|
||||||
|
.name = name,
|
||||||
|
.handler = handler
|
||||||
|
};
|
||||||
|
cleanUpData.handlerCount++;
|
||||||
|
|
||||||
|
return 0; // TODO: we need something better here
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanUp() {
|
||||||
|
for (int i = 0; i < cleanUpData.handlerCount; i++) {
|
||||||
|
int code = cleanUpData.handlers[i].handler();
|
||||||
|
if (showShutdownDetails) {
|
||||||
|
if (code != 0) {
|
||||||
|
fprintf(stderr, "exit handler %s terminated with error (%d)\n", cleanUpData.handlers[i].name, code);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "exit handler %s terminated successfully\n", cleanUpData.handlers[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "freeing exit handler array (%d)\n", cleanUpData.handlerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void bailOut(int exitcode, const char* module, const char* message) {
|
||||||
|
if (module != NULL) {
|
||||||
|
// output is not split into smaller quantities to
|
||||||
|
// ensure error messages of child and parent
|
||||||
|
// don't get mixed
|
||||||
|
if (message == NULL) {
|
||||||
|
if (errno == 0) {
|
||||||
|
(void) fprintf(stderr, "%s: %s\n", PROGRAM_NAME, module);
|
||||||
|
} else {
|
||||||
|
(void) fprintf(stderr, "%s: %s: %s\n", PROGRAM_NAME, module, strerror(errno));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (errno == 0) {
|
||||||
|
(void) fprintf(stderr, "%s: %s (%s)\n", PROGRAM_NAME, module, message);
|
||||||
|
} else {
|
||||||
|
(void) fprintf(stderr, "%s: %s (%s): %s\n", PROGRAM_NAME, module, message, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "cleaning up...");
|
||||||
|
cleanUp();
|
||||||
|
|
||||||
|
fprintf(stderr, "exiting with error code %d", exitcode);
|
||||||
|
exit(exitcode);
|
||||||
|
}
|
34
src/global.h
Normal file
34
src/global.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef GLOBAL_H
|
||||||
|
#define GLOBAL_H
|
||||||
|
|
||||||
|
#define PROGRAM_NAME "gosh"
|
||||||
|
#define VERSION "0.1"
|
||||||
|
|
||||||
|
#define EXIT_SUCCESS 0
|
||||||
|
#define EXIT_FAILURE 1
|
||||||
|
#define EXIT_SETUP_FAILED 10
|
||||||
|
|
||||||
|
#define EXIT_IMPOSSIBLE_STATE 255
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int verbose;
|
||||||
|
} settings_t;
|
||||||
|
|
||||||
|
typedef int (*exit_handler_t)();
|
||||||
|
|
||||||
|
extern bool showShutdownDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 0 on success, 1 else
|
||||||
|
*/
|
||||||
|
int setup();
|
||||||
|
|
||||||
|
int addExitHandler(exit_handler_t, const char*);
|
||||||
|
|
||||||
|
void cleanUp();
|
||||||
|
|
||||||
|
void bailOut(int, const char*, const char*);
|
||||||
|
|
||||||
|
#endif
|
6
src/interpreter/built-in/builtin.h
Normal file
6
src/interpreter/built-in/builtin.h
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef BUILTIN_H
|
||||||
|
#define BUILTIN_H
|
||||||
|
|
||||||
|
int cd(int, char**);
|
||||||
|
|
||||||
|
#endif
|
25
src/interpreter/built-in/cd.c
Normal file
25
src/interpreter/built-in/cd.c
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "builtin.h"
|
||||||
|
|
||||||
|
int cd(int argc, char** argv) {
|
||||||
|
char* dir = getenv("HOME");
|
||||||
|
if (argc == 2) {
|
||||||
|
dir = argv[1];
|
||||||
|
}
|
||||||
|
if (argc > 2) {
|
||||||
|
fprintf(stderr, "cd: too many arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir(dir) < 0) {
|
||||||
|
fprintf(stderr, "cd: chdir: %s", strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
16
src/interpreter/builtin.c
Normal file
16
src/interpreter/builtin.c
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "built-in/builtin.h"
|
||||||
|
|
||||||
|
int hello_world(int argc, char** argv) {
|
||||||
|
|
||||||
|
printf("Hello World\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void builtin_add() {
|
||||||
|
interpreter_addBuiltIn("hello", hello_world);
|
||||||
|
interpreter_addBuiltIn("cd", cd);
|
||||||
|
}
|
171
src/interpreter/interpreter.c
Normal file
171
src/interpreter/interpreter.c
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "../global.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "splitter.h"
|
||||||
|
|
||||||
|
settings_t* settings;
|
||||||
|
|
||||||
|
int builtInCount = 0;
|
||||||
|
program_t* builtIn;
|
||||||
|
|
||||||
|
void interpreter_addBuiltIn(const char* name, program_handler_t handler) {
|
||||||
|
void* tmp = realloc(builtIn, (builtInCount + 1) * sizeof(program_t));
|
||||||
|
if (tmp == NULL)
|
||||||
|
bailOut(EXIT_SETUP_FAILED, "interpreter_addBuiltIn", NULL);
|
||||||
|
|
||||||
|
builtIn = tmp;
|
||||||
|
|
||||||
|
builtIn[builtInCount] = (program_t) {
|
||||||
|
.name = name,
|
||||||
|
.handler = handler
|
||||||
|
};
|
||||||
|
|
||||||
|
builtInCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void interpreter_init(settings_t* set) {
|
||||||
|
settings = set;
|
||||||
|
|
||||||
|
builtin_add();
|
||||||
|
}
|
||||||
|
|
||||||
|
state_t interpreter_initState() {
|
||||||
|
return (state_t) {
|
||||||
|
.last = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
program_handler_t interpreter_searchBuiltIn(char* name) {
|
||||||
|
for (int i = 0; i < builtInCount; i++) {
|
||||||
|
if (strcmp(builtIn[i].name, name) == 0)
|
||||||
|
return builtIn[i].handler;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int interpreter_searchBin(char* name, char** file) {
|
||||||
|
char* tmp = getenv("PATH");
|
||||||
|
char* path = malloc(strlen(tmp) + 1);
|
||||||
|
strcpy(path, tmp);
|
||||||
|
tmp = path;
|
||||||
|
|
||||||
|
char* token;
|
||||||
|
|
||||||
|
int error = ENOENT;
|
||||||
|
while((token = strtok(tmp, ":")) != NULL) {
|
||||||
|
tmp = NULL;
|
||||||
|
|
||||||
|
*file = realloc(*file, strlen(token) + 1 + strlen(name) + 1);
|
||||||
|
if (*file == NULL)
|
||||||
|
bailOut(EXIT_FAILURE, "interpreter_searchBin", NULL);
|
||||||
|
|
||||||
|
(void) strcpy(*file, token);
|
||||||
|
(void) strcat(*file, "/");
|
||||||
|
(void) strcat(*file, name);
|
||||||
|
|
||||||
|
if (access(*file, R_OK | X_OK) < 0) {
|
||||||
|
if (errno == EACCES)
|
||||||
|
error = EACCES;
|
||||||
|
} else {
|
||||||
|
free(path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
*file = NULL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t interpreter_startBuiltIn(program_handler_t handler, args_t args) {
|
||||||
|
return handler(args.argc, args.argv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is problematic: We cannot alter the env or the cwd of the shell-process from a child.
|
||||||
|
* TODO: Find a better solution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*int pid = fork();
|
||||||
|
if (pid < 0)
|
||||||
|
bailOut(EXIT_FAILURE, "interpreter_forkBuiltIn", "fork");
|
||||||
|
if (pid == 0)
|
||||||
|
exit(handler(args.argc, args.argv));
|
||||||
|
|
||||||
|
// parent
|
||||||
|
|
||||||
|
int status;
|
||||||
|
if (waitpid(pid, &status, 0) < 0)
|
||||||
|
bailOut(EXIT_FAILURE, "interpreter_forkBuiltIn", "wait");
|
||||||
|
|
||||||
|
if (!WIFEXITED(status)) {
|
||||||
|
// bad
|
||||||
|
}
|
||||||
|
|
||||||
|
return WEXITSTATUS(status);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t interpreter_startBin(char* file, args_t args) {
|
||||||
|
int pid = fork();
|
||||||
|
if (pid < 0)
|
||||||
|
bailOut(EXIT_FAILURE, "interpreter_forkBin", "fork");
|
||||||
|
if (pid == 0) {
|
||||||
|
if (execv(file, args.argv) < 0)
|
||||||
|
bailOut(EXIT_FAILURE, "interpreter_forkBin", "execv");
|
||||||
|
}
|
||||||
|
|
||||||
|
// parent
|
||||||
|
|
||||||
|
int status;
|
||||||
|
if (waitpid(pid, &status, 0) < 0)
|
||||||
|
bailOut(EXIT_FAILURE, "interpreter_forkBin", "wait");
|
||||||
|
|
||||||
|
if (!WIFEXITED(status)) {
|
||||||
|
// bad
|
||||||
|
}
|
||||||
|
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int interpreter_do(state_t* state, char* input) {
|
||||||
|
if (input == NULL) {
|
||||||
|
// this is: EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
args_t args = splitter_do(input);
|
||||||
|
|
||||||
|
if (args.argc > 0) {
|
||||||
|
|
||||||
|
program_handler_t handler = interpreter_searchBuiltIn(args.argv[0]);
|
||||||
|
|
||||||
|
error_t result = 0;
|
||||||
|
|
||||||
|
if (handler != NULL) {
|
||||||
|
result = interpreter_startBuiltIn(handler, args);
|
||||||
|
} else {
|
||||||
|
char* file = NULL;
|
||||||
|
int error = interpreter_searchBin(args.argv[0], &file);
|
||||||
|
if (error == 0) {
|
||||||
|
result = interpreter_startBin(file, args);
|
||||||
|
} else {
|
||||||
|
result = 127; // TODO export to constant
|
||||||
|
fprintf(stderr, "%s: command not found: %s\n", PROGRAM_NAME, args.argv[0]);
|
||||||
|
}
|
||||||
|
free(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->last = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
splitter_free(args);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
28
src/interpreter/interpreter.h
Normal file
28
src/interpreter/interpreter.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef INTERPRETER_H
|
||||||
|
#define INTERPRETER_H
|
||||||
|
|
||||||
|
#include "../global.h"
|
||||||
|
|
||||||
|
typedef int error_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
error_t last;
|
||||||
|
} state_t;
|
||||||
|
|
||||||
|
typedef int (*program_handler_t)(int, char**);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
program_handler_t handler;
|
||||||
|
} program_t;
|
||||||
|
|
||||||
|
void interpreter_addBuiltIn(const char*, program_handler_t);
|
||||||
|
|
||||||
|
void interpreter_init(settings_t*);
|
||||||
|
state_t interpreter_initState();
|
||||||
|
|
||||||
|
int interpreter_do(state_t*, char*);
|
||||||
|
|
||||||
|
void builtin_add();
|
||||||
|
|
||||||
|
#endif
|
52
src/interpreter/splitter.c
Normal file
52
src/interpreter/splitter.c
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "splitter.h"
|
||||||
|
#include "../global.h"
|
||||||
|
|
||||||
|
#define ARGS_ARRAY_RESIZE_STEP 5
|
||||||
|
|
||||||
|
args_t splitter_do(char* line) {
|
||||||
|
args_t args = (args_t) {0};
|
||||||
|
|
||||||
|
char* tmp = line;
|
||||||
|
char* token;
|
||||||
|
while ((token = strtok(tmp, " ")) != NULL) {
|
||||||
|
tmp = NULL;
|
||||||
|
|
||||||
|
if (args.argc >= args.arraySize) {
|
||||||
|
void* tmp = realloc(args.argv, (args.arraySize + ARGS_ARRAY_RESIZE_STEP) * sizeof(char*));
|
||||||
|
if (tmp == NULL)
|
||||||
|
bailOut(EXIT_FAILURE, "splitter_do", NULL);
|
||||||
|
args.argv = tmp;
|
||||||
|
args.arraySize += ARGS_ARRAY_RESIZE_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* tmp = malloc(strlen(token) + 1);
|
||||||
|
if (tmp == NULL)
|
||||||
|
bailOut(EXIT_FAILURE, "splitter_do", NULL);
|
||||||
|
|
||||||
|
args.argv[args.argc] = tmp;
|
||||||
|
(void) strcpy(args.argv[args.argc], token);
|
||||||
|
args.argc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.argc >= args.arraySize) {
|
||||||
|
void* tmp = realloc(args.argv, (args.arraySize + ARGS_ARRAY_RESIZE_STEP) * sizeof(char*));
|
||||||
|
if (tmp == NULL)
|
||||||
|
bailOut(EXIT_FAILURE, "splitter_do", NULL);
|
||||||
|
args.argv = tmp;
|
||||||
|
args.arraySize += ARGS_ARRAY_RESIZE_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.argv[args.argc] = NULL; // for execv
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
void splitter_free(args_t args) {
|
||||||
|
for (int i = 0; i < args.argc; i++)
|
||||||
|
free(args.argv[i]);
|
||||||
|
|
||||||
|
free(args.argv);
|
||||||
|
}
|
14
src/interpreter/splitter.h
Normal file
14
src/interpreter/splitter.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef SPLITTER_H
|
||||||
|
#define SPLITTER_H
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int argc;
|
||||||
|
int arraySize;
|
||||||
|
char** argv;
|
||||||
|
} args_t;
|
||||||
|
|
||||||
|
args_t splitter_do(char*);
|
||||||
|
|
||||||
|
void splitter_free(args_t);
|
||||||
|
|
||||||
|
#endif
|
77
src/main.c
Normal file
77
src/main.c
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "cli.h"
|
||||||
|
#include "interpreter/interpreter.h"
|
||||||
|
|
||||||
|
void version() {
|
||||||
|
printf("%s Version %s\n", PROGRAM_NAME, VERSION);
|
||||||
|
|
||||||
|
cleanUp();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseArguments(int argc, char** argv, settings_t* settings) {
|
||||||
|
const char* options = "v";
|
||||||
|
const struct option long_options[] = {
|
||||||
|
{"version", no_argument, 0, 'v'}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
while (true) {
|
||||||
|
int option_index = 0;
|
||||||
|
opt = getopt_long(argc, argv, options, long_options, &option_index);
|
||||||
|
|
||||||
|
if (opt == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch(opt) {
|
||||||
|
case 0: // long option
|
||||||
|
if (option_index == 0) // version
|
||||||
|
version();
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
version();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
exit(EXIT_IMPOSSIBLE_STATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc) {
|
||||||
|
// non-option arguments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
settings_t settings = {0};
|
||||||
|
|
||||||
|
parseArguments(argc, argv, &settings);
|
||||||
|
|
||||||
|
cli_init(&settings);
|
||||||
|
interpreter_init(&settings);
|
||||||
|
|
||||||
|
char* input;
|
||||||
|
int result;
|
||||||
|
state_t state = interpreter_initState();
|
||||||
|
while (true) {
|
||||||
|
input = cli_getLine(&state);
|
||||||
|
result = interpreter_do(&state, input);
|
||||||
|
cli_afterLine(input);
|
||||||
|
|
||||||
|
if (result >= 0 && result < 256) {
|
||||||
|
break; // result is exit code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
Loading…
Reference in a new issue