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