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