├── .gitignore ├── LICENSE ├── README └── src ├── mfmt.c └── mfmt.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 G. Elian Gidoni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Small implementation of sprintf() and sscanf(). 2 | It uses just a few bytes of stack. No floating-point support (%f). 3 | -------------------------------------------------------------------------------- /src/mfmt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 G. Elian Gidoni 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | /* 30 | * Private functions. 31 | */ 32 | 33 | static int 34 | is_space(char c) 35 | { 36 | return (c == ' ' || c == '\t' || c == '\v' || 37 | c == '\f' || c == '\r' || c == '\n'); 38 | } 39 | 40 | static char* 41 | skip_spaces(const char *str) 42 | { 43 | while (is_space(*str)){ 44 | ++str; 45 | } 46 | return (char*)str; 47 | } 48 | 49 | /* Returns a pointer after the last read char, or 'str' on error. */ 50 | static char* 51 | dec_to_signed(const char *str, long *out) 52 | { 53 | const char * cur = skip_spaces(str); 54 | long value = 0; 55 | int isneg = 0, isempty = 1; 56 | if (cur[0] == '+'){ 57 | cur += 1; 58 | }else if(cur[0] == '-'){ 59 | cur += 1; 60 | isneg = 1; 61 | } 62 | while (*cur != '\0' && *cur >= '0' && *cur <= '9'){ 63 | value = (value * 10) + (*cur - '0'); 64 | isempty = 0; 65 | ++cur; 66 | } 67 | if (isempty){ 68 | return (char*)str; 69 | } 70 | if (isneg){ 71 | *out = -value; 72 | }else{ 73 | *out = value; 74 | } 75 | return (char*)cur; 76 | } 77 | 78 | /* Returns a pointer after the last read char, or 'str' on error. */ 79 | static char* 80 | dec_to_unsigned(const char *str, unsigned long *out) 81 | { 82 | const char * cur = skip_spaces(str); 83 | unsigned long value = 0; 84 | int isempty = 1; 85 | while (*cur != '\0' && *cur >= '0' && *cur <= '9'){ 86 | value = (value * 10) + (*cur - '0'); 87 | isempty = 0; 88 | ++cur; 89 | } 90 | if (isempty){ 91 | return (char*)str; 92 | } 93 | *out = value; 94 | return (char*)cur; 95 | } 96 | 97 | /* Returns a pointer after the last read char, or 'str' on error. */ 98 | static char* 99 | hex_to_signed(const char *str, long *out) 100 | { 101 | const char * cur = skip_spaces(str); 102 | long value = 0; 103 | int isneg = 0, isempty = 1; 104 | if (cur[0] == '+'){ 105 | cur += 1; 106 | }else if(cur[0] == '-'){ 107 | cur += 1; 108 | isneg = 1; 109 | } 110 | if (cur[0] == '0' && cur[1] == 'x'){ 111 | cur += 2; 112 | } 113 | while (*cur != '\0'){ 114 | if(*cur >= '0' && *cur <= '9'){ 115 | value = (value * 16) + (*cur - '0'); 116 | }else if (*cur >= 'a' && *cur <= 'f'){ 117 | value = (value * 16) + 10 + (*cur - 'a'); 118 | }else if (*cur >= 'A' && *cur <= 'F'){ 119 | value = (value * 16) + 10 + (*cur - 'A'); 120 | }else{ 121 | break; 122 | } 123 | isempty = 0; 124 | ++cur; 125 | } 126 | if (isempty){ 127 | return (char*)str; 128 | } 129 | if (isneg){ 130 | *out = -value; 131 | }else{ 132 | *out = value; 133 | } 134 | return (char*)cur; 135 | } 136 | 137 | /* Returns a pointer after the last read char, or 'str' on error. */ 138 | static char* 139 | hex_to_unsigned(const char *str, unsigned long *out) 140 | { 141 | const char * cur = skip_spaces(str); 142 | unsigned long value = 0; 143 | int isempty = 1; 144 | if (cur[0] == '0' && cur[1] == 'x'){ 145 | cur += 2; 146 | } 147 | while (*cur != '\0'){ 148 | if(*cur >= '0' && *cur <= '9'){ 149 | value = (value * 16) + (*cur - '0'); 150 | }else if (*cur >= 'a' && *cur <= 'f'){ 151 | value = (value * 16) + 10 + (*cur - 'a'); 152 | }else if (*cur >= 'A' && *cur <= 'F'){ 153 | value = (value * 16) + 10 + (*cur - 'A'); 154 | }else{ 155 | break; 156 | } 157 | isempty = 0; 158 | ++cur; 159 | } 160 | if (isempty){ 161 | return (char*)str; 162 | } 163 | *out = value; 164 | return (char*)cur; 165 | } 166 | 167 | #define MFMT_DEC_TO_SIGNED(TYPE, NAME) \ 168 | static char* \ 169 | dec_to_##NAME(const char *str, TYPE *out) \ 170 | { \ 171 | long v; \ 172 | char *cur = dec_to_signed(str, &v); \ 173 | if (cur != str){ \ 174 | *out = (TYPE)v; \ 175 | } \ 176 | return cur; \ 177 | } 178 | 179 | #define MFMT_DEC_TO_UNSIGNED(TYPE, NAME) \ 180 | static char* \ 181 | dec_to_##NAME(const char *str, TYPE *out) \ 182 | { \ 183 | unsigned long v; \ 184 | char *cur = dec_to_unsigned(str, &v); \ 185 | if (cur != str){ \ 186 | *out = (TYPE)v; \ 187 | } \ 188 | return cur; \ 189 | } 190 | 191 | #define MFMT_HEX_TO_SIGNED(TYPE, NAME) \ 192 | static char* \ 193 | hex_to_##NAME(const char *str, TYPE *out) \ 194 | { \ 195 | long v; \ 196 | char *cur = hex_to_signed(str, &v); \ 197 | if (cur != str){ \ 198 | *out = (TYPE)v; \ 199 | } \ 200 | return cur; \ 201 | } 202 | 203 | #define MFMT_HEX_TO_UNSIGNED(TYPE, NAME) \ 204 | static char* \ 205 | hex_to_##NAME(const char *str, TYPE *out) \ 206 | { \ 207 | unsigned long v; \ 208 | char *cur = hex_to_unsigned(str, &v); \ 209 | if (cur != str){ \ 210 | *out = (TYPE)v; \ 211 | } \ 212 | return cur; \ 213 | } 214 | 215 | /* Returns a pointer after the last written char, or 'str' on error. */ 216 | #define MFMT_SIGNED_TO_HEX(TYPE, NAME) \ 217 | static char* \ 218 | NAME##_to_hex(TYPE val, int uppercase, char padchar, size_t padlen, \ 219 | size_t len, char *str) \ 220 | { \ 221 | char buf[24]; \ 222 | size_t isneg = 0, cnt = 0; \ 223 | if (uppercase){ \ 224 | uppercase = 'A'; \ 225 | }else{ \ 226 | uppercase = 'a'; \ 227 | } \ 228 | if (val < 0){ \ 229 | isneg = 1; \ 230 | val = -val; \ 231 | } \ 232 | do{ \ 233 | buf[cnt++] = val % 16; \ 234 | val = val / 16; \ 235 | }while (val != 0); \ 236 | if (padlen > isneg + cnt){ \ 237 | padlen -= isneg + cnt; \ 238 | padlen = (padlen < len ? padlen : len); \ 239 | memset(str, padchar, padlen); \ 240 | str += padlen; \ 241 | len -= padlen; \ 242 | } \ 243 | if (isneg && len > 0){ \ 244 | str[0] = '-'; \ 245 | str += 1; \ 246 | len -= 1; \ 247 | } \ 248 | while (cnt-- > 0 && len-- > 0){ \ 249 | if (buf[cnt] < 10){ \ 250 | *str = buf[cnt] + '0'; \ 251 | }else{ \ 252 | *str = (buf[cnt] - 10) + uppercase; \ 253 | } \ 254 | ++str; \ 255 | } \ 256 | return str; \ 257 | } 258 | 259 | /* Returns a pointer after the last written char, or 'str' on error. */ 260 | #define MFMT_SIGNED_TO_DEC(TYPE, NAME) \ 261 | static char* \ 262 | NAME##_to_dec(TYPE val, char padchar, size_t padlen, \ 263 | size_t len, char *str) \ 264 | { \ 265 | char buf[24]; \ 266 | size_t isneg = 0, cnt = 0; \ 267 | if (val < 0){ \ 268 | isneg = 1; \ 269 | val = -val; \ 270 | } \ 271 | do{ \ 272 | buf[cnt++] = val % 10; \ 273 | val = val / 10; \ 274 | }while (val != 0); \ 275 | if (padlen > isneg + cnt){ \ 276 | padlen -= isneg + cnt; \ 277 | padlen = (padlen < len ? padlen : len); \ 278 | memset(str, padchar, padlen); \ 279 | str += padlen; \ 280 | len -= padlen; \ 281 | } \ 282 | if (isneg && len > 0){ \ 283 | str[0] = '-'; \ 284 | str += 1; \ 285 | len -= 1; \ 286 | } \ 287 | while (cnt-- > 0 && len-- > 0){ \ 288 | *str = buf[cnt] + '0'; \ 289 | ++str; \ 290 | } \ 291 | return str; \ 292 | } 293 | 294 | /* Returns a pointer after the last written char, or 'str' on error. */ 295 | #define MFMT_UNSIGNED_TO_HEX(TYPE, NAME) \ 296 | static char* \ 297 | NAME##_to_hex(TYPE val, int uppercase, char padchar, size_t padlen, \ 298 | size_t len, char *str) \ 299 | { \ 300 | char buf[24]; \ 301 | size_t cnt = 0; \ 302 | if (uppercase){ \ 303 | uppercase = 'A'; \ 304 | }else{ \ 305 | uppercase = 'a'; \ 306 | } \ 307 | do{ \ 308 | buf[cnt++] = val % 16; \ 309 | val = val / 16; \ 310 | }while (val != 0); \ 311 | if (padlen > cnt){ \ 312 | padlen -= cnt; \ 313 | padlen = (padlen < len ? padlen : len); \ 314 | memset(str, padchar, padlen); \ 315 | str += padlen; \ 316 | len -= padlen; \ 317 | } \ 318 | while (cnt-- > 0 && len-- > 0){ \ 319 | if (buf[cnt] < 10){ \ 320 | *str = buf[cnt] + '0'; \ 321 | }else{ \ 322 | *str = (buf[cnt] - 10) + uppercase; \ 323 | } \ 324 | ++str; \ 325 | } \ 326 | return str; \ 327 | } 328 | 329 | /* Returns a pointer after the last written char, or 'str' on error. */ 330 | #define MFMT_UNSIGNED_TO_DEC(TYPE, NAME) \ 331 | static char* \ 332 | NAME##_to_dec(TYPE val, char padchar, size_t padlen, \ 333 | size_t len, char *str) \ 334 | { \ 335 | char buf[24]; \ 336 | size_t cnt = 0; \ 337 | do{ \ 338 | buf[cnt++] = val % 10; \ 339 | val = val / 10; \ 340 | }while (val != 0); \ 341 | if (padlen > cnt){ \ 342 | padlen -= cnt; \ 343 | padlen = (padlen < len ? padlen : len); \ 344 | memset(str, padchar, padlen); \ 345 | str += padlen; \ 346 | len -= padlen; \ 347 | } \ 348 | while (cnt-- > 0 && len-- > 0){ \ 349 | *str = buf[cnt] + '0'; \ 350 | ++str; \ 351 | } \ 352 | return str; \ 353 | } 354 | 355 | MFMT_DEC_TO_SIGNED(int, int) 356 | MFMT_HEX_TO_SIGNED(int, int) 357 | MFMT_SIGNED_TO_DEC(int, int) 358 | MFMT_SIGNED_TO_HEX(int, int) 359 | 360 | MFMT_DEC_TO_UNSIGNED(unsigned int, uint) 361 | MFMT_HEX_TO_UNSIGNED(unsigned int, uint) 362 | MFMT_UNSIGNED_TO_DEC(unsigned int, uint) 363 | MFMT_UNSIGNED_TO_HEX(unsigned int, uint) 364 | MFMT_UNSIGNED_TO_HEX(size_t, siz) 365 | 366 | static const char* 367 | parse_arg(const char *fmt, const char *str, va_list args) 368 | { 369 | int *intp, intv = 0; 370 | unsigned int *uintp, uintv = 0, width = 0; 371 | char *charp; 372 | const char *cur = str; 373 | fmt = dec_to_uint(fmt, &width); 374 | if (*fmt == 'd'){ 375 | cur = dec_to_int(str, &intv); 376 | if (cur != str){ 377 | intp = va_arg(args, int*); 378 | *intp = intv; 379 | } 380 | }else if (*fmt == 'u'){ 381 | cur = dec_to_uint(str, &uintv); 382 | if (cur != str){ 383 | uintp = va_arg(args, unsigned int*); 384 | *uintp = uintv; 385 | } 386 | }else if (*fmt == 'x' || *fmt == 'X'){ 387 | cur = hex_to_uint(str, &uintv); 388 | if (cur != str){ 389 | uintp = va_arg(args, unsigned int*); 390 | *uintp = uintv; 391 | } 392 | }else if (*fmt == 'c'){ 393 | charp = va_arg(args, char*); 394 | if (width == 0){ 395 | width = 1; 396 | } 397 | while (cur[0] != '\0' && uintv < width){ 398 | charp[uintv] = cur[0]; 399 | ++cur; 400 | ++uintv; 401 | } 402 | }else if (*fmt == 's'){ 403 | charp = va_arg(args, char*); 404 | while (cur[0] != '\0' && ! is_space(cur[0]) && 405 | (width == 0 || uintv < width)){ 406 | charp[uintv] = cur[0]; 407 | ++cur; 408 | ++uintv; 409 | } 410 | charp[uintv] = '\0'; 411 | }else if (*fmt == '%' && str[0] == '%'){ 412 | ++cur; 413 | } 414 | return cur; 415 | } 416 | 417 | static char* 418 | print_arg(const char *fmt, char *str, size_t len, va_list args) 419 | { 420 | unsigned int uintv, width = 0; 421 | size_t charplen = 0, padlen = 0; 422 | int intv; 423 | char *charp, padchar = (*fmt == '0' ? '0' : ' '); 424 | fmt = dec_to_uint(fmt, &width); 425 | if (*fmt == 'd' || *fmt == 'i'){ 426 | intv = va_arg(args, int); 427 | str = int_to_dec(intv, padchar, width, len, str); 428 | }else if (*fmt == 'u'){ 429 | uintv = va_arg(args, unsigned int); 430 | str = uint_to_dec(uintv, padchar, width, len, str); 431 | }else if (*fmt == 'x' || *fmt == 'X'){ 432 | uintv = va_arg(args, unsigned int); 433 | str = uint_to_hex(uintv, (*fmt == 'X'), padchar, width, 434 | len, str); 435 | }else if (*fmt == 'p'){ 436 | charp = (char*)va_arg(args, void*); 437 | str = siz_to_hex((size_t)charp, 0, padchar, width, 438 | len, str); 439 | }else if (*fmt == 'c'){ 440 | intv = va_arg(args, int); 441 | if (width > 1){ 442 | padlen = (size_t)width - 1; 443 | padlen = (padlen < len ? padlen : len); 444 | memset(str, ' ', padlen); 445 | str += padlen; 446 | len -= padlen; 447 | } 448 | if (len > 0){ 449 | str[0] = (char) intv; 450 | str += 1; 451 | len -= 1; 452 | } 453 | }else if (*fmt == 's'){ 454 | charp = va_arg(args, char*); 455 | charplen = strlen(charp); 456 | if (width > 0 && (size_t)width > charplen){ 457 | padlen = (size_t)width - charplen; 458 | padlen = (padlen < len ? padlen : len); 459 | memset(str, ' ', padlen); 460 | str += padlen; 461 | len -= padlen; 462 | } 463 | charplen = (charplen < len ? charplen : len); 464 | memcpy(str, charp, charplen); 465 | str += charplen; 466 | len -= charplen; 467 | }else if (*fmt == '%'){ 468 | str[0] = '%'; 469 | ++str; 470 | } 471 | return str; 472 | } 473 | 474 | /* 475 | * Public functions. 476 | */ 477 | 478 | int 479 | mfmt_print(char *str, size_t len, const char *fmt, ...) 480 | { 481 | va_list args; 482 | int cnt = 0; 483 | char *tmp, *cur = str; 484 | if (len == 0){ 485 | return cnt; 486 | } 487 | --len; 488 | va_start(args, fmt); 489 | while (fmt[0] != '\0' && len > 0){ 490 | if (fmt[0] == '%'){ 491 | tmp = print_arg(&fmt[1], cur, len, args); 492 | if (tmp == cur){ 493 | break; 494 | } 495 | len -= (tmp - cur); 496 | cur = tmp; 497 | ++fmt; 498 | while (fmt[0] >= '0' && fmt[0] <= '9'){ 499 | ++fmt; 500 | } 501 | ++fmt; 502 | }else{ 503 | cur[0] = fmt[0]; 504 | --len; 505 | ++cur; 506 | ++fmt; 507 | } 508 | } 509 | va_end(args); 510 | cnt = (int)(cur - str); 511 | str[cnt] = '\0'; 512 | return cnt; 513 | } 514 | 515 | int 516 | mfmt_scan(const char *str, const char *fmt, ...) 517 | { 518 | int ret = 0; 519 | va_list args; 520 | va_start(args, fmt); 521 | while (fmt[0] != '\0' && str[0] != '\0'){ 522 | if (fmt[0] == '%'){ 523 | const char * tmp = parse_arg(&fmt[1], str, args); 524 | if (tmp == str){ 525 | break; 526 | } 527 | if (fmt[1] != '%'){ 528 | ++ret; 529 | } 530 | ++fmt; 531 | while (fmt[0] >= '0' && fmt[0] <= '9'){ 532 | ++fmt; 533 | } 534 | ++fmt; 535 | str = tmp; 536 | }else if (is_space(fmt[0])){ 537 | ++fmt; 538 | str = skip_spaces(str); 539 | }else if (fmt[0] == str[0]){ 540 | ++fmt; 541 | ++str; 542 | }else{ 543 | break; 544 | } 545 | } 546 | 547 | va_end(args); 548 | return ret; 549 | } 550 | 551 | -------------------------------------------------------------------------------- /src/mfmt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 G. Elian Gidoni 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef MFMT_H_ 26 | #define MFMT_H_ 27 | 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | #define MFMT_ASSERT(cond) \ 35 | do{ \ 36 | if (! (cond)){ \ 37 | while(1); \ 38 | } \ 39 | } while(0) 40 | 41 | int 42 | mfmt_print(char *str, size_t len, const char *fmt, ...); 43 | 44 | int 45 | mfmt_scan(const char *str, const char *fmt, ...); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | #endif /* MFMT_H_ */ 51 | --------------------------------------------------------------------------------