aboutsummaryrefslogblamecommitdiffstats
path: root/src/actionparser.c
blob: d5ad0716fb1f719f5d157b3449df6e3799ccbca7 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                                                                                
                         
                   
                   

                     
                      
 

                       

                                                             
                                                                   
                                                             
                                                                          


                                                                             


                                                               


                    
 
                             
                          
                   
 

                                 

                             



                                                 
                  




                      
                 



              

                                
                            

               

                             
 

                                                
                 
   





                                        
                              

                             

                                                       



                               
                               


           
                             
                                    




                              
 

                               


           
                             








                                          
 
 
                              




                                          


                               
           
 
 
                              
                                                  
                                          
                               





                               
 
 
                                
                                              
                                          






                                 
 
 
                              
            


 
                              
                        
                                                  
                                                  
                                 

              


              

 







                                     
                                                                         

                                                                         

                                                                         

                                                                         
             
        


                                

 



                               
                     
                             
             

 


                               
                                  


                             
                       
 
 





                                                

                                                     
                               


                                         
                                   
                   

                        
                                


                                                 
                                               
                                  



                                                   


             
                             
                               
                                                                              
                                                                  





                    
               





                                                     
               





                    
           

 






















                                                      
                                                                 
                  


                                                           







                                                           

              
          

                
   

                    

 
                                       
                     
                    
                                                  
 



                          
                                                 

                       
                                                                                            

                      
                                                                                                       

                      
                                                                       

                       
                                                                                                          

                       
                                                                                                                     

                         
                                                                                          

                       
                                       

          
                    

 
                          
                    

                                   
                                               
                                    
                                              
                                    
                                              
                                       
                                               
                                     
                                               
                                      
                                                 
                                
                                               

                                    













                                                                      
                           


                       
 


                         

                                      
                                                                





















                                       
 
/* actionparser.c - Action 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)
 *
 * -----------------------------------------------------------------------------
 *
 * The following code is resposinble for parsing the command line arguments
 * and report any errors that might come up.
 *
 * -------------------------------------------------------------------------- */
#include "actionparser.h"
#include "ckutil.h"
#include "ckinfo.h"
#include "ckerrlog.h"

