Mention submodule in README
[qpms.git] / qpms / parsing.c
blobef353ee98e599459c75c38b7233f405f657cf1b8
1 #include "parsing.h"
2 #include "qpms_error.h"
3 #include <errno.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <ctype.h>
9 size_t qpms_parse_ndoubles(
10 double *target,
11 size_t n,
12 const char *orig
13 ) {
14 QPMS_ENSURE(target, "The target parameter must not be NULL");
15 char * const dup = strdup(orig);
16 QPMS_ENSURE(dup, "Memory error in a strdup() call.");
18 // Replace commas and semicolons with whitespaces
19 for (char *c = dup; *c; ++c)
20 if (*c == ',' || *c == ';')
21 *c = ' ';
23 errno = 0;
24 size_t i = 0;
26 const char *beg = dup;
27 while(*beg) {
28 char *endptr;
29 double parsed = strtod(beg, &endptr);
30 if (endptr > beg) {
31 if (i >= n) {
32 errno = EOVERFLOW;
33 if (i == n) QPMS_WARN("Supplied string contains additional numbers"
34 " (expected only %zd numbers): %s\n", n, beg);
36 else
37 target[i] = parsed;
38 ++i;
39 beg = endptr;
40 } else {
41 while (*beg) {
42 if (!isspace(*beg)) {
43 QPMS_WARN("Invalid character (expected a double), leaving the rest of the string unprocessed: %s\n", beg);
44 errno = EILSEQ;
45 goto qpms_parse_ndoubles_cleanup;
47 ++beg;
52 qpms_parse_ndoubles_cleanup:
53 free(dup);
54 return i;
58 size_t qpms_parse_doubles(
59 double **target,
60 size_t start_index,
61 const char *orig
62 ) {
63 QPMS_ENSURE(target, "The target parameter must not be NULL");
64 char * const dup = strdup(orig);
65 QPMS_ENSURE(dup, "Memory error in a strdup() call.");
67 size_t capacity = start_index * 2;
68 if (capacity < 128) capacity = 128;
70 // Replace commas and semicolons with whitespaces
71 for (char *c = dup; *c; ++c)
72 if (*c == ',' || *c == ';')
73 *c = ' ';
75 size_t i = start_index;
76 errno = 0;
78 const char *beg = dup;
79 while(*beg) {
80 char *endptr;
81 errno = 0;
82 double parsed = strtod(beg, &endptr);
83 if (endptr > beg) {
84 (*target)[i] = parsed;
85 ++i;
86 if (i >= capacity) {
87 capacity *= 2;
88 QPMS_CRASHING_REALLOC(*target, capacity * sizeof(double));
90 beg = endptr;
91 } else {
92 while (*beg) {
93 if (!isspace(*beg)) {
94 QPMS_WARN("Invalid character (expected a double), leaving the rest of the string unprocessed: %s\n", beg);
95 errno = EILSEQ;
96 goto qpms_parse_doubles_cleanup;
98 ++beg;
103 qpms_parse_doubles_cleanup:
104 free(dup);
105 return i;
109 size_t qpms_parse_doubles_fromfile(
110 double **target,
111 size_t start_index, //< Starting index for writing the parsed values.
112 const char *filepath //< File to read from, or NULL, "", "-" to read from stdin.
114 QPMS_ENSURE(target, "The target parameter must not be NULL");
116 FILE *src;
118 if (!filepath || !strcmp(filepath, "-") || filepath[0]=='\0')
119 src = stdin;
120 else
121 QPMS_ENSURE(src = fopen(filepath, "f"),
122 "Could not open file %s: %s", filepath, strerror(errno));
124 char buf[1024];
125 int scanresult;
126 while (1 == (scanresult = fscanf(src, "%1023s", buf)))
127 start_index = qpms_parse_doubles(target, start_index, buf);
129 if (errno) QPMS_WARN("Problem reading %s: %s",
130 (src==stdin) ? "stdin" : filepath, strerror(errno));
132 qpms_parse_doubles_files_cleanup:
133 if (src != stdin)
134 QPMS_ENSURE(!fclose(src),
135 "Could not close file %s: %s", filepath, strerror(errno));
137 return start_index;