1 /*
2  *
3  * CLEX File Manager
4  *
5  * Copyright (C) 2001-2018 Vlado Potisk <vlado_potisk@clex.sk>
6  *
7  * CLEX is free software without warranty of any kind; see the
8  * GNU General Public License as set out in the "COPYING" document
9  * which accompanies the CLEX File Manager package.
10  *
11  * CLEX can be downloaded from http://www.clex.sk
12  *
13  */
14 
15 #include "clexheaders.h"
16 
17 #include <stdarg.h>				/* va_list */
18 #include <stdlib.h>				/* free() */
19 #include <string.h>				/* strlen() */
20 
21 #include "util.h"				/* emalloc() */
22 
23 /*
24  * The USTRING structure (defined in ustring.h) can store a string
25  * of unlimited length. The memory is allocated dynamically.
26  *
27  * - to initialize (to NULL ptr value) before first use:
28  *   - static and global variables are initialized by default,
29  *     but if you prefer explicit initialization:
30  *       static USTRING us = UNULL;
31  *         which is equivalent to
32  *       static USTRING us = { 0, 0 };
33  *   - dynamic variables are initialized this way:
34  *     US_INIT(ustring);
35  * - to re-initialize, e.g. before deallocating dynamic USTRING:
36  *     us_reset();
37  * - to store a string: (us_copy accepts also a null ptr)
38  *     us_copy();
39  *       or
40  *     us_copyn();
41  * - to retrieve a string:
42  *     USTR(us)
43  *       or
44  *     PUSTR(pus)
45  * - to edit stored name:
46  *     a) allocate enough memory with us_setsize() or us_resize()
47  *     b) edit the string starting at USTR() location
48  *
49  * WARNING: US_INIT, USTR, and PUSTR are macros
50  */
51 
52 /* these are tunable parameters */
53 /* ALLOC_UNIT in ustring.h */
54 #define MINIMUM_FREE		(4 * ALLOC_UNIT)
55 
56 /*
57  * SHOULD_CHANGE_ALLOC() is true if
58  *  1) we need more memory, or
59  *  2) we can free considerable amount of memory (MINIMUM_FREE)
60  */
61 #define SHOULD_CHANGE_ALLOC(RQ)	\
62 	(pustr->USalloc < RQ || pustr->USalloc >= RQ + MINIMUM_FREE)
63 
64 /* memory is allocated in chunks to prevent excessive resizing */
65 #define ROUND_ALLOC(RQ)	\
66 	((1 + (RQ - 1) / ALLOC_UNIT) * ALLOC_UNIT)
67 /* note that ROUND_ALLOC(0) is ALLOC_UNIT and not 0 */
68 
69 /* clear the data and free the memory */
70 void
us_reset(USTRING * pustr)71 us_reset(USTRING *pustr)
72 {
73 	if (pustr->USalloc) {
74 		free(pustr->USstr);
75 		pustr->USalloc = 0;
76 	}
77 	pustr->USstr = 0;
78 }
79 
80 /* wchar version */
81 void
usw_reset(USTRINGW * pustr)82 usw_reset(USTRINGW *pustr)
83 {
84 	if (pustr->USalloc) {
85 		free(pustr->USstr);
86 		pustr->USalloc = 0;
87 	}
88 	pustr->USstr = 0;
89 }
90 
91 /* us_setsize() makes room for at least 'req' characters */
92 size_t
us_setsize(USTRING * pustr,size_t req)93 us_setsize(USTRING *pustr, size_t req)
94 {
95 	if (SHOULD_CHANGE_ALLOC(req)) {
96 		if (pustr->USalloc)
97 			free(pustr->USstr);
98 		pustr->USalloc = ROUND_ALLOC(req);
99 		pustr->USstr = emalloc(pustr->USalloc);
100 	}
101 
102 	return pustr->USalloc;	/* real buffer size is returned */
103 }
104 
105 /* wchar version; note that 'req' is in characters, not bytes */
106 size_t
usw_setsize(USTRINGW * pustr,size_t req)107 usw_setsize(USTRINGW *pustr, size_t req)
108 {
109 	if (SHOULD_CHANGE_ALLOC(req)) {
110 		if (pustr->USalloc)
111 			free(pustr->USstr);
112 		pustr->USalloc = ROUND_ALLOC(req);
113 		pustr->USstr = emalloc(sizeof(wchar_t) * pustr->USalloc);
114 	}
115 
116 	return pustr->USalloc;
117 }
118 
119 /* like us_setsize(), but preserving contents */
120 size_t
us_resize(USTRING * pustr,size_t req)121 us_resize(USTRING *pustr, size_t req)
122 {
123 	if (SHOULD_CHANGE_ALLOC(req)) {
124 		pustr->USalloc = ROUND_ALLOC(req);
125 		pustr->USstr = erealloc(pustr->USstr,pustr->USalloc);
126 	}
127 
128 	return pustr->USalloc;
129 }
130 
131 
132 /* wchar version */
133 size_t
usw_resize(USTRINGW * pustr,size_t req)134 usw_resize(USTRINGW *pustr, size_t req)
135 {
136 	if (SHOULD_CHANGE_ALLOC(req)) {
137 		pustr->USalloc = ROUND_ALLOC(req);
138 		pustr->USstr = erealloc(pustr->USstr,sizeof(wchar_t) * pustr->USalloc);
139 	}
140 
141 	return pustr->USalloc;
142 }
143 
144 /* quick alternative to copy */
145 void
us_xchg(USTRING * s1,USTRING * s2)146 us_xchg(USTRING *s1, USTRING *s2)
147 {
148 	char *xstr;
149 	size_t xalloc;
150 
151 	xstr      = s1->USstr;
152 	s1->USstr = s2->USstr;
153 	s2->USstr = xstr;
154 
155 	xalloc      = s1->USalloc;
156 	s1->USalloc = s2->USalloc;
157 	s2->USalloc = xalloc;
158 }
159 
160 /* wchar version */
161 void
usw_xchg(USTRINGW * s1,USTRINGW * s2)162 usw_xchg(USTRINGW *s1, USTRINGW *s2)
163 {
164 	wchar_t *xstr;
165 	size_t xalloc;
166 
167 	xstr      = s1->USstr;
168 	s1->USstr = s2->USstr;
169 	s2->USstr = xstr;
170 
171 	xalloc      = s1->USalloc;
172 	s1->USalloc = s2->USalloc;
173 	s2->USalloc = xalloc;
174 }
175 
176 char *
us_copy(USTRING * pustr,const char * src)177 us_copy(USTRING *pustr, const char *src)
178 {
179 	if (src == 0) {
180 		us_reset(pustr);
181 		return 0;
182 	}
183 	us_setsize(pustr,strlen(src) + 1);
184 	strcpy(pustr->USstr,src);
185 	return pustr->USstr;
186 }
187 
188 /* wchar version */
189 wchar_t *
usw_copy(USTRINGW * pustr,const wchar_t * src)190 usw_copy(USTRINGW *pustr, const wchar_t *src)
191 {
192 	if (src == 0) {
193 		usw_reset(pustr);
194 		return 0;
195 	}
196 	usw_setsize(pustr,wcslen(src) + 1);
197 	wcscpy(pustr->USstr,src);
198 	return pustr->USstr;
199 }
200 
201 /* note: us_copyn() adds terminating null byte */
202 char *
us_copyn(USTRING * pustr,const char * src,size_t len)203 us_copyn(USTRING *pustr, const char *src, size_t len)
204 {
205 	char *dst;
206 
207 	us_setsize(pustr,len + 1);
208 	dst = pustr->USstr;
209 	dst[len] = '\0';
210 	while (len-- > 0)
211 		dst[len] = src[len];
212 	return dst;
213 }
214 
215 /* wchar version */
216 wchar_t *
usw_copyn(USTRINGW * pustr,const wchar_t * src,size_t len)217 usw_copyn(USTRINGW *pustr, const wchar_t *src, size_t len)
218 {
219 	wchar_t *dst;
220 
221 	usw_setsize(pustr,len + 1);
222 	dst = pustr->USstr;
223 	dst[len] = L'\0';
224 	while (len-- > 0)
225 		dst[len] = src[len];
226 	return dst;
227 }
228 
229 /* concatenation: us_cat(&ustring, str1, str2, ..., strN, (char *)0); */
230 void
us_cat(USTRING * pustr,...)231 us_cat(USTRING *pustr, ...)
232 {
233 	size_t len;
234 	char *str;
235 	va_list argptr;
236 
237 	va_start(argptr,pustr);
238 	for (len = 1; (str = va_arg(argptr, char *)); )
239 		len += strlen(str);
240 	va_end(argptr);
241 	us_setsize(pustr,len);
242 
243 	va_start(argptr,pustr);
244 	for (len = 0; (str = va_arg(argptr, char *)); ) {
245 		strcpy(pustr->USstr + len,str);
246 		len += strlen(str);
247 	}
248 	va_end(argptr);
249 }
250 
251 /* wchar version */
252 void
usw_cat(USTRINGW * pustr,...)253 usw_cat(USTRINGW *pustr, ...)
254 {
255 	size_t len;
256 	wchar_t *str;
257 	va_list argptr;
258 
259 	va_start(argptr,pustr);
260 	for (len = 1; (str = va_arg(argptr, wchar_t *)); )
261 		len += wcslen(str);
262 	va_end(argptr);
263 	usw_setsize(pustr,len);
264 
265 	va_start(argptr,pustr);
266 	for (len = 0; (str = va_arg(argptr, wchar_t *)); ) {
267 		wcscpy(pustr->USstr + len,str);
268 		len += wcslen(str);
269 	}
270 	va_end(argptr);
271 }
272