1 /* xlstr - xlisp string and character built-in functions */
2 /* Copyright (c) 1989, by David Michael Betz. */
3 /* You may give out copies of this software; for conditions see the file */
4 /* COPYING included with this distribution. */
8 /* local definitions */
9 #define fix(n) cvfixnum((FIXTYPE)(n))
13 /* Function prototypes */
14 LOCAL VOID getbounds
P5H(LVAL
, LVAL
, LVAL
, unsigned *, unsigned *);
15 LOCAL LVAL strcompare
P2H(int, int);
16 LOCAL LVAL changecase
P2H(int, int);
17 LOCAL
int inbag
P2H(int, LVAL
);
18 LOCAL LVAL trim
P1H(int);
19 LOCAL LVAL chrcompare
P2H(int, int);
21 /* getbounds - get the start and end bounds of a string */
22 LOCAL VOID getbounds
P5C(LVAL
, str
, LVAL
, skey
, LVAL
, ekey
,
23 unsigned *, pstart
, unsigned *, pend
)
29 /* get the length of the string */
30 len
= getslength(str
);
32 /* get the starting index */
33 if (xlgkfixnum(skey
,&arg
)) {
34 *pstart
= (unsigned) (n
= getfixnum(arg
));
35 if (n
< 0 || n
> (FIXTYPE
)len
)
36 xlerror("string index out of bounds",arg
);
41 /* get the ending index */
42 if (xlgetkeyarg(ekey
, &arg
) && arg
!= NIL
) {
43 if (!fixp(arg
)) xlbadtype(arg
);
44 *pend
= (unsigned)(n
= getfixnum(arg
));
45 if (n
< 0 || n
> (FIXTYPE
)len
)
46 xlerror("string index out of bounds",arg
);
51 /* make sure the start is less than or equal to the end */
53 xlerror("starting index error",cvfixnum((FIXTYPE
)*pstart
));
56 /* strcompare - compare strings */
57 LOCAL LVAL strcompare
P2C(int, fcn
, int, icase
)
59 unsigned start1
,end1
,start2
,end2
;
61 unsigned char *p1
, *p2
;
65 str1
= xlgastrorsym();
66 str2
= xlgastrorsym();
68 /* get the substring specifiers */
69 getbounds(str1
,k_1start
,k_1end
,&start1
,&end1
);
70 getbounds(str2
,k_2start
,k_2end
,&start2
,&end2
);
74 /* setup the string pointers */
75 p1
= (unsigned char *) &getstring(str1
)[start1
];
76 p2
= (unsigned char *) &getstring(str2
)[start2
];
78 /* compare the strings */
79 for (; start1
< end1
&& start2
< end2
; ++start1
,++start2
) {
83 if (ISUPPER(ch1
)) ch1
= TOLOWER(ch1
);
84 if (ISUPPER(ch2
)) ch2
= TOLOWER(ch2
);
88 case '<': return (ch1
< ch2
? fix(start1
) : NIL
);
89 case 'L': return (ch1
<= ch2
? fix(start1
) : NIL
);
90 case '=': return (NIL
);
91 case '#': return (fix(start1
));
92 case 'G': return (ch1
>= ch2
? fix(start1
) : NIL
);
93 case '>': return (ch1
> ch2
? fix(start1
) : NIL
);
97 /* check the termination condition */
99 case '<': return (start1
>= end1
&& start2
< end2
? fix(start1
) : NIL
);
100 case 'L': return (start1
>= end1
? fix(start1
) : NIL
);
101 case '=': return (start1
>= end1
&& start2
>= end2
? s_true
: NIL
);
102 case '#': return (start1
>= end1
&& start2
>= end2
? NIL
: fix(start1
));
103 case 'G': return (start2
>= end2
? fix(start1
) : NIL
);
104 case '>': return (start2
>= end2
&& start1
< end1
? fix(start1
) : NIL
);
106 return (NIL
); /* avoid compiler warning */
109 /* string comparision functions */
110 LVAL
xstrlss(V
) { return (strcompare('<',FALSE
)); } /* string< */
111 LVAL
xstrleq(V
) { return (strcompare('L',FALSE
)); } /* string<= */
112 LVAL
xstreql(V
) { return (strcompare('=',FALSE
)); } /* string= */
113 LVAL
xstrneq(V
) { return (strcompare('#',FALSE
)); } /* string/= */
114 LVAL
xstrgeq(V
) { return (strcompare('G',FALSE
)); } /* string>= */
115 LVAL
xstrgtr(V
) { return (strcompare('>',FALSE
)); } /* string> */
117 /* string comparison functions (not case sensitive) */
118 LVAL
xstrilss(V
) { return (strcompare('<',TRUE
)); } /* string-lessp */
119 LVAL
xstrileq(V
) { return (strcompare('L',TRUE
)); } /* string-not-greaterp */
120 LVAL
xstrieql(V
) { return (strcompare('=',TRUE
)); } /* string-equal */
121 LVAL
xstrineq(V
) { return (strcompare('#',TRUE
)); } /* string-not-equal */
122 LVAL
xstrigeq(V
) { return (strcompare('G',TRUE
)); } /* string-not-lessp */
123 LVAL
xstrigtr(V
) { return (strcompare('>',TRUE
)); } /* string-greaterp */
125 /* changecase - change case */
126 LOCAL LVAL changecase
P2C(int, fcn
, int, destructive
)
129 unsigned start
,end
,len
,i
;
131 int lastspace
= TRUE
;
135 src
= (destructive
? xlgastring() : xlgastrorsym());
137 /* get the substring specifiers */
138 getbounds(src
,k_start
,k_end
,&start
,&end
);
139 len
= getslength(src
);
143 /* make a destination string */
144 dst
= (destructive
? src
: newstring(len
));
146 /* setup the string pointers */
147 srcp
= getstring(src
);
148 dstp
= getstring(dst
);
150 /* copy the source to the destination */
151 for (i
= 0; i
< len
; ++i
) {
153 if (i
>= start
&& i
< end
)
155 case 'U': if (ISLOWER(ch
)) ch
= TOUPPER(ch
); break;
156 case 'D': if (ISUPPER(ch
)) ch
= TOLOWER(ch
); break;
157 case 'C': if (lastspace
&& ISLOWER(ch
)) ch
= TOUPPER(ch
);
158 if (!lastspace
&& ISUPPER(ch
)) ch
= TOLOWER(ch
);
159 lastspace
= !ISLOWERA(ch
) && !ISUPPER(ch
);
166 /* return the new string */
170 /* case conversion functions */
171 LVAL
xupcase(V
) { return (changecase('U',FALSE
)); }
172 LVAL
xdowncase(V
) { return (changecase('D',FALSE
)); }
173 LVAL
xcapcase(V
) { return (changecase('C',FALSE
)); }
175 /* destructive case conversion functions */
176 LVAL
xnupcase(V
) { return (changecase('U',TRUE
)); }
177 LVAL
xndowncase(V
) { return (changecase('D',TRUE
)); }
178 LVAL
xncapcase(V
) { return (changecase('C',TRUE
)); }
180 /* inbag - test if a character is in a bag */
181 LOCAL
int inbag
P2C(int, ch
, LVAL
, bag
)
183 /* TAA MOD -- rewritten for \0 */
184 /* and chars >= 128 */
185 char *p
= getstring(bag
);
186 unsigned len
=getslength(bag
);
194 /* trim - trim character from a string */
195 LOCAL LVAL trim
P1C(int, fcn
)
197 char *leftp
, *rightp
, *dstp
;
200 /* get the bag and the string */
202 src
= xlgastrorsym();
206 bag
= coerce_to_tvec(bag
, a_char
);
208 /* setup the string pointers */
209 leftp
= getstring(src
);
210 rightp
= leftp
+ getslength(src
) - 1;
212 /* trim leading characters */
214 while (leftp
<= rightp
&& inbag(*leftp
,bag
))
217 /* trim character from the right */
219 while (rightp
>= leftp
&& inbag(*rightp
,bag
))
222 /* make a destination string and setup the pointer */
223 dst
= newstring(((unsigned)(rightp
-leftp
))+1);
224 dstp
= getstring(dst
);
226 /* copy the source to the destination */
227 while (leftp
<= rightp
)
233 /* return the new string */
238 LVAL
xtrim(V
) { return (trim(TLEFT
|TRIGHT
)); }
239 LVAL
xlefttrim(V
) { return (trim(TLEFT
)); }
240 LVAL
xrighttrim(V
) { return (trim(TRIGHT
)); }
243 /* xstring - return a string consisting of a single character */
248 /* get the argument */
252 /* check the argument type */
253 switch (ntype(arg
)) {
257 return (getpname(arg
));
259 /* Changed 10/94 to allow string '\000' */
261 val
->n_string
[0] = (char)getchcode(arg
);
262 val
->n_string
[1] = '\0';
265 /* Changed 10/94 to allow string 0 */
267 val
->n_string
[0] = (char)getfixnum(arg
);
268 val
->n_string
[1] = '\0';
272 return (NIL
); /* avoid compiler warning */
276 /* xchar - extract a character from a string */
282 /* get the string and the index */
287 /* range check the index */
288 if ((n
= getfixnum(num
)) < 0 || n
>= (FIXTYPE
)getslength(str
))
289 xlerror("index out of range",num
);
291 /* return the character */
292 return (cvchar(getstringch(str
,(unsigned int)n
)));
295 /* xcharint - convert a character to an integer */
301 return (cvfixnum((FIXTYPE
)getchcode(arg
)));
304 /* xintchar - convert an integer to a character */
310 return (cvchar((int)getfixnum(arg
)));
313 /* xcharcode - built-in function 'char-code' */
314 /* TAA mod so that result is 7 bit ascii code */
318 ch
= 0x7f & getchcode(xlgachar());
320 return (cvfixnum((FIXTYPE
)ch
));
323 /* xcodechar - built-in function 'code-char' */
324 /* like int-char except range must be 0-127 */
333 arg
= xlgafixnum(); ch
= getfixnum(arg
);
337 /* On MVS/CMS, convert EBCDIC character to ASCII for subsequent */
338 /* test - Dave Rivers (rivers@ponds.uucp) */
339 testch
= etoa((unsigned char)ch
);
340 return (testch
>= 0 && testch
<= 127 ? cvchar((int)ch
) : NIL
);
342 return (ch
>= 0 && ch
<= 127 ? cvchar((int)ch
) : NIL
);
346 /* xuppercasep - built-in function 'upper-case-p' */
350 ch
= getchcode(xlgachar());
352 return (ISUPPER(ch
) ? s_true
: NIL
);
355 /* xlowercasep - built-in function 'lower-case-p' */
359 ch
= getchcode(xlgachar());
361 return (ISLOWERA(ch
) ? s_true
: NIL
);
364 /* xbothcasep - built-in function 'both-case-p' */
368 ch
= getchcode(xlgachar());
370 return (ISUPPER(ch
) || ISLOWER(ch
) ? s_true
: NIL
);
373 /* xdigitp - built-in function 'digit-char-p' */
378 ch
= getchcode(xlgachar());
380 radix
= getfixnum(xlgafixnum());
381 if (radix
< 1 || radix
> 36) xlfail("radix out of range");
385 if (isdigit(ch
)) ch
= ch
- '0';
386 else if (ISUPPER(ch
)) ch
= ch
- 'A' + 10;
387 else if (ISLOWER(ch
)) ch
= ch
- 'a' + 10;
390 return (ch
< radix
? cvfixnum((FIXTYPE
) ch
) : NIL
);
393 /* xchupcase - built-in function 'char-upcase' */
398 arg
= xlgachar(); ch
= getchcode(arg
);
400 return (ISLOWER(ch
) ? cvchar(TOUPPER(ch
)) : arg
);
403 /* xchdowncase - built-in function 'char-downcase' */
408 arg
= xlgachar(); ch
= getchcode(arg
);
410 return (ISUPPER(ch
) ? cvchar(TOLOWER(ch
)) : arg
);
413 /* xdigitchar - built-in function 'digit-char' */
416 FIXTYPE n
, radix
= 10;
417 n
= getfixnum(xlgafixnum());
419 radix
= getfixnum(xlgafixnum());
420 if (radix
< 1 || radix
> 36) xlfail("radix out of range");
422 if (moreargs()) xlgetarg(); /* read and ignore font argument */
424 return (n
>= 0 && n
< radix
? cvchar((int) n
+ (n
< 10 ? '0' : 'A' - 10))
428 /* xalphanumericp - built-in function 'alphanumericp' */
429 LVAL
xalphanumericp(V
)
432 ch
= getchcode(xlgachar());
434 return (ISUPPER(ch
) || ISLOWERA(ch
) || isdigit(ch
) ? s_true
: NIL
);
437 /* xalphacharp - built-in function 'alpha-char-p' */
441 ch
= getchcode(xlgachar());
443 return (ISUPPER(ch
) || ISLOWERA(ch
) ? s_true
: NIL
);
446 /* chrcompare - compare characters */
447 LOCAL LVAL chrcompare
P2C(int, fcn
, int, icase
)
452 /* get the characters */
453 arg
= xlgachar(); ch1
= getchcode(arg
);
455 /* convert to lowercase if case insensitive */
456 if (icase
&& ISUPPER(ch1
))
459 /* handle each remaining argument */
460 for (icmp
= TRUE
; icmp
&& moreargs(); ch1
= ch2
) {
462 /* get the next argument */
463 arg
= xlgachar(); ch2
= getchcode(arg
);
465 /* convert to lowercase if case insensitive */
466 if (icase
&& ISUPPER(ch2
))
469 /* compare the characters */
471 case '<': icmp
= (ch1
< ch2
); break;
472 case 'L': icmp
= (ch1
<= ch2
); break;
473 case '=': icmp
= (ch1
== ch2
); break;
474 case '#': icmp
= (ch1
!= ch2
); break;
475 case 'G': icmp
= (ch1
>= ch2
); break;
476 case '>': icmp
= (ch1
> ch2
); break;
480 /* return the result */
481 return (icmp
? s_true
: NIL
);
484 /* character comparision functions */
485 LVAL
xchrlss(V
) { return (chrcompare('<',FALSE
)); } /* char< */
486 LVAL
xchrleq(V
) { return (chrcompare('L',FALSE
)); } /* char<= */
487 LVAL
xchreql(V
) { return (chrcompare('=',FALSE
)); } /* char= */
488 LVAL
xchrneq(V
) { return (chrcompare('#',FALSE
)); } /* char/= */
489 LVAL
xchrgeq(V
) { return (chrcompare('G',FALSE
)); } /* char>= */
490 LVAL
xchrgtr(V
) { return (chrcompare('>',FALSE
)); } /* char> */
492 /* character comparision functions (case insensitive) */
493 LVAL
xchrilss(V
) { return (chrcompare('<',TRUE
)); } /* char-lessp */
494 LVAL
xchrileq(V
) { return (chrcompare('L',TRUE
)); } /* char-not-greaterp */
495 LVAL
xchrieql(V
) { return (chrcompare('=',TRUE
)); } /* char-equalp */
496 LVAL
xchrineq(V
) { return (chrcompare('#',TRUE
)); } /* char-not-equalp */
497 LVAL
xchrigeq(V
) { return (chrcompare('G',TRUE
)); } /* char-not-lessp */
498 LVAL
xchrigtr(V
) { return (chrcompare('>',TRUE
)); } /* char-greaterp */
508 if (n
< 0) xlerror("Not a nonnegative integer", arg
);
510 if (xlgetkeyarg(k_initelem
, &arg
)) {
511 if (! charp(arg
)) xlbadtype(arg
);
514 result
= newstring(n
);
515 for (i
= 0; i
< n
; i
++)
516 setstringch(result
, i
, c
);