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








                     


                     

                        

              
                           
                         
                              


                   

            

                          
 
                                          

 

                                           
 

                       
 

                                  
 

              
 



                     
 



                          
 

                

     

                                  

 

                                
 


                       
                             
                      
                               










                                                   
                                      

               
                                                             







                                    
     
   
 

                                  
 
                 

     
                                

 

                                             
 


















                      
      
    
 

                         
 
                                  
 


                                            
 

                                                                        
 




















                                                                            

            

     


              

 
          
                                          
 
              
                   
                        
 
                                                         
 
























                                                                        

 

                           
 
                 
                 
 
                               





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

#include "filehash.h"
#include "macros.h"

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

typedef struct
{
  const char *hash_program;
  const char *repository;
  unsigned max_per_invocation;
  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_program = "md5sum",
    .repository = ".",
    .max_per_invocation = 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_program = optarg;
        break;
      case 'L':
        if (str_to_uint(optarg, &options.max_per_invocation))
          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++];
  }

  bug_abort;

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

static int
run(const options_t *opts, filehash_t *fh)
{
  int ret = 0;
  void *aux = NULL;
  const char *file_name;

  while (file_name = iter_files(opts, &aux), file_name) {

    switch (filehash_send(fh, file_name)) {
      case fhs_accepts:
        continue;

      case fhs_failure:
        warnx("handle failure!");
        ret = 1;
        goto exit;

      case fhs_full:
        warnx("handle full!");
        filehash_free(fh);
        fh = filehash_new(opts->hash_program, opts->max_per_invocation);
        break;

      case fhs_rejected:
        bug_abort;

    }

  }

exit:
  filehash_free(fh);
  return ret;
}

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

  opts = read_opts(argc, argv);

  fh = filehash_new(opts.hash_program, opts.max_per_invocation);
  if (!fh)
    err(1, "filehash_new");

  return run(&opts, fh);
}