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