src/lib/str.c
branchnew-lib-errors
changeset 216 a10ba529ae39
parent 133 e2d0c0c23b39
child 218 5229a5d098b2
equal deleted inserted replaced
215:85863b89e38b 216:a10ba529ae39
       
     1 #include "str.h"
       
     2 
       
     3 #include <ctype.h>
       
     4 #include <stdio.h>
       
     5 #include <string.h>
       
     6 #include <stdbool.h>
       
     7 
       
     8 #include <assert.h>
       
     9 
       
    10 const struct error_list str_errors = ERROR_LIST("str",
       
    11     ERROR_TYPE(     ERR_STR_FMT_TAG,            "invalid parameter tag syntax"      ),
       
    12     ERROR_TYPE(     ERR_STR_FMT_NAME_LEN,       "invalid parameter name length"     ),
       
    13     ERROR_TYPE(     ERR_STR_FMT_NAME,           "invalid/unknown parameter name"    ),
       
    14     ERROR_TYPE(     ERR_STR_FMT_FLAGS_LEN,      "invalid paramter flags length"     ),
       
    15     ERROR_TYPE(     ERR_STR_FMT_FLAG,           "invalid paramter flag"             ),
       
    16     ERROR_TYPE(     ERR_STR_FMT_BUF_LEN,        "output buffer ran out"             )
       
    17 );
       
    18 
       
    19 size_t str_append_num (char *buf, size_t buf_size, const char *str, ssize_t len)
       
    20 {
       
    21     size_t str_len;
       
    22     
       
    23     // copy the bytes
       
    24     // len will either be >0 or <0
       
    25     for (str_len = 0; *str && buf_size > 1 && len != 0; str_len++, buf_size--, len--)
       
    26         *buf++ = *str++;
       
    27     
       
    28     // count the rest
       
    29     for (; *str && len != 0; str++, len--)
       
    30         str_len++;
       
    31     
       
    32     if (buf_size)
       
    33         // NUL-terminate
       
    34         *buf = '\0';
       
    35 
       
    36     // ok
       
    37     return str_len;
       
    38 
       
    39 }
       
    40 
       
    41 size_t str_append (char *buf, size_t buf_size, const char *str)
       
    42 {
       
    43     return str_append_num(buf, buf_size, str, -1);
       
    44 }
       
    45 
       
    46 size_t str_append_char (char *buf, size_t buf_size, char c)
       
    47 {
       
    48     if (buf_size > 1)
       
    49         *buf++ = c;
       
    50 
       
    51     if (buf_size > 0)
       
    52         *buf = '\0';
       
    53     
       
    54     return 1;
       
    55 }
       
    56 
       
    57 size_t str_append_fmt_va (char *buf, size_t buf_size, const char *fmt, va_list vargs)
       
    58 {
       
    59     int ret;
       
    60 
       
    61     // do the formatting
       
    62     ret = vsnprintf(buf, buf_size, fmt, vargs);
       
    63 
       
    64     // XXX: not all vsnprintf implementations do this
       
    65     assert(ret >= 0);
       
    66 
       
    67     // ok
       
    68     return ret;
       
    69 }
       
    70 
       
    71 size_t str_append_fmt (char *buf, size_t buf_size, const char *fmt, ...)
       
    72 {
       
    73     va_list vargs;
       
    74     int ret;
       
    75 
       
    76     va_start(vargs, fmt);
       
    77     ret = str_append_fmt_va(buf, buf_size, fmt, vargs);
       
    78     va_end(vargs);
       
    79 
       
    80     return ret;
       
    81 }
       
    82 
       
    83 static const struct str_quote_item {
       
    84     /** The char value to quote */
       
    85     char c;
       
    86     
       
    87     /** The quoted value */
       
    88     const char *out;
       
    89 } _quote_table[] = {
       
    90     { '\'', "\\'"   },
       
    91     { '\\', "\\\\"  },
       
    92     { '\0', "\\0"   },
       
    93     { '\r', "\\r"   },
       
    94     { '\n', "\\n"   },
       
    95     { 0,    NULL    }
       
    96 };
       
    97 
       
    98 size_t str_quote_char (char *buf, size_t buf_size, char c)
       
    99 {
       
   100     const struct str_quote_item *quote_item = _quote_table;
       
   101 
       
   102     // special quote?
       
   103     for (quote_item = _quote_table; quote_item->c || quote_item->out; quote_item++) {
       
   104         if (quote_item->c == c)
       
   105             return str_append(buf, buf_size, quote_item->out);
       
   106     }
       
   107     
       
   108     // output directly?
       
   109     if (isprint(c))
       
   110         return str_append_char(buf, buf_size, c); 
       
   111     
       
   112     else 
       
   113         return str_append_fmt(buf, buf_size, "\\x%02x", c);
       
   114 }
       
   115 
       
   116 size_t str_advance (size_t *data_size, size_t *buf_size, size_t len)
       
   117 {
       
   118     if (data_size)
       
   119         *data_size += len;
       
   120 
       
   121     if (len > *buf_size)
       
   122         *buf_size = 0;
       
   123 
       
   124     else
       
   125         *buf_size -= len;
       
   126 
       
   127     return len;
       
   128 }
       
   129 
       
   130 /**
       
   131  * Number of bytes reserved by str_quote for the trailing overflow stuff
       
   132  */
       
   133 #define STR_QUOTE_RESERVED 5
       
   134 
       
   135 size_t str_quote (char *buf, size_t buf_size, const char *str, ssize_t str_len)
       
   136 {
       
   137     size_t data_size = 0;
       
   138 
       
   139     // NULL?
       
   140     if (str == NULL) {
       
   141         return str_append(buf, buf_size, "NULL");
       
   142 
       
   143     } else {
       
   144         // calc length?
       
   145         if (str_len < 0)
       
   146             str_len = strlen(str);
       
   147 
       
   148         // quote
       
   149         buf += str_advance(&data_size, &buf_size, str_append_char(buf, buf_size, '\''));
       
   150     
       
   151         // dump each char
       
   152         while (str_len--) { 
       
   153             if (buf_size > STR_QUOTE_RESERVED) {
       
   154                 size_t char_size;
       
   155 
       
   156                 // output and count the chars
       
   157                 char_size = str_quote_char(buf, buf_size, *str++);
       
   158                 
       
   159                 // if there's still enough room left, commit this and continue
       
   160                 if (buf_size > char_size && buf_size - char_size >= STR_QUOTE_RESERVED)
       
   161                     buf += str_advance(&data_size, &buf_size, char_size);
       
   162                 
       
   163                 else
       
   164                     // keep the buf pointer intact, we'll overwrite it now
       
   165                     data_size += char_size;
       
   166 
       
   167             } else {
       
   168                 // continue counting the chars, but don't output anything anymore
       
   169                 data_size += str_quote_char(NULL, 0, *str++);
       
   170             }
       
   171         }
       
   172 
       
   173         // end quote
       
   174         buf += str_advance(&data_size, &buf_size, str_append_char(buf, buf_size, '\''));
       
   175     }        
       
   176 
       
   177     // overflow -> mark
       
   178     if (buf_size < STR_QUOTE_RESERVED)
       
   179         buf += str_advance(NULL, &buf_size, str_append(buf, buf_size, "..."));
       
   180 
       
   181     // the total outputted chars
       
   182     return data_size;
       
   183 }
       
   184 
       
   185 /**
       
   186  * Output the data for a single parameter
       
   187  */
       
   188 static err_t str_format_param (char **buf, size_t *buf_size, const char *name, const char *flags, str_format_cb func, void *arg)
       
   189 {
       
   190     const char *value;
       
   191     ssize_t value_len = -1;
       
   192     char flag;
       
   193     bool use_quote = false;
       
   194     err_t err;
       
   195     
       
   196     // look it up
       
   197     if ((err = func(name, &value, &value_len, arg)))
       
   198         return err;
       
   199     
       
   200     // not found?
       
   201     if (!value)
       
   202         return ERR_STR_FMT_NAME;
       
   203     
       
   204     // parse flags
       
   205     while ((flag = *flags++)) {
       
   206         switch (flag) {
       
   207             case 'r':
       
   208                 // quote
       
   209                 use_quote = true;
       
   210 
       
   211                 break;
       
   212 
       
   213             default:
       
   214                 // unknown flag
       
   215                 return ERR_STR_FMT_FLAG;
       
   216     
       
   217         }
       
   218     }
       
   219 
       
   220     // output value
       
   221     if (use_quote)
       
   222         *buf += str_advance(NULL, buf_size, str_quote(*buf, *buf_size, value, value_len));
       
   223 
       
   224     else
       
   225         *buf += str_advance(NULL, buf_size, str_append_num(*buf, *buf_size, value, value_len));
       
   226 
       
   227     // ok
       
   228     return SUCCESS;
       
   229 }
       
   230 
       
   231 err_t str_format (char *buf, size_t buf_size, const char *format, str_format_cb func, void *arg, error_t *err)
       
   232 {
       
   233     char name_buf[STR_FORMAT_PARAM_MAX + 1], *name_ptr;
       
   234     size_t name_size;
       
   235     char flags_buf[STR_FORMAT_FLAGS_MAX + 1], *flags_ptr;
       
   236     size_t flags_size;
       
   237     bool in_param = false, in_param_flags = false;
       
   238     
       
   239     // iterate over the format string
       
   240     do {
       
   241         // check buffer state
       
   242         if (!buf_size)
       
   243             return SET_ERROR(err, &str_errors, ERR_STR_FMT_BUF_LEN);
       
   244         
       
   245         // inspect this char
       
   246         switch (*format) {
       
   247             case '{':
       
   248                 // syntax
       
   249                 if (in_param)
       
   250                     return SET_ERROR(err, &str_errors, ERR_STR_FMT_TAG);
       
   251                 
       
   252                 // init state
       
   253                 in_param = true;
       
   254                 name_ptr = name_buf;
       
   255                 name_size = sizeof(name_buf);
       
   256                 flags_ptr = flags_buf;
       
   257                 flags_size = sizeof(flags_buf);
       
   258 
       
   259                 *name_ptr = *flags_ptr = '\0';
       
   260                 
       
   261                 break;
       
   262 
       
   263             case '}':
       
   264                 // syntax
       
   265                 if (!in_param)
       
   266                     return SET_ERROR(err, &str_errors, ERR_STR_FMT_TAG);
       
   267                 
       
   268                 // reset state
       
   269                 in_param = false;
       
   270                 in_param_flags = false;
       
   271 
       
   272                 // output token
       
   273                 if (str_format_param(&buf, &buf_size, name_buf, flags_buf, func, arg, err))
       
   274                     return error_code(err);
       
   275 
       
   276                 break;
       
   277 
       
   278             case ':':
       
   279                 if (in_param) {
       
   280                     // set state
       
   281                     in_param_flags = true;
       
   282 
       
   283                     break;
       
   284                 }
       
   285 
       
   286                 /* Fallthrough */
       
   287 
       
   288             default:
       
   289                 if (in_param && in_param_flags ) {
       
   290                     // add to param flags
       
   291                     flags_ptr += str_advance(NULL, &flags_size, str_append_char(flags_ptr, flags_size, *format));
       
   292 
       
   293                     if (!flags_size)
       
   294                         return SET_ERROR(err, &str_errors, ERR_STR_FMT_FLAGS_LEN);
       
   295                
       
   296                 } else if (in_param) {
       
   297                     // add to param name
       
   298                     name_ptr += str_advance(NULL, &name_size, str_append_char(name_ptr, name_size, *format));
       
   299 
       
   300                     if (!name_size)
       
   301                         return SET_ERROR(err, &str_errors, ERR_STR_FMT_NAME_LEN);
       
   302 
       
   303                 } else {
       
   304                     // add to output
       
   305                     buf += str_advance(NULL, &buf_size, str_append_char(buf, buf_size, *format));
       
   306 
       
   307                 }
       
   308 
       
   309                 break;
       
   310         }
       
   311     } while (*format++);
       
   312 
       
   313     // syntax
       
   314     if (in_param)
       
   315         return SET_ERROR(err, &str_errors, ERR_STR_FMT_TAG);
       
   316     
       
   317     // ok
       
   318     return SUCCESS;
       
   319 }
       
   320