2 * print.c Routines to print stuff.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2006 The FreeRADIUS server project
25 #include <freeradius-devel/libradius.h>
29 /** Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8
31 * @param str input string.
32 * @param inlen length of input string. May be -1 if str is \0 terminated.
34 int fr_utf8_char(uint8_t const *str, ssize_t inlen)
36 if (inlen == 0) return 0;
38 if (inlen < 0) inlen = 4; /* longest char */
40 if (*str < 0x20) return 0;
42 if (*str <= 0x7e) return 1; /* 1 */
44 if (*str <= 0xc1) return 0;
46 if (inlen < 2) return 0;
48 if ((str[0] >= 0xc2) && /* 2 */
55 if (inlen < 3) return 0;
57 if ((str[0] == 0xe0) && /* 3 */
65 if ((str[0] >= 0xe1) && /* 4a */
74 if ((str[0] >= 0xee) && /* 4b */
83 if ((str[0] == 0xed) && /* 5 */
91 if (inlen < 4) return 0;
93 if ((str[0] == 0xf0) && /* 6 */
103 if ((str[0] >= 0xf1) && /* 6 */
115 if ((str[0] == 0xf4) && /* 7 */
126 * Invalid UTF-8 Character
131 /** Return a pointer to the first UTF8 char in a string.
133 * @param[out] chr_len Where to write the length of the multibyte char passed in chr (may be NULL).
134 * @param[in] str Haystack.
135 * @param[in] chr Multibyte needle.
136 * @return The position of chr in str or NULL if not found.
138 char const *fr_utf8_strchr(int *chr_len, char const *str, char const *chr)
142 cchr = fr_utf8_char((uint8_t const *)chr, -1);
143 if (cchr == 0) cchr = 1;
144 if (chr_len) *chr_len = cchr;
149 schr = fr_utf8_char((uint8_t const *) str, -1);
150 if (schr == 0) schr = 1;
151 if (schr != cchr) goto next;
153 if (memcmp(str, chr, schr) == 0) {
154 return (char const *) str;
163 /** Escape any non printable or non-UTF8 characters in the input string
165 * @note Return value should be checked with is_truncated
166 * @note Will always \0 terminate unless outlen == 0.
168 * @param[in] in string to escape.
169 * @param[in] inlen length of string to escape (lets us deal with embedded NULs)
170 * @param[out] out where to write the escaped string.
171 * @param[out] outlen the length of the buffer pointed to by out.
172 * @param[in] quote the quotation character
173 * @return the number of bytes it WOULD HAVE written to the buffer, not including the trailing NUL
175 size_t fr_prints(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
177 uint8_t const *p = (uint8_t const *) in;
182 /* No input, so no output... */
184 if (out && outlen) *out = '\0';
188 /* Figure out the length of the input string */
189 if (inlen < 0) inlen = strlen(in);
192 * No quotation character, just use memcpy, ensuring we
193 * don't overflow the output buffer.
196 if (!out) return inlen;
198 if ((size_t)inlen >= outlen) {
199 memcpy(out, in, outlen - 1);
200 out[outlen - 1] = '\0';
202 memcpy(out, in, inlen);
210 * Check the output buffer and length. Zero both of them
211 * out if either are zero.
214 if (freespace == 0) out = NULL;
215 if (!out) freespace = 0;
223 * Hack: never print trailing zero.
224 * Some clients send pings with an off-by-one
225 * length (confused with strings in C).
227 if ((inlen == 1) && (*p == '\0')) {
233 * Always escape the quotation character.
241 * Escape the backslash ONLY for single quoted strings.
251 * Try to convert 0x0a --> \r, etc.
252 * Backslashes get handled specially.
274 } /* escape the character at *p */
278 if ((freespace > 0) && (freespace <= 2)) {
279 if (out) out[used] = '\0';
283 } else if (freespace > 2) { /* room for char AND trailing zero */
298 * All strings are UTF-8 clean.
300 utf8 = fr_utf8_char(p, inlen);
303 * If we have an invalid UTF-8 character, it gets
304 * copied over as a 1-byte character for single
305 * quoted strings. Which means that the output
306 * isn't strictly UTF-8, but oh well...
308 * For double quoted strints, the invalid
309 * characters get escaped as octal encodings.
316 if ((freespace > 0) && (freespace <= 4)) {
317 if (out) out[used] = '\0';
321 } else if (freespace > 4) { /* room for char AND trailing zero */
322 if (out) snprintf(out + used, freespace, "\\%03o", *p);
333 if ((freespace > 0) && (freespace <= utf8)) {
334 if (out) out[used] = '\0';
338 } else if (freespace > utf8) { /* room for char AND trailing zero */
339 memcpy(out + used, p, utf8);
349 * Ensure that the output buffer is always zero terminated.
351 if (out && freespace) out[used] = '\0';
356 /** Find the length of the buffer required to fully escape a string with fr_prints
358 * Were assuming here that's it's cheaper to figure out the length and do one
359 * alloc than repeatedly expand the buffer when we find extra chars which need
362 * @param in string to calculate the escaped length for.
363 * @param inlen length of the input string, if < 0 strlen will be used to check the length.
364 * @param[in] quote the quotation character.
365 * @return the size of buffer required to hold the escaped string including the NUL byte.
367 size_t fr_prints_len(char const *in, ssize_t inlen, char quote)
369 return fr_prints(NULL, 0, in, inlen, quote) + 1;
372 /** Escape string that may contain binary data, and write it to a new buffer
374 * This is useful in situations where we expect printable strings as input,
375 * but under some conditions may get binary data. A good example is libldap
376 * and the arrays of struct berval ldap_get_values_len returns.
378 * @param[in] ctx To allocate new buffer in.
379 * @param[in] in String to escape.
380 * @param[in] inlen Length of string. Should be >= 0 if the data may contain
381 * embedded \0s. Must be >= 0 if data may not be \0 terminated.
382 * If < 0 inlen will be calculated using strlen.
383 * @param[in] quote the quotation character.
384 * @return new buffer holding the escaped string.
386 char *fr_aprints(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
391 len = fr_prints_len(in, inlen, quote);
393 out = talloc_array(ctx, char, len);
394 ret = fr_prints(out, len, in, inlen, quote);
397 * This is a fatal error, but fr_assert is the strongest
398 * assert we're allowed to use in library functions.
400 if (!fr_assert(ret == (len - 1))) {
408 /** Print the value of an attribute to a string
410 * @param[out] out Where to write the string.
411 * @param[in] outlen Size of outlen (must be at least 3 bytes).
412 * @param[in] vp to print.
413 * @param[in] quote Char to add before and after printed value, if 0 no char will be added, if < 0 raw string will be
415 * @return the length of data written to out, or a value >= outlen on truncation.
417 size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
421 if (vp->type == VT_XLAT) {
422 return snprintf(out, outlen, "%c%s%c", quote, vp->value.xlat, quote);
425 return value_data_prints(out, outlen, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
428 /** Print one attribute value to a string
430 * @param ctx to allocate string in.
431 * @param vp to print.
432 * @param[in] quote the quotation character
433 * @return a talloced buffer with the attribute operator and value.
435 char *vp_aprints_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
439 if (vp->type == VT_XLAT) {
440 return fr_aprints(ctx, vp->value.xlat, talloc_array_length(vp->value.xlat) - 1, quote);
443 return value_data_aprints(ctx, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
446 char *vp_aprints_type(TALLOC_CTX *ctx, PW_TYPE type)
449 case PW_TYPE_STRING :
450 return talloc_typed_strdup(ctx, "_");
452 case PW_TYPE_INTEGER64:
456 case PW_TYPE_INTEGER:
458 return talloc_typed_strdup(ctx, "0");
460 case PW_TYPE_IPV4_ADDR :
461 return talloc_typed_strdup(ctx, "?.?.?.?");
463 case PW_TYPE_IPV4_PREFIX:
464 return talloc_typed_strdup(ctx, "?.?.?.?/?");
466 case PW_TYPE_IPV6_ADDR:
467 return talloc_typed_strdup(ctx, "[:?:]");
469 case PW_TYPE_IPV6_PREFIX:
470 return talloc_typed_strdup(ctx, "[:?:]/?");
473 return talloc_typed_strdup(ctx, "??");
475 case PW_TYPE_ETHERNET:
476 return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
478 #ifdef WITH_ASCEND_BINARY
479 case PW_TYPE_ABINARY:
480 return talloc_typed_strdup(ctx, "??");
487 return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
490 /** Prints attribute enumv escaped suitably for use as JSON enumv
492 * Returns < 0 if the buffer may be (or have been) too small to write the encoded
495 * @param out Where to write the string.
496 * @param outlen Length of output buffer.
497 * @param vp to print.
498 * @return the length of data written to out, or a value >= outlen on truncation.
500 size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
503 size_t len, freespace = outlen;
505 if (!vp->da->flags.has_tag) {
506 switch (vp->da->type) {
507 case PW_TYPE_INTEGER:
508 if (vp->da->flags.has_value) break;
510 return snprintf(out, freespace, "%u", vp->vp_integer);
513 if (vp->da->flags.has_value) break;
515 return snprintf(out, freespace, "%u", (unsigned int) vp->vp_short);
518 if (vp->da->flags.has_value) break;
520 return snprintf(out, freespace, "%u", (unsigned int) vp->vp_byte);
523 return snprintf(out, freespace, "%d", vp->vp_signed);
530 /* Indicate truncation */
531 if (freespace < 2) return outlen + 1;
535 switch (vp->da->type) {
537 for (q = vp->vp_strvalue; q < vp->vp_strvalue + vp->vp_length; q++) {
538 /* Indicate truncation */
539 if (freespace < 3) return outlen + 1;
545 } else if (*q == '\\') {
549 } else if (*q == '/') {
553 } else if (*q >= ' ') {
586 len = snprintf(out, freespace, "u%04X", *q);
587 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
596 len = vp_prints_value(out, freespace, vp, 0);
597 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
603 /* Indicate truncation */
604 if (freespace < 2) return outlen + 1;
607 *out = '\0'; // We don't increment out, because the nul byte should not be included in the length
609 return outlen - freespace;
613 * This is a hack, and has to be kept in sync with tokens.h
615 static char const *vp_tokens[] = {
646 /** Print one attribute and value to a string
648 * Print a VALUE_PAIR in the format:
650 <attribute_name>[:tag] <op> <value>
654 * @param out Where to write the string.
655 * @param outlen Length of output buffer.
656 * @param vp to print.
657 * @return the length of data written to out, or a value >= outlen on truncation.
659 size_t vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp)
661 char const *token = NULL;
662 size_t len, freespace = outlen;
667 if (!vp || !vp->da) return 0;
671 if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
672 token = vp_tokens[vp->op];
674 token = "<INVALID-TOKEN>";
677 if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
678 len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
680 len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
683 if (is_truncated(len, freespace)) return len;
687 len = vp_prints_value(out, freespace, vp, '"');
688 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
691 return (outlen - freespace);
694 /** Print one attribute and value to FP
696 * Complete string with '\\t' and '\\n' is written to buffer before printing to
697 * avoid issues when running with multiple threads.
699 * @param fp to output to.
700 * @param vp to print.
702 void vp_print(FILE *fp, VALUE_PAIR const *vp)
711 len = vp_prints(p, sizeof(buf) - 1, vp);
718 * Deal with truncation gracefully
720 if (((size_t) (p - buf)) >= (sizeof(buf) - 2)) {
721 p = buf + (sizeof(buf) - 2);
731 /** Print a list of attributes and enumv
733 * @param fp to output to.
734 * @param const_vp to print.
736 void vp_printlist(FILE *fp, VALUE_PAIR const *const_vp)
741 memcpy(&vp, &const_vp, sizeof(vp)); /* const work-arounds */
743 for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) {
748 /** Print one attribute and value to a string
750 * Print a VALUE_PAIR in the format:
752 <attribute_name>[:tag] <op> <value>
756 * @param ctx to allocate string in.
757 * @param vp to print.
758 * @param[in] quote the quotation character
759 * @return a talloced buffer with the attribute operator and value.
761 char *vp_aprints(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
763 char const *token = NULL;
766 if (!vp || !vp->da) return 0;
770 if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
771 token = vp_tokens[vp->op];
773 token = "<INVALID-TOKEN>";
776 value = vp_aprints_value(ctx, vp, quote);
778 if (vp->da->flags.has_tag) {
779 if (quote && (vp->da->type == PW_TYPE_STRING)) {
780 str = talloc_asprintf(ctx, "%s:%d %s %c%s%c", vp->da->name, vp->tag, token, quote, value, quote);
782 str = talloc_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value);
785 if (quote && (vp->da->type == PW_TYPE_STRING)) {
786 str = talloc_asprintf(ctx, "%s %s %c%s%c", vp->da->name, token, quote, value, quote);
788 str = talloc_asprintf(ctx, "%s %s %s", vp->da->name, token, value);