1 /* $Id: Str.c,v 1.8 2002/12/24 17:20:46 ukai Exp $ */
2 /*
3 * String manipulation library for Boehm GC
4 *
5 * (C) Copyright 1998-1999 by Akinori Ito
6 *
7 * This software may be redistributed freely for this purpose, in full
8 * or in part, provided that this entire copyright notice is included
9 * on any copies of this software and applications and derivations thereof.
10 *
11 * This software is provided on an "as is" basis, without warranty of any
12 * kind, either expressed or implied, as to any matter including, but not
13 * limited to warranty of fitness of purpose, or merchantability, or
14 * results obtained from use of this software.
15 */
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <gc.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #ifdef __EMX__ /* or include "fm.h" for HAVE_BCOPY? */
22 #include <strings.h>
23 #endif
24 #include "Str.h"
25 #include "myctype.h"
26
27 #define INITIAL_STR_SIZE 32
28
29 #ifdef STR_DEBUG
30 /* This is obsolete, because "Str" can handle a '\0' character now. */
31 #define STR_LENGTH_CHECK(x) if (((x)->ptr==0&&(x)->length!=0)||(strlen((x)->ptr)!=(x)->length))abort();
32 #else /* not STR_DEBUG */
33 #define STR_LENGTH_CHECK(x)
34 #endif /* not STR_DEBUG */
35
36 Str
Strnew()37 Strnew()
38 {
39 Str x = GC_MALLOC(sizeof(struct _Str));
40 if (x == NULL)
41 exit(1);
42 x->ptr = GC_MALLOC_ATOMIC(INITIAL_STR_SIZE);
43 if (x->ptr == NULL)
44 exit(1);
45 x->ptr[0] = '\0';
46 x->area_size = INITIAL_STR_SIZE;
47 x->length = 0;
48 return x;
49 }
50
51 Str
Strnew_size(int n)52 Strnew_size(int n)
53 {
54 Str x = GC_MALLOC(sizeof(struct _Str));
55 if (x == NULL)
56 exit(1);
57 if (n < 0 || n >= STR_SIZE_MAX)
58 n = STR_SIZE_MAX - 1;
59 else if (n + 1 < INITIAL_STR_SIZE)
60 n = INITIAL_STR_SIZE - 1;
61 x->ptr = GC_MALLOC_ATOMIC(n + 1);
62 if (x->ptr == NULL)
63 exit(1);
64 x->ptr[0] = '\0';
65 x->area_size = n + 1;
66 x->length = 0;
67 return x;
68 }
69
70 Str
Strnew_charp(const char * p)71 Strnew_charp(const char *p)
72 {
73 Str x;
74 int n, len;
75
76 if (p == NULL)
77 return Strnew();
78 x = GC_MALLOC(sizeof(struct _Str));
79 if (x == NULL)
80 exit(1);
81 n = strlen(p) + 1;
82 if (n <= 0 || n > STR_SIZE_MAX)
83 n = STR_SIZE_MAX;
84 len = n - 1;
85 if (n < INITIAL_STR_SIZE)
86 n = INITIAL_STR_SIZE;
87 x->ptr = GC_MALLOC_ATOMIC(n);
88 if (x->ptr == NULL)
89 exit(1);
90 x->area_size = n;
91 x->length = len;
92 bcopy((void *)p, (void *)x->ptr, len);
93 x->ptr[x->length] = '\0';
94 return x;
95 }
96
97 Str
Strnew_m_charp(const char * p,...)98 Strnew_m_charp(const char *p, ...)
99 {
100 va_list ap;
101 Str r = Strnew();
102
103 va_start(ap, p);
104 while (p != NULL) {
105 Strcat_charp(r, p);
106 p = va_arg(ap, char *);
107 }
108 return r;
109 }
110
111 Str
Strnew_charp_n(const char * p,int n)112 Strnew_charp_n(const char *p, int n)
113 {
114 Str x;
115 int len;
116
117 if (p == NULL)
118 return Strnew_size(n);
119 x = GC_MALLOC(sizeof(struct _Str));
120 if (x == NULL)
121 exit(1);
122 if (n < 0 || n >= STR_SIZE_MAX)
123 n = STR_SIZE_MAX - 1;
124 len = n;
125 if (n + 1 < INITIAL_STR_SIZE)
126 n = INITIAL_STR_SIZE - 1;
127 x->ptr = GC_MALLOC_ATOMIC(n + 1);
128 if (x->ptr == NULL)
129 exit(1);
130 x->area_size = n + 1;
131 x->length = len;
132 bcopy((void *)p, (void *)x->ptr, len);
133 x->ptr[x->length] = '\0';
134 return x;
135 }
136
137 Str
Strdup(Str s)138 Strdup(Str s)
139 {
140 Str n = Strnew_size(s->length);
141 STR_LENGTH_CHECK(s);
142 Strcopy(n, s);
143 return n;
144 }
145
146 void
Strclear(Str s)147 Strclear(Str s)
148 {
149 s->length = 0;
150 s->ptr[0] = '\0';
151 }
152
153 void
Strfree(Str x)154 Strfree(Str x)
155 {
156 GC_free(x->ptr);
157 GC_free(x);
158 }
159
160 void
Strcopy(Str x,Str y)161 Strcopy(Str x, Str y)
162 {
163 STR_LENGTH_CHECK(x);
164 STR_LENGTH_CHECK(y);
165 if (x->area_size < y->length + 1) {
166 x->ptr = GC_REALLOC(x->ptr, y->length + 1);
167 if (x->ptr == NULL)
168 exit(1);
169 x->area_size = y->length + 1;
170 }
171 bcopy((void *)y->ptr, (void *)x->ptr, y->length + 1);
172 x->length = y->length;
173 }
174
175 void
Strcopy_charp(Str x,const char * y)176 Strcopy_charp(Str x, const char *y)
177 {
178 int len;
179
180 STR_LENGTH_CHECK(x);
181 if (y == NULL) {
182 x->length = 0;
183 x->ptr[0] = '\0';
184 return;
185 }
186 len = strlen(y);
187 if (len < 0 || len >= STR_SIZE_MAX)
188 len = STR_SIZE_MAX - 1;
189 if (x->area_size < len + 1) {
190 x->ptr = GC_REALLOC(x->ptr, len + 1);
191 if (x->ptr == NULL)
192 exit(1);
193 x->area_size = len + 1;
194 }
195 bcopy((void *)y, (void *)x->ptr, len);
196 x->ptr[len] = '\0';
197 x->length = len;
198 }
199
200 void
Strcopy_charp_n(Str x,const char * y,int n)201 Strcopy_charp_n(Str x, const char *y, int n)
202 {
203 int len = n;
204
205 STR_LENGTH_CHECK(x);
206 if (y == NULL) {
207 x->length = 0;
208 x->ptr[0] = '\0';
209 return;
210 }
211 if (len < 0 || len >= STR_SIZE_MAX)
212 len = STR_SIZE_MAX - 1;
213 if (x->area_size < len + 1) {
214 x->ptr = GC_REALLOC(x->ptr, len + 1);
215 if (x->ptr == NULL)
216 exit(1);
217 x->area_size = len + 1;
218 }
219 bcopy((void *)y, (void *)x->ptr, len);
220 x->ptr[len] = '\0';
221 x->length = len;
222 }
223
224 void
Strcat_charp_n(Str x,const char * y,int n)225 Strcat_charp_n(Str x, const char *y, int n)
226 {
227 int newlen;
228
229 STR_LENGTH_CHECK(x);
230 if (y == NULL || n == 0)
231 return;
232 if (n < 0)
233 n = STR_SIZE_MAX - 1;
234 newlen = x->length + n + 1;
235 if (newlen <= 0 || newlen > STR_SIZE_MAX) {
236 newlen = STR_SIZE_MAX;
237 n = newlen - x->length - 1;
238 if (n <= 0)
239 return;
240 }
241 if (x->area_size < newlen) {
242 newlen += newlen / 2;
243 if (newlen <= 0 || newlen > STR_SIZE_MAX)
244 newlen = STR_SIZE_MAX;
245 x->ptr = GC_REALLOC(x->ptr, newlen);
246 if (x->ptr == NULL)
247 exit(1);
248 x->area_size = newlen;
249 }
250 bcopy((void *)y, (void *)&x->ptr[x->length], n);
251 x->length += n;
252 x->ptr[x->length] = '\0';
253 }
254
255 void
Strcat(Str x,Str y)256 Strcat(Str x, Str y)
257 {
258 STR_LENGTH_CHECK(y);
259 Strcat_charp_n(x, y->ptr, y->length);
260 }
261
262 void
Strcat_charp(Str x,const char * y)263 Strcat_charp(Str x, const char *y)
264 {
265 if (y == NULL)
266 return;
267 Strcat_charp_n(x, y, strlen(y));
268 }
269
270 void
Strcat_m_charp(Str x,...)271 Strcat_m_charp(Str x, ...)
272 {
273 va_list ap;
274 char *p;
275
276 va_start(ap, x);
277 while ((p = va_arg(ap, char *)) != NULL)
278 Strcat_charp_n(x, p, strlen(p));
279 }
280
281 void
Strgrow(Str x)282 Strgrow(Str x)
283 {
284 int newlen, addlen;
285
286 if (x->area_size < 8192)
287 addlen = x->area_size;
288 else
289 addlen = x->area_size / 2;
290 if (addlen < INITIAL_STR_SIZE)
291 addlen = INITIAL_STR_SIZE;
292 newlen = x->area_size + addlen;
293 if (newlen <= 0 || newlen > STR_SIZE_MAX) {
294 newlen = STR_SIZE_MAX;
295 if (x->length + 1 >= newlen)
296 x->length = newlen - 2;
297 }
298 if (x->area_size < newlen) {
299 x->ptr = GC_REALLOC(x->ptr, newlen);
300 if (x->ptr == NULL)
301 exit(1);
302 x->area_size = newlen;
303 }
304 x->ptr[x->length] = '\0';
305 }
306
307 Str
Strsubstr(Str s,int beg,int len)308 Strsubstr(Str s, int beg, int len)
309 {
310 Str new_s;
311 int i;
312
313 STR_LENGTH_CHECK(s);
314 new_s = Strnew();
315 if (beg >= s->length)
316 return new_s;
317 for (i = 0; i < len && beg + i < s->length; i++)
318 Strcat_char(new_s, s->ptr[beg + i]);
319 return new_s;
320 }
321
322 void
Strlower(Str s)323 Strlower(Str s)
324 {
325 int i;
326 STR_LENGTH_CHECK(s);
327 for (i = 0; i < s->length; i++)
328 s->ptr[i] = TOLOWER(s->ptr[i]);
329 }
330
331 void
Strupper(Str s)332 Strupper(Str s)
333 {
334 int i;
335 STR_LENGTH_CHECK(s);
336 for (i = 0; i < s->length; i++)
337 s->ptr[i] = TOUPPER(s->ptr[i]);
338 }
339
340 void
Strchop(Str s)341 Strchop(Str s)
342 {
343 STR_LENGTH_CHECK(s);
344 while (s->length > 0 &&
345 (s->ptr[s->length - 1] == '\n' || s->ptr[s->length - 1] == '\r')) {
346 s->length--;
347 }
348 s->ptr[s->length] = '\0';
349 }
350
351 void
Strinsert_char(Str s,int pos,char c)352 Strinsert_char(Str s, int pos, char c)
353 {
354 int i;
355 STR_LENGTH_CHECK(s);
356 if (pos < 0 || s->length < pos)
357 return;
358 if (s->length + 2 > s->area_size)
359 Strgrow(s);
360 if (s->length < pos)
361 return;
362 for (i = s->length; i > pos; i--)
363 s->ptr[i] = s->ptr[i - 1];
364 s->ptr[++s->length] = '\0';
365 s->ptr[pos] = c;
366 }
367
368 void
Strinsert_charp(Str s,int pos,const char * p)369 Strinsert_charp(Str s, int pos, const char *p)
370 {
371 STR_LENGTH_CHECK(s);
372 while (*p)
373 Strinsert_char(s, pos++, *(p++));
374 }
375
376 void
Strdelete(Str s,int pos,int n)377 Strdelete(Str s, int pos, int n)
378 {
379 int i;
380 STR_LENGTH_CHECK(s);
381 if (pos < 0 || s->length < pos)
382 return;
383 if (n < 0)
384 n = STR_SIZE_MAX - pos - 1;
385 if (s->length <= pos + n) {
386 s->ptr[pos] = '\0';
387 s->length = pos;
388 return;
389 }
390 for (i = pos; i < s->length - n; i++)
391 s->ptr[i] = s->ptr[i + n];
392 s->ptr[i] = '\0';
393 s->length = i;
394 }
395
396 void
Strtruncate(Str s,int pos)397 Strtruncate(Str s, int pos)
398 {
399 STR_LENGTH_CHECK(s);
400 if (pos < 0 || s->length < pos)
401 return;
402 s->ptr[pos] = '\0';
403 s->length = pos;
404 }
405
406 void
Strshrink(Str s,int n)407 Strshrink(Str s, int n)
408 {
409 STR_LENGTH_CHECK(s);
410 if (n >= s->length) {
411 s->length = 0;
412 s->ptr[0] = '\0';
413 }
414 else if (n > 0) {
415 s->length -= n;
416 s->ptr[s->length] = '\0';
417 }
418 }
419
420 void
Strremovefirstspaces(Str s)421 Strremovefirstspaces(Str s)
422 {
423 int i;
424
425 STR_LENGTH_CHECK(s);
426 for (i = 0; i < s->length && IS_SPACE(s->ptr[i]); i++) ;
427 if (i == 0)
428 return;
429 Strdelete(s, 0, i);
430 }
431
432 void
Strremovetrailingspaces(Str s)433 Strremovetrailingspaces(Str s)
434 {
435 int i;
436
437 STR_LENGTH_CHECK(s);
438 for (i = s->length - 1; i >= 0 && IS_SPACE(s->ptr[i]); i--) ;
439 s->length = i + 1;
440 s->ptr[i + 1] = '\0';
441 }
442
443 Str
Stralign_left(Str s,int width)444 Stralign_left(Str s, int width)
445 {
446 Str n;
447 int i;
448
449 STR_LENGTH_CHECK(s);
450 if (s->length >= width)
451 return Strdup(s);
452 n = Strnew_size(width);
453 Strcopy(n, s);
454 for (i = s->length; i < width; i++)
455 Strcat_char(n, ' ');
456 return n;
457 }
458
459 Str
Stralign_right(Str s,int width)460 Stralign_right(Str s, int width)
461 {
462 Str n;
463 int i;
464
465 STR_LENGTH_CHECK(s);
466 if (s->length >= width)
467 return Strdup(s);
468 n = Strnew_size(width);
469 for (i = s->length; i < width; i++)
470 Strcat_char(n, ' ');
471 Strcat(n, s);
472 return n;
473 }
474
475 Str
Stralign_center(Str s,int width)476 Stralign_center(Str s, int width)
477 {
478 Str n;
479 int i, w;
480
481 STR_LENGTH_CHECK(s);
482 if (s->length >= width)
483 return Strdup(s);
484 n = Strnew_size(width);
485 w = (width - s->length) / 2;
486 for (i = 0; i < w; i++)
487 Strcat_char(n, ' ');
488 Strcat(n, s);
489 for (i = w + s->length; i < width; i++)
490 Strcat_char(n, ' ');
491 return n;
492 }
493
494 #define SP_NORMAL 0
495 #define SP_PREC 1
496 #define SP_PREC2 2
497
498 Str
Sprintf(char * fmt,...)499 Sprintf(char *fmt, ...)
500 {
501 int len = 0;
502 int status = SP_NORMAL;
503 int p = 0;
504 char *f;
505 Str s;
506 va_list ap;
507
508 va_start(ap, fmt);
509 for (f = fmt; *f; f++) {
510 redo:
511 switch (status) {
512 case SP_NORMAL:
513 if (*f == '%') {
514 status = SP_PREC;
515 p = 0;
516 }
517 else
518 len++;
519 break;
520 case SP_PREC:
521 if (IS_ALPHA(*f)) {
522 /* conversion char. */
523 double vd;
524 int vi;
525 char *vs;
526 void *vp;
527
528 switch (*f) {
529 case 'l':
530 case 'h':
531 case 'L':
532 case 'w':
533 continue;
534 case 'd':
535 case 'i':
536 case 'o':
537 case 'x':
538 case 'X':
539 case 'u':
540 vi = va_arg(ap, int);
541 len += (p > 0) ? p : 10;
542 break;
543 case 'f':
544 case 'g':
545 case 'e':
546 case 'G':
547 case 'E':
548 vd = va_arg(ap, double);
549 len += (p > 0) ? p : 15;
550 break;
551 case 'c':
552 len += 1;
553 vi = va_arg(ap, int);
554 break;
555 case 's':
556 vs = va_arg(ap, char *);
557 vi = strlen(vs);
558 len += (p > vi) ? p : vi;
559 break;
560 case 'p':
561 vp = va_arg(ap, void *);
562 len += 10;
563 break;
564 case 'n':
565 vp = va_arg(ap, void *);
566 break;
567 }
568 status = SP_NORMAL;
569 }
570 else if (IS_DIGIT(*f))
571 p = p * 10 + *f - '0';
572 else if (*f == '.')
573 status = SP_PREC2;
574 else if (*f == '%') {
575 status = SP_NORMAL;
576 len++;
577 }
578 break;
579 case SP_PREC2:
580 if (IS_ALPHA(*f)) {
581 status = SP_PREC;
582 goto redo;
583 }
584 break;
585 }
586 }
587 va_end(ap);
588 s = Strnew_size(len * 2);
589 va_start(ap, fmt);
590 vsprintf(s->ptr, fmt, ap);
591 va_end(ap);
592 s->length = strlen(s->ptr);
593 if (s->length > len * 2) {
594 fprintf(stderr, "Sprintf: string too long\n");
595 exit(1);
596 }
597 return s;
598 }
599
600 Str
Strfgets(FILE * f)601 Strfgets(FILE * f)
602 {
603 Str s = Strnew();
604 int c;
605 while ((c = fgetc(f)) != EOF) {
606 Strcat_char(s, c);
607 if (c == '\n')
608 break;
609 }
610 return s;
611 }
612
613 Str
Strfgetall(FILE * f)614 Strfgetall(FILE * f)
615 {
616 Str s = Strnew();
617 int c;
618 while ((c = fgetc(f)) != EOF) {
619 Strcat_char(s, c);
620 }
621 return s;
622 }
623