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