1 /*
2  * PROPRIETARY INFORMATION.  This software is proprietary to POWDER
3  * Development, and is not to be reproduced, transmitted, or disclosed
4  * in any way without written permission.
5  *
6  * Produced by:	Jeff Lait
7  *
8  *      	POWDER Development
9  *
10  * NAME:        buf.cpp ( POWDER Library, C++ )
11  *
12  * COMMENTS:
13  */
14 
15 #include "assert.h"
16 #include "buf.h"
17 #include <string.h>
18 #include <stdio.h>
19 #include <ctype.h>
20 
21 int		glbBufferCount = 0;
22 
23 int
buf_numbufs()24 buf_numbufs()
25 {
26     return glbBufferCount;
27 }
28 
29 class BUF_int
30 {
31 public:
32     BUF_int();
33     // Takes ownership of the given text, will delete[] it.
34     BUF_int(char *text, int len);
35     // Does not take ownership of the text
36     BUF_int(const char *text);
37 
38     void	 incref();
39     void	 decref();
refcnt()40     int		 refcnt() { return myRefCount; }
41 
42     // Read methods works for shared objects
buffer() const43     const char	*buffer() const { return myData; }
datalen()44     int		 datalen() { return myDataLen; }
strlen()45     int		 strlen() { return ::strlen(myData); }
46 
47     // Write methods require you have a uniqued buffer.
48     void	 resize(int newlen);
data()49     char	*data() { return myData; }
50 
51 private:
52     ~BUF_int();
53 
54 private:
55     int		 myRefCount;
56     char	*myData;
57     // Allocated bytes for data.  -1 means we don't own it.
58     int		 myDataLen;
59 };
60 
BUF_int()61 BUF_int::BUF_int()
62 {
63     myData = (char *) "";
64     myDataLen = -1;
65     myRefCount = 0;
66 
67     glbBufferCount++;
68 }
69 
BUF_int(char * text,int len)70 BUF_int::BUF_int(char *text, int len)
71 {
72     myData = text;
73     myDataLen = len;
74     myRefCount = 0;
75 
76     glbBufferCount++;
77 }
78 
BUF_int(const char * text)79 BUF_int::BUF_int(const char *text)
80 {
81     myData = (char *) text;
82     myDataLen = -1;
83     myRefCount = 0;
84 
85     glbBufferCount++;
86 }
87 
~BUF_int()88 BUF_int::~BUF_int()
89 {
90     UT_ASSERT(myRefCount <= 0);
91     if (myDataLen >= 0)
92 	delete [] myData;
93 
94     glbBufferCount--;
95 }
96 
97 void
incref()98 BUF_int::incref()
99 {
100     myRefCount++;
101 }
102 
103 void
decref()104 BUF_int::decref()
105 {
106     myRefCount--;
107     if (myRefCount <= 0)
108 	delete this;
109 }
110 
111 void
resize(int newlen)112 BUF_int::resize(int newlen)
113 {
114     UT_ASSERT(myRefCount <= 1);
115 
116     char		*text;
117     int			 slen;
118 
119     // This is an inplace resize so we can never reduce!
120     slen = strlen() + 1;
121     if (slen > newlen)
122 	newlen = slen;
123 
124     // If this is a downsize, ignore it.
125     if (newlen < myDataLen)
126 	return;
127 
128     text = new char[newlen];
129     ::strcpy(text, myData);
130 
131     delete [] myData;
132     myData = text;
133     myDataLen = newlen;
134 }
135 
136 ///
137 /// BUF methods
138 ///
139 
BUF()140 BUF::BUF()
141 {
142     myBuffer = 0;
143 }
144 
BUF(int len)145 BUF::BUF(int len)
146 {
147     myBuffer = 0;
148 
149     allocate(len);
150 }
151 
~BUF()152 BUF::~BUF()
153 {
154     if (myBuffer)
155 	myBuffer->decref();
156 }
157 
BUF(const BUF & buf)158 BUF::BUF(const BUF &buf)
159 {
160     myBuffer = 0;
161     *this = buf;
162 }
163 
164 BUF &
operator =(const BUF & buf)165 BUF::operator=(const BUF &buf)
166 {
167     if (buf.myBuffer)
168 	buf.myBuffer->incref();
169     if (myBuffer)
170 	myBuffer->decref();
171     myBuffer = buf.myBuffer;
172     return *this;
173 }
174 
175 const char *
buffer() const176 BUF::buffer() const
177 {
178     if (!myBuffer)
179 	return 0;
180 
181     return myBuffer->buffer();
182 }
183 
184 void
steal(char * text)185 BUF::steal(char *text)
186 {
187     if (myBuffer)
188 	myBuffer->decref();
189 
190     if (!text)
191 	myBuffer = 0;
192     else
193     {
194 	myBuffer = new BUF_int(text, ::strlen(text)+1);
195 	myBuffer->incref();
196     }
197 }
198 
199 void
reference(const char * text)200 BUF::reference(const char *text)
201 {
202     if (myBuffer)
203 	myBuffer->decref();
204 
205     if (!text)
206 	myBuffer = 0;
207     else
208     {
209 	myBuffer = new BUF_int(text);
210 	myBuffer->incref();
211     }
212 }
213 
214 void
strcpy(const char * src)215 BUF::strcpy(const char *src)
216 {
217     if (myBuffer)
218 	myBuffer->decref();
219 
220     myBuffer = 0;
221 
222     if (src)
223     {
224 	char	*text = new char [::strlen(src)+1];
225 	::strcpy(text, src);
226 	steal(text);
227     }
228 }
229 
230 int
strlen() const231 BUF::strlen() const
232 {
233     if (!myBuffer)
234 	return 0;
235 
236     return myBuffer->strlen();
237 }
238 
239 int
strcmp(const char * cmp) const240 BUF::strcmp(const char *cmp) const
241 {
242     if (!myBuffer)
243     {
244 	if (!cmp)
245 	    return 0;
246 	return -1;
247     }
248 
249     return ::strcmp(buffer(), cmp);
250 }
251 
252 void
strcat(const char * src)253 BUF::strcat(const char *src)
254 {
255     // Trivial strcat.
256     if (!src)
257 	return;
258 
259     if (!myBuffer)
260     {
261 	// Equivalent to strcpy
262 	strcpy(src);
263 	return;
264     }
265 
266     // Find our total length
267     int		srclen, mylen;
268 
269     uniquify();
270 
271     mylen = strlen();
272     srclen = ::strlen(src);
273     mylen += srclen + 1;
274 
275     myBuffer->resize(mylen);
276     // Now safe...
277     ::strcat(myBuffer->data(), src);
278 }
279 
280 void
append(char c)281 BUF::append(char c)
282 {
283     char	s[2];
284 
285     s[0] = c;
286     s[1] = '\0';
287     strcat(s);
288 }
289 
290 void
clear()291 BUF::clear()
292 {
293     uniquify();
294     evildata()[0] = '\0';
295 }
296 
297 bool
isstring() const298 BUF::isstring() const
299 {
300     if (!myBuffer)
301 	return false;
302 
303     if (buffer()[0])
304 	return true;
305 
306     return false;
307 }
308 
309 char
lastchar(int nthlast) const310 BUF::lastchar(int nthlast) const
311 {
312     if (!myBuffer)
313 	return 0;
314 
315     int		len;
316     len = strlen();
317     len -= nthlast + 1;
318     if (len < 0)
319 	return 0;
320     return buffer()[len];
321 }
322 
323 void
allocate(int len)324 BUF::allocate(int len)
325 {
326     char *text = new char[len];
327 
328     // We always want to be null terminated!
329     *text = 0;
330 
331     if (myBuffer)
332 	myBuffer->decref();
333 
334     myBuffer = new BUF_int(text, len);
335     myBuffer->incref();
336 }
337 
338 int
OURvsnprintf(char * str,size_t size,const char * format,va_list ap)339 OURvsnprintf(char *str, size_t size, const char *format, va_list ap)
340 {
341     int		result;
342 #ifdef WIN32
343     result = _vsnprintf(str, size, format, ap);
344 #else
345     va_list	ap_copy;
346     // Apparently va_list can't be reused on modern compilers.  Same
347     // compilers require support for va_copy which older compilers
348     // lack.  *sigh*
349     va_copy(ap_copy, ap);
350     result = vsnprintf(str, size, format, ap_copy);
351     va_end(ap_copy);
352 #endif
353     return result;
354 }
355 
356 int
vsprintf(const char * fmt,va_list ap)357 BUF::vsprintf(const char *fmt, va_list ap)
358 {
359     int	result;
360     int	cursize;
361 
362     // We wipe ourself out, so is safe to allocate.
363     cursize = 100;
364     while (1)
365     {
366 	// Reallocate with the new (larger?) size.
367 	allocate(cursize);
368 
369 	// Marker so we can tell if we overflowed vs having a bad
370 	// format tag
371 	myBuffer->data()[cursize-1] = '\0';
372 	result = OURvsnprintf(myBuffer->data(), cursize, fmt, ap);
373 	if (result < 0)
374 	{
375 	    // Check if the null is still there, signifying something
376 	    // went wrong with formatting.
377 	    if (myBuffer->data()[cursize-1] == '\0')
378 	    {
379 		// Treat this as the final buffer.
380 		result = strlen();
381 		break;
382 	    }
383 	}
384 
385 	// We really must have a final null, thus this paranoia...
386 	if (result < 0 || result > cursize-1)
387 	{
388 	    cursize *= 2;
389 	}
390 	else
391 	{
392 	    // Success!
393 	    break;
394 	}
395     }
396     return result;
397 }
398 
399 int
sprintf(const char * fmt,...)400 BUF::sprintf(const char *fmt, ...)
401 {
402     va_list	marker;
403     int		result;
404 
405     va_start(marker, fmt);
406     result = vsprintf(fmt, marker);
407     va_end(marker);
408 
409     return result;
410 }
411 
412 int
appendSprintf(const char * fmt,...)413 BUF::appendSprintf(const char *fmt, ...)
414 {
415     BUF		newtext;
416     va_list	marker;
417     int		result;
418 
419     va_start(marker, fmt);
420     result = newtext.vsprintf(fmt, marker);
421     va_end(marker);
422 
423     strcat(newtext);
424 
425     return result;
426 }
427 
428 char *
strdup() const429 BUF::strdup() const
430 {
431     if (!myBuffer)
432 	return ::strdup("");
433 
434     return ::strdup(buffer());
435 }
436 
437 char *
evildata()438 BUF::evildata()
439 {
440     UT_ASSERT(myBuffer);
441     UT_ASSERT(myBuffer->refcnt() <= 1);
442     return (char *) buffer();
443 }
444 
445 void
uniquify()446 BUF::uniquify()
447 {
448     if (!myBuffer)
449     {
450 	// We want a writeable buffer
451 	allocate(50);
452     }
453     else
454     {
455 	// If ref count is 1 or 0, we are only reference, so good.
456 	// UNLESS the current dude is a read only buffer.
457 	if (myBuffer->refcnt() <= 1 && myBuffer->datalen() >= 0)
458 	{
459 	    // Already writeable.
460 	    return;
461 	}
462 	// Copy the current buffer.
463 	int		len;
464 	char		*text;
465 	if (myBuffer->datalen() >= 0)
466 	    len = myBuffer->datalen();
467 	else
468 	    len = myBuffer->strlen() + 1;
469 	text = new char[len];
470 	::strcpy(text, myBuffer->buffer());
471 
472 	// Throw away this buffer and make a new one.
473 	myBuffer->decref();
474 	myBuffer = new BUF_int(text, len);
475 	myBuffer->incref();
476     }
477 }
478 
479 void
makeFileSafe()480 BUF::makeFileSafe()
481 {
482     uniquify();
483 
484     // Strip out evil characters
485     unsigned char	*c;
486     for (c = (unsigned char *)evildata(); *c; c++)
487     {
488 	if (!isalnum(*c) && *c != '.')
489 	    *c = '_';
490     }
491 }
492