rnsl

Load a list of strings expanded by the shell
git clone git://git.meso-star.fr/rnsl.git
Log | Files | Refs | README | LICENSE

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 }