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