File: paste.c

package info (click to toggle)
zsv 1.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 49,160 kB
  • sloc: ansic: 175,811; cpp: 56,301; sh: 3,623; makefile: 3,048; javascript: 577; cs: 90; awk: 70; python: 41; sql: 15
file content (175 lines) | stat: -rw-r--r-- 5,223 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
 * Copyright (C) 2021 Liquidaty and the zsv/lib contributors
 * All rights reserved
 *
 * This file is part of zsv/lib, distributed under the license defined at
 * https://opensource.org/licenses/MIT
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <zsv/utils/writer.h>

#define ZSV_COMMAND paste
#include "zsv_command.h"

static int zsv_paste_usage(void) {
  static const char *usage[] = {
    "Usage: paste <filename> [<filename> ...]",
    "",
    "Horizontally paste two tables together: given inputs X, Y, ... of N rows",
    "outputs 1...N rows, where each row i contains:",
    "  row i of input X,",
    "  followed by row i of input Y,",
    "  [followed by ...]",
    "",
    "Options:",
    "  -h,--help : show usage",
    NULL,
  };
  for (size_t i = 0; usage[i]; i++)
    printf("%s\n", usage[i]);
  return 0;
}

struct zsv_paste_input_file {
  struct zsv_paste_input_file *next;
  const char *fname;
  struct zsv_opts opts;
  FILE *f;
  unsigned col_count;
  zsv_parser parser;
  enum zsv_status zsv_status; // parser status
};

enum zsv_paste_status {
  zsv_paste_status_ok = 0,
  zsv_paste_status_file,
  zsv_paste_status_memory,
  zsv_paste_status_error
};

// zsv_paste_load_row: return number of inputs that a row was retrieved from
static int zsv_paste_load_row(struct zsv_paste_input_file *inputs) {
  int have_row = 0;
  for (struct zsv_paste_input_file *pf = inputs; pf; pf = pf->next) {
    if (pf->zsv_status == zsv_status_row) {
      pf->zsv_status = zsv_next_row(pf->parser);
      if (pf->zsv_status == zsv_status_row)
        have_row++;
    }
  }
  return have_row;
}

static void zsv_paste_print_row(zsv_csv_writer w, struct zsv_paste_input_file *inputs) {
  char first = 1;
  for (struct zsv_paste_input_file *pf = inputs; pf; pf = pf->next) {
    unsigned int j = pf->zsv_status == zsv_status_row ? zsv_cell_count(pf->parser) : 0;
    unsigned int k = pf->col_count;

    for (unsigned int i = 0; i < j && i < k; i++) {
      struct zsv_cell cell = zsv_get_cell(pf->parser, i);
      zsv_writer_cell(w, first, cell.str, cell.len, cell.quoted);
      first = 0;
    }
    for (unsigned int i = j; i < k; i++) {
      zsv_writer_cell(w, first, (const unsigned char *)"", 0, 0);
      first = 0;
    }
  }
}

static void zsv_paste_delete_input(struct zsv_paste_input_file *pf) {
  if (pf->parser)
    zsv_delete(pf->parser);
  if (pf->opts.stream)
    fclose(pf->opts.stream);
  free(pf);
}

// zsv_paste_add_input(): return error
static enum zsv_paste_status zsv_paste_add_input(const char *fname, struct zsv_paste_input_file **next,
                                                 struct zsv_paste_input_file ***next_next, struct zsv_opts *opts,
                                                 struct zsv_prop_handler *custom_prop_handler) {
  FILE *f = fopen(fname, "rb");
  if (!f) {
    perror(fname);
    return zsv_paste_status_file;
  }
  struct zsv_paste_input_file *pf = calloc(1, sizeof(*pf));
  if (!pf) {
    fclose(f);
    return zsv_paste_status_memory;
  }

  pf->opts = *opts;
  pf->opts.stream = f;
  pf->fname = fname;
  *next = pf;
  *next_next = &pf->next;

  if (zsv_new_with_properties(&pf->opts, custom_prop_handler, fname, &pf->parser) != zsv_status_ok) {
    fprintf(stderr, "Unable to initialize parser for %s\n", fname);
    return zsv_paste_status_error;
  } else {
    if ((pf->zsv_status = zsv_next_row(pf->parser)) == zsv_status_row)
      pf->col_count = zsv_cell_count(pf->parser);
    else
      fprintf(stderr, "Warning: no data read from %s\n", fname);
  }
  return zsv_paste_status_ok;
}

int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *opts,
                               struct zsv_prop_handler *custom_prop_handler) {
  for (int i = 1; i < argc; i++) {
    const char *arg = argv[i];
    if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
      return zsv_paste_usage();
  }

  struct zsv_paste_input_file *inputs = NULL;
  struct zsv_paste_input_file **next_input = &inputs;
  enum zsv_paste_status status = zsv_paste_status_ok;
  struct zsv_csv_writer_options writer_opts = zsv_writer_get_default_opts();
  zsv_csv_writer writer = zsv_writer_new(&writer_opts);
  if (!writer) {
    status = zsv_printerr(zsv_paste_status_error, "Unable to create csv writer");
    goto zsv_paste_done;
  }

  for (int i = 1; status == zsv_paste_status_ok && i < argc; i++) {
    const char *arg = argv[i];
    if (!(!strcmp(arg, "-h") || !strcmp(arg, "--help")))
      status = zsv_paste_add_input(arg, next_input, &next_input, opts, custom_prop_handler);
  }

  if (status == zsv_paste_status_ok) {
    if (!inputs) {
      fprintf(stderr, "Please specify at least one input file\n");
      status = zsv_paste_status_error;
      goto zsv_paste_done;
    }

    // print headers
    zsv_paste_print_row(writer, inputs);

    // print one row at a time
    while (zsv_paste_load_row(inputs))
      zsv_paste_print_row(writer, inputs);
  }

zsv_paste_done:
  for (struct zsv_paste_input_file *next, *pf = inputs; pf; pf = next) {
    next = pf->next;
    zsv_paste_delete_input(pf);
  }

  if (opts->stream && opts->stream != stdin)
    fclose(opts->stream);

  zsv_writer_delete(writer);
  return status;
}