]> git.lyx.org Git - lyx.git/blob - src/LString.C
merge in the class Path changes
[lyx.git] / src / LString.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *       
6  *          Copyright (C) 1995 Matthias Ettrich
7  *          Copyright (C) 1995-1998 The LyX Team.
8  *
9  *======================================================*/
10
11
12 #include <config.h>
13
14 #ifdef __GNUG__
15 #pragma implementation "LString.h"
16 #endif
17
18 // #include <assert.h> // Hmm, make depend crashes with this one in. (Asger)
19 #include "LString.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23
24 //      $Id: LString.C,v 1.1 1999/09/27 18:44:36 larsbj Exp $   
25
26 #if !defined(lint) && !defined(WITH_WARNINGS)
27 static char vcid[] = "$Id: LString.C,v 1.1 1999/09/27 18:44:36 larsbj Exp $";
28 #endif /* lint */
29
30 static const unsigned short xtra = 4;
31 // The extra space is used to reduce the number of allocations
32 // and copies required if a string is unshared. The performance
33 // boost can be considerable -- in some tests using LyX I found
34 // a 97% reduction in the runtime of operator+=(char) in some
35 // code for writing LaTeX files using LStrings.
36 // This was originally implemented using:
37 //     xtra = 4 - (sizeof(srep) + len) % 4;
38 // where len is the length of the new string.
39 // This was intended to ensure the string was always aligned
40 // within 4-byte boundaries but after testing with xtra = 4,
41 // and finding a significant improvement I decided to just
42 // leave it at 4. ARRae.
43
44 LString::LString()
45 {
46         static srep empty_rep;
47         
48         p = &empty_rep;
49         empty_rep.n++;
50 }
51
52
53 LString::LString(char const *s)
54 {
55         static srep empty_rep;
56         
57         if (s && *s) {
58                 // > 99 %
59                 register unsigned int const len = strlen(s);
60                 p = (srep *) new char[sizeof(srep) + len + xtra];
61                 p->l = len;
62                 p->n = 0;
63                 p->e = xtra;
64                 memcpy(p->s, s, len + 1);
65         } else {
66                 // < 1 %
67                 p = &empty_rep;
68                 empty_rep.n++;
69         }
70 }
71
72
73 LString::LString(char const c)
74 {
75         static srep empty_rep;
76
77         if (c) {
78                 p = (srep *) new char[sizeof(srep) + 1 + xtra];
79                 p->l = 1;
80                 p->n = 0;
81                 p->e = xtra;
82                 p->s[0] = c;
83                 p->s[1] = '\0';
84         } else {
85                 p = &empty_rep;
86                 empty_rep.n++;
87         }
88 }
89
90
91 LString& LString::operator=(char const *s)
92 {
93         static srep empty_rep;
94 //      assert(p);
95
96         lose(); // disconnect self
97
98         if (s && *s) {
99                 register unsigned int const len = strlen(s);
100                 p = (srep *) new char[sizeof(srep) + len + xtra];
101                 p->l = len;
102                 p->n = 0;
103                 p->e = xtra;
104                 memcpy(p->s, s, len + 1);
105         } else {
106                 p = &empty_rep;
107                 empty_rep.n++;
108         }
109         
110         return *this;
111 }
112
113
114 LString& LString::operator=(LString const &x)
115 {
116 //      assert(p);
117         x.p->n++; // protect against ``st = st''
118
119         lose(); // disconnect self
120
121         p = x.p;
122         return *this;
123 }
124
125
126 LString& LString::operator=(char c)
127 {
128         static srep empty_rep;
129
130 //      assert(p);
131
132         lose(); // disconnect self
133
134         if (c) {
135                 p = (srep *) new char[sizeof(srep) + 1 + xtra];
136                 p->l = 1;
137                 p->n = 0;
138                 p->e = xtra;
139                 p->s[0] = c;
140                 p->s[1] = '\0';
141         } else {
142                 p = &empty_rep;
143                 empty_rep.n++;
144         }
145
146         return *this;
147 }
148
149
150 LString &LString::clean()
151 {
152         static srep empty_rep;
153
154         lose();
155         p = &empty_rep;
156         empty_rep.n++;
157         return *this;
158 }
159
160
161
162 char& LString::operator[](int i)
163 {
164 #ifdef DEVEL_VERSION
165         if (i < 0 || i >= length()) {
166                 fprintf(stderr,"LString::operator[]: Index out of range: '%s' %d\n", p->s, i);
167                 abort();
168         }
169 #endif
170
171         if (p->n > 0) { // clone to maintain value semantics
172                 srep * np = (srep *) new char[sizeof(srep) + p->l];
173                 np->l = p->l;
174                 np->n = 0;
175                 np->e = 0;
176                 memcpy(np->s, p->s, length() + 1);
177                 p->n--;
178                 p = np;
179         }
180         return p->s[i];
181 }
182
183 #ifndef const
184 char const& LString::operator[](int i) const
185 {
186 #ifdef DEVEL_VERSION
187         if (i < 0 || i >= length()) {
188                 fprintf(stderr,"LString::operator[] const: Index out of range: '%s' i:%d.\n",p->s,i);
189                 abort();
190         }
191 #endif
192
193         return p->s[i];
194 }
195 #endif /* ndef const */
196
197 LString &LString::operator+=(LString const & x)
198 {
199         if (x.empty()) return *this;
200
201         register unsigned int const len = length() + x.length();
202         if (p->n || p->e < x.length()) {
203                 srep *np = (srep *) new char[sizeof(srep) + len + xtra];
204                 np->l = len;
205                 np->n = 0;
206                 np->e = xtra;
207                 memcpy(np->s, p->s, length());
208                 memcpy(np->s + length(), x.p->s, x.length() + 1);
209                 lose(); // disconnect self
210                 p = np;
211         } else {
212                 // in cases where x += x and x is short the
213                 // explicit setting of the '\0' stops any problems
214                 memcpy(p->s + length(), x.p->s, x.length());
215                 p->s[len] = '\0';
216                 p->l += x.length();
217                 p->e -= x.length();
218         }
219
220         return *this;
221 }
222
223
224 LString &LString::operator+=(char const *x)
225 {
226         if (!x || *x==0) return *this;
227
228         register unsigned int const xlen = strlen(x);
229         register unsigned int const len = length() + xlen;
230         if (p->n || p->e < xlen) {
231                 srep *np = (srep *) new char[sizeof(srep) + len + xtra];
232                 np->l = len;
233                 np->n = 0;
234                 np->e = xtra;
235                 memcpy(np->s, p->s, length());
236                 memcpy(np->s + length(), x, xlen + 1);
237                 lose(); // disconnect self
238                 p = np;
239         } else {
240                 // Explicitly setting the '\0' stops any
241                 // problems caused by x += x.c_str()
242                 memcpy(p->s + length(), x, xlen);
243                 p->s[len] = '\0';
244                 p->l += xlen;
245                 p->e -= xlen;
246         }
247
248         return *this;
249 }
250
251
252 LString &LString::operator+=(char c)
253 {
254         register unsigned int const len = length() + 1;
255         if (!p->n && p->e) {
256                 // 80% (from profiling)
257                 // This is where all the speed gains are made.
258                 p->s[length()] = c;
259                 p->s[len] = '\0';
260                 p->l = len;
261                 p->e -= 1;
262         } else {
263                 // 20%
264                 srep *np = (srep *) new char[sizeof(srep) + len + xtra];
265                 np->l = len;
266                 np->n = 0;
267                 np->e = xtra;
268                 memcpy(np->s, p->s, length());
269                 np->s[length()] = c;
270                 np->s[len] = '\0';
271                 lose(); // disconnect self
272                 p = np;
273         }
274
275         return *this;
276 }
277
278
279 LString &LString::operator+=(int i)
280 {
281         return this->operator+=((long)i);
282 }
283
284
285 LString &LString::operator+=(long i)
286 {
287         unsigned int tmplen = 0;
288         long a = i;
289         // calculate the length of i
290         if (!i) {
291                 tmplen = 1;
292         } else {
293                 if (a < 0) {
294                         tmplen++; // minus sign
295                         a = -a; // switch sign
296                 }
297                 while(a >= 1) { a = a/10; tmplen++;}
298         }
299         char *str = new char[tmplen + 1];
300         sprintf(str, "%ld", i);
301         this->operator+=(str);
302         delete[] str;
303
304         return *this;
305 }
306
307
308 char * LString::copy() const
309 {
310         char * new_string = new char[length()+1];
311         memcpy(new_string, p->s, length() + 1);
312         return new_string;
313 }
314
315
316 bool LString::contains(char const *a) const
317 {
318         return strstr(p->s, a) != NULL;
319 }
320
321
322 LString& LString::substring(int i1, int i2)
323 {
324 #ifdef DEVEL_VERSION
325         if (i1 > i2 || i1 >= length() || i2 >= length()) {
326                 fprintf(stderr,
327                         "LString::substring: Wrong indexing in substring:"
328                         " '%s' i1=%d i2=%d\n", p->s, i1, i2);
329                 abort();
330         }
331 #endif
332
333         if (i1==i2)
334                 this->operator=(p->s[i1]);
335         else {
336                 char *str = new char[i2 - i1 +2];
337                 int i;
338                 for (i=0; i1<=i2; str[i++] = p->s[i1++]);
339                 str[i] = 0;
340                 this->operator=(str);
341                 delete[] (char*)str;
342         }
343         return *this;
344 }
345
346
347 // ale970405+lasgoutt-970425
348 LString LString::token(char delim, int n) const
349 {
350         int k=0, i;
351         LString tokbuf;
352
353         tokbuf = *this;   
354         // Find delimiter or end of string
355         for (i = 0; i < tokbuf.length(); i++) {
356                 if (tokbuf[i] == delim) {
357                         if (n > 0) {
358                                 k = i+1;
359                                 n--;
360                         } else break;
361                 }
362         }
363
364         // Return the token if not empty
365         if (n == 0 && k<i){
366                 return tokbuf.substring(k, i-1);
367         } else {
368                 return LString();
369         }
370 }
371
372
373 // this could probably be faster and/or cleaner, but it seems to work (JMarc)
374 int LString::tokenPos(char delim, LString const &tok)
375 {
376         int i=0;
377         LString str = *this;
378         LString tmptok;
379
380         while (!str.empty()) {
381                 str.split(tmptok, delim);
382                 if (tok==tmptok)
383                         return i;
384                 i++;
385         }
386         return -1;
387 }
388
389
390 LString& LString::split(LString & piece, char delim)
391 {
392         int i=0;
393         // Find delimiter or end of string
394         while (i<length() && p->s[i] != delim)
395                 i++;
396         // If not the first, we go for a substring
397         if (i>0) {
398                 piece = *this;
399                 piece.substring(0, i-1);
400         } else
401                 piece.clean();
402
403         if (i < length()-1)
404                 this->substring(i+1, length()-1);
405         else
406                 clean();
407         return *this;
408 }
409
410
411 LString& LString::split(char delim)
412 {
413         int i=0;
414         // Find delimiter or end of string
415         while (i<length() && p->s[i] != delim)
416                 i++;
417
418         if (i < length()-1)
419                 this->substring(i+1, length()-1);
420         else
421                 clean();
422         return *this;
423 }
424
425
426 // ale970521
427 LString& LString::rsplit(LString & piece, char delim)
428 {
429         int i=length()-1;
430         // Find delimiter or begin of string
431         while (i>=0 && p->s[i] != delim)
432                 i--;
433         // If not the last, we go for a substring
434         if (i < length()-1) {
435                 piece = *this;
436                 piece.substring(0, i-1);
437                 this->substring(i+1, length()-1);
438         } else {
439                 piece.clean();
440                 clean();
441         }
442         return *this;
443 }
444
445
446 LString& LString::strip(char const c)
447 {
448         int i=length()-1;
449         for (; i>=0 && p->s[i] == c; i--);
450         if (i<0) 
451                 clean();
452         else
453                 this->substring(0, i);
454         return *this;
455 }
456
457
458 LString& LString::frontStrip(char const c)
459 {
460         int i=0;
461         while (i < length() && p->s[i] == c) i++;
462         if (i > 0)
463                 if (i == length())
464                         clean();
465                 else
466                         this->substring (i, length()-1);
467         return *this;
468 }
469
470
471 bool LString::prefixIs(char const * pre) const
472 {
473         if ((int) strlen(pre) > length())
474                 return false;
475         else
476                 return strncmp(p->s, pre, strlen(pre))==0;
477 }
478
479
480 bool LString::suffixIs(char c) const
481 {
482         if (empty()) return false;
483         return p->s[length()-1] == c;
484 }
485
486
487 bool LString::suffixIs(char const * suf) const
488 {
489         int suflen = (int) strlen(suf);
490         if (suflen > length())
491                 return false;
492         else
493                 return strncmp(p->s + (length()-suflen), suf, suflen)==0;
494 }
495
496
497 LString& LString::subst(char oldchar, char newchar)
498 {
499         for (int i=0; i<length() ; i++)
500                 if (p->s[i]==oldchar)
501                         p->s[i]=newchar;
502         return *this;
503 }
504
505
506 LString& LString::subst(char const * oldstr, LString const & newstr)
507 {
508         LString lstr = *this;
509         char * str = lstr.copy();
510         char * first;
511       
512         while((first=strstr(str,oldstr))){
513                 if (first==str) lstr.clean();
514                 else lstr.substring(0,first-str-1);
515                 lstr+=newstr;
516                 lstr+=first+strlen(oldstr);
517                 delete[] (char*)str;
518                 str = lstr.copy();
519         }
520         delete[] (char*)str;
521         return *this=lstr;
522 }
523
524
525 LString& LString::lowercase()
526 {
527         for (int i=0; i<length() ; i++)
528                 p->s[i] = tolower((unsigned char) p->s[i]);
529         return *this;
530 }
531
532
533 bool LString::regexMatch(LString const & pattern) const
534 {
535         if (pattern.empty())
536                 return true;
537         if (empty())
538                 return false;
539         
540         int si=0, pi=0;
541         int const sl = length();
542         int const pl = pattern.length();        
543
544         while (si < sl && pi < pl) {
545                 if (pattern[pi]=='*') {
546                         // Skip all consequtive *s
547                         while (pattern[pi] == '*') {
548                                 pi++;
549                                 if (pi == pl)
550                                         return true;
551                         }
552
553                         // Get next chunk of pattern to match
554                         LString temp= pattern;
555                         temp.substring(pi, pl-1);
556                         LString chunk;
557                         temp.split(chunk, '*');
558
559                         if (!chunk.empty() && pattern[pl-1] == '*' && 
560                             temp.empty())
561                                 temp = '*';
562
563                         if (temp.empty()) {
564                                 // Last chunk, see if tail matches
565                                 temp = *this;
566                                 temp.substring(sl - chunk.length(), sl - 1);
567                                 return temp == chunk;
568                         } else {
569                                 // Middle chunk, see if we can find a match
570                                 bool match = false;
571                                 while (!match && si<sl) {
572                                         temp = *this;
573                                         temp.substring(si, sl - 1);
574                                         match = temp.prefixIs(chunk.c_str());
575                                         si++;
576                                 };
577                                 if (!match)
578                                         return false;
579                                 si += chunk.length()-1;
580                                 pi += chunk.length();
581                                 if (si==sl && pi==pl-1)
582                                         return true;
583                         }
584                 } else if (operator[](si++) != pattern[pi++]) {
585                                 return false;
586                 }
587         }
588         if (pi < pl || si < sl)
589                 return false;   
590         return true;
591 }