All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
print.c
Go to the documentation of this file.
1 /*
2  * print.c Routines to print stuff.
3  *
4  * Version: $Id: 73a66b35e9f4595244d7e985f29177b75eebd181 $
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2000,2006 The FreeRADIUS server project
21  */
22 
23 RCSID("$Id: 73a66b35e9f4595244d7e985f29177b75eebd181 $")
24 
25 #include <freeradius-devel/libradius.h>
26 
27 #include <ctype.h>
28 
29 /** Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8
30  *
31  * @param str input string.
32  * @param inlen length of input string. May be -1 if str is \0 terminated.
33  */
34 int fr_utf8_char(uint8_t const *str, ssize_t inlen)
35 {
36  if (inlen == 0) return 0;
37 
38  if (inlen < 0) inlen = 4; /* longest char */
39 
40  if (*str < 0x20) return 0;
41 
42  if (*str <= 0x7e) return 1; /* 1 */
43 
44  if (*str <= 0xc1) return 0;
45 
46  if (inlen < 2) return 0;
47 
48  if ((str[0] >= 0xc2) && /* 2 */
49  (str[0] <= 0xdf) &&
50  (str[1] >= 0x80) &&
51  (str[1] <= 0xbf)) {
52  return 2;
53  }
54 
55  if (inlen < 3) return 0;
56 
57  if ((str[0] == 0xe0) && /* 3 */
58  (str[1] >= 0xa0) &&
59  (str[1] <= 0xbf) &&
60  (str[2] >= 0x80) &&
61  (str[2] <= 0xbf)) {
62  return 3;
63  }
64 
65  if ((str[0] >= 0xe1) && /* 4a */
66  (str[0] <= 0xec) &&
67  (str[1] >= 0x80) &&
68  (str[1] <= 0xbf) &&
69  (str[2] >= 0x80) &&
70  (str[2] <= 0xbf)) {
71  return 3;
72  }
73 
74  if ((str[0] >= 0xee) && /* 4b */
75  (str[0] <= 0xef) &&
76  (str[1] >= 0x80) &&
77  (str[1] <= 0xbf) &&
78  (str[2] >= 0x80) &&
79  (str[2] <= 0xbf)) {
80  return 3;
81  }
82 
83  if ((str[0] == 0xed) && /* 5 */
84  (str[1] >= 0x80) &&
85  (str[1] <= 0x9f) &&
86  (str[2] >= 0x80) &&
87  (str[2] <= 0xbf)) {
88  return 3;
89  }
90 
91  if (inlen < 4) return 0;
92 
93  if ((str[0] == 0xf0) && /* 6 */
94  (str[1] >= 0x90) &&
95  (str[1] <= 0xbf) &&
96  (str[2] >= 0x80) &&
97  (str[2] <= 0xbf) &&
98  (str[3] >= 0x80) &&
99  (str[3] <= 0xbf)) {
100  return 4;
101  }
102 
103  if ((str[0] >= 0xf1) && /* 6 */
104  (str[1] <= 0xf3) &&
105  (str[1] >= 0x80) &&
106  (str[1] <= 0xbf) &&
107  (str[2] >= 0x80) &&
108  (str[2] <= 0xbf) &&
109  (str[3] >= 0x80) &&
110  (str[3] <= 0xbf)) {
111  return 4;
112  }
113 
114 
115  if ((str[0] == 0xf4) && /* 7 */
116  (str[1] >= 0x80) &&
117  (str[1] <= 0x8f) &&
118  (str[2] >= 0x80) &&
119  (str[2] <= 0xbf) &&
120  (str[3] >= 0x80) &&
121  (str[3] <= 0xbf)) {
122  return 4;
123  }
124 
125  /*
126  * Invalid UTF-8 Character
127  */
128  return 0;
129 }
130 
131 /** Return a pointer to the first UTF8 char in a string.
132  *
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
137  * - Position of chr in str.
138  * - NULL if not found.
139  */
140 char const *fr_utf8_strchr(int *chr_len, char const *str, char const *chr)
141 {
142  int cchr;
143 
144  cchr = fr_utf8_char((uint8_t const *)chr, -1);
145  if (cchr == 0) cchr = 1;
146  if (chr_len) *chr_len = cchr;
147 
148  while (*str) {
149  int schr;
150 
151  schr = fr_utf8_char((uint8_t const *) str, -1);
152  if (schr == 0) schr = 1;
153  if (schr != cchr) goto next;
154 
155  if (memcmp(str, chr, schr) == 0) {
156  return (char const *) str;
157  }
158  next:
159  str += schr;
160  }
161 
162  return NULL;
163 }
164 
165 /** Escape any non printable or non-UTF8 characters in the input string
166  *
167  * @note Return value should be checked with is_truncated
168  * @note Will always \0 terminate unless outlen == 0.
169  *
170  * @param[in] in string to escape.
171  * @param[in] inlen length of string to escape (lets us deal with embedded NULs)
172  * @param[out] out where to write the escaped string.
173  * @param[out] outlen the length of the buffer pointed to by out.
174  * @param[in] quote the quotation character
175  * @return
176  * - The number of bytes written to the out buffer.
177  * - A number >= outlen if truncation has occurred.
178  */
179 size_t fr_snprint(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
180 {
181  uint8_t const *p = (uint8_t const *) in;
182  size_t utf8;
183  size_t used;
184  size_t freespace;
185 
186  /* No input, so no output... */
187  if (!in) {
188  if (out && outlen) *out = '\0';
189  return 0;
190  }
191 
192  /* Figure out the length of the input string */
193  if (inlen < 0) inlen = strlen(in);
194 
195  /*
196  * No quotation character, just use memcpy, ensuring we
197  * don't overflow the output buffer.
198  */
199  if (!quote) {
200  if (!out) return inlen;
201 
202  if ((size_t)inlen >= outlen) {
203  memcpy(out, in, outlen - 1);
204  out[outlen - 1] = '\0';
205  } else {
206  memcpy(out, in, inlen);
207  out[inlen] = '\0';
208  }
209 
210  return inlen;
211  }
212 
213  /*
214  * Check the output buffer and length. Zero both of them
215  * out if either are zero.
216  */
217  freespace = outlen;
218  if (freespace == 0) out = NULL;
219  if (!out) freespace = 0;
220 
221  used = 0;
222 
223  while (inlen > 0) {
224  int sp = 0;
225 
226  /*
227  * Hack: never print trailing zero.
228  * Some clients send pings with an off-by-one
229  * length (confused with strings in C).
230  */
231  if ((inlen == 1) && (*p == '\0')) {
232  inlen--;
233  break;
234  }
235 
236  /*
237  * Always escape the quotation character.
238  */
239  if (*p == quote) {
240  sp = quote;
241  goto do_escape;
242  }
243 
244  /*
245  * Escape the backslash ONLY for single quoted strings.
246  */
247  if (quote == '\'') {
248  if (*p == '\\') {
249  sp = '\\';
250  }
251  goto do_escape;
252  }
253 
254  /*
255  * Try to convert 0x0a --> \r, etc.
256  * Backslashes get handled specially.
257  */
258  switch (*p) {
259  case '\r':
260  sp = 'r';
261  break;
262 
263  case '\n':
264  sp = 'n';
265  break;
266 
267  case '\t':
268  sp = 't';
269  break;
270 
271  case '\\':
272  sp = '\\';
273  break;
274 
275  default:
276  sp = '\0';
277  break;
278  } /* escape the character at *p */
279 
280  do_escape:
281  if (sp) {
282  if ((freespace > 0) && (freespace <= 2)) {
283  if (out) out[used] = '\0';
284  out = NULL;
285  freespace = 0;
286 
287  } else if (freespace > 2) { /* room for char AND trailing zero */
288  if (out) {
289  out[used] = '\\';
290  out[used + 1] = sp;
291  }
292  freespace -= 2;
293  }
294 
295  used += 2;
296  p++;
297  inlen--;
298  continue;
299  }
300 
301  /*
302  * All strings are UTF-8 clean.
303  */
304  utf8 = fr_utf8_char(p, inlen);
305 
306  /*
307  * If we have an invalid UTF-8 character, it gets
308  * copied over as a 1-byte character for single
309  * quoted strings. Which means that the output
310  * isn't strictly UTF-8, but oh well...
311  *
312  * For double quoted strints, the invalid
313  * characters get escaped as octal encodings.
314  */
315  if (utf8 == 0) {
316  if (quote == '\'') {
317  utf8 = 1;
318 
319  } else {
320  if ((freespace > 0) && (freespace <= 4)) {
321  if (out) out[used] = '\0';
322  out = NULL;
323  freespace = 0;
324 
325  } else if (freespace > 4) { /* room for char AND trailing zero */
326  if (out) snprintf(out + used, freespace, "\\%03o", *p);
327  freespace -= 4;
328  }
329 
330  used += 4;
331  p++;
332  inlen--;
333  continue;
334  }
335  }
336 
337  if ((freespace > 0) && (freespace <= utf8)) {
338  if (out) out[used] = '\0';
339  out = NULL;
340  freespace = 0;
341 
342  } else if (freespace > utf8) { /* room for char AND trailing zero */
343  memcpy(out + used, p, utf8);
344  freespace -= utf8;
345  }
346 
347  used += utf8;
348  p += utf8;
349  inlen -= utf8;
350  }
351 
352  /*
353  * Ensure that the output buffer is always zero terminated.
354  */
355  if (out && freespace) out[used] = '\0';
356 
357  return used;
358 }
359 
360 /** Find the length of the buffer required to fully escape a string with fr_prints
361  *
362  * Were assuming here that's it's cheaper to figure out the length and do one
363  * alloc than repeatedly expand the buffer when we find extra chars which need
364  * to be added.
365  *
366  * @param in string to calculate the escaped length for.
367  * @param inlen length of the input string, if < 0 strlen will be used to check the length.
368  * @param[in] quote the quotation character.
369  * @return the size of buffer required to hold the escaped string including the NUL byte.
370  */
371 size_t fr_snprint_len(char const *in, ssize_t inlen, char quote)
372 {
373  return fr_snprint(NULL, 0, in, inlen, quote) + 1;
374 }
375 
376 /** Escape string that may contain binary data, and write it to a new buffer
377  *
378  * This is useful in situations where we expect printable strings as input,
379  * but under some conditions may get binary data. A good example is libldap
380  * and the arrays of struct berval ldap_get_values_len returns.
381  *
382  * @param[in] ctx To allocate new buffer in.
383  * @param[in] in String to escape.
384  * @param[in] inlen Length of string. Should be >= 0 if the data may contain
385  * embedded \0s. Must be >= 0 if data may not be \0 terminated.
386  * If < 0 inlen will be calculated using strlen.
387  * @param[in] quote the quotation character.
388  * @return new buffer holding the escaped string.
389  */
390 char *fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
391 {
392  size_t len, ret;
393  char *out;
394 
395  len = fr_snprint_len(in, inlen, quote);
396 
397  out = talloc_array(ctx, char, len);
398  ret = fr_snprint(out, len, in, inlen, quote);
399  /*
400  * This is a fatal error, but fr_assert is the strongest
401  * assert we're allowed to use in library functions.
402  */
403  if (!fr_assert(ret == (len - 1))) {
404  talloc_free(out);
405  return NULL;
406  }
407 
408  return out;
409 }
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
#define fr_assert(_x)
Definition: libradius.h:505
#define RCSID(id)
Definition: build.h:135