1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2  * Copyright (C) 1998-1999  Brian Bruns
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include <config.h>
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 
25 #if HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif /* HAVE_STDLIB_H */
28 
29 #if HAVE_STRING_H
30 #include <string.h>
31 #endif /* HAVE_STRING_H */
32 
33 #include <freetds/tds.h>
34 #include <freetds/replacements.h>
35 
36 struct string_linked_list
37 {
38 	char *str;
39 	struct string_linked_list *next;
40 };
41 
42 /*
43  * XXX The magic use of \xFF is bletcherous, but I can't think of anything
44  *     better right now.
45  */
46 
47 static char *
norm_fmt(const char * fmt,int fmtlen)48 norm_fmt(const char *fmt, int fmtlen)
49 {
50 	char *newfmt;
51 	char *cp;
52 	char skip = 0;
53 
54 	if (fmtlen == TDS_NULLTERM) {
55 		fmtlen = strlen(fmt);
56 	}
57 	if ((newfmt = tds_new(char, fmtlen + 1)) == NULL)
58 		return NULL;
59 
60 	for (cp = newfmt; fmtlen > 0; fmtlen--, fmt++) {
61 		switch (*fmt) {
62 		case ',':
63 		case ' ':
64 			if (!skip) {
65 				*cp++ = '\377';
66 				skip = 1;
67 			}
68 			break;
69 		default:
70 			skip = 0;
71 			*cp++ = *fmt;
72 			break;
73 		}
74 	}
75 	*cp = '\0';
76 	return newfmt;
77 }
78 
79 TDSRET
tds_vstrbuild(char * buffer,int buflen,int * resultlen,const char * text,int textlen,const char * formats,int formatlen,va_list ap)80 tds_vstrbuild(char *buffer, int buflen, int *resultlen, const char *text, int textlen, const char *formats, int formatlen, va_list ap)
81 {
82 	char *newformat;
83 	char *params;
84 	char *token;
85 	const char *sep = "\377";
86 	char *lasts;
87 	int tokcount = 0;
88 	struct string_linked_list *head = NULL;
89 	struct string_linked_list *item = NULL;
90 	struct string_linked_list **tail = &head;
91 	int i;
92 	int state;
93 	char **string_array = NULL;
94 	int pnum = 0;
95 	int pdigit;
96 	char *paramp = NULL;
97 	TDSRET rc = TDS_FAIL;
98 
99 	*resultlen = 0;
100 	if (textlen == TDS_NULLTERM) {
101 		textlen = (int)strlen(text);
102 	}
103 	if ((newformat = norm_fmt(formats, formatlen)) == NULL) {
104 		return TDS_FAIL;
105 	}
106 	if (vasprintf(&params, newformat, ap) < 0) {
107 		free(newformat);
108 		return TDS_FAIL;
109 	}
110 	free(newformat);
111 	for (token = strtok_r(params, sep, &lasts); token != NULL; token = strtok_r(NULL, sep, &lasts)) {
112 		if ((*tail = tds_new(struct string_linked_list, 1)) == NULL) {
113 			goto out;
114 		}
115 		(*tail)->str = token;
116 		(*tail)->next = NULL;
117 		tail = &((*tail)->next);
118 		tokcount++;
119 	}
120 	if ((string_array = tds_new(char *, tokcount + 1)) == NULL) {
121 		goto out;
122 	}
123 	for (item = head, i = 0; i < tokcount; item = item->next, i++) {
124 		if (item == NULL) {
125 			goto out;
126 		}
127 		string_array[i] = item->str;
128 		while (*(string_array[i]) == ' ') {
129 			string_array[i]++;
130 		}
131 	}
132 
133 #define COPYING 1
134 #define CALCPARAM 2
135 #define OUTPARAM 3
136 
137 	state = COPYING;
138 	while ((buflen > 0) && (textlen > 0)) {
139 		switch (state) {
140 		case COPYING:
141 			switch (*text) {
142 			case '%':
143 				state = CALCPARAM;
144 				text++;
145 				textlen--;
146 				pnum = 0;
147 				break;
148 			default:
149 				*buffer++ = *text++;
150 				buflen--;
151 				textlen--;
152 				(*resultlen)++;
153 				break;
154 			}
155 			break;
156 		case CALCPARAM:
157 			switch (*text) {
158 			case '!':
159 				if (pnum <= tokcount) {
160 					paramp = string_array[pnum - 1];
161 					state = OUTPARAM;
162 				}
163 				text++;
164 				textlen--;
165 				break;
166 			default:
167 				pdigit = *text++ - '0';
168 				if ((pdigit >= 0) && (pdigit <= 9)) {
169 					pnum *= 10;
170 					pnum += pdigit;
171 				}
172 				textlen--;
173 				break;
174 			}
175 			break;
176 		case OUTPARAM:
177 			switch (*paramp) {
178 			case 0:
179 				state = COPYING;
180 				break;
181 			default:
182 				*buffer++ = *paramp++;
183 				buflen--;
184 				(*resultlen)++;
185 			}
186 			break;
187 		default:
188 			/* unknown state */
189 			goto out;
190 			break;
191 
192 		}
193 	}
194 
195 	rc = TDS_SUCCESS;
196 
197       out:
198 	free(string_array);
199 	for (item = head; item != NULL; item = head) {
200 		head = head->next;
201 		free(item);
202 	}
203 	free(params);
204 
205 	return rc;
206 }
207