Intial commit
[profile/ivi/w3m.git] / Str.c
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
37 Strnew()
38 {
39     Str x = GC_MALLOC(sizeof(struct _Str));
40     x->ptr = GC_MALLOC_ATOMIC(INITIAL_STR_SIZE);
41     x->ptr[0] = '\0';
42     x->area_size = INITIAL_STR_SIZE;
43     x->length = 0;
44     return x;
45 }
46
47 Str
48 Strnew_size(int n)
49 {
50     Str x = GC_MALLOC(sizeof(struct _Str));
51     x->ptr = GC_MALLOC_ATOMIC(n + 1);
52     x->ptr[0] = '\0';
53     x->area_size = n + 1;
54     x->length = 0;
55     return x;
56 }
57
58 Str
59 Strnew_charp(char *p)
60 {
61     Str x;
62     int n;
63
64     if (p == NULL)
65         return Strnew();
66     x = GC_MALLOC(sizeof(struct _Str));
67     n = strlen(p) + 1;
68     x->ptr = GC_MALLOC_ATOMIC(n);
69     x->area_size = n;
70     x->length = n - 1;
71     bcopy((void *)p, (void *)x->ptr, n);
72     return x;
73 }
74
75 Str
76 Strnew_m_charp(char *p, ...)
77 {
78     va_list ap;
79     Str r = Strnew();
80
81     va_start(ap, p);
82     while (p != NULL) {
83         Strcat_charp(r, p);
84         p = va_arg(ap, char *);
85     }
86     return r;
87 }
88
89 Str
90 Strnew_charp_n(char *p, int n)
91 {
92     Str x;
93
94     if (p == NULL)
95         return Strnew_size(n);
96     x = GC_MALLOC(sizeof(struct _Str));
97     x->ptr = GC_MALLOC_ATOMIC(n + 1);
98     x->area_size = n + 1;
99     x->length = n;
100     bcopy((void *)p, (void *)x->ptr, n);
101     x->ptr[n] = '\0';
102     return x;
103 }
104
105 Str
106 Strdup(Str s)
107 {
108     Str n = Strnew_size(s->length);
109     STR_LENGTH_CHECK(s);
110     Strcopy(n, s);
111     return n;
112 }
113
114 void
115 Strclear(Str s)
116 {
117     s->length = 0;
118     s->ptr[0] = '\0';
119 }
120
121 void
122 Strfree(Str x)
123 {
124     GC_free(x->ptr);
125     GC_free(x);
126 }
127
128 void
129 Strcopy(Str x, Str y)
130 {
131     STR_LENGTH_CHECK(x);
132     STR_LENGTH_CHECK(y);
133     if (x->area_size < y->length + 1) {
134         GC_free(x->ptr);
135         x->ptr = GC_MALLOC_ATOMIC(y->length + 1);
136         x->area_size = y->length + 1;
137     }
138     bcopy((void *)y->ptr, (void *)x->ptr, y->length + 1);
139     x->length = y->length;
140 }
141
142 void
143 Strcopy_charp(Str x, char *y)
144 {
145     int len;
146
147     STR_LENGTH_CHECK(x);
148     if (y == NULL) {
149         x->length = 0;
150         return;
151     }
152     len = strlen(y);
153     if (x->area_size < len + 1) {
154         GC_free(x->ptr);
155         x->ptr = GC_MALLOC_ATOMIC(len + 1);
156         x->area_size = len + 1;
157     }
158     bcopy((void *)y, (void *)x->ptr, len + 1);
159     x->length = len;
160 }
161
162 void
163 Strcopy_charp_n(Str x, char *y, int n)
164 {
165     int len = n;
166
167     STR_LENGTH_CHECK(x);
168     if (y == NULL) {
169         x->length = 0;
170         return;
171     }
172     if (x->area_size < len + 1) {
173         GC_free(x->ptr);
174         x->ptr = GC_MALLOC_ATOMIC(len + 1);
175         x->area_size = len + 1;
176     }
177     bcopy((void *)y, (void *)x->ptr, n);
178     x->ptr[n] = '\0';
179     x->length = n;
180 }
181
182 void
183 Strcat_charp_n(Str x, char *y, int n)
184 {
185     int newlen;
186
187     STR_LENGTH_CHECK(x);
188     if (y == NULL)
189         return;
190     newlen = x->length + n + 1;
191     if (x->area_size < newlen) {
192         char *old = x->ptr;
193         newlen = newlen * 3 / 2;
194         x->ptr = GC_MALLOC_ATOMIC(newlen);
195         x->area_size = newlen;
196         bcopy((void *)old, (void *)x->ptr, x->length);
197         GC_free(old);
198     }
199     bcopy((void *)y, (void *)&x->ptr[x->length], n);
200     x->length += n;
201     x->ptr[x->length] = '\0';
202 }
203
204 void
205 Strcat(Str x, Str y)
206 {
207     STR_LENGTH_CHECK(y);
208     Strcat_charp_n(x, y->ptr, y->length);
209 }
210
211 void
212 Strcat_charp(Str x, char *y)
213 {
214     if (y == NULL)
215         return;
216     Strcat_charp_n(x, y, strlen(y));
217 }
218
219 void
220 Strcat_m_charp(Str x, ...)
221 {
222     va_list ap;
223     char *p;
224
225     va_start(ap, x);
226     while ((p = va_arg(ap, char *)) != NULL)
227          Strcat_charp_n(x, p, strlen(p));
228 }
229
230 void
231 Strgrow(Str x)
232 {
233     char *old = x->ptr;
234     int newlen;
235     newlen = x->length * 6 / 5;
236     if (newlen == x->length)
237         newlen += 2;
238     x->ptr = GC_MALLOC_ATOMIC(newlen);
239     x->area_size = newlen;
240     bcopy((void *)old, (void *)x->ptr, x->length);
241     GC_free(old);
242 }
243
244 Str
245 Strsubstr(Str s, int beg, int len)
246 {
247     Str new_s;
248     int i;
249
250     STR_LENGTH_CHECK(s);
251     new_s = Strnew();
252     if (beg >= s->length)
253         return new_s;
254     for (i = 0; i < len && beg + i < s->length; i++)
255         Strcat_char(new_s, s->ptr[beg + i]);
256     return new_s;
257 }
258
259 void
260 Strlower(Str s)
261 {
262     int i;
263     STR_LENGTH_CHECK(s);
264     for (i = 0; i < s->length; i++)
265         s->ptr[i] = TOLOWER(s->ptr[i]);
266 }
267
268 void
269 Strupper(Str s)
270 {
271     int i;
272     STR_LENGTH_CHECK(s);
273     for (i = 0; i < s->length; i++)
274         s->ptr[i] = TOUPPER(s->ptr[i]);
275 }
276
277 void
278 Strchop(Str s)
279 {
280     STR_LENGTH_CHECK(s);
281     while ((s->ptr[s->length - 1] == '\n' || s->ptr[s->length - 1] == '\r') &&
282            s->length > 0) {
283         s->length--;
284     }
285     s->ptr[s->length] = '\0';
286 }
287
288 void
289 Strinsert_char(Str s, int pos, char c)
290 {
291     int i;
292     STR_LENGTH_CHECK(s);
293     if (pos < 0 || s->length < pos)
294         return;
295     if (s->length + 2 > s->area_size)
296         Strgrow(s);
297     for (i = s->length; i > pos; i--)
298         s->ptr[i] = s->ptr[i - 1];
299     s->ptr[++s->length] = '\0';
300     s->ptr[pos] = c;
301 }
302
303 void
304 Strinsert_charp(Str s, int pos, char *p)
305 {
306     STR_LENGTH_CHECK(s);
307     while (*p)
308         Strinsert_char(s, pos++, *(p++));
309 }
310
311 void
312 Strdelete(Str s, int pos, int n)
313 {
314     int i;
315     STR_LENGTH_CHECK(s);
316     if (s->length <= pos + n) {
317         s->ptr[pos] = '\0';
318         s->length = pos;
319         return;
320     }
321     for (i = pos; i < s->length - n; i++)
322         s->ptr[i] = s->ptr[i + n];
323     s->ptr[i] = '\0';
324     s->length = i;
325 }
326
327 void
328 Strtruncate(Str s, int pos)
329 {
330     STR_LENGTH_CHECK(s);
331     s->ptr[pos] = '\0';
332     s->length = pos;
333 }
334
335 void
336 Strshrink(Str s, int n)
337 {
338     STR_LENGTH_CHECK(s);
339     if (n >= s->length) {
340         s->length = 0;
341         s->ptr[0] = '\0';
342     }
343     else {
344         s->length -= n;
345         s->ptr[s->length] = '\0';
346     }
347 }
348
349 void
350 Strremovefirstspaces(Str s)
351 {
352     int i;
353
354     STR_LENGTH_CHECK(s);
355     for (i = 0; i < s->length && IS_SPACE(s->ptr[i]); i++) ;
356     if (i == 0)
357         return;
358     Strdelete(s, 0, i);
359 }
360
361 void
362 Strremovetrailingspaces(Str s)
363 {
364     int i;
365
366     STR_LENGTH_CHECK(s);
367     for (i = s->length - 1; i >= 0 && IS_SPACE(s->ptr[i]); i--) ;
368     s->length = i + 1;
369     s->ptr[i + 1] = '\0';
370 }
371
372 Str
373 Stralign_left(Str s, int width)
374 {
375     Str n;
376     int i;
377
378     STR_LENGTH_CHECK(s);
379     if (s->length >= width)
380         return Strdup(s);
381     n = Strnew_size(width);
382     Strcopy(n, s);
383     for (i = s->length; i < width; i++)
384         Strcat_char(n, ' ');
385     return n;
386 }
387
388 Str
389 Stralign_right(Str s, int width)
390 {
391     Str n;
392     int i;
393
394     STR_LENGTH_CHECK(s);
395     if (s->length >= width)
396         return Strdup(s);
397     n = Strnew_size(width);
398     for (i = s->length; i < width; i++)
399         Strcat_char(n, ' ');
400     Strcat(n, s);
401     return n;
402 }
403
404 Str
405 Stralign_center(Str s, int width)
406 {
407     Str n;
408     int i, w;
409
410     STR_LENGTH_CHECK(s);
411     if (s->length >= width)
412         return Strdup(s);
413     n = Strnew_size(width);
414     w = (width - s->length) / 2;
415     for (i = 0; i < w; i++)
416         Strcat_char(n, ' ');
417     Strcat(n, s);
418     for (i = w + s->length; i < width; i++)
419         Strcat_char(n, ' ');
420     return n;
421 }
422
423 #define SP_NORMAL 0
424 #define SP_PREC   1
425 #define SP_PREC2  2
426
427 Str
428 Sprintf(char *fmt, ...)
429 {
430     int len = 0;
431     int status = SP_NORMAL;
432     int p = 0;
433     char *f;
434     Str s;
435     va_list ap;
436
437     va_start(ap, fmt);
438     for (f = fmt; *f; f++) {
439       redo:
440         switch (status) {
441         case SP_NORMAL:
442             if (*f == '%') {
443                 status = SP_PREC;
444                 p = 0;
445             }
446             else
447                 len++;
448             break;
449         case SP_PREC:
450             if (IS_ALPHA(*f)) {
451                 /* conversion char. */
452                 double vd;
453                 int vi;
454                 char *vs;
455                 void *vp;
456
457                 switch (*f) {
458                 case 'l':
459                 case 'h':
460                 case 'L':
461                 case 'w':
462                     continue;
463                 case 'd':
464                 case 'i':
465                 case 'o':
466                 case 'x':
467                 case 'X':
468                 case 'u':
469                     vi = va_arg(ap, int);
470                     len += (p > 0) ? p : 10;
471                     break;
472                 case 'f':
473                 case 'g':
474                 case 'e':
475                 case 'G':
476                 case 'E':
477                     vd = va_arg(ap, double);
478                     len += (p > 0) ? p : 15;
479                     break;
480                 case 'c':
481                     len += 1;
482                     vi = va_arg(ap, int);
483                     break;
484                 case 's':
485                     vs = va_arg(ap, char *);
486                     vi = strlen(vs);
487                     len += (p > vi) ? p : vi;
488                     break;
489                 case 'p':
490                     vp = va_arg(ap, void *);
491                     len += 10;
492                     break;
493                 case 'n':
494                     vp = va_arg(ap, void *);
495                     break;
496                 }
497                 status = SP_NORMAL;
498             }
499             else if (IS_DIGIT(*f))
500                 p = p * 10 + *f - '0';
501             else if (*f == '.')
502                 status = SP_PREC2;
503             else if (*f == '%') {
504                 status = SP_NORMAL;
505                 len++;
506             }
507             break;
508         case SP_PREC2:
509             if (IS_ALPHA(*f)) {
510                 status = SP_PREC;
511                 goto redo;
512             }
513             break;
514         }
515     }
516     va_end(ap);
517     s = Strnew_size(len * 2);
518     va_start(ap, fmt);
519     vsprintf(s->ptr, fmt, ap);
520     va_end(ap);
521     s->length = strlen(s->ptr);
522     if (s->length > len * 2) {
523         fprintf(stderr, "Sprintf: string too long\n");
524         exit(1);
525     }
526     return s;
527 }
528
529 Str
530 Strfgets(FILE * f)
531 {
532     Str s = Strnew();
533     char c;
534     while (1) {
535         c = fgetc(f);
536         if (feof(f) || ferror(f))
537             break;
538         Strcat_char(s, c);
539         if (c == '\n')
540             break;
541     }
542     return s;
543 }
544
545 Str
546 Strfgetall(FILE * f)
547 {
548     Str s = Strnew();
549     char c;
550     while (1) {
551         c = fgetc(f);
552         if (feof(f) || ferror(f))
553             break;
554         Strcat_char(s, c);
555     }
556     return s;
557 }