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 = "&quot;";	break;
148 						case '<': copy = "&lt;";	break;
149 						case '>': copy = "&gt;";	break;
150 						case '&': copy = "&amp;";	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