1/* -----------------------------------------------------------------------------
2 * string.c
3 *
4 *     Implements a string object that supports both sequence operations and
5 *     file semantics.
6 *
7 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
8 *
9 * Copyright (C) 1999-2000.  The University of Chicago
10 * See the file LICENSE for information on usage and redistribution.
11 * ----------------------------------------------------------------------------- */
12
13char cvsroot_string_c[] = "$Id: string.c 10926 2008-11-11 22:17:40Z wsfulton $";
14
15#include "dohint.h"
16
17extern DohObjInfo DohStringType;
18
19typedef struct String {
20  DOH *file;
21  int line;
22  int maxsize;			/* Max size allocated */
23  int len;			/* Current length     */
24  int hashkey;			/* Hash key value     */
25  int sp;			/* Current position   */
26  char *str;			/* String data        */
27} String;
28
29/* -----------------------------------------------------------------------------
30 * void *String_data() - Return as a 'void *'
31 * ----------------------------------------------------------------------------- */
32
33static void *String_data(DOH *so) {
34  String *s = (String *) ObjData(so);
35  s->str[s->len] = 0;
36  return (void *) s->str;
37}
38
39/* static char *String_char(DOH *so) {
40  return (char *) String_data(so);
41}
42*/
43
44/* -----------------------------------------------------------------------------
45 * int String_dump() - Serialize a string onto out
46 * ----------------------------------------------------------------------------- */
47
48static int String_dump(DOH *so, DOH *out) {
49  int nsent;
50  int ret;
51  String *s = (String *) ObjData(so);
52  nsent = 0;
53  while (nsent < s->len) {
54    ret = Write(out, s->str + nsent, (s->len - nsent));
55    if (ret < 0)
56      return ret;
57    nsent += ret;
58  }
59  return nsent;
60}
61
62/* -----------------------------------------------------------------------------
63 * CopyString() - Copy a string
64 * ----------------------------------------------------------------------------- */
65
66static DOH *CopyString(DOH *so) {
67  String *str;
68  String *s = (String *) ObjData(so);
69  str = (String *) DohMalloc(sizeof(String));
70  str->hashkey = s->hashkey;
71  str->sp = s->sp;
72  str->line = s->line;
73  str->file = s->file;
74  if (str->file)
75    Incref(str->file);
76  str->str = (char *) DohMalloc(s->len + 1);
77  memcpy(str->str, s->str, s->len);
78  str->maxsize = s->len;
79  str->len = s->len;
80  str->str[str->len] = 0;
81
82  return DohObjMalloc(&DohStringType, str);
83}
84
85/* -----------------------------------------------------------------------------
86 * DelString() - Delete a string
87 * ----------------------------------------------------------------------------- */
88
89static void DelString(DOH *so) {
90  String *s = (String *) ObjData(so);
91  DohFree(s->str);
92  DohFree(s);
93}
94
95/* -----------------------------------------------------------------------------
96 * DohString_len() - Length of a string
97 * ----------------------------------------------------------------------------- */
98
99static int String_len(DOH *so) {
100  String *s = (String *) ObjData(so);
101  return s->len;
102}
103
104
105/* -----------------------------------------------------------------------------
106 * int String_cmp() - Compare two strings
107 * ----------------------------------------------------------------------------- */
108
109static int String_cmp(DOH *so1, DOH *so2) {
110  String *s1, *s2;
111  char *c1, *c2;
112  int maxlen, i;
113  s1 = (String *) ObjData(so1);
114  s2 = (String *) ObjData(so2);
115  maxlen = s1->len;
116  if (s2->len < maxlen)
117    maxlen = s2->len;
118  c1 = s1->str;
119  c2 = s2->str;
120  for (i = maxlen; i; --i, c1++, c2++) {
121    if (*c1 != *c2)
122      break;
123  }
124  if (i != 0) {
125    if (*c1 < *c2)
126      return -1;
127    else
128      return 1;
129  }
130  if (s1->len == s2->len)
131    return 0;
132  if (s1->len > s2->len)
133    return 1;
134  return -1;
135}
136
137/* -----------------------------------------------------------------------------
138 * int String_equal() - Say if two string are equal
139 * ----------------------------------------------------------------------------- */
140
141static int String_equal(DOH *so1, DOH *so2) {
142  String *s1 = (String *) ObjData(so1);
143  String *s2 = (String *) ObjData(so2);
144  register int len = s1->len;
145  if (len != s2->len) {
146    return 0;
147  } else {
148    register char *c1 = s1->str;
149    register char *c2 = s2->str;
150#if 0
151    register int mlen = len >> 2;
152    register int i = mlen;
153    for (; i; --i) {
154      if (*(c1++) != *(c2++))
155	return 0;
156      if (*(c1++) != *(c2++))
157	return 0;
158      if (*(c1++) != *(c2++))
159	return 0;
160      if (*(c1++) != *(c2++))
161	return 0;
162    }
163    for (i = len - (mlen << 2); i; --i) {
164      if (*(c1++) != *(c2++))
165	return 0;
166    }
167    return 1;
168#else
169    return memcmp(c1, c2, len) == 0;
170#endif
171  }
172}
173
174/* -----------------------------------------------------------------------------
175 * int String_hash() - Compute string hash value
176 * ----------------------------------------------------------------------------- */
177
178static int String_hash(DOH *so) {
179  String *s = (String *) ObjData(so);
180  if (s->hashkey >= 0) {
181    return s->hashkey;
182  } else {
183    register char *c = s->str;
184    register int len = s->len > 50 ? 50 : s->len;
185    register int h = 0;
186    register int mlen = len >> 2;
187    register int i = mlen;
188    for (; i; --i) {
189      h = (h << 5) + *(c++);
190      h = (h << 5) + *(c++);
191      h = (h << 5) + *(c++);
192      h = (h << 5) + *(c++);
193    }
194    for (i = len - (mlen << 2); i; --i) {
195      h = (h << 5) + *(c++);
196    }
197    h &= 0x7fffffff;
198    s->hashkey = h;
199    return h;
200  }
201}
202
203/* -----------------------------------------------------------------------------
204 * DohString_append(String *s, const char *newstr) - Append to s
205 * ----------------------------------------------------------------------------- */
206
207void DohString_append(DOH *so, DOH *str) {
208  int oldlen, newlen, newmaxsize, l, sp;
209  char *tc;
210  String *s = (String *) ObjData(so);
211  char *newstr = 0;
212
213  if (DohCheck(str)) {
214    String *ss = (String *) ObjData(str);
215    newstr = (char *) String_data((DOH *) str);
216    l = ss->len;
217  } else {
218    newstr = (char *) (str);
219    l = (int) strlen(newstr);
220  }
221  if (!newstr)
222    return;
223  s->hashkey = -1;
224
225  oldlen = s->len;
226  newlen = oldlen + l + 1;
227  if (newlen >= s->maxsize - 1) {
228    newmaxsize = 2 * s->maxsize;
229    if (newlen >= newmaxsize - 1)
230      newmaxsize = newlen + 1;
231    s->str = (char *) DohRealloc(s->str, newmaxsize);
232    assert(s->str);
233    s->maxsize = newmaxsize;
234  }
235  tc = s->str;
236  memcpy(tc + oldlen, newstr, l + 1);
237  sp = s->sp;
238  if (sp >= oldlen) {
239    int i = oldlen + l - sp;
240    tc += sp;
241    for (; i; --i) {
242      if (*(tc++) == '\n')
243	s->line++;
244    }
245    s->sp = oldlen + l;
246  }
247  s->len += l;
248}
249
250
251/* -----------------------------------------------------------------------------
252 * void String_clear() - Clear a string
253 * ----------------------------------------------------------------------------- */
254
255static void String_clear(DOH *so) {
256  String *s = (String *) ObjData(so);
257  s->hashkey = -1;
258  s->len = 0;
259  *(s->str) = 0;
260  s->sp = 0;
261  s->line = 1;
262}
263
264/* -----------------------------------------------------------------------------
265 * void String_insert() - Insert a string
266 * ----------------------------------------------------------------------------- */
267
268static int String_insert(DOH *so, int pos, DOH *str) {
269  String *s;
270  char *nstr;
271  int len;
272  char *data;
273
274  if (pos == DOH_END) {
275    DohString_append(so, str);
276    return 0;
277  }
278
279
280  s = (String *) ObjData(so);
281  s->hashkey = -1;
282  if (DohCheck(str)) {
283    String *ss = (String *) ObjData(str);
284    data = (char *) String_data(str);
285    len = ss->len;
286  } else {
287    data = (char *) (str);
288    len = (int) strlen(data);
289  }
290  nstr = s->str;
291
292  if (pos < 0)
293    pos = 0;
294  else if (pos > s->len)
295    pos = s->len;
296
297  /* See if there is room to insert the new data */
298  while (s->maxsize <= s->len + len) {
299    int newsize = 2 * s->maxsize;
300    s->str = (char *) DohRealloc(s->str, newsize);
301    assert(s->str);
302    s->maxsize = newsize;
303  }
304  memmove(s->str + pos + len, s->str + pos, (s->len - pos));
305  memcpy(s->str + pos, data, len);
306  if (s->sp >= pos) {
307    int i;
308
309    for (i = 0; i < len; i++) {
310      if (data[i] == '\n')
311	s->line++;
312    }
313    s->sp += len;
314  }
315  s->len += len;
316  s->str[s->len] = 0;
317  return 0;
318}
319
320/* -----------------------------------------------------------------------------
321 * int String_delitem() - Delete a character
322 * ----------------------------------------------------------------------------- */
323
324static int String_delitem(DOH *so, int pos) {
325  String *s = (String *) ObjData(so);
326  s->hashkey = -1;
327  if (pos == DOH_END)
328    pos = s->len - 1;
329  if (pos == DOH_BEGIN)
330    pos = 0;
331  if (s->len == 0)
332    return 0;
333
334  if (s->sp > pos) {
335    s->sp--;
336    assert(s->sp >= 0);
337    if (s->str[pos] == '\n')
338      s->line--;
339  }
340  memmove(s->str + pos, s->str + pos + 1, ((s->len - 1) - pos));
341  s->len--;
342  s->str[s->len] = 0;
343  return 0;
344}
345
346/* -----------------------------------------------------------------------------
347 * int String_delslice() -  Delete a range
348 * ----------------------------------------------------------------------------- */
349
350static int String_delslice(DOH *so, int sindex, int eindex) {
351  String *s = (String *) ObjData(so);
352  int size;
353  if (s->len == 0)
354    return 0;
355  s->hashkey = -1;
356  if (eindex == DOH_END)
357    eindex = s->len;
358  if (sindex == DOH_BEGIN)
359    sindex = 0;
360
361  size = eindex - sindex;
362  if (s->sp > sindex) {
363    /* Adjust the file pointer and line count */
364    int i, end;
365    if (s->sp > eindex) {
366      end = eindex;
367      s->sp -= size;
368    } else {
369      end = s->sp;
370      s->sp = sindex;
371    }
372    for (i = sindex; i < end; i++) {
373      if (s->str[i] == '\n')
374	s->line--;
375    }
376    assert(s->sp >= 0);
377  }
378  memmove(s->str + sindex, s->str + eindex, s->len - eindex);
379  s->len -= size;
380  s->str[s->len] = 0;
381  return 0;
382}
383
384/* -----------------------------------------------------------------------------
385 * DOH *String_str() - Returns a string (used by printing commands)
386 * ----------------------------------------------------------------------------- */
387
388static DOH *String_str(DOH *so) {
389  String *s = (String *) ObjData(so);
390  s->str[s->len] = 0;
391  return NewString(s->str);
392}
393
394/* -----------------------------------------------------------------------------
395 * int String_read() - Read data from a string
396 * ----------------------------------------------------------------------------- */
397
398static int String_read(DOH *so, void *buffer, int len) {
399  int reallen, retlen;
400  char *cb;
401  String *s = (String *) ObjData(so);
402  if ((s->sp + len) > s->len)
403    reallen = (s->len - s->sp);
404  else
405    reallen = len;
406
407  cb = (char *) buffer;
408  retlen = reallen;
409
410  if (reallen > 0) {
411    memmove(cb, s->str + s->sp, reallen);
412    s->sp += reallen;
413  }
414  return retlen;
415}
416
417/* -----------------------------------------------------------------------------
418 * int String_write() - Write data to a string
419 * ----------------------------------------------------------------------------- */
420static int String_write(DOH *so, void *buffer, int len) {
421  int newlen;
422  String *s = (String *) ObjData(so);
423  s->hashkey = -1;
424  if (s->sp > s->len)
425    s->sp = s->len;
426  newlen = s->sp + len + 1;
427  if (newlen > s->maxsize) {
428    s->str = (char *) DohRealloc(s->str, newlen);
429    assert(s->str);
430    s->maxsize = newlen;
431    s->len = s->sp + len;
432  }
433  if ((s->sp + len) > s->len)
434    s->len = s->sp + len;
435  memmove(s->str + s->sp, buffer, len);
436  s->sp += len;
437  s->str[s->len] = 0;
438  return len;
439}
440
441/* -----------------------------------------------------------------------------
442 * int String_seek() - Seek to a new position
443 * ----------------------------------------------------------------------------- */
444
445static int String_seek(DOH *so, long offset, int whence) {
446  int pos, nsp, inc;
447  String *s = (String *) ObjData(so);
448  if (whence == SEEK_SET)
449    pos = 0;
450  else if (whence == SEEK_CUR)
451    pos = s->sp;
452  else if (whence == SEEK_END) {
453    pos = s->len;
454    offset = -offset;
455  } else
456    pos = s->sp;
457
458  nsp = pos + offset;
459  if (nsp < 0)
460    nsp = 0;
461  if (s->len > 0 && nsp > s->len)
462    nsp = s->len;
463
464  inc = (nsp > s->sp) ? 1 : -1;
465
466  {
467#if 0
468    register int sp = s->sp;
469    register char *tc = s->str;
470    register int len = s->len;
471    while (sp != nsp) {
472      int prev = sp + inc;
473      if (prev >= 0 && prev <= len && tc[prev] == '\n')
474	s->line += inc;
475      sp += inc;
476    }
477#else
478    register int sp = s->sp;
479    register char *tc = s->str;
480    if (inc > 0) {
481      while (sp != nsp) {
482	if (tc[++sp] == '\n')
483	  ++s->line;
484      }
485    } else {
486      while (sp != nsp) {
487	if (tc[--sp] == '\n')
488	  --s->line;
489      }
490    }
491#endif
492    s->sp = sp;
493  }
494  assert(s->sp >= 0);
495  return 0;
496}
497
498/* -----------------------------------------------------------------------------
499 * long String_tell() - Return current position
500 * ----------------------------------------------------------------------------- */
501
502static long String_tell(DOH *so) {
503  String *s = (String *) ObjData(so);
504  return (long) (s->sp);
505}
506
507/* -----------------------------------------------------------------------------
508 * int String_putc()
509 * ----------------------------------------------------------------------------- */
510
511static int String_putc(DOH *so, int ch) {
512  String *s = (String *) ObjData(so);
513  register int len = s->len;
514  register int sp = s->sp;
515  s->hashkey = -1;
516  if (sp >= len) {
517    register int maxsize = s->maxsize;
518    register char *tc = s->str;
519    if (len > (maxsize - 2)) {
520      maxsize *= 2;
521      tc = (char *) DohRealloc(tc, maxsize);
522      assert(tc);
523      s->maxsize = (int) maxsize;
524      s->str = tc;
525    }
526    tc += sp;
527    *tc = (char) ch;
528    *(++tc) = 0;
529    s->len = s->sp = sp + 1;
530  } else {
531    s->str[s->sp++] = (char) ch;
532  }
533  if (ch == '\n')
534    s->line++;
535  return ch;
536}
537
538/* -----------------------------------------------------------------------------
539 * int String_getc()
540 * ----------------------------------------------------------------------------- */
541
542static int String_getc(DOH *so) {
543  int c;
544  String *s = (String *) ObjData(so);
545  if (s->sp >= s->len)
546    c = EOF;
547  else
548    c = (int)(unsigned char) s->str[s->sp++];
549  if (c == '\n')
550    s->line++;
551  return c;
552}
553
554/* -----------------------------------------------------------------------------
555 * int String_ungetc()
556 * ----------------------------------------------------------------------------- */
557
558static int String_ungetc(DOH *so, int ch) {
559  String *s = (String *) ObjData(so);
560  if (ch == EOF)
561    return ch;
562  if (s->sp <= 0)
563    return EOF;
564  s->sp--;
565  if (ch == '\n')
566    s->line--;
567  return ch;
568}
569
570/* -----------------------------------------------------------------------------
571 * replace_simple(String *str, char *token, char *rep, int flags, int count)
572 *
573 * Replaces count non-overlapping occurrences of token with rep in a string.
574 * ----------------------------------------------------------------------------- */
575
576static char *end_quote(char *s) {
577  char *qs;
578  char qc;
579  char *q;
580  char *nl;
581  qc = *s;
582  qs = s;
583  while (1) {
584    q = strpbrk(s + 1, "\"\'");
585    nl = strchr(s + 1, '\n');
586    if (nl && (nl < q)) {
587      /* A new line appears before the end of the string */
588      if (*(nl - 1) == '\\') {
589	s = nl + 1;
590	continue;
591      }
592      /* String was terminated by a newline.  Wing it */
593      return qs;
594    }
595    if (!q && nl) {
596      return qs;
597    }
598    if (!q)
599      return 0;
600    if ((*q == qc) && (*(q - 1) != '\\'))
601      return q;
602    s = q;
603  }
604}
605
606static char *match_simple(char *base, char *s, char *token, int tokenlen) {
607  (void) base;
608  (void) tokenlen;
609  return strstr(s, token);
610}
611
612static char *match_identifier(char *base, char *s, char *token, int tokenlen) {
613  while (s) {
614    s = strstr(s, token);
615    if (!s)
616      return 0;
617    if ((s > base) && (isalnum((int) *(s - 1)) || (*(s - 1) == '_'))) {
618      s += tokenlen;
619      continue;
620    }
621    if (isalnum((int) *(s + tokenlen)) || (*(s + tokenlen) == '_')) {
622      s += tokenlen;
623      continue;
624    }
625    return s;
626  }
627  return 0;
628}
629
630
631static char *match_identifier_begin(char *base, char *s, char *token, int tokenlen) {
632  while (s) {
633    s = strstr(s, token);
634    if (!s)
635      return 0;
636    if ((s > base) && (isalnum((int) *(s - 1)) || (*(s - 1) == '_'))) {
637      s += tokenlen;
638      continue;
639    }
640    return s;
641  }
642  return 0;
643}
644
645static char *match_identifier_end(char *base, char *s, char *token, int tokenlen) {
646  (void) base;
647  while (s) {
648    s = strstr(s, token);
649    if (!s)
650      return 0;
651    if (isalnum((int) *(s + tokenlen)) || (*(s + tokenlen) == '_')) {
652      s += tokenlen;
653      continue;
654    }
655    return s;
656  }
657  return 0;
658}
659
660static int replace_simple(String *str, char *token, char *rep, int flags, int count, char *(*match) (char *, char *, char *, int)) {
661  int tokenlen;			/* Length of the token */
662  int replen;			/* Length of the replacement */
663  int delta, expand = 0;
664  int ic;
665  int rcount = 0;
666  int noquote = 0;
667  char *c, *s, *t, *first;
668  char *q, *q2;
669  register char *base;
670  int i;
671
672  /* Figure out if anything gets replaced */
673  if (!strlen(token))
674    return 0;
675
676  base = str->str;
677  tokenlen = strlen(token);
678  s = (*match) (base, base, token, tokenlen);
679
680  if (!s)
681    return 0;			/* No matches.  Who cares */
682
683  str->hashkey = -1;
684
685  if (flags & DOH_REPLACE_NOQUOTE)
686    noquote = 1;
687
688  /* If we are not replacing inside quotes, we need to do a little extra work */
689  if (noquote) {
690    q = strpbrk(base, "\"\'");
691    if (!q) {
692      noquote = 0;		/* Well, no quotes to worry about. Oh well */
693    } else {
694      while (q && (q < s)) {
695	/* First match was found inside a quote.  Try to find another match */
696	q2 = end_quote(q);
697	if (!q2) {
698	  return 0;
699	}
700	if (q2 > s) {
701	  /* Find next match */
702	  s = (*match) (base, q2 + 1, token, tokenlen);
703	}
704	if (!s)
705	  return 0;		/* Oh well, no matches */
706	q = strpbrk(q2 + 1, "\"\'");
707	if (!q)
708	  noquote = 0;		/* No more quotes */
709      }
710    }
711  }
712
713  first = s;
714  replen = strlen(rep);
715
716  delta = (replen - tokenlen);
717
718  if (delta <= 0) {
719    /* String is either shrinking or staying the same size */
720    /* In this case, we do the replacement in place without memory reallocation */
721    ic = count;
722    t = s;			/* Target of memory copies */
723    while (ic && s) {
724      if (replen) {
725	memcpy(t, rep, replen);
726	t += replen;
727      }
728      rcount++;
729      expand += delta;
730      /* Find the next location */
731      s += tokenlen;
732      if (ic == 1)
733	break;
734      c = (*match) (base, s, token, tokenlen);
735
736      if (noquote) {
737	q = strpbrk(s, "\"\'");
738	if (!q) {
739	  noquote = 0;
740	} else {
741	  while (q && (q < c)) {
742	    /* First match was found inside a quote.  Try to find another match */
743	    q2 = end_quote(q);
744	    if (!q2) {
745	      c = 0;
746	      break;
747	    }
748	    if (q2 > c)
749	      c = (*match) (base, q2 + 1, token, tokenlen);
750	    if (!c)
751	      break;
752	    q = strpbrk(q2 + 1, "\"\'");
753	    if (!q)
754	      noquote = 0;	/* No more quotes */
755	  }
756	}
757      }
758      if (delta) {
759	if (c) {
760	  memmove(t, s, c - s);
761	  t += (c - s);
762	} else {
763	  memmove(t, s, (str->str + str->len) - s + 1);
764	}
765      } else {
766	t += (c - s);
767      }
768      s = c;
769      ic--;
770    }
771    if (s && delta) {
772      memmove(t, s, (str->str + str->len) - s + 1);
773    }
774    str->len += expand;
775    str->str[str->len] = 0;
776    if (str->sp >= str->len)
777      str->sp += expand;	/* Fix the end of file pointer */
778    return rcount;
779  }
780  /* The string is expanding as a result of the replacement */
781  /* Figure out how much expansion is going to occur and allocate a new string */
782  {
783    char *ns;
784    int newsize;
785
786    rcount++;
787    ic = count - 1;
788    s += tokenlen;
789    while (ic && (c = (*match) (base, s, token, tokenlen))) {
790      if (noquote) {
791	q = strpbrk(s, "\"\'");
792	if (!q) {
793	  break;
794	} else {
795	  while (q && (q < c)) {
796	    /* First match was found inside a quote.  Try to find another match */
797	    q2 = end_quote(q);
798	    if (!q2) {
799	      c = 0;
800	      break;
801	    }
802	    if (q2 > c) {
803	      c = (*match) (base, q2 + 1, token, tokenlen);
804	      if (!c)
805		break;
806	    }
807	    q = strpbrk(q2 + 1, "\"\'");
808	    if (!q)
809	      noquote = 0;
810	  }
811	}
812      }
813      if (c) {
814	rcount++;
815	ic--;
816	s = c + tokenlen;
817      } else {
818	break;
819      }
820    }
821
822    expand = delta * rcount;	/* Total amount of expansion for the replacement */
823    newsize = str->maxsize;
824    while ((str->len + expand) >= newsize)
825      newsize *= 2;
826
827    ns = (char *) DohMalloc(newsize);
828    assert(ns);
829    t = ns;
830    s = first;
831
832    /* Copy the first part of the string */
833    if (first > str->str) {
834      memcpy(t, str->str, (first - str->str));
835      t += (first - str->str);
836    }
837    for (i = 0; i < rcount; i++) {
838      memcpy(t, rep, replen);
839      t += replen;
840      s += tokenlen;
841      c = (*match) (base, s, token, tokenlen);
842      if (noquote) {
843	q = strpbrk(s, "\"\'");
844	if (!q) {
845	  noquote = 0;
846	} else {
847	  while (q && (q < c)) {
848	    /* First match was found inside a quote.  Try to find another match */
849	    q2 = end_quote(q);
850	    if (!q2) {
851	      c = 0;
852	      break;
853	    }
854	    if (q2 > c) {
855	      c = (*match) (base, q2 + 1, token, tokenlen);
856	      if (!c)
857		break;
858	    }
859	    q = strpbrk(q2 + 1, "\"\'");
860	    if (!q)
861	      noquote = 0;	/* No more quotes */
862	  }
863	}
864      }
865      if (i < (rcount - 1)) {
866	memcpy(t, s, c - s);
867	t += (c - s);
868      } else {
869	memcpy(t, s, (str->str + str->len) - s + 1);
870      }
871      s = c;
872    }
873    c = str->str;
874    str->str = ns;
875    if (str->sp >= str->len)
876      str->sp += expand;
877    str->len += expand;
878    str->str[str->len] = 0;
879    str->maxsize = newsize;
880    DohFree(c);
881    return rcount;
882  }
883}
884
885/* -----------------------------------------------------------------------------
886 * int String_replace()
887 * ----------------------------------------------------------------------------- */
888
889static int String_replace(DOH *stro, DOH *token, DOH *rep, int flags) {
890  int count = -1;
891  String *str = (String *) ObjData(stro);
892
893  if (flags & DOH_REPLACE_FIRST)
894    count = 1;
895
896  if (flags & DOH_REPLACE_ID_END) {
897    return replace_simple(str, Char(token), Char(rep), flags, count, match_identifier_end);
898  } else if (flags & DOH_REPLACE_ID_BEGIN) {
899    return replace_simple(str, Char(token), Char(rep), flags, count, match_identifier_begin);
900  } else if (flags & DOH_REPLACE_ID) {
901    return replace_simple(str, Char(token), Char(rep), flags, count, match_identifier);
902  } else {
903    return replace_simple(str, Char(token), Char(rep), flags, count, match_simple);
904  }
905}
906
907/* -----------------------------------------------------------------------------
908 * void String_chop(DOH *str)
909 * ----------------------------------------------------------------------------- */
910
911static void String_chop(DOH *so) {
912  char *c;
913  String *str = (String *) ObjData(so);
914  /* Replace trailing whitespace */
915  c = str->str + str->len - 1;
916  while ((str->len > 0) && (isspace((int) *c))) {
917    if (str->sp >= str->len) {
918      str->sp--;
919      if (*c == '\n')
920	str->line--;
921    }
922    str->len--;
923    c--;
924  }
925  str->str[str->len] = 0;
926  assert(str->sp >= 0);
927  str->hashkey = -1;
928}
929
930static void String_setfile(DOH *so, DOH *file) {
931  DOH *fo;
932  String *str = (String *) ObjData(so);
933
934  if (!DohCheck(file)) {
935    fo = NewString(file);
936    Decref(fo);
937  } else
938    fo = file;
939  Incref(fo);
940  Delete(str->file);
941  str->file = fo;
942}
943
944static DOH *String_getfile(DOH *so) {
945  String *str = (String *) ObjData(so);
946  return str->file;
947}
948
949static void String_setline(DOH *so, int line) {
950  String *str = (String *) ObjData(so);
951  str->line = line;
952}
953
954static int String_getline(DOH *so) {
955  String *str = (String *) ObjData(so);
956  return str->line;
957}
958
959static DohListMethods StringListMethods = {
960  0,				/* doh_getitem */
961  0,				/* doh_setitem */
962  String_delitem,		/* doh_delitem */
963  String_insert,		/* doh_insitem */
964  String_delslice,		/* doh_delslice */
965};
966
967static DohFileMethods StringFileMethods = {
968  String_read,
969  String_write,
970  String_putc,
971  String_getc,
972  String_ungetc,
973  String_seek,
974  String_tell,
975  0,				/* close */
976};
977
978static DohStringMethods StringStringMethods = {
979  String_replace,
980  String_chop,
981};
982
983DohObjInfo DohStringType = {
984  "String",			/* objname */
985  DelString,			/* doh_del */
986  CopyString,			/* doh_copy */
987  String_clear,			/* doh_clear */
988  String_str,			/* doh_str */
989  String_data,			/* doh_data */
990  String_dump,			/* doh_dump */
991  String_len,	    	        /* doh_len */
992  String_hash,			/* doh_hash    */
993  String_cmp,			/* doh_cmp */
994  String_equal,	    	        /* doh_equal */
995  0,				/* doh_first    */
996  0,				/* doh_next     */
997  String_setfile,		/* doh_setfile */
998  String_getfile,		/* doh_getfile */
999  String_setline,		/* doh_setline */
1000  String_getline,		/* doh_getline */
1001  0,				/* doh_mapping */
1002  &StringListMethods,		/* doh_sequence */
1003  &StringFileMethods,		/* doh_file */
1004  &StringStringMethods,		/* doh_string */
1005  0,				/* doh_position */
1006  0
1007};
1008
1009
1010#define INIT_MAXSIZE  16
1011
1012/* -----------------------------------------------------------------------------
1013 * NewString(const char *c) - Create a new string
1014 * ----------------------------------------------------------------------------- */
1015
1016DOHString *DohNewString(const DOH *so) {
1017  int l = 0, max;
1018  String *str;
1019  char *s;
1020  int hashkey = -1;
1021  if (DohCheck(so)) {
1022    str = (String *) ObjData(so);
1023    s = (char *) String_data((String *) so);
1024    l = s ? str->len : 0;
1025    hashkey = str->hashkey;
1026  } else {
1027    s = (char *) so;
1028    l = s ? (int) strlen(s) : 0;
1029  }
1030
1031  str = (String *) DohMalloc(sizeof(String));
1032  str->hashkey = hashkey;
1033  str->sp = 0;
1034  str->line = 1;
1035  str->file = 0;
1036  max = INIT_MAXSIZE;
1037  if (s) {
1038    if ((l + 1) > max)
1039      max = l + 1;
1040  }
1041  str->str = (char *) DohMalloc(max);
1042  str->maxsize = max;
1043  if (s) {
1044    strcpy(str->str, s);
1045    str->len = l;
1046    str->sp = l;
1047  } else {
1048    str->str[0] = 0;
1049    str->len = 0;
1050  }
1051  return DohObjMalloc(&DohStringType, str);
1052}
1053
1054
1055/* -----------------------------------------------------------------------------
1056 * NewStringEmpty() - Create a new string
1057 * ----------------------------------------------------------------------------- */
1058
1059DOHString *DohNewStringEmpty(void) {
1060  int max = INIT_MAXSIZE;
1061  String *str = (String *) DohMalloc(sizeof(String));
1062  str->hashkey = 0;
1063  str->sp = 0;
1064  str->line = 1;
1065  str->file = 0;
1066  str->str = (char *) DohMalloc(max);
1067  str->maxsize = max;
1068  str->str[0] = 0;
1069  str->len = 0;
1070  return DohObjMalloc(&DohStringType, str);
1071}
1072
1073/* -----------------------------------------------------------------------------
1074 * NewStringWithSize(const char *c, int len) - Create a new string
1075 * ----------------------------------------------------------------------------- */
1076
1077DOHString *DohNewStringWithSize(const DOH *so, int len) {
1078  int l = 0, max;
1079  String *str;
1080  char *s;
1081  if (DohCheck(so)) {
1082    s = (char *) String_data((String *) so);
1083  } else {
1084    s = (char *) so;
1085  }
1086
1087  str = (String *) DohMalloc(sizeof(String));
1088  str->hashkey = -1;
1089  str->sp = 0;
1090  str->line = 1;
1091  str->file = 0;
1092  max = INIT_MAXSIZE;
1093  if (s) {
1094    l = (int) len;
1095    if ((l + 1) > max)
1096      max = l + 1;
1097  }
1098  str->str = (char *) DohMalloc(max);
1099  str->maxsize = max;
1100  if (s) {
1101    strncpy(str->str, s, len);
1102    str->len = l;
1103    str->sp = l;
1104  } else {
1105    str->str[0] = 0;
1106    str->len = 0;
1107  }
1108  return DohObjMalloc(&DohStringType, str);
1109}
1110
1111/* -----------------------------------------------------------------------------
1112 * NewStringf(DOH *fmt, ...)
1113 *
1114 * Create a new string from a list of objects.
1115 * ----------------------------------------------------------------------------- */
1116
1117DOHString *DohNewStringf(const DOH *fmt, ...) {
1118  va_list ap;
1119  DOH *r;
1120  va_start(ap, fmt);
1121  r = NewStringEmpty();
1122  DohvPrintf(r, Char(fmt), ap);
1123  va_end(ap);
1124  return (DOHString *) r;
1125}
1126
1127/* -----------------------------------------------------------------------------
1128 * Strcmp()
1129 * Strncmp()
1130 * Strstr()
1131 * Strchr()
1132 *
1133 * Some utility functions.
1134 * ----------------------------------------------------------------------------- */
1135
1136int DohStrcmp(const DOHString_or_char *s1, const DOHString_or_char *s2) {
1137  const char *c1 = Char(s1);
1138  const char *c2 = Char(s2);
1139  if (c1 && c2) {
1140    return strcmp(c1, c2);
1141  } else {
1142    return c1 < c2;
1143  }
1144}
1145
1146int DohStrncmp(const DOHString_or_char *s1, const DOHString_or_char *s2, int n) {
1147  return strncmp(Char(s1), Char(s2), n);
1148}
1149
1150char *DohStrstr(const DOHString_or_char *s1, const DOHString_or_char *s2) {
1151  char *p1 = Char(s1);
1152  char *p2 = Char(s2);
1153  return p1 == 0 || p2 == 0 || *p2 == '\0' ? p1 : strstr(p1, p2);
1154}
1155
1156char *DohStrchr(const DOHString_or_char *s1, int ch) {
1157  return strchr(Char(s1), ch);
1158}
1159