1 /* Part of XPCE --- The SWI-Prolog GUI toolkit
2
3 Author: Jan Wielemaker and Anjo Anjewierden
4 E-mail: jan@swi.psy.uva.nl
5 WWW: http://www.swi.psy.uva.nl/projects/xpce/
6 Copyright (c) 1985-2002, University of Amsterdam
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in
18 the documentation and/or other materials provided with the
19 distribution.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <h/kernel.h>
36 #include <h/unix.h>
37
38 static status appendString(StringObj, CharArray);
39 static status setString(StringObj str, PceString s);
40
41 StringObj
create_string_from_str(PceString s,int tmp)42 create_string_from_str(PceString s, int tmp)
43 { string s2;
44 CharArray c;
45 StringObj str;
46 charA *do_free = NULL;
47
48 if ( s->s_iswide )
49 { const charW *txt = s->s_textW;
50 const charW *end = &txt[s->s_size];
51 charA *p;
52
53 for( ; txt < end; txt++ )
54 { if ( *txt > 0xff )
55 goto canonical;
56 }
57
58 str_inithdr(&s2, FALSE);
59 s2.s_size = s->s_size;
60 if ( !(s2.s_textA = alloca(s->s_size)) )
61 { s2.s_textA = pceMalloc(s->s_size);
62 do_free = s2.s_textA;
63 }
64 for(txt = s->s_textW, p = s2.s_textA; txt < end; )
65 *p++ = *txt++;
66
67 s = &s2;
68 }
69
70 canonical:
71 c = StringToScratchCharArray(s);
72 if ( tmp )
73 str = tempObject(ClassString, name_procent_s, c, EAV);
74 else
75 str = answerObject(ClassString, name_procent_s, c, EAV);
76 doneScratchCharArray(c);
77
78 if ( do_free )
79 pceFree(do_free);
80
81 return str;
82 }
83
84
85 StringObj
StringToString(PceString s)86 StringToString(PceString s)
87 { return create_string_from_str(s, FALSE);
88 }
89
90
91 StringObj
StringToTempString(PceString s)92 StringToTempString(PceString s)
93 { return create_string_from_str(s, TRUE);
94 }
95
96
97 StringObj
CtoString(const char * s)98 CtoString(const char *s)
99 { CharArray c = CtoScratchCharArray(s);
100 StringObj str = answerObject(ClassString, name_procent_s, c, EAV);
101 doneScratchCharArray(c);
102
103 return str;
104 }
105
106
107 StringObj
staticCtoString(const char * s)108 staticCtoString(const char *s)
109 { CharArray c = CtoScratchCharArray(s);
110 StringObj str;
111
112 c->data.s_readonly = TRUE;
113 str = answerObject(ClassString, name_procent_s, c, EAV);
114 doneScratchCharArray(c);
115
116 return str;
117 }
118
119
120 StringObj
CtoTempString(char * s)121 CtoTempString(char *s)
122 { CharArray c = CtoScratchCharArray(s);
123 StringObj str = tempObject(ClassString, name_procent_s, c, EAV);
124 doneScratchCharArray(c);
125
126 return str;
127 }
128
129
130 static StringObj
getModifyString(StringObj str,CharArray value)131 getModifyString(StringObj str, CharArray value)
132 { answer(answerObject(classOfObject(str), name_procent_s, value, EAV));
133 }
134
135
136 static void
prepareWriteString(StringObj s)137 prepareWriteString(StringObj s)
138 { if ( s->data.s_readonly )
139 setString(s, &s->data);
140 }
141
142
143 static void
promoteString(StringObj s)144 promoteString(StringObj s)
145 { if ( !s->data.s_iswide )
146 { string ws;
147 const charA *f = s->data.s_textA;
148 const charA *e = &f[s->data.s_size];
149 charW *t;
150
151 str_inithdr(&ws, TRUE);
152 ws.s_size = s->data.s_size;
153 str_alloc(&ws);
154
155 for(t=ws.s_textW; f<e;)
156 *t++ = *f++;
157
158 s->data = ws;
159 }
160 }
161
162
163 status
initialiseStringv(StringObj str,CharArray fmt,int argc,Any * argv)164 initialiseStringv(StringObj str, CharArray fmt, int argc, Any *argv)
165 { if ( isDefault(fmt) )
166 { str_inithdr(&str->data, FALSE);
167 str->data.s_size = 0;
168 str_alloc(&str->data);
169 } else if ( (Name) fmt == name_procent_s &&
170 argc == 1 && instanceOfObject(argv[0], ClassCharArray) )
171 { CharArray v = argv[0];
172
173 str_cphdr(&str->data, &v->data);
174 if ( v->data.s_readonly )
175 { str->data.s_textA = v->data.s_textA;
176
177 DEBUG(NAME_readOnly, Cprintf("Shared %s\n", pp(str)));
178 } else
179 { str_alloc(&str->data);
180 memcpy(str->data.s_textA, v->data.s_textA, str_datasize(&v->data));
181 }
182 } else
183 TRY(str_writefv(&str->data, fmt, argc, argv));
184
185 succeed;
186 }
187
188
189 static StringObj
getCopyString(StringObj s)190 getCopyString(StringObj s)
191 { answer(answerObject(classOfObject(s), name_procent_s, s, EAV));
192 }
193
194
195 static StringObj
convertString(Class class,Any obj)196 convertString(Class class, Any obj)
197 { if ( instanceOfObject(obj, ClassString) )
198 answer((StringObj) obj);
199 else if ( instanceOfObject(obj, ClassCharArray) )
200 answer(answerObject(ClassString, name_procent_s, obj, EAV));
201 else
202 { char *s = toCharp(obj);
203
204 if ( s != NULL )
205 answer(CtoString(s));
206 else
207 fail;
208 }
209 }
210
211
212 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
213 Load/store a string to/from file. Format:
214
215 <string> ::= <charp>
216 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
217
218 static status
storeString(StringObj s,FileObj file)219 storeString(StringObj s, FileObj file)
220 { TRY(storeSlotsObject(s, file));
221
222 return storeStringFile(file, &s->data);
223 }
224
225
226 static status
loadString(StringObj s,IOSTREAM * fd,ClassDef def)227 loadString(StringObj s, IOSTREAM *fd, ClassDef def)
228 { TRY(loadSlotsObject(s, fd, def));
229
230 return loadStringFile(fd, &s->data);
231 }
232
233
234 static status
formatString(StringObj s,CharArray fmt,int argc,Any * argv)235 formatString(StringObj s, CharArray fmt, int argc, Any *argv)
236 { prepareWriteString(s);
237
238 str_unalloc(&s->data);
239 str_writefv(&s->data, fmt, argc, argv);
240
241 return setString(s, &s->data);
242 }
243
244
245 status
valueString(StringObj s1,CharArray s2)246 valueString(StringObj s1, CharArray s2)
247 { if ( equalCharArray((CharArray) s1, s2, OFF) )
248 succeed;
249
250 return setString(s1, &s2->data);
251 }
252
253
254 static status
prependString(StringObj s1,StringObj s2)255 prependString(StringObj s1, StringObj s2)
256 { return str_insert_string(s1, ZERO, &s2->data);
257 }
258
259
260 static status
ensureNlString(StringObj s1,CharArray s2)261 ensureNlString(StringObj s1, CharArray s2)
262 { if ( s1->data.s_size > 0 && str_fetch(&s1->data, s1->data.s_size-1) != '\n' )
263 str_insert_string(s1, DEFAULT, str_nl(&s1->data));
264
265 if ( notDefault(s2) )
266 return appendString(s1, s2);
267
268 succeed;
269 }
270
271
272 static status
ensureSuffixString(StringObj s,CharArray suff,BoolObj ign_case)273 ensureSuffixString(StringObj s, CharArray suff, BoolObj ign_case)
274 { if ( !suffixCharArray((CharArray) s, suff, ign_case) )
275 appendString(s, suff);
276
277 succeed;
278 }
279
280
281 static status
newlineString(StringObj s,Int times)282 newlineString(StringObj s, Int times)
283 { int tms;
284
285 if ( isDefault(times) )
286 times = ONE;
287 tms = valInt(times);
288
289 { PceString nl = str_nl(&s->data);
290 LocalString(buf, s->data.s_iswide, nl->s_size * tms);
291 int i;
292
293 for(i=0; i<tms; i++)
294 str_ncpy(buf, i * nl->s_size, nl, 0, nl->s_size);
295 buf->s_size = nl->s_size * tms;
296
297 return str_insert_string(s, DEFAULT, buf);
298 }
299 }
300
301
302 status
insertCharacterString(StringObj str,Int chr,Int where,Int times)303 insertCharacterString(StringObj str, Int chr, Int where, Int times)
304 { int tms = isDefault(times) ? 1 : valInt(times);
305 wint_t c = valInt(chr);
306 int iswide = (c <= 0xff);
307 LocalString(buf, iswide, tms);
308 int i;
309
310 for(i=0; i<tms; i++)
311 str_store(buf, i, c);
312 buf->s_size = tms;
313 str_insert_string(str, where, buf);
314
315 succeed;
316 }
317
318
319 static status
appendString(StringObj s1,CharArray s2)320 appendString(StringObj s1, CharArray s2)
321 { return str_insert_string(s1, DEFAULT, &s2->data);
322 }
323
324
325 static status
stripString(StringObj str,Name where)326 stripString(StringObj str, Name where)
327 { PceString s = &str->data;
328 int size = s->s_size;
329 int from = 0;
330 int to = size;
331 string buf;
332
333 if ( where != NAME_trailing )
334 { while( from < size && iswspace(str_fetch(s, from)))
335 from++;
336 }
337
338 if ( where != NAME_leading )
339 { while( to > from && iswspace(str_fetch(s, to-1)) )
340 to--;
341 }
342
343 str_cphdr(&buf, s);
344 buf.s_text = str_textp(s, from);
345 buf.s_size = to - from;
346
347 return setString(str, &buf);
348 }
349
350
351 static status
untabifyString(StringObj str,Any tabs)352 untabifyString(StringObj str, Any tabs)
353 { Int n;
354
355 if ( isDefault(tabs) )
356 tabs = toInt(8);
357
358 if ( instanceOfObject(tabs, ClassVector) )
359 { int size = valInt(((Vector)tabs)->size);
360 Any *elements = ((Vector)tabs)->elements;
361 int maxtab = -1;
362 int n;
363
364 for(n = 0; n<size; n++)
365 { if ( !isInteger(elements[n]) )
366 return errorPce(elements[n], NAME_unexpectedType, TypeInt);
367 if ( n <= maxtab )
368 return errorPce(str, NAME_badTabStopVector);
369 maxtab = n;
370 }
371
372 { int size = str->data.s_size;
373 PceString s = &str->data;
374 LocalString(buf, s->s_iswide, size + maxtab);
375 int i=0, o=0, col=0;
376
377 for( ; i < size; i++ )
378 { wint_t c = str_fetch(s, i);
379
380 if ( c == '\t' )
381 { int destcol = col+1;
382
383 for(n=0; n<size; n++)
384 { if ( valInt(elements[n]) >= destcol )
385 { destcol = valInt(elements[n]);
386 break;
387 }
388 }
389
390 do
391 { str_store(buf, o++, ' ');
392 col++;
393 } while ( col != destcol );
394 } else
395 { str_store(buf, o++, c);
396 if ( c == '\n' )
397 col = 0;
398 else
399 col++;
400 }
401 }
402 buf->s_size = o;
403
404 return setString(str, buf);
405 }
406 } else if ( (n = checkType(tabs, TypeInt, NIL)) )
407 { int size = str->data.s_size;
408 int d = valInt(n);
409 PceString s = &str->data;
410 int tabs = str_count_chr(s, 0, size, '\t');
411 LocalString(buf, s->s_iswide, size + d * tabs);
412 int i=0, o=0, col=0;
413
414 for( ; i < size; i++ )
415 { wint_t c = str_fetch(s, i);
416
417 if ( c == '\t' )
418 { do
419 { str_store(buf, o++, ' ');
420 col++;
421 } while ( col % d );
422 } else
423 { str_store(buf, o++, c);
424 if ( c == '\n' )
425 col = 0;
426 else
427 col++;
428 }
429 }
430 buf->s_size = o;
431
432 return setString(str, buf);
433 }
434
435 fail;
436 }
437
438
439 status
upcaseString(StringObj s)440 upcaseString(StringObj s)
441 { prepareWriteString(s);
442
443 str_upcase(&s->data, 0, s->data.s_size);
444 return setString(s, &s->data);
445 }
446
447
448 static status
downcaseString(StringObj s)449 downcaseString(StringObj s)
450 { prepareWriteString(s);
451
452 str_downcase(&s->data, 0, s->data.s_size);
453 return setString(s, &s->data);
454 }
455
456
457 static status
truncateString(StringObj s,Int n)458 truncateString(StringObj s, Int n)
459 { return deleteString(s, n, DEFAULT);
460 }
461
462
463 static status
translateString(StringObj str,Int c1,Int c2)464 translateString(StringObj str, Int c1, Int c2)
465 { wint_t f = valInt(c1);
466 int changed = 0;
467 PceString s = &str->data;
468 int size = s->s_size;
469 int i = 0;
470
471 if ( notNil(c2) )
472 { wint_t t = valInt(c2);
473
474 if ( t > 0xff )
475 promoteString(str);
476 else
477 prepareWriteString(str);
478
479 for(;;)
480 { if ( (i = str_next_index(s, i, f)) >= 0 )
481 { str_store(s, i++, t);
482 changed++;
483 } else
484 break;
485 }
486
487 if ( changed )
488 setString(str, &str->data); /* forward changes */
489 } else /* delete c1's */
490 { LocalString(buf, s->s_iswide, size);
491 int o = 0;
492
493 for(;;)
494 { int ni;
495
496 if ( (ni = str_next_index(s, i, f)) >= 0 )
497 { str_ncpy(buf, o, s, i, ni-i);
498 o += ni-i;
499 i = ni+1;
500 changed++;
501 } else
502 break;
503 }
504 if ( changed )
505 { str_ncpy(buf, o, s, i, size-i);
506 o += size-i;
507 buf->s_size = o;
508
509 setString(str, buf);
510 }
511 }
512
513 succeed;
514 }
515
516
517 static status
characterString(StringObj str,Int index,Int chr)518 characterString(StringObj str, Int index, Int chr)
519 { int i = valInt(index);
520 wint_t c = valInt(chr);
521
522 if ( i < 0 || i >= str->data.s_size )
523 fail;
524
525 if ( str_fetch(&str->data, i) != c )
526 { if ( c > 0xff && !str->data.s_iswide )
527 promoteString(str);
528 else
529 prepareWriteString(str);
530 str_store(&str->data, i, c);
531 setString(str, &str->data);
532 }
533
534 succeed;
535 }
536
537
538 status
deleteString(StringObj str,Int start,Int length)539 deleteString(StringObj str, Int start, Int length)
540 { PceString s = &str->data;
541 int size = s->s_size;
542 int f = valInt(start);
543 int e = (isDefault(length) ? size : valInt(length)) + f - 1;
544 int d;
545
546 if ( f < 0 ) s = 0;
547 if ( f >= size ) succeed;
548 if ( e < f ) succeed;
549 if ( e >= size )
550 e = size - 1;
551 d = e - f + 1;
552
553 { LocalString(buf, s->s_iswide, size-d);
554
555 str_ncpy(buf, 0, s, 0, f);
556 str_ncpy(buf, f, s, e+1, size - (e+1));
557 buf->s_size = size-d;
558
559 setString(str, buf);
560 }
561
562 succeed;
563 }
564
565
566 status
insertString(StringObj s1,Int n,CharArray s2)567 insertString(StringObj s1, Int n, CharArray s2)
568 { return str_insert_string(s1, n, &s2->data);
569 }
570
571
572 /********************************
573 * NON-PCE-TYPE MANIPULATION *
574 *********************************/
575
576 static status
setString(StringObj str,PceString s)577 setString(StringObj str, PceString s)
578 { Class class = classOfObject(str);
579
580 if ( str->data.s_text != s->s_text ||
581 str_allocsize(&str->data) != str_allocsize(s) ||
582 str->data.s_readonly )
583 { string s2 = *s;
584
585 DEBUG(NAME_readOnly,
586 if ( str->data.s_readonly )
587 Cprintf("Copying %s", pp(str)));
588
589 str_alloc(&s2);
590 memcpy(s2.s_textA, s->s_textA, str_datasize(s));
591 str_unalloc(&str->data);
592 str->data = s2;
593 } else
594 str->data = *s;
595
596 if ( notNil(class->changed_messages) )
597 changedObject(str, NAME_text, EAV);
598
599 succeed;
600 }
601
602
603 status
str_insert_string(StringObj str,Int where,PceString s)604 str_insert_string(StringObj str, Int where, PceString s)
605 { int sz = str->data.s_size;
606 int iswide = (str->data.s_iswide || s->s_iswide);
607 LocalString(buf, iswide, sz + s->s_size);
608 int p = (isDefault(where) ? sz : valInt(where));
609
610 if ( p < 0 ) p = 0;
611 if ( p > sz ) p = sz;
612
613 str_ncpy(buf, 0, &str->data, 0, p);
614 str_ncpy(buf, p, s, 0, s->s_size);
615 str_ncpy(buf, p+s->s_size, &str->data, p, str->data.s_size - p);
616 buf->s_size = sz + s->s_size;
617
618 return setString(str, buf);
619 }
620
621
622 /* used in text <-selected */
623 StringObj
getSubString(StringObj n,Int start,Int end)624 getSubString(StringObj n, Int start, Int end)
625 { string s;
626 int x, y;
627 int len = n->data.s_size;
628
629 x = valInt(start);
630 y = (isDefault(end) ? len : valInt(end));
631 if ( x < 0 || y > len || x > y )
632 fail;
633
634 str_cphdr(&s, &n->data);
635 s.s_size = y-x;
636 if ( isstrA(&n->data) )
637 s.s_textA = &n->data.s_textA[x];
638 else
639 s.s_textW = &n->data.s_textW[x];
640
641 answer(StringToString(&s));
642 }
643
644
645 /*******************************
646 * CLASS DECLARATION *
647 *******************************/
648
649 /* Type declarations */
650
651 static char *T_insert[] =
652 { "at=[int]", "text=char_array" };
653 static char *T_character[] =
654 { "at=int", "char=char" };
655 static char *T_insertCharacter[] =
656 { "char=char", "at=[0..]", "times=[0..]" };
657 static char *T_format[] =
658 { "format=[char_array]", "argument=any ..." };
659 static char *T_translate[] =
660 { "from=char", "into=char*" };
661 static char *T_delete[] =
662 { "from=int", "length=[int]" };
663 static char *T_ensureSuffix[] =
664 { "text=char_array", "ignore_case=[bool]" };
665
666 /* Instance Variables */
667
668 #define var_string NULL
669 /*
670 vardecl var_string[] =
671 {
672 };
673 */
674
675 /* Send Methods */
676
677 static senddecl send_string[] =
678 { SM(NAME_initialise, 2, T_format, initialiseStringv,
679 DEFAULT, "Create a string, initialise as ->format"),
680 SM(NAME_downcase, 0, NULL, downcaseString,
681 NAME_case, "Change all letters in string to lower case"),
682 SM(NAME_upcase, 0, NULL, upcaseString,
683 NAME_case, "Change all letters in string to upper case"),
684 SM(NAME_append, 1, "text=char_array", appendString,
685 NAME_content, "Append to the string"),
686 SM(NAME_character, 2, T_character, characterString,
687 NAME_content, "Change character at 0-based index"),
688 SM(NAME_delete, 2, T_delete, deleteString,
689 NAME_content, "Delete range from 0-based start and length"),
690 SM(NAME_ensureNl, 1, "[char_array]", ensureNlString,
691 NAME_content, "Ensure string has trailing newline [and append string]"),
692 SM(NAME_ensureSuffix, 2, T_ensureSuffix, ensureSuffixString,
693 NAME_content, "Ensure string has indicated suffix"),
694 SM(NAME_insert, 2, T_insert, insertString,
695 NAME_content, "Insert string at 0-based index"),
696 SM(NAME_insertCharacter, 3, T_insertCharacter, insertCharacterString,
697 NAME_content, "Insert times character(s) at location"),
698 SM(NAME_newline, 1, "times=[0..]", newlineString,
699 NAME_content, "Append a newline to string"),
700 SM(NAME_prepend, 1, "char_array", prependString,
701 NAME_content, "Add argument at the beginning"),
702 SM(NAME_strip, 1, "[{leading,trailing}]", stripString,
703 NAME_content, "Strip leading/trailing blanks"),
704 SM(NAME_translate, 2, T_translate, translateString,
705 NAME_content, "Map occurrences of 1-st arg into 2-nd arg"),
706 SM(NAME_truncate, 1, "int", truncateString,
707 NAME_content, "Truncate string to argument characters"),
708 SM(NAME_value, 1, "text=char_array", valueString,
709 NAME_copy, "Set the contents of the string"),
710 SM(NAME_format, 2, T_format, formatString,
711 NAME_format, "Format (like printf) in string"),
712 SM(NAME_untabify, 1, "tabs=[int|vector]", untabifyString,
713 NAME_indentation, "Replace tab characters by spaces")
714 };
715
716 /* Get Methods */
717
718 static getdecl get_string[] =
719 { GM(NAME_convert, 1, "string", "any", convertString,
720 DEFAULT, "Convert name, int, real, etc."),
721 GM(NAME_modify, 1, "string", "char_array", getModifyString,
722 DEFAULT, "Make modified version"),
723 GM(NAME_copy, 0, "string", NULL, getCopyString,
724 NAME_copy, "Copy with the same text")
725 };
726
727 /* Resources */
728
729 #define rc_string NULL
730 /*
731 static classvardecl rc_string[] =
732 {
733 };
734 */
735
736 /* Class Declaration */
737
738 static Name string_termnames[] = { NAME_value };
739
740 ClassDecl(string_decls,
741 var_string, send_string, get_string, rc_string,
742 1, string_termnames,
743 "$Rev$");
744
745 status
makeClassString(Class class)746 makeClassString(Class class)
747 { declareClass(class, &string_decls);
748 setLoadStoreFunctionClass(class, loadString, storeString);
749
750 succeed;
751 }
752
753