1 /*-------
2 * Module: misc.c
3 *
4 * Description: This module contains miscellaneous routines
5 * such as for debugging/logging and string functions.
6 *
7 * Classes: n/a
8 *
9 * API functions: none
10 *
11 * Comments: See "readme.txt" for copyright and license information.
12 *-------
13 */
14
15 #include "psqlodbc.h"
16 #include "misc.h"
17
18 #include <stdio.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <time.h>
23
24 #ifndef WIN32
25 #include <pwd.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #else
29 #include <process.h> /* Byron: is this where Windows keeps def.
30 * of getpid ? */
31 #endif
32
33 /*
34 * returns STRCPY_FAIL, STRCPY_TRUNCATED, or #bytes copied
35 * (not including null term)
36 */
37 ssize_t
my_strcpy(char * dst,ssize_t dst_len,const char * src,ssize_t src_len)38 my_strcpy(char *dst, ssize_t dst_len, const char *src, ssize_t src_len)
39 {
40 if (dst_len <= 0)
41 return STRCPY_FAIL;
42
43 if (src_len == SQL_NULL_DATA)
44 {
45 dst[0] = '\0';
46 return STRCPY_NULL;
47 }
48 else if (src_len == SQL_NTS)
49 src_len = strlen(src);
50
51 if (src_len <= 0)
52 return STRCPY_FAIL;
53 else
54 {
55 if (src_len < dst_len)
56 {
57 memcpy(dst, src, src_len);
58 dst[src_len] = '\0';
59 }
60 else
61 {
62 memcpy(dst, src, dst_len - 1);
63 dst[dst_len - 1] = '\0'; /* truncated */
64 return STRCPY_TRUNCATED;
65 }
66 }
67
68 return strlen(dst);
69 }
70
71
72 /*
73 * strncpy copies up to len characters, and doesn't terminate
74 * the destination string if src has len characters or more.
75 * instead, I want it to copy up to len-1 characters and always
76 * terminate the destination string.
77 */
78 size_t
strncpy_null(char * dst,const char * src,ssize_t len)79 strncpy_null(char *dst, const char *src, ssize_t len)
80 {
81 int i;
82
83 if (NULL != dst && len > 0)
84 {
85 for (i = 0; src[i] && i < len - 1; i++)
86 dst[i] = src[i];
87
88 dst[i] = '\0';
89 }
90 else
91 return 0;
92 if (src[i])
93 return strlen(src);
94 return i;
95 }
96
97
98 /*------
99 * Create a null terminated string (handling the SQL_NTS thing):
100 * 1. If buf is supplied, place the string in there
101 * (assumes enough space) and return buf.
102 * 2. If buf is not supplied, malloc space and return this string
103 *------
104 */
105 char *
make_string(const SQLCHAR * s,SQLINTEGER len,char * buf,size_t bufsize)106 make_string(const SQLCHAR *s, SQLINTEGER len, char *buf, size_t bufsize)
107 {
108 size_t length;
109 char *str;
110
111 if (!s || SQL_NULL_DATA == len)
112 return NULL;
113 if (len >= 0)
114 length =len;
115 else if (SQL_NTS == len)
116 length = strlen((char *) s);
117 else
118 {
119 MYLOG(0, "invalid length=" FORMAT_INTEGER "\n", len);
120 return NULL;
121 }
122 if (buf)
123 {
124 strncpy_null(buf, (char *) s, bufsize > length ? length + 1 : bufsize);
125 return buf;
126 }
127
128 MYLOG(DETAIL_LOG_LEVEL, "malloc size=" FORMAT_SIZE_T "\n", length);
129 str = malloc(length + 1);
130 MYLOG(DETAIL_LOG_LEVEL, "str=%p\n", str);
131 if (!str)
132 return NULL;
133
134 strncpy_null(str, (char *) s, length + 1);
135 return str;
136 }
137
138 char *
my_trim(char * s)139 my_trim(char *s)
140 {
141 char *last;
142
143 for (last = s + strlen(s) - 1; last >= s; last--)
144 {
145 if (*last == ' ')
146 *last = '\0';
147 else
148 break;
149 }
150
151 return s;
152 }
153
154 /*
155 * snprintfcat is a extension to snprintf
156 * It add format to buf at given pos
157 */
158 #ifdef POSIX_SNPRINTF_REQUIRED
159 static posix_vsnprintf(char *str, size_t size, const char *format, va_list ap);
160 #define vsnprintf posix_vsnprintf
161 #endif /* POSIX_SNPRINTF_REQUIRED */
162
163 int
snprintfcat(char * buf,size_t size,const char * format,...)164 snprintfcat(char *buf, size_t size, const char *format, ...)
165 {
166 int len;
167 size_t pos = strlen(buf);
168 va_list arglist;
169
170 va_start(arglist, format);
171 len = vsnprintf(buf + pos, size - pos, format, arglist);
172 va_end(arglist);
173 return len + pos;
174 }
175
176 /*
177 * snprintf_len is a extension to snprintf
178 * It returns strlen of buf every time (not -1 when truncated)
179 */
180
181 size_t
snprintf_len(char * buf,size_t size,const char * format,...)182 snprintf_len(char *buf, size_t size, const char *format, ...)
183 {
184 ssize_t len;
185 va_list arglist;
186
187 va_start(arglist, format);
188 if ((len = vsnprintf(buf, size, format, arglist)) < 0)
189 len = size;
190 va_end(arglist);
191 return len;
192 }
193
194 /*
195 * Windows doesn't have snprintf(). It has _snprintf() which is similar,
196 * but it behaves differently wrt. truncation. This is a compatibility
197 * function that uses _snprintf() to provide POSIX snprintf() behavior.
198 *
199 * Our strategy, if the output doesn't fit, is to create a temporary buffer
200 * and call _snprintf() on that. If it still doesn't fit, enlarge the buffer
201 * and repeat.
202 */
203 #ifdef POSIX_SNPRINTF_REQUIRED
204 static int
posix_vsnprintf(char * str,size_t size,const char * format,va_list ap)205 posix_vsnprintf(char *str, size_t size, const char *format, va_list ap)
206 {
207 int len;
208 char *tmp;
209 size_t newsize;
210
211 len = _vsnprintf(str, size, format, ap);
212 if (len < 0)
213 {
214 if (size == 0)
215 newsize = 100;
216 else
217 newsize = size;
218 do
219 {
220 newsize *= 2;
221 tmp = malloc(newsize);
222 if (!tmp)
223 return -1;
224 len = _vsnprintf(tmp, newsize, format, ap);
225 if (len >= 0)
226 memcpy(str, tmp, size);
227 free(tmp);
228 } while (len < 0);
229 }
230 if (len >= size && size > 0)
231 {
232 /* Ensure the buffer is NULL-terminated */
233 str[size - 1] = '\0';
234 }
235 return len;
236 }
237
238 int
posix_snprintf(char * buf,size_t size,const char * format,...)239 posix_snprintf(char *buf, size_t size, const char *format, ...)
240 {
241 int len;
242 va_list arglist;
243
244 va_start(arglist, format);
245 len = posix_vsnprintf(buf, size, format, arglist);
246 va_end(arglist);
247 return len;
248 }
249 #endif /* POSIX_SNPRINTF_REQUIRED */
250
251 #ifndef HAVE_STRLCAT
252 size_t
strlcat(char * dst,const char * src,size_t size)253 strlcat(char *dst, const char *src, size_t size)
254 {
255 size_t ttllen;
256 char *pd = dst;
257 const char *ps= src;
258
259 for (ttllen = 0; ttllen < size; ttllen++, pd++)
260 {
261 if (0 == *pd)
262 break;
263 }
264 if (ttllen >= size - 1)
265 return ttllen + strlen(src);
266 for (; ttllen < size - 1; ttllen++, pd++, ps++)
267 {
268 if (0 == (*pd = *ps))
269 return ttllen;
270 }
271 *pd = 0;
272 for (; *ps; ttllen++, ps++)
273 ;
274 return ttllen;
275 }
276 #endif /* HAVE_STRLCAT */
277
278
279 /*
280 * Proprly quote and escape a possibly schema-qualified table name.
281 */
282 char *
quote_table(const pgNAME schema,const pgNAME table,char * buf,int buf_size)283 quote_table(const pgNAME schema, const pgNAME table, char *buf, int buf_size)
284 {
285 const char *ptr;
286 int i;
287
288 i = 0;
289
290 if (NAME_IS_VALID(schema))
291 {
292 buf[i++] = '"';
293 for (ptr = SAFE_NAME(schema); *ptr != '\0' && i < buf_size - 6; ptr++)
294 {
295 buf[i++] = *ptr;
296 if (*ptr == '"')
297 buf[i++] = '"'; /* escape quotes by doubling them */
298 }
299 buf[i++] = '"';
300 buf[i++] = '.';
301 }
302
303 buf[i++] = '"';
304 for (ptr = SAFE_NAME(table); *ptr != '\0' && i < buf_size - 3; ptr++)
305 {
306 buf[i++] = *ptr;
307 if (*ptr == '"')
308 buf[i++] = '"'; /* escape quotes by doubling them */
309 }
310 buf[i++] = '"';
311 buf[i] = '\0';
312
313 return buf;
314 }
315