about summary refs log blame commit diff stats
path: root/forg-import.c
blob: 9f6cdb4e6292bf4065b2743ade7816e7068e61fa (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                        







                          

            

                          
 
                                          

 

                                           
 

                       
 

                                  
 

              
 



                     
 



                          
 

                

     

                                  

 

                                
 




























                                                         
     
   
 

                                  
 
                 

     
                                

 

                                             
 


















                      
      
    
 

                         
 
                                  
 


                                            
 

                                                                        
 





















                                                                            

     


              

 

                          
 

                   
 

                                             
 
           

 

                           
 
                 
 

                               
 
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>

#define optstr "0h:L:r:"

typedef struct
{
  const char *hash;
  const char *repository;
  unsigned hash_max_lines;
  char **files;
  unsigned n_files;
  char input_sep;
} options_t;

static void
usage(const char *prgname)
{
  errx(EX_USAGE, "usage %s ...", prgname);
}

static int
str_to_uint(const char *str, unsigned *out)
{
  unsigned long result;
  char *end;

  errno = 0;
  result = strtoul(str, &end, 10);

  if (errno)
    goto fail;

  if (*end != '\0') {
    errno = EINVAL;
    goto fail;
  }

  if (result > UINT_MAX) {
    errno = ERANGE;
    goto fail;
  }

  *out = result;
  return 0;

fail:
  warn("str_to_uint \"%s\"", str);
  return -1;
}

static options_t
read_opts(int argc, char **argv)
{
  int o;
  const char *prgname;
  options_t options = {
    .hash = "md5sum",
    .repository = ".",
    .hash_max_lines = 4096,
    .input_sep = '\n',
  };

  prgname = argc < 1 ? "keep" : argv[0];

  while (o = getopt(argc, argv, optstr), o != -1) {
    switch (o) {
      case '0':
        options.input_sep = '\0';
        break;
      case 'h':
        options.hash = optarg;
        break;
      case 'L':
        if (str_to_uint(optarg, &options.hash_max_lines))
          goto fail;
        break;
      case 'r':
        options.repository = optarg;
        break;
      case '?':
      default:
        usage(prgname);
    }
  }

  options.files = argv + optind;
  options.n_files = argc - optind;

  return options;

fail:
  err(EX_CONFIG, "bad options");
}

static const char *
iter_files(const options_t *opts, void **aux)
{
  struct ctx
  {
    enum
    {
      from_files_list,
      from_stdin,
    } from;

    union
    {
      struct
      {
        int next;
      } files_list;
      struct
      {
        char *line;
        size_t len;
      } stdin;
    };
  };

  struct ctx *ctx = *aux;
  ssize_t nread;

  if (!ctx) { /* initialization */

    *aux = ctx = malloc(sizeof(struct ctx));
    if (!ctx)
      err(EX_OSERR, "malloc");

    *ctx = (struct ctx){ opts->n_files ? from_files_list : from_stdin };
  }

  switch (ctx->from) {
    case from_stdin:
      errno = 0;
      nread =
        getdelim(&ctx->stdin.line, &ctx->stdin.len, opts->input_sep, stdin);
      if (nread == -1) {
        if (!errno) {
          free(ctx->stdin.line);
          goto done;
        }
        err(EX_OSERR, "getdelim(..., stdin)");
      }
      if (opts->input_sep && ctx->stdin.line[nread - 1] == opts->input_sep)
        ctx->stdin.line[nread - 1] = '\0';
      return ctx->stdin.line;

    case from_files_list:
      if (ctx->files_list.next >= opts->n_files)
        goto done;
      return opts->files[ctx->files_list.next++];
  }
  err(EX_SOFTWARE, __FILE__ ":%d - buggy", __LINE__);

done:
  free(*aux);
  *aux = NULL;
  return NULL;
}

static int
run(const options_t *opts)
{
  void *aux = NULL;
  const char *file;

  while (file = iter_files(opts, &aux), file)
    warnx("[%s]", file);

  return 0;
}

int
main(int argc, char **argv)
{
  options_t opts;

  opts = read_opts(argc, argv);
  return run(&opts);
}