aboutsummaryrefslogblamecommitdiffstats
path: root/src/clparser.c
blob: 3a2a9a639c4a409c899991a84263940aee0c5177 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                                







                                                                                
                                                                                
                       
                   
                     
                    
 
               
 

                       




                                                                           
                                                                      
                                                                              
                                                              
                                                                    
                                                          
                                                           
                                                                
                                                           


                    
 
                             
                          
                         
 
                                 
                         

                             

                   


                         
                  

                      

             

        
                 
             


   
                                                       
                                     


                                 
   

 









                                                 
   

          
 
                                            













                                                     
                                     






                                       
                        
                                                  
                                                  
                                 

              


              

 
                                                                
        
                                                                         
                                                                         




                                                                         

                                                                         
             
        


                         
                                      
              
                               
                       
                     
                             
             

 


                               
                                  


                             
                       
 
 
                             
                                     
                            
                     

                                                       
                            


                                      
                             











                                                   
       
     
                


                      
 
                          

 
                      
                       
                     




                                                       
     


                      
   
           

 
                       
                       
                     




                                                        
     
 




                                                        
     
 



                      

 
                                                                   
                       
                     
                                                                     
                                                           







                                                           

              
          
                
                
   

                    
              

 
                                              
                         
                                                  
 


                     
                           

                                     
                                                           


                                                 
          

               
   

 
                                 
                         

                                   






                                                                         
                                                                       
                                                                   
                

 
                                                             

                               

                

                             

                         
                           
                  
              
   
                                      
                        
              
   
                                       
                           



                         
                    


                            
   
                               
             
   

                          
 
/* clparser.c - Command line parser for ck -----------------------------*- C -*-
 *
 * This file is part of ck, the config keeper
 *
 * -----------------------------------------------------------------------------
 *
 * Copyright (C) 2018  Anastasis Grammenos
 * GPLv3 (see LICENCE for the full notice)
 *
 * -------------------------------------------------------------------------- */
#include "confparser.h"
#include "ckinfo.h"
#include "ckerrlog.h"
#include "actions.h"

ERRLOG(parser);

/* accepted commands */
/* [0] is the count */
const char* const strINIT[]     = {"3", "init",   "i",  "-i"};
const char* const strADD[]      = {"3", "add",    "a",  "-a"};
const char* const strDEL[]      = {"4", "delete", "del","d",  "-d"};
const char* const strEDIT[]     = {"3", "edit",   "e",  "-e"};
const char* const strLIST[]     = {"5", "list",   "ls", "l",  "-l", "-ls"};
const char* const strSEARCH[]   = {"4", "search", "grep", "s",  "-s"};
const char* const strHELP[]     = {"5", "help",   "h",  "-?", "-h", "--help"};
const char* const strRESTORE[]  = {"3", "restore","r",  "-r"};
const char* const strEXPORT[]   = {"3", "export", "ex", "--export"};
const char* const strConfDir[]  = {"2", "--config", "-c"};
const char* const strVerbose1[] = {"2", "--verbose", "-v"};
const char* const strVersion[]  = {"2", "version", "--version"};
const char* const strVerbose2[] = {"2", "--Verbose", "-V"};

/* Number of opts */
static int optNum;

/* holds the list of the opts
 * as given by the user */
static const char **opts;

/* points to the current token */
static const char *token;

/* the position to be read */
static int pos = 0;

/* Advance one token.
 * Returns 1 if it exists
 * 0 otherwise */
int next_token() {
  if (pos < optNum) {
    token = opts[pos];
    pos++;
    return 1;
  }
  else {
    token = NULL;
    return 0;
  }
}

static void fill_args_list(int arg_num, UserOpt *opt) {
  for (int i = 0; i < arg_num; i++) {
    if (next_token()) {
      list_add(opt->args, token);
    }
  }
}

#define X(ACTION, MIN, MAX)                     \
  static int parse_ ##ACTION(UserOpt *opt) {    \
    if (optNum < pos + MIN                      \
        || optNum > pos + MAX) {                \
      opt->err = PERR_ ##ACTION## _WRONG;       \
      return -1;                                \
    }                                           \
    int arg_num = optNum - pos;                 \
    fill_args_list(arg_num, opt);               \
    return 0;                                   \
  }
CK_ACTIONS
#undef X

static void determine_action(UserOpt *opt) {
  /* get action */
  if (!next_token()) {
    opt->action = CK_WRONG_ACTION;
    return;
  }

  char actionName[STR_S] = "";
  opt->action = parser_get_action(token, actionName);
  if (opt->action != CK_WRONG_ACTION) {
    LOG("Action to perform: %s", actionName);
  }
  return;
}

static int parse_vals(UserOpt *opt) {
  /* find the action */
  determine_action(opt);
  if (opt->action == CK_WRONG_ACTION) {
    opt->err = PERR_UNKNOWN_ACTION;
    return -1;
  }

  switch (opt->action) {
#define X(ACTION, MIN, MAX)                      \
    case CKA_##ACTION:                           \
      return parse_##ACTION(opt);
    CK_ACTIONS
#undef X
  default:
    return -1;
  }
}

