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(¶ms, 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