init version.
[bush.git] / examples / loadables / csv.c
bloba12f4b5b80629ca883fa5d91f5c9bbf3291fdc00
1 /* csv - process a line of csv data and populate an indexed array with the
2 fields */
4 /*
5 Copyright (C) 2020 Free Software Foundation, Inc.
7 Bush is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 Bush is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Bush. If not, see <http://www.gnu.org/licenses/>.
21 /* See Makefile for compilation details. */
23 #include <config.h>
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>
27 #endif
28 #include "bushansi.h"
29 #include <stdio.h>
31 #include "loadables.h"
33 #define CSV_ARRAY_DEFAULT "CSV"
35 #define NQUOTE 0
36 #define DQUOTE 1
38 /* Split LINE into comma-separated fields, storing each field into a separate
39 element of array variable CSV, starting at index 0. The format of LINE is
40 as described in RFC 4180. */
41 static int
42 csvsplit (csv, line)
43 SHELL_VAR *csv;
44 char *line;
46 arrayind_t ind;
47 char *field, *prev, *buf, *xbuf;
48 int delim, qstate;
49 int b, rval;
51 xbuf = 0;
52 ind = 0;
53 field = prev = line;
57 if (*prev == '"')
59 if (xbuf == 0)
60 xbuf = xmalloc (strlen (prev) + 1);
61 buf = xbuf;
62 b = 0;
63 qstate = DQUOTE;
64 for (field = ++prev; *field; field++)
66 if (qstate == DQUOTE && *field == '"' && field[1] == '"')
67 buf[b++] = *field++; /* skip double quote */
68 else if (qstate == DQUOTE && *field == '"')
69 qstate = NQUOTE;
70 else if (qstate == NQUOTE && *field == ',')
71 break;
72 else
73 /* This copies any text between a closing double quote and the
74 delimiter. If you want to change that, make sure to do the
75 copy only if qstate == DQUOTE. */
76 buf[b++] = *field;
78 buf[b] = '\0';
80 else
82 buf = prev;
83 field = prev + strcspn (prev, ",");
86 delim = *field;
87 *field = '\0';
89 bind_array_element (csv, ind, buf, 0);
90 ind++;
92 *field = delim;
94 if (delim == ',')
95 prev = field + 1;
97 while (delim == ',');
99 if (xbuf)
100 free (xbuf);
102 return (rval = ind); /* number of fields */
106 csv_builtin (list)
107 WORD_LIST *list;
109 int opt, rval;
110 char *array_name, *csvstring;
111 SHELL_VAR *v;
113 array_name = 0;
114 rval = EXECUTION_SUCCESS;
116 reset_internal_getopt ();
117 while ((opt = internal_getopt (list, "a:")) != -1)
119 switch (opt)
121 case 'a':
122 array_name = list_optarg;
123 break;
124 CASE_HELPOPT;
125 default:
126 builtin_usage ();
127 return (EX_USAGE);
130 list = loptend;
132 if (array_name == 0)
133 array_name = CSV_ARRAY_DEFAULT;
135 if (legal_identifier (array_name) == 0)
137 sh_invalidid (array_name);
138 return (EXECUTION_FAILURE);
141 if (list == 0)
143 builtin_error ("csv string argument required");
144 return (EX_USAGE);
147 v = find_or_make_array_variable (array_name, 1);
148 if (v == 0 || readonly_p (v) || noassign_p (v))
150 if (v && readonly_p (v))
151 err_readonly (array_name);
152 return (EXECUTION_FAILURE);
154 else if (array_p (v) == 0)
156 builtin_error ("%s: not an indexed array", array_name);
157 return (EXECUTION_FAILURE);
159 if (invisible_p (v))
160 VUNSETATTR (v, att_invisible);
161 array_flush (array_cell (v));
163 csvstring = list->word->word;
165 if (csvstring == 0 || *csvstring == 0)
166 return (EXECUTION_SUCCESS);
168 opt = csvsplit (v, csvstring);
169 /* Maybe do something with OPT here, it's the number of fields */
171 return (rval);
174 /* Called when builtin is enabled and loaded from the shared object. If this
175 function returns 0, the load fails. */
177 csv_builtin_load (name)
178 char *name;
180 return (1);
183 /* Called when builtin is disabled. */
184 void
185 csv_builtin_unload (name)
186 char *name;
190 char *csv_doc[] = {
191 "Read comma-separated fields from a string.",
193 "Parse STRING, a line of comma-separated values, into individual fields,",
194 "and store them into the indexed array ARRAYNAME starting at index 0.",
195 "If ARRAYNAME is not supplied, \"CSV\" is the default array name.",
196 (char *)NULL
199 struct builtin csv_struct = {
200 "csv", /* builtin name */
201 csv_builtin, /* function implementing the builtin */
202 BUILTIN_ENABLED, /* initial flags for builtin */
203 csv_doc, /* array of long documentation strings. */
204 "csv [-a ARRAY] string", /* usage synopsis; becomes short_doc */
205 0 /* reserved for internal use */