diff --git a/src/argparse.c b/src/argparse.c new file mode 100644 index 0000000..8cc7218 --- /dev/null +++ b/src/argparse.c @@ -0,0 +1,313 @@ +/* +argparse functions + +Copyright (C) 2018 Dominik Meyer + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** +* @file argparse.c +* @brief file for all argparse functions +* @author Dominik Meyer +* @copyright 2018 by Dominik Meyer +*/ + +#include +#include +#include +#include "argparse.h" + +/** +* @brief initialize the argparse context +* +* This function has to be called at every start of the programm and before +* any other argparse functions are called!!! +* +* @return returns an initialized arg parse structure +*/ +struct arg_parse_ctx * argparse_init() +{ + // allocate memory for the arg_parse_ctx structure + struct arg_parse_ctx *ctx = (struct arg_parse_ctx *) calloc(sizeof(struct arg_parse_ctx),1); + if (ctx == NULL) + { + return NULL; + } + + // allocate memory for the arg_parse_cmd array + ctx->arguments=(void **)calloc(sizeof(void *), ARGPARSE_INITIAL_COMMAND_NR); + if (ctx->arguments == NULL) + { + free(ctx); + return NULL; + } + + return ctx; +} + +/** +* @brief free an arg_parse context (all memory will be freed) +* +* this function should be called at the end of the programm +* +* @param ctx - the arg_parse_ctx structure to free +*/ +void argparse_free(struct arg_parse_ctx *ctx) +{ + // free the arg_parse_cmd array + free(ctx->arguments); + + // free the context itself + free(ctx); +} + +/** +* @brief add an arg_parse_cmd to the context for later parsing +* +* @param ctx - the context the command should be added to +* @param cmd - the command to add to the context +* +* @return 0 = everything is fine, -1 error occured +*/ +int argparse_add_command(struct arg_parse_ctx *ctx, struct arg_parse_cmd *cmd) +{ + if (ctx == NULL || cmd == NULL) + { + return -1; + } + + cmd->base.type=ARG_CMD; + + // check if the array size has to be increased + if (ctx->nr_arguments % ARGPARSE_INITIAL_COMMAND_NR == 0) + { + ctx->arguments= (void **) realloc(ctx->arguments, sizeof(void *)*(ctx->nr_arguments+ARGPARSE_INITIAL_COMMAND_NR)); + } + + // append the command to the array + ctx->arguments[ctx->nr_arguments]=cmd; + ctx->nr_arguments++; + + return 0; +} + +/** +* @brief add an arg_str to the context for later parsing +* +* @param ctx - the context the command should be added to +* @param str - the arg_str to add +* +* @return 0 = everything is fine, -1 error occured +*/ +int argparse_add_string(struct arg_parse_ctx *ctx, struct arg_str *str) +{ + if (ctx == NULL || str == NULL) + { + return -1; + } + + str->base.type=ARG_STR; + + + // check if the array size has to be increased + if (ctx->nr_arguments % ARGPARSE_INITIAL_COMMAND_NR == 0) + { + ctx->arguments= (void **) realloc(ctx->arguments, sizeof(void *)*(ctx->nr_arguments+ARGPARSE_INITIAL_COMMAND_NR)); + } + + // append the command to the array + ctx->arguments[ctx->nr_arguments]=str; + ctx->nr_arguments++; + + return 0; +} + + +/** +* @brief internal function to print usage information, not exported to user +* +* @param ctx - the context for which to print the usage information +* @param program - the program name the application was called with +* +*/ +void argparse_usage(struct arg_parse_ctx *ctx, char *program) +{ + int i=0; + + printf("usage: %s []\n",program); + printf("\n"); + + printf("Available commands: \n"); + + for(i=0; inr_arguments; i++) + { + switch (to_argbase(ctx->arguments[i])->type) + { + case ARG_CMD: + printf("\t%20s\t\t%s\n", to_cmd(ctx->arguments[i])->command, to_cmd(ctx->arguments[i])->description); + break; + default: + break; + }; + } + + printf("\nAvailable Arguments: \n"); + for(i=0; inr_arguments; i++) + { + switch (to_argbase(ctx->arguments[i])->type) + { + case ARG_STR: + printf("\t-%c |--%s=\t\t%s\n", to_str(ctx->arguments[i])->short_flag, + to_str(ctx->arguments[i])->long_flag, + to_str(ctx->arguments[i])->description); + default: + break; + }; + } + + printf("\n"); + +} + +/** +* @brief internal function to print usage information, if an unknown command has been found +* +* @param ctx - the context for which to print the usage information +* @param program - the program name the application was called with +* @param command - the command found +* +*/ +void argparse_unknown_command(struct arg_parse_ctx *ctx, char *program, char *command) +{ + printf("%s: \'%s\' is not a supported command.\n\n", program, command); + argparse_usage(ctx, program); +} + + +/** +* @brief parse the given commandline +* +* @param ctx - the arg parse context to use +* @param argc - the argument count of the commandline +* @param argv - the argument list of the commandline +* +* @return 0 - everything is fine +* @return <0 - parsing failed +*/ +int argparse_parse(struct arg_parse_ctx *ctx, int argc, char **argv) +{ + int i=0; + int r=0; + int found=0; + int newargc=argc; + char **newargv=argv; + + if (ctx == NULL) + { + return -1; + } + + // check if there are arguments to check for + if (ctx->nr_arguments == 0) + { + return 0; + } + + // check if one of the command line args matches a given argument type + if (argc > 1) + { + for (i=1; i< argc; i++) + { + found=0; + for(r=0; r < ctx->nr_arguments; r++) + { + // check if the argument is a command + if (argv[i][0]!='-' && to_argbase(ctx->arguments[r])->type == ARG_CMD ) + { + if (strcmp(to_cmd(ctx->arguments[r])->command,argv[i])==0) + { + found=1; + newargc--; + newargv=&argv[i]; + return to_cmd(ctx->arguments[r])->cb(newargc,newargv); + } + } + // check if the argument is a string argument + else if (argv[i][0]=='-' && to_argbase(ctx->arguments[r])->type == ARG_STR) + { + // check for long argument format or short + if(argv[i][1]=='-') + { + } + else + { + if(argv[i][1] == to_str(ctx->arguments[r])->short_flag) + { + found=1; + if (i+1arguments[r])->value, argv[i+1], to_str(ctx->arguments[r])->maxchars); + i++; + } + else + { + printf("value missing for -%c parameter\n\n",to_str(ctx->arguments[r])->short_flag); + argparse_usage(ctx, argv[0]); + return -1; + } + to_argbase(ctx->arguments[r])->set=1; + } + } + } + } + + if (found == 0) + { + printf("%s is an unknown command/argument\n\n", argv[i]); + argparse_usage(ctx, argv[0]); + return -1; + } + } + + + + } + + // check if all mandatory parameters have been found + for(i=0; inr_arguments; i++) + { + + if (to_argbase(ctx->arguments[i])->mandatory==1 && to_argbase(ctx->arguments[i])->set==0) + { + printf("mandatory parameter missing "); + switch (to_argbase(ctx->arguments[i])->type) + { + case ARG_STR: + printf("-%c|--%s\n\n",to_str(ctx->arguments[i])->short_flag,to_str(ctx->arguments[i])->long_flag); + break; + + default: + printf("\n"); + break; + }; + argparse_usage(ctx, argv[0]); + return -1; + } + + } + + + return 0; +} diff --git a/src/argparse.h b/src/argparse.h new file mode 100644 index 0000000..5352aaa --- /dev/null +++ b/src/argparse.h @@ -0,0 +1,133 @@ +#ifndef __ARGPARSE_H__ +#define __ARGPARSE_H__ +/* +argparse functions + +Copyright (C) 2018 Dominik Meyer + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** +* @file argparse.h +* @brief header file for all argparse functions +* @author Dominik Meyer +* @copyright 2018 by Dominik Meyer +*/ + + +/** @mainpage +* +* \section intro_sec Introduction +* YAAP is a simple command line argument parser for C. +* It uses functions and structures to add arguments to the parsing context and +* parse the command line. +* \section Features +* - git like command support (git commit) +* - short and long flag support ( -t YAAP or --title=YAAP) +* - string argument support ( -t YAAP or -- title=YAAP) +* +* \section coming +* - simple flags (-v, -V, -h, --help) +* - integer arguments (-c 10) +* - hex arguments ( -c 0x5A) +* +* \section Installation +* YAAP is intented to be copied into your project directory. Different approaches are +* available: +* - simpe copy argparse.h and argparse.c into your source directory +* - add the github repository as a submodule +* - add the github repository as a subtree +* - add the github repository as a subrepo +* +* In all cases you have to make sure that argparse.c is compiled into an object file +* and is appended to your link stage. +* +* \section Functions +* All available functions can be found in argparse.c. +* +* @example example1.c +*/ + + + + +#define ARGPARSE_INITIAL_COMMAND_NR 5 ///< initial size for the command array + +#define to_argbase(ptr) ((struct arg_base *) ptr) ///< convert a pointer to an arg_base structure pointer +#define to_cmd(ptr) ((struct arg_parse_cmd *)ptr) ///< convert a pointer to an arg_parse_cmd structure pointer +#define to_str(ptr) ((struct arg_str *)ptr) ///< convert a pointer to an arg_str structure pointer + +/** +* @brief supported argument types +*/ +enum argtypes { + ARG_CMD, ///< a command argument like git uses it (git commit) + ARG_FLAG, ///< a simple flag like -v without additional parameter + ARG_STR, ///< an argument with a string parameter (-t hallo, --title=hallo) + ARG_INT ///< an argument with a integer parameter (-s 10, --size=10) +}; + +/** +* @brief base structure for a command line argument +*/ +struct arg_base +{ + enum argtypes type; ///< type of the argument + int mandatory; ///< is the argument mandatory + int set; ///< has the argument been set +}; + +/** +* @brief structure representing a command +*/ +struct arg_parse_cmd +{ + struct arg_base base; ///< base of the command line argument + int no_command; ///< if this is 1, the callback will be called if no command is given + const char *command; ///< the name of the command on the commandline, no spaces or special chars allowed, only ascii + const char *description; ///< short description of the command + int (*cb)(int argc, char **argv); ///< function to call if the command is found on commandline +}; + +/** +* @brief structure representing a string command line argument +*/ +struct arg_str { + struct arg_base base; ///< base of the command line argument + const char short_flag; ///< one char flag identifying the argument, ignored if NULL + const char *long_flag; ///< multi char flag identifying the argument, ignore if NULL + char *value; ///< memory already allocated for the string parameter + int maxchars; ///< the maximum number of chars for the string parameters + const char *description; ///< short description of the argument +}; + + +/** +* @brief structure for the argparse context, holding argparse specific data structures +*/ +struct arg_parse_ctx +{ + void **arguments; ///< array for all argparse elementsdd + int nr_arguments; ///< how many arguments are currently registered +}; + + +struct arg_parse_ctx * argparse_init(); ///< initialize parsing context +void argparse_free(struct arg_parse_ctx *ctx); ///< free the parsing context at program end +int argparse_add_command(struct arg_parse_ctx *ctx, struct arg_parse_cmd *cmd); ///< add a commandline command to the context +int argparse_add_string(struct arg_parse_ctx *ctx, struct arg_str *str); ///< add a string argument to the context +int argparse_parse(struct arg_parse_ctx *ctx,int argc, char **argv); ///< parse the given commandline in the context + +#endif