diff options
Diffstat (limited to 'lobject.c')
-rw-r--r-- | lobject.c | 144 |
1 files changed, 107 insertions, 37 deletions
@@ -364,25 +364,44 @@ int luaO_utf8esc (char *buff, unsigned long x) { | |||
364 | 364 | ||
365 | 365 | ||
366 | /* | 366 | /* |
367 | ** Convert a number object to a string | 367 | ** Convert a number object to a string, adding it to a buffer |
368 | */ | 368 | */ |
369 | void luaO_tostring (lua_State *L, TValue *obj) { | 369 | static size_t tostringbuff (TValue *obj, char *buff) { |
370 | char buff[MAXNUMBER2STR]; | ||
371 | size_t len; | 370 | size_t len; |
372 | lua_assert(ttisnumber(obj)); | 371 | lua_assert(ttisnumber(obj)); |
373 | if (ttisinteger(obj)) | 372 | if (ttisinteger(obj)) |
374 | len = lua_integer2str(buff, sizeof(buff), ivalue(obj)); | 373 | len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); |
375 | else { | 374 | else { |
376 | len = lua_number2str(buff, sizeof(buff), fltvalue(obj)); | 375 | len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); |
377 | if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ | 376 | if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ |
378 | buff[len++] = lua_getlocaledecpoint(); | 377 | buff[len++] = lua_getlocaledecpoint(); |
379 | buff[len++] = '0'; /* adds '.0' to result */ | 378 | buff[len++] = '0'; /* adds '.0' to result */ |
380 | } | 379 | } |
381 | } | 380 | } |
381 | return len; | ||
382 | } | ||
383 | |||
384 | |||
385 | /* | ||
386 | ** Convert a number object to a Lua string, replacing the value at 'obj' | ||
387 | */ | ||
388 | void luaO_tostring (lua_State *L, TValue *obj) { | ||
389 | char buff[MAXNUMBER2STR]; | ||
390 | size_t len = tostringbuff(obj, buff); | ||
382 | setsvalue(L, obj, luaS_newlstr(L, buff, len)); | 391 | setsvalue(L, obj, luaS_newlstr(L, buff, len)); |
383 | } | 392 | } |
384 | 393 | ||
385 | 394 | ||
395 | /* size for buffer used by 'luaO_pushvfstring' */ | ||
396 | #define BUFVFS 400 | ||
397 | |||
398 | /* buffer used by 'luaO_pushvfstring' */ | ||
399 | typedef struct BuffFS { | ||
400 | int blen; /* length of partial string in 'buff' */ | ||
401 | char buff[BUFVFS]; /* holds last part of the result */ | ||
402 | } BuffFS; | ||
403 | |||
404 | |||
386 | static void pushstr (lua_State *L, const char *str, size_t l) { | 405 | static void pushstr (lua_State *L, const char *str, size_t l) { |
387 | setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); | 406 | setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); |
388 | L->top++; | 407 | L->top++; |
@@ -390,59 +409,109 @@ static void pushstr (lua_State *L, const char *str, size_t l) { | |||
390 | 409 | ||
391 | 410 | ||
392 | /* | 411 | /* |
412 | ** empty the buffer into the stack | ||
413 | */ | ||
414 | static void clearbuff (lua_State *L, BuffFS *buff) { | ||
415 | pushstr(L, buff->buff, buff->blen); /* push buffer */ | ||
416 | buff->blen = 0; /* buffer now is empty */ | ||
417 | } | ||
418 | |||
419 | |||
420 | /* | ||
421 | ** Add 'str' to the buffer. It buffer has no enough space, | ||
422 | ** empty the buffer. If string is still larger than the buffer, | ||
423 | ** push the string directly to the stack. Return number of items | ||
424 | ** pushed. | ||
425 | */ | ||
426 | static int addstr2buff (lua_State *L, BuffFS *buff, const char *str, | ||
427 | size_t slen) { | ||
428 | int pushed = 0; /* number of items pushed to the stack */ | ||
429 | lua_assert(buff->blen <= BUFVFS); | ||
430 | if (slen > BUFVFS - cast_sizet(buff->blen)) { /* string does not fit? */ | ||
431 | clearbuff(L, buff); | ||
432 | pushed = 1; | ||
433 | if (slen >= BUFVFS) { /* string still does not fit into buffer? */ | ||
434 | pushstr(L, str, slen); /* push string */ | ||
435 | return 2; | ||
436 | } | ||
437 | } | ||
438 | memcpy(buff->buff + buff->blen, str, slen); /* add string to buffer */ | ||
439 | buff->blen += slen; | ||
440 | return pushed; | ||
441 | } | ||
442 | |||
443 | |||
444 | /* | ||
445 | ** Add a number to the buffer; return number of strings pushed into | ||
446 | ** the stack. (At most one, to free buffer space.) | ||
447 | */ | ||
448 | static int addnum2buff (lua_State *L, BuffFS *buff, TValue *num) { | ||
449 | char numbuff[MAXNUMBER2STR]; | ||
450 | size_t len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ | ||
451 | return addstr2buff(L, buff, numbuff, len); | ||
452 | } | ||
453 | |||
454 | |||
455 | /* | ||
393 | ** this function handles only '%d', '%c', '%f', '%p', and '%s' | 456 | ** this function handles only '%d', '%c', '%f', '%p', and '%s' |
394 | conventional formats, plus Lua-specific '%I' and '%U' | 457 | conventional formats, plus Lua-specific '%I' and '%U' |
395 | */ | 458 | */ |
396 | const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { | 459 | const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { |
397 | int n = 0; /* number of strings in the stack to concatenate */ | 460 | BuffFS buff; /* holds last part of the result */ |
398 | const char *e; /* points to next conversion specifier */ | 461 | int pushed = 0; /* number of strings in the stack to concatenate */ |
462 | const char *e; /* points to next '%' */ | ||
463 | buff.blen = 0; | ||
399 | while ((e = strchr(fmt, '%')) != NULL) { | 464 | while ((e = strchr(fmt, '%')) != NULL) { |
400 | pushstr(L, fmt, e - fmt); /* string up to conversion specifier */ | 465 | pushed += addstr2buff(L, &buff, fmt, e - fmt); /* add 'fmt' up to '%' */ |
401 | switch (*(e+1)) { | 466 | switch (*(e + 1)) { /* conversion specifier */ |
402 | case 's': { /* zero-terminated string */ | 467 | case 's': { /* zero-terminated string */ |
403 | const char *s = va_arg(argp, char *); | 468 | const char *s = va_arg(argp, char *); |
404 | if (s == NULL) s = "(null)"; | 469 | if (s == NULL) s = "(null)"; |
405 | pushstr(L, s, strlen(s)); | 470 | pushed += addstr2buff(L, &buff, s, strlen(s)); |
406 | break; | 471 | break; |
407 | } | 472 | } |
408 | case 'c': { /* an 'int' as a character */ | 473 | case 'c': { /* an 'int' as a character */ |
409 | char buff = cast_char(va_arg(argp, int)); | 474 | /* if non-printable character, print its code */ |
410 | if (lisprint(cast_uchar(buff))) | 475 | char bf[10]; |
411 | pushstr(L, &buff, 1); | 476 | int c = va_arg(argp, int); |
412 | else /* non-printable character; print its code */ | 477 | int l = (lisprint(c)) ? l_sprintf(bf, sizeof(bf), "%c", c) |
413 | luaO_pushfstring(L, "<\\%d>", cast_uchar(buff)); | 478 | : l_sprintf(bf, sizeof(bf), "<\\%u>", c); |
479 | pushed += addstr2buff(L, &buff, bf, l); | ||
414 | break; | 480 | break; |
415 | } | 481 | } |
416 | case 'd': { /* an 'int' */ | 482 | case 'd': { /* an 'int' */ |
417 | setivalue(s2v(L->top), va_arg(argp, int)); | 483 | TValue num; |
418 | goto top2str; | 484 | setivalue(&num, va_arg(argp, int)); |
485 | pushed += addnum2buff(L, &buff, &num); | ||
486 | break; | ||
419 | } | 487 | } |
420 | case 'I': { /* a 'lua_Integer' */ | 488 | case 'I': { /* a 'lua_Integer' */ |
421 | setivalue(s2v(L->top), cast(lua_Integer, va_arg(argp, l_uacInt))); | 489 | TValue num; |
422 | goto top2str; | 490 | setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); |
491 | pushed += addnum2buff(L, &buff, &num); | ||
492 | break; | ||
423 | } | 493 | } |
424 | case 'f': { /* a 'lua_Number' */ | 494 | case 'f': { /* a 'lua_Number' */ |
425 | setfltvalue(s2v(L->top), cast_num(va_arg(argp, l_uacNumber))); | 495 | TValue num; |
426 | top2str: /* convert the top element to a string */ | 496 | setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); |
427 | L->top++; | 497 | pushed += addnum2buff(L, &buff, &num); |
428 | luaO_tostring(L, s2v(L->top - 1)); | ||
429 | break; | 498 | break; |
430 | } | 499 | } |
431 | case 'p': { /* a pointer */ | 500 | case 'p': { /* a pointer */ |
432 | char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */ | 501 | char bf[3 * sizeof(void*) + 8]; /* should be enough space for '%p' */ |
433 | void *p = va_arg(argp, void *); | 502 | void *p = va_arg(argp, void *); |
434 | int l = lua_pointer2str(buff, sizeof(buff), p); | 503 | int l = l_sprintf(bf, sizeof(bf), "%p", p); |
435 | pushstr(L, buff, l); | 504 | pushed += addstr2buff(L, &buff, bf, l); |
436 | break; | 505 | break; |
437 | } | 506 | } |
438 | case 'U': { /* a 'long' as a UTF-8 sequence */ | 507 | case 'U': { /* a 'long' as a UTF-8 sequence */ |
439 | char buff[UTF8BUFFSZ]; | 508 | char bf[UTF8BUFFSZ]; |
440 | int l = luaO_utf8esc(buff, va_arg(argp, long)); | 509 | int l = luaO_utf8esc(bf, va_arg(argp, long)); |
441 | pushstr(L, buff + UTF8BUFFSZ - l, l); | 510 | pushed += addstr2buff(L, &buff, bf + UTF8BUFFSZ - l, l); |
442 | break; | 511 | break; |
443 | } | 512 | } |
444 | case '%': { | 513 | case '%': { |
445 | pushstr(L, "%", 1); | 514 | pushed += addstr2buff(L, &buff, "%", 1); |
446 | break; | 515 | break; |
447 | } | 516 | } |
448 | default: { | 517 | default: { |
@@ -450,15 +519,16 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { | |||
450 | *(e + 1)); | 519 | *(e + 1)); |
451 | } | 520 | } |
452 | } | 521 | } |
453 | n += 2; | 522 | if (pushed > 1 && L->top + 2 > L->stack_last) { /* no free stack space? */ |
454 | if (L->top + 2 > L->stack_last) { /* no free stack space? */ | 523 | luaV_concat(L, pushed); /* join all partial results into one */ |
455 | luaV_concat(L, n); | 524 | pushed = 1; |
456 | n = 1; | ||
457 | } | 525 | } |
458 | fmt = e + 2; | 526 | fmt = e + 2; /* skip '%' and the specifier */ |
459 | } | 527 | } |
460 | pushstr(L, fmt, strlen(fmt)); | 528 | pushed += addstr2buff(L, &buff, fmt, strlen(fmt)); /* rest of 'fmt' */ |
461 | if (n > 0) luaV_concat(L, n + 1); | 529 | clearbuff(L, &buff); /* empty buffer into the stack */ |
530 | if (pushed > 0) | ||
531 | luaV_concat(L, pushed + 1); /* join all partial results */ | ||
462 | return svalue(s2v(L->top - 1)); | 532 | return svalue(s2v(L->top - 1)); |
463 | } | 533 | } |
464 | 534 | ||