1 /*
2 * $Id: printh.c,v 1.1.2.2 2004/11/14 18:05:55 tomcollins Exp $
3 * Copyright (C) 2004 Tom Logic LLC
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 */
19
20 #include "printh.h"
21 /* included from printh.h:
22 #include <stdlib.h>
23 #include <stdarg.h>
24 */
25 #include <string.h>
26 #include <stdio.h>
27 #include <ctype.h>
28
29 /* vsnprinth - Custom version of sprintf for escaping strings for use on HTML
30 * pages and in cgi script parameters.
31 *
32 * (based on qnprintf from vpopmail)
33 *
34 * int vsnprinth (char *buffer, size_t size, const char *format, va_list ap)
35 * int snprinth (char *buffer, size_t size, const char *format, ...);
36 * int printh (const char *format, ...);
37 *
38 * buffer - buffer to print string to
39 * size - size of buffer
40 * format - a printf-style format string*
41 * ... - variable arguments for the format string
42 *
43 * NOTE: Currently supported formats: %%, %c, %s, %d/%i, %u, %ld/%li, %lu
44 * Since this function was designed to escape strings for use on HTML pages,
45 * the formats don't support any extended options.
46 *
47 * Extra formats: %C (like %s, but converts string to cgi-parameter safe
48 * version) and %H (like %s, but converts to HTML-safe version)
49 *
50 * Returns the number of characters that would have been printed to buffer
51 * if it was big enough. (i.e., if return value is larger than (size-1),
52 * buffer received an incomplete copy of the formatted string).
53 *
54 * It is possible to call snprinth with a NULL buffer of 0 bytes to determine
55 * how large the buffer needs to be. This is inefficient, as snprinth has
56 * to run twice.
57 *
58 * snprinth written November 2004 by Tom Collins <tom@tomlogic.com>
59 * qnprintf written February 2004 by Tom Collins <tom@tomlogic.com>
60 */
vsnprinth(char * buffer,size_t size,const char * format,va_list ap)61 int vsnprinth (char *buffer, size_t size, const char *format, va_list ap)
62 {
63 const char hex[] = "0123456789abcdef";
64
65 int printed; /* number of characters that would have been printed */
66 const char *f; /* current position in format string */
67 char *b; /* current position in output buffer */
68 char n[20]; /* buffer to hold string representation of number */
69
70 char *s; /* pointer to string to insert */
71 char *copy; /* pointer to replacement string for special char */
72
73 int stringtype;
74 #define SPRINTH_NORMAL 0
75 #define SPRINTH_HTML 1
76 #define SPRINTH_CGI 2
77
78 if (buffer == NULL && size > 0) return -1;
79
80 printed = 0;
81 b = buffer;
82 for (f = format; *f != '\0'; f++) {
83 if (*f != '%') {
84 if (++printed < size) *b++ = *f;
85 } else {
86 f++;
87 s = n;
88 stringtype = SPRINTH_NORMAL;
89 switch (*f) {
90 case '%':
91 strcpy (n, "%");
92 break;
93
94 case 'c':
95 snprintf (n, sizeof(n), "%c", va_arg (ap, int));
96 break;
97
98 case 'd':
99 case 'i':
100 snprintf (n, sizeof(n), "%d", va_arg (ap, int));
101 break;
102
103 case 'u':
104 snprintf (n, sizeof(n), "%u", va_arg (ap, unsigned int));
105 break;
106
107 case 'l':
108 f++;
109 switch (*f) {
110 case 'd':
111 case 'i':
112 snprintf (n, sizeof(n), "%ld", va_arg (ap, long));
113 break;
114
115 case 'u':
116 snprintf (n, sizeof(n), "%lu", va_arg (ap, unsigned long));
117 break;
118
119 default:
120 strcpy (n, "*");
121 }
122 break;
123
124 case 's':
125 s = va_arg (ap, char *);
126 break;
127
128 case 'H':
129 s = va_arg (ap, char *);
130 stringtype = SPRINTH_HTML;
131 break;
132
133 case 'C':
134 s = va_arg (ap, char *);
135 stringtype = SPRINTH_CGI;
136 break;
137
138 default:
139 strcpy (n, "*");
140 }
141
142 /* now copy the string parameter into the buffer */
143 while (*s != '\0') {
144 copy = NULL; /* default to no special handling */
145 if (stringtype == SPRINTH_HTML) {
146 switch (*s) {
147 case '"': copy = """; break;
148 case '<': copy = "<"; break;
149 case '>': copy = ">"; break;
150 case '&': copy = "&"; break;
151 }
152 } else if (stringtype == SPRINTH_CGI) {
153 if (*s == ' ')
154 copy = strcpy (n, "+");
155 else if (! isalnum(*s) && (strchr("._-", *s) == NULL)) {
156 copy = n;
157 sprintf (n, "%%%c%c", hex[*s >> 4 & 0x0F], hex[*s & 0x0F]);
158 }
159 }
160 if (copy == NULL) {
161 if (++printed < size) *b++ = *s;
162 } else {
163 /* replace *s with buffer pointed to by copy */
164 while (*copy != '\0') {
165 if (++printed < size) *b++ = *copy;
166 copy++;
167 }
168 }
169 s++;
170 }
171 }
172 }
173
174 *b = '\0';
175
176 /* If the formatted string doesn't fit in the buffer, zero out the buffer. */
177 if (printed >= size) {
178 memset (buffer, '\0', size);
179 }
180
181 return printed;
182 }
183
snprinth(char * buffer,size_t size,const char * format,...)184 int snprinth (char *buffer, size_t size, const char *format, ...)
185 {
186 int ret;
187
188 va_list argp;
189
190 va_start (argp, format);
191 ret = vsnprinth (buffer, size, format, argp);
192 va_end (argp);
193
194 return ret;
195 }
196
printh(const char * format,...)197 int printh (const char *format, ...)
198 {
199 int ret;
200 char buffer[1024];
201
202 va_list argp;
203
204 va_start (argp, format);
205 ret = vsnprinth (buffer, sizeof(buffer), format, argp);
206 va_end (argp);
207
208 printf ("%s", buffer);
209
210 return ret;
211 }
212