CkAction parser_get_action(const char *name, char *actionName) {
  int i;
#define X(ACTION, MIN, MAX)                                             \
  for (i = 1; i < atoi(str##ACTION[0]) + 1; i++) {                      \
    if (strcmp(name, str##ACTION[i]) == 0) {                            \
      if (actionName) {                                                 \
        strcpy(actionName, str##ACTION[1]);                             \
      }                                                                 \
      return CKA_##ACTION;                                              \
    }                                                                   \
  }
  CK_ACTIONS;
#undef X
  return CK_WRONG_ACTION;
}

static UserOpt make_empty_user_opt() {
  UserOpt opt;
  opt.action = CK_WRONG_ACTION;
  opt.err = PERR_NOERR;
  opt.confDir = NULL;
  opt.args = list_make_new();
  return opt;
}


/* called to free the resources
 * UserOpt holds */
void free_user_opt(UserOpt *opt) {
  if (opt->confDir != NULL) {
    free(opt->confDir);
  }
  list_free(opt->args);
}

/* find the correct config */
static int get_config(UserOpt *opt) {
  /* If it's a cli option */
  if (next_token()) {
    for (int i = 1; i < atoi(strConfDir[0]) + 1; i++) {
      if (strcmp(token, strConfDir[i]) == 0) {
        if (!next_token()) {
          ERR("Config needs a value");
          return -1;
        }
        char dir[STR_L] = "";
        realpath(token, dir);
        if (!util_is_dir(dir)) {
          ERR("%s is not a directory", token);
          return -1;
        }
        opt->confDir = malloc(strlen(dir) + 1);
        strcpy(opt->confDir, dir);
        // remove trailing `/`
        if (opt->confDir[strlen(dir) - 1] == '/') {
          opt->confDir[strlen(dir) - 1] = '\0';
        }
        return 0;
      }
    }
    /* rewind */
    pos = pos - 1;
    token = opts[pos];
   }

  return find_config(opt);
}

static int version() {
  /* get first token */
  if (next_token()) {
    for (int i = 1; i < atoi(strVersion[0]) + 1; i++) {
      if (strcmp(token, strVersion[i]) == 0) {
        print_version();
        return 1;
      }
    }
    // rewind
    pos = pos - 1;
    token = opts[pos];
  }
  return 0;
}

static void verbose() {
  /* get first token */
  if (next_token()) {
    for (int i = 1; i < atoi(strVerbose1[0]) + 1; i++) {
      if (strcmp(token, strVerbose1[i]) == 0) {
        errlog_set_verbose(1);
        return;
      }
    }

    for (int i = 1; i < atoi(strVerbose2[0]) + 1; i++) {
      if (strcmp(token, strVerbose2[i]) == 0) {
        errlog_set_verbose(2);
        return;
      }
    }

    // rewind
    pos = pos - 1;
    token = opts[pos];
  }
}

char * get_possible_action_strings(char *dest, CkAction ckAction) {
  char buf[STR_M] = "";
  switch (ckAction) {
#define X(ACTION, MIN, MAX)                                         \
    case CKA_##ACTION:                                    \
      strcpy(buf, "{ ");                                  \
      for (int i = 1; i < atoi(str##ACTION[0]); i++) {    \
        strcat(buf, str##ACTION[i]);                      \
        strcat(buf, ", ");                                \
      }                                                   \
      strcat(buf, str##ACTION[atoi(str##ACTION[0])]);     \
      strcat(buf, " }");                                  \
      break;
    CK_ACTIONS
#undef X
  default:
    dest = NULL;
    return NULL;
  }

  strcpy(dest, buf);
  return dest;
}

static void print_parser_error(UserOpt *opt) {
  char names[STR_M] = "";
  get_possible_action_strings(names, opt->action);

  switch (opt->err) {
  case PERR_NOERR:
    return;
  case PERR_UNKNOWN_ACTION:
    ERR("Unknown action: %s", token);
    return;
#define X(ACTION, MIN, MAX)                               \
  case PERR_ ##ACTION## _WRONG:                 \
    HELP("Usage:\n%s", names);                  \
    print_##ACTION##_help();                    \
    break;
    CK_ACTIONS;
#undef X
  }
}

static void print_parser_help() {
  char names[STR_M] = "";
  ckhelp("ck - the config keeper");
  ckhelp("Usage:");
  ckhelp("Init\t%s", get_possible_action_strings(names, CKA_INIT));
  ckhelp("Add\t%s", get_possible_action_strings(names, CKA_ADD));
  ckhelp("Delete\t%s", get_possible_action_strings(names, CKA_DEL));
  ckhelp("Edit\t%s", get_possible_action_strings(names, CKA_EDIT));
  ckhelp("List\t%s", get_possible_action_strings(names, CKA_LIST));
  ckhelp("Search\t%s", get_possible_action_strings(names, CKA_SEARCH));
  ckhelp("Restore\t%s", get_possible_action_strings(names, CKA_RESTORE));
  ckhelp("Export\t%s", get_possible_action_strings(names, CKA_EXPORT));
  ckhelp("Help\t%s", get_possible_action_strings(names, CKA_HELP));
  report_help();
}

int parse_action(int argc, const char **argv, UserOpt *opt) {
  /* make empty user opt */
  *opt = make_empty_user_opt();
  opts = argv;
  optNum = argc;
  /* skip the program name */
  next_token();
  /* set verbose level */
  verbose();
  /* handle version info */
  if (version()) {
    return -1;
  }
  /* figure what is the config file */
  if (get_config(opt)) {
    return -1;
  }
  /* If the remaining arguments are < 1
   * print help and exit */
  if (optNum - pos < 1) {
    print_parser_help();
    return -1;
  }
  /* parse values */
  if (parse_vals(opt)) {
    print_parser_error(opt);
    return -1;
  }
  if (opt->err == PERR_NOERR) {
    return 0;
  }
  print_parser_error(opt);
  return -1;
}