printf-format-fix.c revision 4baf980b75800ad3595c39dc3b8d913f2686affd
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "printf-format-fix.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstatic const char *
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenfix_format_real(const char *fmt, const char *p, size_t *len_r)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen{
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen const char *errstr;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen char *buf;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t len1, len2, len3;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen i_assert((size_t)(p - fmt) < INT_MAX);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(p[0] == '%' && p[1] == 'm');
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen errstr = strerror(errno);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen /* we'll assume that there's only one %m in the format string.
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen this simplifies the code and there's really no good reason to have
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen it multiple times. Callers can trap this case themselves. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen len1 = p - fmt;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen len2 = strlen(errstr);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen len3 = strlen(p + 2);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen /* @UNSAFE */
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen buf = t_buffer_get(len1 + len2 + len3 + 1);
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen memcpy(buf, fmt, len1);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen memcpy(buf + len1, errstr, len2);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen memcpy(buf + len1 + len2, p + 2, len3 + 1);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen *len_r = len1 + len2 + len3;
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen return buf;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen}
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainenprintf_format_fix_noalloc(const char *format, size_t *len_r)
7c95b03620a03a43dd72d39608cea5fc77393ad6Timo Sirainen{
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen static const char *printf_skip_chars = "# -+'I.*0123456789hlLjzt";
73e7998716853b5b7621c06aea0022dccda70ad1Timo Sirainen const char *ret, *p, *p2;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen p = ret = format;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen while ((p2 = strchr(p, '%')) != NULL) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen p = p2+1;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen while (*p != '\0' && strchr(printf_skip_chars, *p) != NULL)
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen p++;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen switch (*p) {
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen case 'n':
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen i_panic("%%n modifier used");
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen case 'm':
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (ret != format)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_panic("%%m used twice");
ret = fix_format_real(format, p-1, len_r);
break;
case '\0':
i_panic("%% modifier missing in '%s'", format);
}
p++;
}
if (ret == format)
*len_r = p - format + strlen(p);
return ret;
}
const char *printf_format_fix_get_len(const char *format, size_t *len_r)
{
const char *ret;
ret = printf_format_fix_noalloc(format, len_r);
if (ret != format)
t_buffer_alloc(*len_r + 1);
return ret;
}
const char *printf_format_fix(const char *format)
{
const char *ret;
size_t len;
ret = printf_format_fix_noalloc(format, &len);
if (ret != format)
t_buffer_alloc(len + 1);
return ret;
}
const char *printf_format_fix_unsafe(const char *format)
{
size_t len;
return printf_format_fix_noalloc(format, &len);
}