rnsl.c (8479B)
1 /* Copyright (C) 2022, 2023 Centre National de la Recherche Scientifique 2 * Copyright (C) 2022, 2023 Institut Pierre-Simon Laplace 3 * Copyright (C) 2022, 2023 Institut de Physique du Globe de Paris 4 * Copyright (C) 2022, 2023 |Méso|Star> (contact@meso-star.com) 5 * Copyright (C) 2022, 2023 Observatoire de Paris 6 * Copyright (C) 2022, 2023 Université de Reims Champagne-Ardenne 7 * Copyright (C) 2022, 2023 Université de Versaille Saint-Quentin 8 * Copyright (C) 2022, 2023 Université Paul Sabatier 9 * 10 * This program is free software: you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation, either version 3 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 22 23 #define _POSIX_C_SOURCE 200112L /* strtok_r and wordexp */ 24 25 #include "rnsl.h" 26 #include "rnsl_c.h" 27 #include "rnsl_log.h" 28 29 #include <rsys/cstr.h> 30 #include <rsys/text_reader.h> 31 32 #include <string.h> 33 #include <wordexp.h> 34 35 /******************************************************************************* 36 * Helper functions 37 ******************************************************************************/ 38 static res_T 39 parse_string(struct rnsl* rnsl, struct txtrdr* txtrdr, struct str* str) 40 { 41 wordexp_t wexp; 42 char* tk = NULL; 43 char* tk_ctx = NULL; 44 int wexp_is_allocated = 0; 45 res_T res = RES_OK; 46 int err = 0; 47 ASSERT(rnsl && txtrdr && str); 48 49 res = txtrdr_read_line(txtrdr); 50 if(res != RES_OK) { 51 log_err(rnsl, "%s: can't read the line `%lu' -- %s\n", 52 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 53 res_to_cstr(res)); 54 goto error; 55 } 56 57 if(!txtrdr_get_cline(txtrdr)) { 58 const size_t nexpect = darray_str_size_get(&rnsl->strings); 59 const size_t nparsed = (size_t)(str - darray_str_cdata_get(&rnsl->strings)); 60 log_err(rnsl, 61 "%s:%lu: missing a string. " 62 "Expecting %lu string%swhile %lu %s parsed.\n", 63 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 64 (unsigned long)nexpect, nexpect == 1 ? " " : "s ", 65 (unsigned long)nparsed, nparsed > 1 ? "were" : "was"); 66 res = RES_BAD_ARG; 67 goto error; 68 } 69 70 tk = strtok_r(txtrdr_get_line(txtrdr), "", &tk_ctx); 71 ASSERT(tk); 72 73 err = wordexp(tk, &wexp, 0/*flags*/); 74 if(err) { 75 log_err(rnsl, "%s:%lu: unable to expand string\n", 76 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); 77 res = RES_BAD_ARG; 78 goto error; 79 } 80 wexp_is_allocated = 1; 81 ASSERT(wexp.we_wordc != 0); 82 83 if(wexp.we_wordc > 1) { 84 log_warn(rnsl, 85 "%s:%lu: multiple strings on the same line. " 86 "Only the first one will be loaded\n", 87 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); 88 } 89 90 res = str_set(str, wexp.we_wordv[0]); 91 if(res != RES_OK) { 92 log_err(rnsl, "%s:%lu: unable to store the parsed string `%s' -- %s\n", 93 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 94 wexp.we_wordv[0], res_to_cstr(res)); 95 goto error; 96 } 97 98 exit: 99 if(wexp_is_allocated) wordfree(&wexp); 100 return res; 101 error: 102 goto exit; 103 } 104 105 106 static res_T 107 load_stream(struct rnsl* rnsl, FILE* file, const char* path) 108 { 109 struct txtrdr* txtrdr = NULL; 110 char* tk = NULL; 111 char* tk_ctx = NULL; 112 size_t istr = 0; 113 unsigned long nstrs = 0; 114 res_T res = RES_OK; 115 ASSERT(rnsl && file && path); 116 117 darray_str_clear(&rnsl->strings); /* Clean up */ 118 119 res = txtrdr_stream(rnsl->allocator, file, path, '#', &txtrdr); 120 if(res != RES_OK) { 121 log_err(rnsl, "could not create text reader to parse file `%s' -- %s\n", 122 path, res_to_cstr(res)); 123 goto error; 124 } 125 126 res = txtrdr_read_line(txtrdr); 127 if(res != RES_OK) { 128 log_err(rnsl, "%s: can't read the line %lu --%s\n", 129 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 130 res_to_cstr(res)); 131 goto error; 132 } 133 134 if(!txtrdr_get_cline(txtrdr)) { 135 log_err(rnsl, "%s: file cannot be empty\n", txtrdr_get_name(txtrdr)); 136 res = RES_BAD_ARG; 137 goto error; 138 } 139 140 tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); 141 ASSERT(tk); 142 143 res = cstr_to_ulong(tk, &nstrs); 144 if(res == RES_OK && nstrs == 0) res = RES_BAD_ARG; 145 if(res != RES_OK) { 146 log_err(rnsl, "%s:%lu: invalid number of string %lu\n", 147 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 148 nstrs); 149 goto error; 150 } 151 152 res = darray_str_resize(&rnsl->strings, nstrs); 153 if(res != RES_OK) { 154 log_err(rnsl, "%s: could not allocate the list of %lu strings -- %s\n", 155 txtrdr_get_name(txtrdr), nstrs, res_to_cstr(res)); 156 goto error; 157 } 158 159 tk = strtok_r(NULL, " \t", &tk_ctx); 160 if(tk) { 161 log_warn(rnsl, "%s:%lu: unexpected text `%s'\n", 162 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); 163 } 164 165 FOR_EACH(istr, 0, nstrs) { 166 struct str* str = darray_str_data_get(&rnsl->strings)+istr; 167 res = parse_string(rnsl, txtrdr, str); 168 if(res != RES_OK) goto error; 169 } 170 171 exit: 172 if(txtrdr) txtrdr_ref_put(txtrdr); 173 return res; 174 error: 175 darray_str_clear(&rnsl->strings); 176 goto exit; 177 } 178 179 180 static INLINE res_T 181 check_rnsl_create_args(const struct rnsl_create_args* args) 182 { 183 /* Nothing to check. Only return RES_BAD_ARG if args is NULL */ 184 return args ? RES_OK : RES_BAD_ARG; 185 } 186 187 static void 188 release_rnsl(ref_T* ref) 189 { 190 struct rnsl* rnsl; 191 ASSERT(ref); 192 rnsl = CONTAINER_OF(ref, struct rnsl, ref); 193 if(rnsl->logger == &rnsl->logger__) logger_release(&rnsl->logger__); 194 darray_str_release(&rnsl->strings); 195 MEM_RM(rnsl->allocator, rnsl); 196 } 197 198 /******************************************************************************* 199 * Exported functions 200 ******************************************************************************/ 201 res_T 202 rnsl_create 203 (const struct rnsl_create_args* args, 204 struct rnsl** out_rnsl) 205 { 206 struct rnsl* rnsl = NULL; 207 struct mem_allocator* allocator = NULL; 208 res_T res = RES_OK; 209 210 if(!out_rnsl) { res = RES_BAD_ARG; goto error; } 211 res = check_rnsl_create_args(args); 212 if(res != RES_OK) goto error; 213 214 allocator = args->allocator ? args->allocator : &mem_default_allocator; 215 rnsl = MEM_CALLOC(allocator, 1, sizeof(*rnsl)); 216 if(!rnsl) { 217 if(args->verbose) { 218 #define ERR_STR "Could not allocate the Rad-Net String List device.\n" 219 if(args->logger) { 220 logger_print(args->logger, LOG_ERROR, ERR_STR); 221 } else { 222 fprintf(stderr, MSG_ERROR_PREFIX ERR_STR); 223 } 224 #undef ERR_STR 225 } 226 res = RES_MEM_ERR; 227 goto error; 228 } 229 ref_init(&rnsl->ref); 230 rnsl->allocator = allocator; 231 rnsl->verbose = args->verbose; 232 darray_str_init(rnsl->allocator, &rnsl->strings); 233 if(args->logger) { 234 rnsl->logger = args->logger; 235 } else { 236 setup_log_default(rnsl); 237 } 238 239 exit: 240 if(out_rnsl) *out_rnsl = rnsl; 241 return res; 242 error: 243 if(rnsl) { 244 RNSL(ref_put(rnsl)); 245 rnsl = NULL; 246 } 247 goto exit; 248 } 249 250 res_T 251 rnsl_ref_get(struct rnsl* rnsl) 252 { 253 if(!rnsl) return RES_BAD_ARG; 254 ref_get(&rnsl->ref); 255 return RES_OK; 256 } 257 258 res_T 259 rnsl_ref_put(struct rnsl* rnsl) 260 { 261 if(!rnsl) return RES_BAD_ARG; 262 ref_put(&rnsl->ref, release_rnsl); 263 return RES_OK; 264 } 265 266 res_T 267 rnsl_load(struct rnsl* rnsl, const char* path) 268 { 269 FILE* file = NULL; 270 res_T res = RES_OK; 271 272 if(!rnsl || !path) { 273 res = RES_BAD_ARG; 274 goto error; 275 } 276 277 file = fopen(path, "r"); 278 if(!file) { 279 log_err(rnsl, "%s: error opening file `%s'.\n", FUNC_NAME, path); 280 res = RES_IO_ERR; 281 goto error; 282 } 283 284 res = load_stream(rnsl, file, path); 285 if(res != RES_OK) goto error; 286 287 exit: 288 if(file) fclose(file); 289 return res; 290 error: 291 goto exit; 292 } 293 294 res_T 295 rnsl_load_stream 296 (struct rnsl* rnsl, 297 FILE* stream, 298 const char* stream_name) 299 { 300 if(!rnsl || !stream) return RES_BAD_ARG; 301 return load_stream(rnsl, stream, stream_name ? stream_name : "<stream>"); 302 } 303 304 size_t 305 rnsl_get_strings_count(const struct rnsl* rnsl) 306 { 307 ASSERT(rnsl); 308 return darray_str_size_get(&rnsl->strings); 309 } 310 311 const char* 312 rnsl_get_string(const struct rnsl* rnsl, const size_t istring) 313 { 314 ASSERT(rnsl && istring < rnsl_get_strings_count(rnsl)); 315 return str_cget(darray_str_cdata_get(&rnsl->strings)+istring); 316 }