ERRLOG(action 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[]  = {"3", "search", "s",  "-s"};
const char* const strHELP[]    = {"5", "help",   "h",  "-?", "-h", "--help"};
const char* const strConfDir[] = {"4", "config", "conf", "c", "-c"};
const char* const strVersion[] = {"2", "version", "--version"};
const char* const strVerbose1[] = {"2", "--verbose", "-v"};
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 char **opts;

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

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

/* Reads the next token and returns it's position
 * Returns -1 otherwise */
int next_token() {
  if (pos < optNum) {
    token = opts[pos];
    return pos++;
  }
  else {
    token = NULL;
    return -1;
  }
}

/* copy the option from the list
 * to the UserOpt struct */
void get_opt(UserOpt *opt) {
  /* get arg */
  next_token();
  list_add(opt->args, token);
}

void fill_args_list(int arg_num, UserOpt *opt) {
  for (int i = 0; i < arg_num; i++) {
    get_opt(opt);
  }
}

/* When starting to parse the action,
 * `pos` should be at 2
 * like so "ck ACTION ..."
 *                    ^               */
int parse_INIT(UserOpt *opt) {
  /* INIT expects 2 arguments
   * starting from 0 */
  int arg_num = 2;
  if (optNum != pos /* already consumed */ + arg_num) {
    opt->err = PERR_INIT_WRONG;
    return -1;
  }

  fill_args_list(arg_num, opt);
  return 1;
}

int parse_ADD(UserOpt *opt) {
  /* ADD expects 2 to 4 arguments */
  if (optNum <= pos + 1
      || optNum > pos + 4) {
    opt->err = PERR_ADD_WRONG;
    return -1;
  }

  int arg_num = optNum - pos;
  fill_args_list(arg_num, opt);
  return 1;
}

int parse_DEL(UserOpt *opt) {
  /* DEL expects 1 to 2 arguments */
  if (optNum <= pos || optNum > pos + 2) {
    opt->err = PERR_DEL_WRONG;
    return -1;
  }

  int arg_num = optNum - pos;
  fill_args_list(arg_num, opt);
  return 1;
}

int parse_EDIT(UserOpt *opt) {
  /* EDIT expects 1 to 2 arguments */
  if (optNum <= pos || optNum > pos + 2) {
    opt->err = PERR_EDIT_WRONG;
    return -1;
  }

  int arg_num = optNum - pos;
  fill_args_list(arg_num, opt);
  return 1;
}

int parse_LIST(UserOpt *opt) {
  /* List expects a maximum of than 3 arguments */
  if (optNum <= pos || optNum > pos + 4) {
    opt->err = PERR_LIST_WRONG;
    return -1;
  }

  int arg_num = optNum - pos;
  fill_args_list(arg_num, opt);
  return 1;
}

int parse_SEARCH(UserOpt *opt) {
  /* Search expects a maximum of 1 argument */
  if (optNum <= pos || optNum > pos + 1) {
    opt->err = PERR_SEARCH_WRONG;
    return -1;
  }

  int arg_num = optNum - pos;
  fill_args_list(arg_num, opt);  
  return 1;
}

int parse_HELP(UserOpt *opt) {
  return -1;
}


int parse_vals(UserOpt *opt) {
  switch (opt->action) {
#define X(ACTION)                                \
    case CKA_##ACTION:                           \
      return parse_##ACTION(opt);
    CK_ACTIONS
#undef X
  default:
    return -1;
  }
}

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

  int i;
#define X(ACTION)                                                       \
  for (i = 1; i < atoi(str##ACTION[0]) + 1; i++) {                      \
    if (strcmp(token, str##ACTION[i]) == 0) {                           \
      opt->action = CKA_##ACTION;                                       \
      return;                                                           \
    }                                                                   \
  }
  CK_ACTIONS;
#undef X

  opt->action = CK_WRONG_ACTION;
  return;
}

UserOpt make_empty_user_opt() {
  UserOpt opt;
  opt.action = -1;
  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);
}

/* If the used has specified a config file other
 * than the default get it now */
void get_config(UserOpt *opt) {
  /* get first token */
  next_token();

  for (int i = 1; i < atoi(strConfDir[0]) + 1; i++) {
    if (strcmp(token, strConfDir[i]) == 0) {
      if (next_token() == -1) {
        printf("Config needs a value\n");
        exit(1);
      }
      if (strcmp(token, ".") == 0){
        /* WHAT? */
        printf("Dot\n");
      }
      if (!util_is_dir(token)) {
        printf("%s is not a directory\n", token);
        exit(1);
      }
      opt->confDir = malloc(strlen(token) + 1);
      strcpy(opt->confDir, token);
      // remove trailing `/`
      if (opt->confDir[strlen(token) - 1] == '/') {
        opt->confDir[strlen(token) - 1] = '\0';
      }
      return;
    }
  }
  char * defaultConf = ".ck";
  char * home = getenv("HOME");
  opt->confDir = malloc(strlen(defaultConf) + 1 /* '/' */ + strlen(home) + 1);
  str_join_dirname_with_basename(opt->confDir, home, defaultConf);

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

int version() {
  /* get first token */
  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;
}

void verbose() {
  /* get first token */
  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];
}

void get_possible_action_strings(char *dest, CkAction ckAction) {
  char buf[STR_S];
  switch (ckAction) {
#define X(ACTION)                                         \
    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;
  }

  strcpy(dest, buf);
}

void print_parser_error(UserOpt *opt) {
  char errStr[STR_L];
  char names[STR_S];
  get_possible_action_strings(names, opt->action);

  switch (opt->err) {
  case PERR_NOERR:
    return;
  case PERR_UNKONW_ACTION:
    sprintf(errStr, "Unknown action: %s", token);
    break;
  case PERR_INIT_WRONG:
    sprintf(errStr, "Initialize database\nUsage: %s version_control_dir secret_dir", names);
    break;
  case PERR_ADD_WRONG:
    sprintf(errStr, "Add config \nUsage: %s ProgramName ConfigPath [-s](secret) [-p](primary)", names);
    break;
  case PERR_DEL_WRONG:
    sprintf(errStr, "Delete config or program\nUsage: %s ....", names);
    break;
  case PERR_EDIT_WRONG:
    sprintf(errStr, "Edit config with $EDITOR\nUsage: %s ProgramName or configBasename (or both)", names);
    break;
  case PERR_LIST_WRONG:
    sprintf(errStr, "List programs, configs and more\nUsage: %s value-to-list (or tree) [-t list-type] [-a]", names);
    break;
  case PERR_SEARCH_WRONG:
    sprintf(errStr, "Search through the configs with grep\nUsage: %s search-term", names);
    break;
  case PERR_HELP_WRONG:
    sprintf(errStr, "Usage: ........");
    break;
  }
  ERR("%s", errStr);
}

void print_parser_help() {
  char names[STR_S];
  ckhelp("ck - the config keeper");
  ckhelp("Usage:");
  get_possible_action_strings(names, CKA_INIT);
  ckhelp("Initialize: \t%s", names);
  get_possible_action_strings(names, CKA_ADD);
  ckhelp("Add config: \t%s", names);
  get_possible_action_strings(names, CKA_DEL);
  ckhelp("Delete config: \t%s", names);
  get_possible_action_strings(names, CKA_EDIT);
  ckhelp("Edit config: \t%s", names);
  get_possible_action_strings(names, CKA_LIST);
  ckhelp("List configs: \t%s", names);
  get_possible_action_strings(names, CKA_SEARCH);
  ckhelp("Search: \t%s", names);
  get_possible_action_strings(names, CKA_HELP);
  ckhelp("Print this: \t%s", names);
  report_help();
}

ActionParseResult parse_action(int argc, char* argv[], UserOpt *opt) {
  /* make empty user opt */
  *opt = make_empty_user_opt();
  if (argc < 2) {
    return APR_HELP;
  }
  opts = argv;
  optNum = argc;

  /* skip the program name */
  next_token();

  /* handle version info */
  if (version()) {
    return APR_VERSION;
  }

  /* set verbose level */
  verbose();

  /* figure what is the config file */
  get_config(opt);
  cklog("Found ck configuration directory in %s", opt->confDir);

  /* find the action */
  determine_action(opt);
  if (opt->action == CK_WRONG_ACTION) {
    opt->err = PERR_UNKONW_ACTION;
    return APR_ERR;
  }
  if (opt->action == CKA_HELP) {
    return APR_HELP;
  }

  // parse values
  if (!parse_vals(opt)) {
    return APR_ERR;
  }

  if (opt->err == PERR_NOERR) {
    return APR_OK;
  }
  else {
    return APR_ERR;
  }
}