1 /* String intrinsics helper functions.
2    Copyright (C) 2002-2019 Free Software Foundation, Inc.
3 
4 This file is part of the GNU Fortran runtime library (libgfortran).
5 
6 Libgfortran is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public
8 License as published by the Free Software Foundation; either
9 version 3 of the License, or (at your option) any later version.
10 
11 Libgfortran is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
19 
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 <http://www.gnu.org/licenses/>.  */
24 
25 
26 /* Rename the functions.  */
27 #define concat_string SUFFIX(concat_string)
28 #define string_len_trim SUFFIX(string_len_trim)
29 #define adjustl SUFFIX(adjustl)
30 #define adjustr SUFFIX(adjustr)
31 #define string_index SUFFIX(string_index)
32 #define string_scan SUFFIX(string_scan)
33 #define string_verify SUFFIX(string_verify)
34 #define string_trim SUFFIX(string_trim)
35 #define string_minmax SUFFIX(string_minmax)
36 #define zero_length_string SUFFIX(zero_length_string)
37 #define compare_string SUFFIX(compare_string)
38 
39 
40 /* The prototypes.  */
41 
42 extern void concat_string (gfc_charlen_type, CHARTYPE *,
43 			   gfc_charlen_type, const CHARTYPE *,
44 			   gfc_charlen_type, const CHARTYPE *);
45 export_proto(concat_string);
46 
47 extern void adjustl (CHARTYPE *, gfc_charlen_type, const CHARTYPE *);
48 export_proto(adjustl);
49 
50 extern void adjustr (CHARTYPE *, gfc_charlen_type, const CHARTYPE *);
51 export_proto(adjustr);
52 
53 extern gfc_charlen_type string_index (gfc_charlen_type, const CHARTYPE *,
54 				      gfc_charlen_type, const CHARTYPE *,
55 				      GFC_LOGICAL_4);
56 export_proto(string_index);
57 
58 extern gfc_charlen_type string_scan (gfc_charlen_type, const CHARTYPE *,
59 				     gfc_charlen_type, const CHARTYPE *,
60 				     GFC_LOGICAL_4);
61 export_proto(string_scan);
62 
63 extern gfc_charlen_type string_verify (gfc_charlen_type, const CHARTYPE *,
64 				       gfc_charlen_type, const CHARTYPE *,
65 				       GFC_LOGICAL_4);
66 export_proto(string_verify);
67 
68 extern void string_trim (gfc_charlen_type *, CHARTYPE **, gfc_charlen_type,
69 			 const CHARTYPE *);
70 export_proto(string_trim);
71 
72 extern void string_minmax (gfc_charlen_type *, CHARTYPE **, int, int, ...);
73 export_proto(string_minmax);
74 
75 
76 /* Use for functions which can return a zero-length string.  */
77 static CHARTYPE zero_length_string = 0;
78 
79 
80 /* Strings of unequal length are extended with pad characters.  */
81 
82 int
compare_string(gfc_charlen_type len1,const CHARTYPE * s1,gfc_charlen_type len2,const CHARTYPE * s2)83 compare_string (gfc_charlen_type len1, const CHARTYPE *s1,
84 		gfc_charlen_type len2, const CHARTYPE *s2)
85 {
86   const UCHARTYPE *s;
87   gfc_charlen_type len;
88   int res;
89 
90   /* Placate the sanitizer.  */
91   if (!s1 && !s2)
92     return 0;
93   if (!s1)
94     return -1;
95   if (!s2)
96     return 1;
97 
98   res = MEMCMP (s1, s2, ((len1 < len2) ? len1 : len2));
99   if (res != 0)
100     return res;
101 
102   if (len1 == len2)
103     return 0;
104 
105   if (len1 < len2)
106     {
107       len = len2 - len1;
108       s = (UCHARTYPE *) &s2[len1];
109       res = -1;
110     }
111   else
112     {
113       len = len1 - len2;
114       s = (UCHARTYPE *) &s1[len2];
115       res = 1;
116     }
117 
118   while (len--)
119     {
120       if (*s != ' ')
121         {
122           if (*s > ' ')
123             return res;
124           else
125             return -res;
126         }
127       s++;
128     }
129 
130   return 0;
131 }
132 iexport(compare_string);
133 
134 
135 /* The destination and source should not overlap.  */
136 
137 void
concat_string(gfc_charlen_type destlen,CHARTYPE * dest,gfc_charlen_type len1,const CHARTYPE * s1,gfc_charlen_type len2,const CHARTYPE * s2)138 concat_string (gfc_charlen_type destlen, CHARTYPE * dest,
139 	       gfc_charlen_type len1, const CHARTYPE * s1,
140 	       gfc_charlen_type len2, const CHARTYPE * s2)
141 {
142   if (len1 >= destlen)
143     {
144       memcpy (dest, s1, destlen * sizeof (CHARTYPE));
145       return;
146     }
147   memcpy (dest, s1, len1 * sizeof (CHARTYPE));
148   dest += len1;
149   destlen -= len1;
150 
151   if (len2 >= destlen)
152     {
153       memcpy (dest, s2, destlen * sizeof (CHARTYPE));
154       return;
155     }
156 
157   memcpy (dest, s2, len2 * sizeof (CHARTYPE));
158   MEMSET (&dest[len2], ' ', destlen - len2);
159 }
160 
161 
162 /* Return string with all trailing blanks removed.  */
163 
164 void
string_trim(gfc_charlen_type * len,CHARTYPE ** dest,gfc_charlen_type slen,const CHARTYPE * src)165 string_trim (gfc_charlen_type *len, CHARTYPE **dest, gfc_charlen_type slen,
166 	     const CHARTYPE *src)
167 {
168   *len = string_len_trim (slen, src);
169 
170   if (*len == 0)
171     *dest = &zero_length_string;
172   else
173     {
174       /* Allocate space for result string.  */
175       *dest = xmallocarray (*len, sizeof (CHARTYPE));
176 
177       /* Copy string if necessary.  */
178       memcpy (*dest, src, *len * sizeof (CHARTYPE));
179     }
180 }
181 
182 
183 /* The length of a string not including trailing blanks.  */
184 
185 gfc_charlen_type
string_len_trim(gfc_charlen_type len,const CHARTYPE * s)186 string_len_trim (gfc_charlen_type len, const CHARTYPE *s)
187 {
188   if (len <= 0)
189     return 0;
190 
191   const size_t long_len = sizeof (unsigned long);
192 
193   size_t i = len - 1;
194 
195   /* If we've got the standard (KIND=1) character type, we scan the string in
196      long word chunks to speed it up (until a long word is hit that does not
197      consist of ' 's).  */
198   if (sizeof (CHARTYPE) == 1 && i >= long_len)
199     {
200       size_t starting;
201       unsigned long blank_longword;
202 
203       /* Handle the first characters until we're aligned on a long word
204 	 boundary.  Actually, s + i + 1 must be properly aligned, because
205 	 s + i will be the last byte of a long word read.  */
206       starting = (
207 #ifdef __INTPTR_TYPE__
208 		  (__INTPTR_TYPE__)
209 #endif
210 		  (s + i + 1)) % long_len;
211       i -= starting;
212       for (; starting > 0; --starting)
213 	if (s[i + starting] != ' ')
214 	  return i + starting + 1;
215 
216       /* Handle the others in a batch until first non-blank long word is
217 	 found.  Here again, s + i is the last byte of the current chunk,
218 	 to it starts at s + i - sizeof (long) + 1.  */
219 
220 #if __SIZEOF_LONG__ == 4
221       blank_longword = 0x20202020L;
222 #elif __SIZEOF_LONG__ == 8
223       blank_longword = 0x2020202020202020L;
224 #else
225       #error Invalid size of long!
226 #endif
227 
228       while (i >= long_len)
229 	{
230 	  i -= long_len;
231 	  if (*((unsigned long*) (s + i + 1)) != blank_longword)
232 	    {
233 	      i += long_len;
234 	      break;
235 	    }
236 	}
237     }
238 
239   /* Simply look for the first non-blank character.  */
240   while (s[i] == ' ')
241     {
242       if (i == 0)
243 	return 0;
244       --i;
245     }
246   return i + 1;
247 }
248 
249 
250 /* Find a substring within a string.  */
251 
252 gfc_charlen_type
string_index(gfc_charlen_type slen,const CHARTYPE * str,gfc_charlen_type sslen,const CHARTYPE * sstr,GFC_LOGICAL_4 back)253 string_index (gfc_charlen_type slen, const CHARTYPE *str,
254 	      gfc_charlen_type sslen, const CHARTYPE *sstr,
255 	      GFC_LOGICAL_4 back)
256 {
257   gfc_charlen_type start, last, delta, i;
258 
259   if (sslen == 0)
260     return back ? (slen + 1) : 1;
261 
262   if (sslen > slen)
263     return 0;
264 
265   if (!back)
266     {
267       last = slen + 1 - sslen;
268       start = 0;
269       delta = 1;
270     }
271   else
272     {
273       last = -1;
274       start = slen - sslen;
275       delta = -1;
276     }
277 
278   for (; start != last; start+= delta)
279     {
280       for (i = 0; i < sslen; i++)
281         {
282           if (str[start + i] != sstr[i])
283             break;
284         }
285       if (i == sslen)
286         return (start + 1);
287     }
288   return 0;
289 }
290 
291 
292 /* Remove leading blanks from a string, padding at end.  The src and dest
293    should not overlap.  */
294 
295 void
adjustl(CHARTYPE * dest,gfc_charlen_type len,const CHARTYPE * src)296 adjustl (CHARTYPE *dest, gfc_charlen_type len, const CHARTYPE *src)
297 {
298   gfc_charlen_type i;
299 
300   i = 0;
301   while (i < len && src[i] == ' ')
302     i++;
303 
304   if (i < len)
305     memcpy (dest, &src[i], (len - i) * sizeof (CHARTYPE));
306   if (i > 0)
307     MEMSET (&dest[len - i], ' ', i);
308 }
309 
310 
311 /* Remove trailing blanks from a string.  */
312 
313 void
adjustr(CHARTYPE * dest,gfc_charlen_type len,const CHARTYPE * src)314 adjustr (CHARTYPE *dest, gfc_charlen_type len, const CHARTYPE *src)
315 {
316   gfc_charlen_type i;
317 
318   i = len;
319   while (i > 0 && src[i - 1] == ' ')
320     i--;
321 
322   if (i < len)
323     MEMSET (dest, ' ', len - i);
324   memcpy (&dest[len - i], src, i * sizeof (CHARTYPE));
325 }
326 
327 
328 /* Scan a string for any one of the characters in a set of characters.  */
329 
330 gfc_charlen_type
string_scan(gfc_charlen_type slen,const CHARTYPE * str,gfc_charlen_type setlen,const CHARTYPE * set,GFC_LOGICAL_4 back)331 string_scan (gfc_charlen_type slen, const CHARTYPE *str,
332 	     gfc_charlen_type setlen, const CHARTYPE *set, GFC_LOGICAL_4 back)
333 {
334   gfc_charlen_type i, j;
335 
336   if (slen == 0 || setlen == 0)
337     return 0;
338 
339   if (back)
340     {
341       for (i = slen; i != 0; i--)
342 	{
343 	  for (j = 0; j < setlen; j++)
344 	    {
345 	      if (str[i - 1] == set[j])
346 		return i;
347 	    }
348 	}
349     }
350   else
351     {
352       for (i = 0; i < slen; i++)
353 	{
354 	  for (j = 0; j < setlen; j++)
355 	    {
356 	      if (str[i] == set[j])
357 		return (i + 1);
358 	    }
359 	}
360     }
361 
362   return 0;
363 }
364 
365 
366 /* Verify that a set of characters contains all the characters in a
367    string by identifying the position of the first character in a
368    characters that does not appear in a given set of characters.  */
369 
370 gfc_charlen_type
string_verify(gfc_charlen_type slen,const CHARTYPE * str,gfc_charlen_type setlen,const CHARTYPE * set,GFC_LOGICAL_4 back)371 string_verify (gfc_charlen_type slen, const CHARTYPE *str,
372 	       gfc_charlen_type setlen, const CHARTYPE *set,
373 	       GFC_LOGICAL_4 back)
374 {
375   gfc_charlen_type start, last, delta, i;
376 
377   if (slen == 0)
378     return 0;
379 
380   if (back)
381     {
382       last = -1;
383       start = slen - 1;
384       delta = -1;
385     }
386   else
387     {
388       last = slen;
389       start = 0;
390       delta = 1;
391     }
392   for (; start != last; start += delta)
393     {
394       for (i = 0; i < setlen; i++)
395         {
396           if (str[start] == set[i])
397             break;
398         }
399       if (i == setlen)
400         return (start + 1);
401     }
402 
403   return 0;
404 }
405 
406 
407 /* MIN and MAX intrinsics for strings.  The front-end makes sure that
408    nargs is at least 2.  */
409 
410 void
string_minmax(gfc_charlen_type * rlen,CHARTYPE ** dest,int op,int nargs,...)411 string_minmax (gfc_charlen_type *rlen, CHARTYPE **dest, int op, int nargs, ...)
412 {
413   va_list ap;
414   int i;
415   CHARTYPE *next, *res;
416   gfc_charlen_type nextlen, reslen;
417 
418   va_start (ap, nargs);
419   reslen = va_arg (ap, gfc_charlen_type);
420   res = va_arg (ap, CHARTYPE *);
421   *rlen = reslen;
422 
423   if (res == NULL)
424     runtime_error ("First argument of '%s' intrinsic should be present",
425 		   op > 0 ? "MAX" : "MIN");
426 
427   for (i = 1; i < nargs; i++)
428     {
429       nextlen = va_arg (ap, gfc_charlen_type);
430       next = va_arg (ap, CHARTYPE *);
431 
432       if (next == NULL)
433 	{
434 	  if (i == 1)
435 	    runtime_error ("Second argument of '%s' intrinsic should be "
436 			   "present", op > 0 ? "MAX" : "MIN");
437 	  else
438 	    continue;
439 	}
440 
441       if (nextlen > *rlen)
442 	*rlen = nextlen;
443 
444       if (op * compare_string (reslen, res, nextlen, next) < 0)
445 	{
446 	  reslen = nextlen;
447 	  res = next;
448 	}
449     }
450   va_end (ap);
451 
452   if (*rlen == 0)
453     *dest = &zero_length_string;
454   else
455     {
456       CHARTYPE *tmp = xmallocarray (*rlen, sizeof (CHARTYPE));
457       memcpy (tmp, res, reslen * sizeof (CHARTYPE));
458       MEMSET (&tmp[reslen], ' ', *rlen - reslen);
459       *dest = tmp;
460     }
461 }
462