]> git.sur5r.net Git - cc65/blob - src/common/strbuf.c
320e912932ac86ca1c9a15979a522a22e38cc98e
[cc65] / src / common / strbuf.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 strbuf.c                                  */
4 /*                                                                           */
5 /*                       Variable sized string buffers                       */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001-2008 Ullrich von Bassewitz                                       */
10 /*               Roemerstrasse 52                                            */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37 #include <ctype.h>
38
39 /* common */
40 #include "chartype.h"
41 #include "strbuf.h"
42 #include "va_copy.h"
43 #include "xmalloc.h"
44 #include "xsprintf.h"
45
46
47
48 /*****************************************************************************/
49 /*                                   Data                                    */
50 /*****************************************************************************/
51
52
53
54 /* An empty string buf */
55 const StrBuf EmptyStrBuf = STATIC_STRBUF_INITIALIZER;
56
57
58
59 /*****************************************************************************/
60 /*                                   Code                                    */
61 /*****************************************************************************/
62
63
64
65 #if !defined(HAVE_INLINE)
66 StrBuf* SB_Init (StrBuf* B)
67 /* Initialize a string buffer */
68 {
69     *B = EmptyStrBuf;
70     return B;
71 }
72 #endif
73
74
75
76 StrBuf* SB_InitFromString (StrBuf* B, const char* S)
77 /* Initialize a string buffer from a literal string. Beware: The buffer won't
78  * store a copy but a pointer to the actual string.
79  */
80 {
81     B->Allocated = 0;
82     B->Len       = strlen (S);
83     B->Index     = 0;
84     B->Buf       = (char*) S;
85     return B;
86 }
87
88
89
90 void SB_Done (StrBuf* B)
91 /* Free the data of a string buffer (but not the struct itself) */
92 {
93     if (B->Allocated) {
94         xfree (B->Buf);
95     }
96 }
97
98
99
100 StrBuf* NewStrBuf (void)
101 /* Allocate, initialize and return a new StrBuf */
102 {
103     /* Allocate a new string buffer */
104     StrBuf* B = xmalloc (sizeof (StrBuf));
105
106     /* Initialize the struct... */
107     SB_Init (B);
108
109     /* ...and return it */
110     return B;
111 }
112
113
114
115 void FreeStrBuf (StrBuf* B)
116 /* Free a string buffer */
117 {
118     SB_Done (B);
119     xfree (B);
120 }
121
122
123
124 void SB_Realloc (StrBuf* B, unsigned NewSize)
125 /* Reallocate the string buffer space, make sure at least NewSize bytes are
126  * available.
127  */
128 {
129     /* Get the current size, use a minimum of 8 bytes */
130     unsigned NewAllocated = B->Allocated;
131     if (NewAllocated == 0) {
132         NewAllocated = 8;
133     }
134
135     /* Round up to the next power of two */
136     while (NewAllocated < NewSize) {
137         NewAllocated *= 2;
138     }
139
140     /* Reallocate the buffer. Beware: The allocated size may be zero while the
141      * length is not. This means that we have a buffer that wasn't allocated
142      * on the heap.
143      */
144     if (B->Allocated) {
145         /* Just reallocate the block */
146         B->Buf   = xrealloc (B->Buf, NewAllocated);
147     } else {
148         /* Allocate a new block and copy */
149         B->Buf   = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
150     }
151
152     /* Remember the new block size */
153     B->Allocated = NewAllocated;
154 }
155
156
157
158 static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
159 /* Reallocate the string buffer space, make sure at least NewSize bytes are
160  * available. This function won't copy the old buffer contents over to the new
161  * buffer and may be used if the old contents are overwritten later.
162  */
163 {
164     /* Get the current size, use a minimum of 8 bytes */
165     unsigned NewAllocated = B->Allocated;
166     if (NewAllocated == 0) {
167         NewAllocated = 8;
168     }
169
170     /* Round up to the next power of two */
171     while (NewAllocated < NewSize) {
172         NewAllocated *= 2;
173     }
174
175     /* Free the old buffer if there is one */
176     if (B->Allocated) {
177         xfree (B->Buf);
178     }
179
180     /* Allocate a fresh block */
181     B->Buf = xmalloc (NewAllocated);
182                
183     /* Remember the new block size */
184     B->Allocated = NewAllocated;
185 }
186
187
188
189 #if !defined(HAVE_INLINE)
190 char SB_At (const StrBuf* B, unsigned Index)
191 /* Get a character from the buffer */
192 {
193     PRECONDITION (Index < B->Len);
194     return B->Buf[Index];
195 }
196 #endif
197
198
199
200 void SB_Drop (StrBuf* B, unsigned Count)
201 /* Drop characters from the end of the string. */
202 {
203     PRECONDITION (Count <= B->Len);
204     B->Len -= Count;
205     if (B->Index > B->Len) {
206         B->Index = B->Len;
207     }
208 }
209
210
211
212 void SB_Terminate (StrBuf* B)
213 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
214  * accounted for in B->Len, if you want that, you have to use AppendChar!
215  */
216 {
217     unsigned NewLen = B->Len + 1;
218     if (NewLen > B->Allocated) {
219         SB_Realloc (B, NewLen);
220     }
221     B->Buf[B->Len] = '\0';
222 }
223
224
225
226 void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
227 /* Copy Buf to Target, discarding the old contents of Target */
228 {    
229     if (Size) {
230         if (Target->Allocated < Size) {
231             SB_CheapRealloc (Target, Size);
232         }
233         memcpy (Target->Buf, Buf, Size);
234     }
235     Target->Len = Size;
236 }
237
238
239
240 #if !defined(HAVE_INLINE)
241 void SB_CopyStr (StrBuf* Target, const char* S)
242 /* Copy S to Target, discarding the old contents of Target */
243 {
244     SB_CopyBuf (Target, S, strlen (S));
245 }
246 #endif
247
248
249
250 #if !defined(HAVE_INLINE)
251 void SB_Copy (StrBuf* Target, const StrBuf* Source)
252 /* Copy Source to Target, discarding the old contents of Target */
253 {
254     SB_CopyBuf (Target, Source->Buf, Source->Len);
255     Target->Index = Source->Index;
256 }
257 #endif
258
259
260
261 void SB_AppendChar (StrBuf* B, int C)
262 /* Append a character to a string buffer */
263 {
264     unsigned NewLen = B->Len + 1;
265     if (NewLen > B->Allocated) {
266         SB_Realloc (B, NewLen);
267     }
268     B->Buf[B->Len] = (char) C;
269     B->Len = NewLen;
270 }
271
272
273
274 void SB_AppendBuf (StrBuf* B, const char* S, unsigned Size)
275 /* Append a character buffer to the end of the string buffer */
276 {
277     unsigned NewLen = B->Len + Size;
278     if (NewLen > B->Allocated) {
279         SB_Realloc (B, NewLen);
280     }
281     memcpy (B->Buf + B->Len, S, Size);
282     B->Len = NewLen;
283 }
284
285
286
287 #if !defined(HAVE_INLINE)
288 void SB_AppendStr (StrBuf* B, const char* S)
289 /* Append a string to the end of the string buffer */
290 {
291     SB_AppendBuf (B, S, strlen (S));
292 }
293 #endif
294
295
296
297 #if !defined(HAVE_INLINE)
298 void SB_Append (StrBuf* Target, const StrBuf* Source)
299 /* Append the contents of Source to Target */
300 {
301     SB_AppendBuf (Target, Source->Buf, Source->Len);
302 }
303 #endif
304
305
306
307 #if !defined(HAVE_INLINE)
308 void SB_Cut (StrBuf* B, unsigned Len)
309 /* Cut the contents of B at the given length. If the current length of the
310  * buffer is smaller than Len, nothing will happen.
311  */
312 {
313     if (Len < B->Len) {
314         B->Len = Len;
315     }
316 }
317 #endif
318
319
320
321 void SB_Slice (StrBuf* Target, const StrBuf* Source, unsigned Start, unsigned Len)
322 /* Copy a slice from Source into Target. The current contents of Target are
323  * destroyed. If Start is greater than the length of Source, or if Len
324  * characters aren't available, the result will be a buffer with less than Len
325  * bytes.
326  */
327 {
328     /* Calculate the length of the resulting buffer */
329     if (Start >= Source->Len) {
330         /* Target will be empty */
331         SB_Clear (Target);
332         return;
333     }
334     if (Start + Len > Source->Len) {
335         Len = Source->Len - Start;
336     }
337
338     /* Make sure we have enough room in the target string buffer */
339     if (Len > Target->Allocated) {
340         SB_Realloc (Target, Len);
341     }
342
343     /* Copy the slice */
344     memcpy (Target->Buf, Source->Buf + Start, Len);
345     Target->Len = Len;
346 }
347
348
349
350 void SB_Move (StrBuf* Target, StrBuf* Source)
351 /* Move the complete contents of Source to target. This will delete the old
352  * contents of Target, and Source will be empty after the call.
353  */
354 {
355     /* Free the target string */
356     if (Target->Allocated) {
357         xfree (Target->Buf);
358     }
359
360     /* Move all data from Source to Target */
361     *Target = *Source;
362
363     /* Clear Source */
364     SB_Init (Source);
365 }
366
367
368
369 void SB_ToLower (StrBuf* S)
370 /* Convert all characters in S to lower case */
371 {
372     unsigned I;
373     char* B = S->Buf;
374     for (I = 0; I < S->Len; ++I, ++B) {
375         if (IsUpper (*B)) {
376             *B = tolower (*B);
377         }
378     }
379 }
380
381
382
383 void SB_ToUpper (StrBuf* S)
384 /* Convert all characters in S to upper case */
385 {
386     unsigned I;
387     char* B = S->Buf;
388     for (I = 0; I < S->Len; ++I, ++B) {
389         if (IsLower (*B)) {
390             *B = toupper (*B);
391         }
392     }
393 }
394
395
396
397 int SB_Compare (const StrBuf* S1, const StrBuf* S2)
398 /* Do a lexical compare of S1 and S2. See strcmp for result codes. */
399 {
400     int Result;
401     if (S1->Len < S2->Len) {
402         Result = memcmp (S1->Buf, S2->Buf, S1->Len);
403         if (Result == 0) {
404             /* S1 considered lesser because it's shorter */
405             Result = -1;
406         }
407     } else if (S1->Len > S2->Len) {
408         Result = memcmp (S1->Buf, S2->Buf, S2->Len);
409         if (Result == 0) {
410             /* S2 considered lesser because it's shorter */
411             Result = 1;
412         }
413     } else {
414         Result = memcmp (S1->Buf, S2->Buf, S1->Len);
415     }
416     return Result;
417 }
418
419
420
421 int SB_CompareStr (const StrBuf* S1, const char* S2)
422 /* Do a lexical compare of S1 and S2. See strcmp for result codes. */
423 {
424     int Result;
425     unsigned S2Len = strlen (S2);
426     if (S1->Len < S2Len) {
427         Result = memcmp (S1->Buf, S2, S1->Len);
428         if (Result == 0) {
429             /* S1 considered lesser because it's shorter */
430             Result = -1;
431         }
432     } else if (S1->Len > S2Len) {
433         Result = memcmp (S1->Buf, S2, S2Len);
434         if (Result == 0) {
435             /* S2 considered lesser because it's shorter */
436             Result = 1;
437         }
438     } else {
439         Result = memcmp (S1->Buf, S2, S1->Len);
440     }
441     return Result;
442 }
443
444
445
446 void SB_VPrintf (StrBuf* S, const char* Format, va_list ap)
447 /* printf function with S as target. The function is safe, which means that
448  * the current contents of S are discarded, and are allocated again with
449  * a matching size for the output. The function will call FAIL when problems
450  * are detected (anything that let xsnprintf return -1).
451  */
452 {
453     va_list tmp;
454     int SizeNeeded;
455
456     /* Since we must determine the space needed anyway, we will try with
457      * the currently allocated memory. If the call succeeds, we've saved
458      * an allocation. If not, we have to reallocate and try again.
459      */
460     va_copy (tmp, ap);
461     SizeNeeded = xvsnprintf (S->Buf, S->Allocated, Format, tmp);
462     va_end (tmp);
463
464     /* Check the result, the xvsnprintf function should not fail */
465     CHECK (SizeNeeded >= 0);
466
467     /* Check if we must reallocate */
468     if ((unsigned) SizeNeeded >= S->Allocated) {
469         /* Must retry. Use CheapRealloc to avoid copying */
470         SB_CheapRealloc (S, SizeNeeded + 1);    /* Account for '\0' */
471         (void) xvsnprintf (S->Buf, S->Allocated, Format, ap);
472     }
473
474     /* Update string buffer variables */
475     S->Len = SizeNeeded;
476     S->Index = 0;
477 }
478
479
480
481 void SB_Printf (StrBuf* S, const char* Format, ...)
482 /* vprintf function with S as target. The function is safe, which means that
483  * the current contents of S are discarded, and are allocated again with
484  * a matching size for the output. The function will call FAIL when problems
485  * are detected (anything that let xsnprintf return -1).
486  */
487 {
488     va_list ap;
489     va_start (ap, Format);
490     SB_VPrintf (S, Format, ap);
491     va_end (ap);
492 }
493
494
495