1 /*
2 * Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11 #pragma ident "%Z%%M% %I% %E% SMI"
12
13 #include <sm/gen.h>
14
15 SM_RCSID("@(#)$Id: util.c,v 1.9 2006/08/30 18:35:51 ca Exp $")
16 #include <sm/setjmp.h>
17 #include <sm/conf.h>
18 #include <sm/assert.h>
19 #include <sm/heap.h>
20 #include <sm/string.h>
21 #include <sm/sendmail.h>
22 #include <ctype.h>
23
24 /*
25 ** STR2PRT -- convert "unprintable" characters in a string to \oct
26 **
27 ** Parameters:
28 ** s -- string to convert
29 **
30 ** Returns:
31 ** converted string.
32 ** This is a static local buffer, string must be copied
33 ** before this function is called again!
34 */
35
36 char *
37 str2prt(s)
38 char *s;
39 {
40 int l;
41 char c, *h;
42 bool ok;
43 static int len = 0;
44 static char *buf = NULL;
45
46 if (s == NULL)
47 return NULL;
48 ok = true;
49 for (h = s, l = 1; *h != '\0'; h++, l++)
50 {
51 if (*h == '\\')
52 {
53 ++l;
54 ok = false;
55 }
56 else if (!(isascii(*h) && isprint(*h)))
57 {
58 l += 3;
59 ok = false;
60 }
61 }
62 if (ok)
63 return s;
64 if (l > len)
65 {
66 char *nbuf = sm_pmalloc_x(l);
67
68 if (buf != NULL)
69 sm_free(buf);
70 len = l;
71 buf = nbuf;
72 }
73 for (h = buf; *s != '\0' && l > 0; s++, l--)
74 {
75 c = *s;
76 if (isascii(c) && isprint(c) && c != '\\')
77 {
78 *h++ = c;
79 }
80 else
81 {
82 *h++ = '\\';
83 --l;
84 switch (c)
85 {
86 case '\\':
87 *h++ = '\\';
88 break;
89 case '\t':
90 *h++ = 't';
91 break;
92 case '\n':
93 *h++ = 'n';
94 break;
95 case '\r':
96 *h++ = 'r';
97 break;
98 default:
99 SM_ASSERT(l >= 2);
100 (void) sm_snprintf(h, l, "%03o",
101 (unsigned int)((unsigned char) c));
102
103 /*
104 ** XXX since l is unsigned this may
105 ** wrap around if the calculation is screwed
106 ** up...
107 */
108
109 l -= 2;
110 h += 3;
111 break;
112 }
113 }
114 }
115 *h = '\0';
116 buf[len - 1] = '\0';
117 return buf;
118 }
119
120 /*
121 ** QUOTE_INTERNAL_CHARS -- do quoting of internal characters
122 **
123 ** Necessary to make sure that we don't have metacharacters such
124 ** as the internal versions of "$*" or "$&" in a string.
125 ** The input and output pointers can be the same.
126 **
127 ** Parameters:
128 ** ibp -- a pointer to the string to translate
129 ** obp -- a pointer to an output buffer
130 ** bsp -- pointer to the length of the output buffer
131 **
132 ** Returns:
133 ** A possibly new bp (if the buffer needed to grow); if
134 ** it is different, *bsp will updated to the size of
135 ** the new buffer and the caller is responsible for
136 ** freeing the memory.
137 */
138
139 #define SM_MM_QUOTE(ch) (((ch) & 0377) == METAQUOTE || (((ch) & 0340) == 0200))
140
141 char *
quote_internal_chars(ibp,obp,bsp)142 quote_internal_chars(ibp, obp, bsp)
143 char *ibp;
144 char *obp;
145 int *bsp;
146 {
147 char *ip, *op;
148 int bufused, olen;
149 bool buffer_same, needs_quoting;
150
151 buffer_same = ibp == obp;
152 needs_quoting = false;
153
154 /* determine length of output string (starts at 1 for trailing '\0') */
155 for (ip = ibp, olen = 1; *ip != '\0'; ip++, olen++)
156 {
157 if (SM_MM_QUOTE(*ip))
158 {
159 olen++;
160 needs_quoting = true;
161 }
162 }
163
164 /* is the output buffer big enough? */
165 if (olen > *bsp)
166 {
167 obp = sm_malloc_x(olen);
168 buffer_same = false;
169 *bsp = olen;
170 }
171
172 /*
173 ** shortcut: no change needed?
174 ** Note: we don't check this first as some bozo may use the same
175 ** buffers but restrict the size of the output buffer to less
176 ** than the length of the input buffer in which case we need to
177 ** allocate a new buffer.
178 */
179
180 if (!needs_quoting)
181 {
182 if (!buffer_same)
183 {
184 bufused = sm_strlcpy(obp, ibp, *bsp);
185 SM_ASSERT(bufused <= olen);
186 }
187 return obp;
188 }
189
190 if (buffer_same)
191 {
192 obp = sm_malloc_x(olen);
193 buffer_same = false;
194 *bsp = olen;
195 }
196
197 for (ip = ibp, op = obp, bufused = 0; *ip != '\0'; ip++)
198 {
199 if (SM_MM_QUOTE(*ip))
200 {
201 SM_ASSERT(bufused < olen);
202 op[bufused++] = METAQUOTE;
203 }
204 SM_ASSERT(bufused < olen);
205 op[bufused++] = *ip;
206 }
207 op[bufused] = '\0';
208 return obp;
209 }
210
211 /*
212 ** DEQUOTE_INTERNAL_CHARS -- undo the effect of quote_internal_chars
213 **
214 ** Parameters:
215 ** ibp -- a pointer to the string to be translated.
216 ** obp -- a pointer to the output buffer. Can be the
217 ** same as ibp.
218 ** obs -- the size of the output buffer.
219 **
220 ** Returns:
221 ** number of character added to obp
222 */
223
224 int
dequote_internal_chars(ibp,obp,obs)225 dequote_internal_chars(ibp, obp, obs)
226 char *ibp;
227 char *obp;
228 int obs;
229 {
230 char *ip, *op;
231 int len;
232 bool quoted;
233
234 quoted = false;
235 len = 0;
236 for (ip = ibp, op = obp; *ip != '\0'; ip++)
237 {
238 if ((*ip & 0377) == METAQUOTE && !quoted)
239 {
240 quoted = true;
241 continue;
242 }
243 if (op < &obp[obs - 1])
244 {
245 *op++ = *ip;
246 ++len;
247 }
248 quoted = false;
249 }
250 *op = '\0';
251 return len;
252 }
253