Quantcast
Channel: Library for managing CLI flags - Code Review Stack Exchange
Viewing all articles
Browse latest Browse all 2

Library for managing CLI flags

$
0
0

I'm pretty new to C, started learning it less than a month ago, the only language I had previous experience was Javascript.

Anyways, I wrote a library for dealing with CLI flags. It's not a serious project by any means, I wrote it for practice and fun.

It's composed of a source file and a header file (with declarations etc..). I'll list these functions then I'll try to describe them briefly. I didn't use array subscripting as I'm trying to gain a serious understanding of how pointers work etc...

libflagutil.c:

#include "../flagutil.h" //make sure definitions match declarations and protos etc..#include "../butil.h" //terminal text styling#include <stdio.h>#include <stdbool.h>#include <string.h>bool flag_present(int argc, char **argv, const char *flag) {    /* a single flag is defined as present IF:        - an arg that exactly matches the provided `flag` string is found        - an arg that exactly matches the provided `flag` string BUT includes an `=` (and optionally somed data)          after it is found    */    bool present = false;    for(char **cur_flag = argv; cur_flag < argv + argc; cur_flag++) {        if(strncmp(flag, *cur_flag, strlen(flag)) == 0 && (*(*cur_flag + strlen(flag)) == '=' || *(*cur_flag + strlen(flag)) == '\0')) {            present = true;        }    }    return present;}const Flag *flag_get(int argc, char **argv, const char *flag) {    /* gets the value of the provided flag. flag must begin with 2 consecutive hyphens (`--`)       - value of the flag may be provided within the flag itself by providing an `=` and some value after it       - OR it may be provided in an argument after the said flag       - the function looks for the value in that order         - doesn't check if the flag itself exists. use flag_present prior to calling         this function.    */    if(*flag != '-' || !(*(flag + 1))) {        printf("Invalid flag structure for flag " bold("%s") " passed to get_flag.\n", flag);        return NULL;    }    static Flag internal; //static buffer of last-fetched flag    for(char **cur_flag = argv; cur_flag < argv + argc; cur_flag++) {        if(strncmp(flag, *cur_flag, strlen(flag)) == 0) {            char *char_after_flag = *cur_flag + strlen(flag);            if(*char_after_flag == '='&& *(char_after_flag + 1) != '\0') {                internal.data = char_after_flag + 1;                internal.location = (const char **) cur_flag;                strncpy(internal.name, flag, strlen(flag) + 1);            } else if(cur_flag + 1 && **(cur_flag + 1) != '-') {                internal.data = *(cur_flag + 1);                internal.location = (const char **) cur_flag;                strncpy(internal.name, flag, strlen(flag) + 1);            }        }    }    return &internal;}void flag_startswith(int argc, char **argv, const char *start_string, Flag *storage) {    /* assumes that storage is large enough to accomodate all valid flags */    //TODO: handle `=` within flag_string, for now function doesn't work with those flags    for(char **flag = argv; flag < argv + argc; flag++) {        if(**flag != '-') {            continue;        } //First char isn't a `-`, skip arg        if(*(*flag + 1) == '-') {            if(strncmp(*flag + 2, start_string, strlen(start_string)) == 0) {                char flag_string[2 + strlen(*flag + 2) + 1];                strcpy(flag_string, "--");                strcpy(flag_string + 2, *flag + 2);                *storage = *f_get(flag_string);            }         } else {            if(strncmp(*flag + 1, start_string, strlen(start_string)) == 0) {                char flag_string[1 + strlen(*flag + 1) + 1];                strcpy(flag_string, "-");                strcpy(flag_string + 1, *flag + 1);                *storage = *f_get(flag_string);            }        }        storage++;    }}char *flag_nextarg(signed int argc, char **argv) {    //oversimplify fetching args from argv... (kinda useless)    static size_t counter = 0;    return *(argv + (counter++));}

flagutil.h:

#include <stdio.h>#include <stdbool.h>#include <string.h>#include "./butil.h"#pragma once/*define these yourself if you'd like to use short version but use non-"standard"  names for main's 2 args */#ifndef FLAGUTIL_ARGC_NAME    #define FLAGUTIL_ARGC_NAME argc#endif#ifndef FLAGUTIL_ARGV_NAME    #define FLAGUTIL_ARGV_NAME argv#endif/* expose functions as short versions taking less arguments, for easier usage */#define f_present(s)       flag_present(FLAGUTIL_ARGC_NAME, FLAGUTIL_ARGV_NAME, s)#define f_get(s)           flag_get(FLAGUTIL_ARGC_NAME, FLAGUTIL_ARGV_NAME, s)#define f_startswith(s, g) flag_startswith(FLAGUTIL_ARGC_NAME, FLAGUTIL_ARGV_NAME, s, g);#define f_nextarg()        flag_nextarg(FLAGUTIL_ARGC_NAME, FLAGUTIL_ARGV_NAME)struct flag {    char name[100];    const char *data;    const char **location; //actual pointer within argv};typedef struct flag Flag;//prototypes:bool flag_present(int, char **, const char *);const Flag *flag_get(int, char **, const char *);void flag_startswith(int, char **, const char *, Flag *);char *flag_easyArg(int, char **);

Currently all of the functions accept argc and argv as their first 2 arguments, but flagutil.h exposes these functions as macros with shorter names taking only the necessary arguments, and automatically feeding argc and argv (also defined as macros for compatibility in case of non-standard parameter names inside main).

All of the functions (except flag_startswith) take flag names in the form -flagName or --flagName.

Data is passed to flags in the form of -flagName=DATA or -flagName DATA.

bool flag_present(...): Returns true if provided flag exists within argv, false otherwise.

const Flag *flag_get(...): Returns a pointer to an internal static struct which holds data about a certain flag. The actual struct is defined in flagutil.h and holds the name of the flag, its data (if it has any) and a pointer to its location inside of argv.

void flag_startswith(...): Stores struct representations of all flags that start with start_string inside of provided array.

char *flag_nextarg(...): Pretty pointless, wrote it for fun.

I realize some of these functions are unsafe, sometimes broken etc.. but so far they get the job done and I'll be looking to improve them further. I'm looking for general advice, best practices, stuff to avoid, etc... I've tested it only with GCC and linux64 and it compiles without any warnings (-Wall, -Wextra).

Please disregard the use of bil() and bold(), they're macros for styling text inside the terminal..


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images