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








                                                                                
                                                                                
                   
                         
                   

                     
               
 

                       




                                                                           
                                                                      
                                                                              
                                                        
                                                                

                                                           


                    
 
                             
                          
                         
 
                                 
                         

                             

                   


                         
                  

                      

             

        
                 
             


   

                                                


                                 
   





                                        
                              

                             

                                                       



                               
                               
           

 
                             
                                    
                      



                              
 

                               
           

 
                             
                                    

                            





                               
           
 
 
                              
                                     

                            


                               


                               
           
 
 
                              


                                     
                               




                               
           
 
 
                                
                                              

                            





                                 
           
 
 
                              
                                            

                            






                               


 
                              
                        
                                                  
                                                  
                                 

              


              

 
                                                                
        
                                                                         
                                                                         




                                                                         

                                                                         
             
        




                                     
                      


                                  
 




                                                     
         

 

                               
                               
                       
                     
                             
             

 


                               
                                  


                             
                       
 
 

                                                
                              
                       
                     

                                                       
                            















                                                   
       
     



                      
                                  
                               
                                                                              
                                                                  
 
           

 
               
                       
                     




                                                       
     


                      
   
           

 

                       
                     




                                                        
     
 




                                                        
     
 



                      

 
                                                                 
                  


                                                           







                                                           

              
          

                
   

                    

 
                                       
                     
                    
                                                  
 


                     
                           

                                     
                       
                                                                                            

                      
                                                                                                       

                      
                                                                                                                        

                       
                                                                                                                      

                       
                                                                                                                                               

                         
                                                                                          

                       
                                                                          
   
                     

 
                          
                    

                                   
                                               
                                    
                                              
                                    
                                              
                                       
                                               
                                     
                                               
                                      
                                                 
                                
                                               
                                      
                

 
                                                             

                               

                

                             

                         
                           
                  
              
   
                                      
                        
              
   
                                       
                           



                         


                                       
                                   

                            
   
                    


                            
   
                               
             
   

                          
 
/* 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)
 *
 * -------------------------------------------------------------------------- */
#include "ckutil.h"
#include "actionparser.h"
#include "ckinfo.h"
#include "ckerrlog.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 strConfDir[]  = {"2", "config", "-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 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;
  }
}

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);
    }
  }
}

/* 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 0;
}

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

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

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

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

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

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

int parse_LIST(UserOpt *opt) {
  /* List expects 1 to 5 arguments */
  if (optNum < pos + 1
      || optNum > pos + 5) {
    opt->err = PERR_LIST_WRONG;
    return -1;
  }

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

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

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

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

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


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;
  }
}

CkAction parser_get_action(const char *name, char *actionName) {
  int i;
#define X(ACTION)                                                       \
  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;
}

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;
}

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);
}

/* If the used has specified a config file other
 * than the default get it now */
int get_config(UserOpt *opt) {
  /* get first token */
  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];
   }
  char defaultConf[STR_S] = ".ck";
  char * home = getenv("HOME");
  opt->confDir = malloc(strlen(defaultConf) + 1 /* '/' */ + strlen(home) + 1);
  str_join_dirname_with_basename(opt->confDir, home, defaultConf);

  return 0;
}

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;
}

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];
  }
}

void get_possible_action_strings(char *dest, CkAction ckAction) {
  char buf[STR_M];
  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_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;
  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 {ProgramName} | {-c ConfigPath (as shown by ck list)}", names);
    break;
  case PERR_EDIT_WRONG:
    sprintf(errStr, "Edit config with $EDITOR (%s)\nUsage: %s ProgramName [configBasename]", getenv("EDITOR"), names);
    break;
  case PERR_LIST_WRONG:
    sprintf(errStr, "List programs, configs and more\nUsage: %s {programs|paths|-p ProgramName} [-t list-type] | {tree | ckconf} [-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, "Get help for a ck action.\nUsage: %s action", names);
  }
  HELP("%s", errStr);
}

void print_parser_help() {
  char names[STR_M];
  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("Actions Help: \t%s", names);
  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;
  }
  /* find the action */
  determine_action(opt);
  if (opt->action == CK_WRONG_ACTION) {
    opt->err = PERR_UNKNOWN_ACTION;
    print_parser_error(opt);
    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;
}