diff options
author | Lai Jiangshan <laijs@cn.fujitsu.com> | 2009-03-06 11:21:46 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-03-06 11:39:04 -0500 |
commit | 4370aa4aa75391a5e2e06bccb0919109f725ed8e (patch) | |
tree | 602a6ea4e404d17e610d4a9979d615ff2d0bfd98 /lib/vsprintf.c | |
parent | f036be96dd9ce442ffb9ab33e3c165f5178815c0 (diff) |
vsprintf: add binary printf
Impact: add new APIs for binary trace printk infrastructure
vbin_printf(): write args to binary buffer, string is copied
when "%s" is occurred.
bstr_printf(): read from binary buffer for args and format a string
[fweisbec@gmail.com: rebase]
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
LKML-Reference: <1236356510-8381-2-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'lib/vsprintf.c')
-rw-r--r-- | lib/vsprintf.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 0fbd0121d91d..3543bbe8b1bc 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c | |||
@@ -1058,6 +1058,448 @@ int sprintf(char * buf, const char *fmt, ...) | |||
1058 | } | 1058 | } |
1059 | EXPORT_SYMBOL(sprintf); | 1059 | EXPORT_SYMBOL(sprintf); |
1060 | 1060 | ||
1061 | #ifdef CONFIG_BINARY_PRINTF | ||
1062 | /* | ||
1063 | * bprintf service: | ||
1064 | * vbin_printf() - VA arguments to binary data | ||
1065 | * bstr_printf() - Binary data to text string | ||
1066 | */ | ||
1067 | |||
1068 | /** | ||
1069 | * vbin_printf - Parse a format string and place args' binary value in a buffer | ||
1070 | * @bin_buf: The buffer to place args' binary value | ||
1071 | * @size: The size of the buffer(by words(32bits), not characters) | ||
1072 | * @fmt: The format string to use | ||
1073 | * @args: Arguments for the format string | ||
1074 | * | ||
1075 | * The format follows C99 vsnprintf, except %n is ignored, and its argument | ||
1076 | * is skiped. | ||
1077 | * | ||
1078 | * The return value is the number of words(32bits) which would be generated for | ||
1079 | * the given input. | ||
1080 | * | ||
1081 | * NOTE: | ||
1082 | * If the return value is greater than @size, the resulting bin_buf is NOT | ||
1083 | * valid for bstr_printf(). | ||
1084 | */ | ||
1085 | int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) | ||
1086 | { | ||
1087 | char *str, *end; | ||
1088 | int qualifier; | ||
1089 | |||
1090 | str = (char *)bin_buf; | ||
1091 | end = (char *)(bin_buf + size); | ||
1092 | |||
1093 | #define save_arg(type) \ | ||
1094 | do { \ | ||
1095 | if (sizeof(type) == 8) { \ | ||
1096 | unsigned long long value; \ | ||
1097 | str = PTR_ALIGN(str, sizeof(u32)); \ | ||
1098 | value = va_arg(args, unsigned long long); \ | ||
1099 | if (str + sizeof(type) <= end) { \ | ||
1100 | *(u32 *)str = *(u32 *)&value; \ | ||
1101 | *(u32 *)(str + 4) = *((u32 *)&value + 1); \ | ||
1102 | } \ | ||
1103 | } else { \ | ||
1104 | unsigned long value; \ | ||
1105 | str = PTR_ALIGN(str, sizeof(type)); \ | ||
1106 | value = va_arg(args, int); \ | ||
1107 | if (str + sizeof(type) <= end) \ | ||
1108 | *(typeof(type) *)str = (type)value; \ | ||
1109 | } \ | ||
1110 | str += sizeof(type); \ | ||
1111 | } while (0) | ||
1112 | |||
1113 | for (; *fmt ; ++fmt) { | ||
1114 | if (*fmt != '%') | ||
1115 | continue; | ||
1116 | |||
1117 | repeat: | ||
1118 | /* parse flags */ | ||
1119 | ++fmt; /* this also skips first '%' */ | ||
1120 | if (*fmt == '-' || *fmt == '+' || *fmt == ' ' | ||
1121 | || *fmt == '#' || *fmt == '0') | ||
1122 | goto repeat; | ||
1123 | |||
1124 | /* parse field width */ | ||
1125 | if (isdigit(*fmt)) | ||
1126 | skip_atoi(&fmt); | ||
1127 | else if (*fmt == '*') { | ||
1128 | ++fmt; | ||
1129 | /* it's the next argument */ | ||
1130 | save_arg(int); | ||
1131 | } | ||
1132 | |||
1133 | /* parse the precision */ | ||
1134 | if (*fmt == '.') { | ||
1135 | ++fmt; | ||
1136 | if (isdigit(*fmt)) | ||
1137 | skip_atoi(&fmt); | ||
1138 | else if (*fmt == '*') { | ||
1139 | ++fmt; | ||
1140 | /* it's the next argument */ | ||
1141 | save_arg(int); | ||
1142 | } | ||
1143 | } | ||
1144 | |||
1145 | /* parse the conversion qualifier */ | ||
1146 | qualifier = -1; | ||
1147 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || | ||
1148 | *fmt == 'Z' || *fmt == 'z' || *fmt == 't') { | ||
1149 | qualifier = *fmt; | ||
1150 | ++fmt; | ||
1151 | if (qualifier == 'l' && *fmt == 'l') { | ||
1152 | qualifier = 'L'; | ||
1153 | ++fmt; | ||
1154 | } | ||
1155 | } | ||
1156 | |||
1157 | /* parse format type */ | ||
1158 | switch (*fmt) { | ||
1159 | case 'c': | ||
1160 | save_arg(char); | ||
1161 | continue; | ||
1162 | case 's': { | ||
1163 | /* save the string argument */ | ||
1164 | const char *save_str = va_arg(args, char *); | ||
1165 | size_t len; | ||
1166 | if ((unsigned long)save_str > (unsigned long)-PAGE_SIZE | ||
1167 | || (unsigned long)save_str < PAGE_SIZE) | ||
1168 | save_str = "<NULL>"; | ||
1169 | len = strlen(save_str); | ||
1170 | if (str + len + 1 < end) | ||
1171 | memcpy(str, save_str, len + 1); | ||
1172 | str += len + 1; | ||
1173 | continue; | ||
1174 | } | ||
1175 | case 'p': | ||
1176 | save_arg(void *); | ||
1177 | /* skip all alphanumeric pointer suffixes */ | ||
1178 | while (isalnum(fmt[1])) | ||
1179 | fmt++; | ||
1180 | continue; | ||
1181 | case 'n': { | ||
1182 | /* skip %n 's argument */ | ||
1183 | void *skip_arg; | ||
1184 | if (qualifier == 'l') | ||
1185 | skip_arg = va_arg(args, long *); | ||
1186 | else if (qualifier == 'Z' || qualifier == 'z') | ||
1187 | skip_arg = va_arg(args, size_t *); | ||
1188 | else | ||
1189 | skip_arg = va_arg(args, int *); | ||
1190 | continue; | ||
1191 | } | ||
1192 | case 'o': | ||
1193 | case 'x': | ||
1194 | case 'X': | ||
1195 | case 'd': | ||
1196 | case 'i': | ||
1197 | case 'u': | ||
1198 | /* save arg for case: 'o', 'x', 'X', 'd', 'i', 'u' */ | ||
1199 | if (qualifier == 'L') | ||
1200 | save_arg(long long); | ||
1201 | else if (qualifier == 'l') | ||
1202 | save_arg(unsigned long); | ||
1203 | else if (qualifier == 'Z' || qualifier == 'z') | ||
1204 | save_arg(size_t); | ||
1205 | else if (qualifier == 't') | ||
1206 | save_arg(ptrdiff_t); | ||
1207 | else if (qualifier == 'h') | ||
1208 | save_arg(short); | ||
1209 | else | ||
1210 | save_arg(int); | ||
1211 | continue; | ||
1212 | default: | ||
1213 | if (!*fmt) | ||
1214 | --fmt; | ||
1215 | continue; | ||
1216 | } | ||
1217 | } | ||
1218 | #undef save_arg | ||
1219 | |||
1220 | return (u32 *)(PTR_ALIGN(str, sizeof(u32))) - bin_buf; | ||
1221 | } | ||
1222 | EXPORT_SYMBOL_GPL(vbin_printf); | ||
1223 | |||
1224 | /** | ||
1225 | * bstr_printf - Format a string from binary arguments and place it in a buffer | ||
1226 | * @buf: The buffer to place the result into | ||
1227 | * @size: The size of the buffer, including the trailing null space | ||
1228 | * @fmt: The format string to use | ||
1229 | * @bin_buf: Binary arguments for the format string | ||
1230 | * | ||
1231 | * This function like C99 vsnprintf, but the difference is that vsnprintf gets | ||
1232 | * arguments from stack, and bstr_printf gets arguments from @bin_buf which is | ||
1233 | * a binary buffer that generated by vbin_printf. | ||
1234 | * | ||
1235 | * The format follows C99 vsnprintf, but has some extensions: | ||
1236 | * %pS output the name of a text symbol | ||
1237 | * %pF output the name of a function pointer | ||
1238 | * %pR output the address range in a struct resource | ||
1239 | * %n is ignored | ||
1240 | * | ||
1241 | * The return value is the number of characters which would | ||
1242 | * be generated for the given input, excluding the trailing | ||
1243 | * '\0', as per ISO C99. If you want to have the exact | ||
1244 | * number of characters written into @buf as return value | ||
1245 | * (not including the trailing '\0'), use vscnprintf(). If the | ||
1246 | * return is greater than or equal to @size, the resulting | ||
1247 | * string is truncated. | ||
1248 | */ | ||
1249 | int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) | ||
1250 | { | ||
1251 | unsigned long long num; | ||
1252 | int base; | ||
1253 | char *str, *end, c; | ||
1254 | const char *args = (const char *)bin_buf; | ||
1255 | |||
1256 | int flags; | ||
1257 | int field_width; | ||
1258 | int precision; | ||
1259 | int qualifier; | ||
1260 | |||
1261 | if (unlikely((int) size < 0)) { | ||
1262 | /* There can be only one.. */ | ||
1263 | static char warn = 1; | ||
1264 | WARN_ON(warn); | ||
1265 | warn = 0; | ||
1266 | return 0; | ||
1267 | } | ||
1268 | |||
1269 | str = buf; | ||
1270 | end = buf + size; | ||
1271 | |||
1272 | #define get_arg(type) \ | ||
1273 | ({ \ | ||
1274 | typeof(type) value; \ | ||
1275 | if (sizeof(type) == 8) { \ | ||
1276 | args = PTR_ALIGN(args, sizeof(u32)); \ | ||
1277 | *(u32 *)&value = *(u32 *)args; \ | ||
1278 | *((u32 *)&value + 1) = *(u32 *)(args + 4); \ | ||
1279 | } else { \ | ||
1280 | args = PTR_ALIGN(args, sizeof(type)); \ | ||
1281 | value = *(typeof(type) *)args; \ | ||
1282 | } \ | ||
1283 | args += sizeof(type); \ | ||
1284 | value; \ | ||
1285 | }) | ||
1286 | |||
1287 | /* Make sure end is always >= buf */ | ||
1288 | if (end < buf) { | ||
1289 | end = ((void *)-1); | ||
1290 | size = end - buf; | ||
1291 | } | ||
1292 | |||
1293 | for (; *fmt ; ++fmt) { | ||
1294 | if (*fmt != '%') { | ||
1295 | if (str < end) | ||
1296 | *str = *fmt; | ||
1297 | ++str; | ||
1298 | continue; | ||
1299 | } | ||
1300 | |||
1301 | /* process flags */ | ||
1302 | flags = 0; | ||
1303 | repeat: | ||
1304 | ++fmt; /* this also skips first '%' */ | ||
1305 | switch (*fmt) { | ||
1306 | case '-': | ||
1307 | flags |= LEFT; | ||
1308 | goto repeat; | ||
1309 | case '+': | ||
1310 | flags |= PLUS; | ||
1311 | goto repeat; | ||
1312 | case ' ': | ||
1313 | flags |= SPACE; | ||
1314 | goto repeat; | ||
1315 | case '#': | ||
1316 | flags |= SPECIAL; | ||
1317 | goto repeat; | ||
1318 | case '0': | ||
1319 | flags |= ZEROPAD; | ||
1320 | goto repeat; | ||
1321 | } | ||
1322 | |||
1323 | /* get field width */ | ||
1324 | field_width = -1; | ||
1325 | if (isdigit(*fmt)) | ||
1326 | field_width = skip_atoi(&fmt); | ||
1327 | else if (*fmt == '*') { | ||
1328 | ++fmt; | ||
1329 | /* it's the next argument */ | ||
1330 | field_width = get_arg(int); | ||
1331 | if (field_width < 0) { | ||
1332 | field_width = -field_width; | ||
1333 | flags |= LEFT; | ||
1334 | } | ||
1335 | } | ||
1336 | |||
1337 | /* get the precision */ | ||
1338 | precision = -1; | ||
1339 | if (*fmt == '.') { | ||
1340 | ++fmt; | ||
1341 | if (isdigit(*fmt)) | ||
1342 | precision = skip_atoi(&fmt); | ||
1343 | else if (*fmt == '*') { | ||
1344 | ++fmt; | ||
1345 | /* it's the next argument */ | ||
1346 | precision = get_arg(int); | ||
1347 | } | ||
1348 | if (precision < 0) | ||
1349 | precision = 0; | ||
1350 | } | ||
1351 | |||
1352 | /* get the conversion qualifier */ | ||
1353 | qualifier = -1; | ||
1354 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || | ||
1355 | *fmt == 'Z' || *fmt == 'z' || *fmt == 't') { | ||
1356 | qualifier = *fmt; | ||
1357 | ++fmt; | ||
1358 | if (qualifier == 'l' && *fmt == 'l') { | ||
1359 | qualifier = 'L'; | ||
1360 | ++fmt; | ||
1361 | } | ||
1362 | } | ||
1363 | |||
1364 | /* default base */ | ||
1365 | base = 10; | ||
1366 | |||
1367 | switch (*fmt) { | ||
1368 | case 'c': | ||
1369 | if (!(flags & LEFT)) { | ||
1370 | while (--field_width > 0) { | ||
1371 | if (str < end) | ||
1372 | *str = ' '; | ||
1373 | ++str; | ||
1374 | } | ||
1375 | } | ||
1376 | c = (unsigned char) get_arg(char); | ||
1377 | if (str < end) | ||
1378 | *str = c; | ||
1379 | ++str; | ||
1380 | while (--field_width > 0) { | ||
1381 | if (str < end) | ||
1382 | *str = ' '; | ||
1383 | ++str; | ||
1384 | } | ||
1385 | continue; | ||
1386 | |||
1387 | case 's':{ | ||
1388 | const char *str_arg = args; | ||
1389 | size_t len = strlen(str_arg); | ||
1390 | args += len + 1; | ||
1391 | str = string(str, end, (char *)str_arg, field_width, | ||
1392 | precision, flags); | ||
1393 | continue; | ||
1394 | } | ||
1395 | |||
1396 | case 'p': | ||
1397 | str = pointer(fmt+1, str, end, get_arg(void *), | ||
1398 | field_width, precision, flags); | ||
1399 | /* Skip all alphanumeric pointer suffixes */ | ||
1400 | while (isalnum(fmt[1])) | ||
1401 | fmt++; | ||
1402 | continue; | ||
1403 | |||
1404 | case 'n': | ||
1405 | /* skip %n */ | ||
1406 | continue; | ||
1407 | |||
1408 | case '%': | ||
1409 | if (str < end) | ||
1410 | *str = '%'; | ||
1411 | ++str; | ||
1412 | continue; | ||
1413 | |||
1414 | /* integer number formats - set up the flags and "break" */ | ||
1415 | case 'o': | ||
1416 | base = 8; | ||
1417 | break; | ||
1418 | |||
1419 | case 'x': | ||
1420 | flags |= SMALL; | ||
1421 | case 'X': | ||
1422 | base = 16; | ||
1423 | break; | ||
1424 | |||
1425 | case 'd': | ||
1426 | case 'i': | ||
1427 | flags |= SIGN; | ||
1428 | case 'u': | ||
1429 | break; | ||
1430 | |||
1431 | default: | ||
1432 | if (str < end) | ||
1433 | *str = '%'; | ||
1434 | ++str; | ||
1435 | if (*fmt) { | ||
1436 | if (str < end) | ||
1437 | *str = *fmt; | ||
1438 | ++str; | ||
1439 | } else { | ||
1440 | --fmt; | ||
1441 | } | ||
1442 | continue; | ||
1443 | } | ||
1444 | if (qualifier == 'L') | ||
1445 | num = get_arg(long long); | ||
1446 | else if (qualifier == 'l') { | ||
1447 | num = get_arg(unsigned long); | ||
1448 | if (flags & SIGN) | ||
1449 | num = (signed long) num; | ||
1450 | } else if (qualifier == 'Z' || qualifier == 'z') { | ||
1451 | num = get_arg(size_t); | ||
1452 | } else if (qualifier == 't') { | ||
1453 | num = get_arg(ptrdiff_t); | ||
1454 | } else if (qualifier == 'h') { | ||
1455 | num = (unsigned short) get_arg(short); | ||
1456 | if (flags & SIGN) | ||
1457 | num = (signed short) num; | ||
1458 | } else { | ||
1459 | num = get_arg(unsigned int); | ||
1460 | if (flags & SIGN) | ||
1461 | num = (signed int) num; | ||
1462 | } | ||
1463 | str = number(str, end, num, base, | ||
1464 | field_width, precision, flags); | ||
1465 | } | ||
1466 | if (size > 0) { | ||
1467 | if (str < end) | ||
1468 | *str = '\0'; | ||
1469 | else | ||
1470 | end[-1] = '\0'; | ||
1471 | } | ||
1472 | #undef get_arg | ||
1473 | |||
1474 | /* the trailing null byte doesn't count towards the total */ | ||
1475 | return str - buf; | ||
1476 | } | ||
1477 | EXPORT_SYMBOL_GPL(bstr_printf); | ||
1478 | |||
1479 | /** | ||
1480 | * bprintf - Parse a format string and place args' binary value in a buffer | ||
1481 | * @bin_buf: The buffer to place args' binary value | ||
1482 | * @size: The size of the buffer(by words(32bits), not characters) | ||
1483 | * @fmt: The format string to use | ||
1484 | * @...: Arguments for the format string | ||
1485 | * | ||
1486 | * The function returns the number of words(u32) written | ||
1487 | * into @bin_buf. | ||
1488 | */ | ||
1489 | int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) | ||
1490 | { | ||
1491 | va_list args; | ||
1492 | int ret; | ||
1493 | |||
1494 | va_start(args, fmt); | ||
1495 | ret = vbin_printf(bin_buf, size, fmt, args); | ||
1496 | va_end(args); | ||
1497 | return ret; | ||
1498 | } | ||
1499 | EXPORT_SYMBOL_GPL(bprintf); | ||
1500 | |||
1501 | #endif /* CONFIG_BINARY_PRINTF */ | ||
1502 | |||
1061 | /** | 1503 | /** |
1062 | * vsscanf - Unformat a buffer into a list of arguments | 1504 | * vsscanf - Unformat a buffer into a list of arguments |
1063 | * @buf: input buffer | 1505 | * @buf: input buffer |