xref: /petsc/src/sys/utils/str.c (revision fbf9dbe564678ed6eff1806adbc4c4f01b9743f4)
1 /*
2     We define the string operations here. The reason we just do not use
3   the standard string routines in the PETSc code is that on some machines
4   they are broken or have the wrong prototypes.
5 */
6 #include <petsc/private/petscimpl.h> /*I  "petscsys.h"   I*/
7 #if defined(PETSC_HAVE_STRINGS_H)
8   #include <strings.h> /* strcasecmp */
9 #endif
10 
11 /*@C
12    PetscStrToArray - Separates a string by a character (for example ' ' or '\n') and creates an array of strings
13 
14    Not Collective; No Fortran Support
15 
16    Input Parameters:
17 +  s - pointer to string
18 -  sp - separator character
19 
20    Output Parameters:
21 +   argc - the number of entries in the array
22 -   args - an array of the entries with a `NULL` at the end
23 
24    Level: intermediate
25 
26    Note:
27     This may be called before `PetscInitialize()` or after `PetscFinalize()`
28 
29    Developer Notes:
30    Uses raw `malloc()` and does not call error handlers since this may be used before PETSc is initialized.
31 
32    Used to generate argc, args arguments passed to `MPI_Init()`
33 
34 .seealso: `PetscStrToArrayDestroy()`, `PetscToken`, `PetscTokenCreate()`
35 @*/
36 PetscErrorCode PetscStrToArray(const char s[], char sp, int *argc, char ***args)
37 {
38   int       i, j, n, *lens, cnt = 0;
39   PetscBool flg = PETSC_FALSE;
40 
41   if (!s) n = 0;
42   else n = strlen(s);
43   *argc = 0;
44   *args = NULL;
45   for (; n > 0; n--) { /* remove separator chars at the end - and will empty the string if all chars are separator chars */
46     if (s[n - 1] != sp) break;
47   }
48   if (!n) return PETSC_SUCCESS;
49   for (i = 0; i < n; i++) {
50     if (s[i] != sp) break;
51   }
52   for (; i < n + 1; i++) {
53     if ((s[i] == sp || s[i] == 0) && !flg) {
54       flg = PETSC_TRUE;
55       (*argc)++;
56     } else if (s[i] != sp) {
57       flg = PETSC_FALSE;
58     }
59   }
60   (*args) = (char **)malloc(((*argc) + 1) * sizeof(char *));
61   if (!*args) return PETSC_ERR_MEM;
62   lens = (int *)malloc((*argc) * sizeof(int));
63   if (!lens) return PETSC_ERR_MEM;
64   for (i = 0; i < *argc; i++) lens[i] = 0;
65 
66   *argc = 0;
67   for (i = 0; i < n; i++) {
68     if (s[i] != sp) break;
69   }
70   for (; i < n + 1; i++) {
71     if ((s[i] == sp || s[i] == 0) && !flg) {
72       flg = PETSC_TRUE;
73       (*argc)++;
74     } else if (s[i] != sp) {
75       lens[*argc]++;
76       flg = PETSC_FALSE;
77     }
78   }
79 
80   for (i = 0; i < *argc; i++) {
81     (*args)[i] = (char *)malloc((lens[i] + 1) * sizeof(char));
82     if (!(*args)[i]) {
83       free(lens);
84       for (j = 0; j < i; j++) free((*args)[j]);
85       free(*args);
86       return PETSC_ERR_MEM;
87     }
88   }
89   free(lens);
90   (*args)[*argc] = NULL;
91 
92   *argc = 0;
93   for (i = 0; i < n; i++) {
94     if (s[i] != sp) break;
95   }
96   for (; i < n + 1; i++) {
97     if ((s[i] == sp || s[i] == 0) && !flg) {
98       flg                   = PETSC_TRUE;
99       (*args)[*argc][cnt++] = 0;
100       (*argc)++;
101       cnt = 0;
102     } else if (s[i] != sp && s[i] != 0) {
103       (*args)[*argc][cnt++] = s[i];
104       flg                   = PETSC_FALSE;
105     }
106   }
107   return PETSC_SUCCESS;
108 }
109 
110 /*@C
111    PetscStrToArrayDestroy - Frees array created with `PetscStrToArray()`.
112 
113    Not Collective; No Fortran Support
114 
115    Output Parameters:
116 +  argc - the number of arguments
117 -  args - the array of arguments
118 
119    Level: intermediate
120 
121    Note:
122     This may be called before `PetscInitialize()` or after `PetscFinalize()`
123 
124 .seealso: `PetscStrToArray()`
125 @*/
126 PetscErrorCode PetscStrToArrayDestroy(int argc, char **args)
127 {
128   for (int i = 0; i < argc; ++i) free(args[i]);
129   if (args) free(args);
130   return PETSC_SUCCESS;
131 }
132 
133 /*@C
134    PetscStrArrayallocpy - Allocates space to hold a copy of an array of strings then copies the strings
135 
136    Not Collective; No Fortran Support
137 
138    Input Parameter:
139 .  s - pointer to array of strings (final string is a `NULL`)
140 
141    Output Parameter:
142 .  t - the copied array string
143 
144    Level: intermediate
145 
146    Note:
147    If `t` has previously been allocated then that memory is lost, you may need to `PetscStrArrayDestroy()`
148    the array before calling this routine.
149 
150 .seealso: `PetscStrallocpy()`, `PetscStrArrayDestroy()`, `PetscStrNArrayallocpy()`
151 @*/
152 PetscErrorCode PetscStrArrayallocpy(const char *const *list, char ***t)
153 {
154   PetscInt n = 0;
155 
156   PetscFunctionBegin;
157   while (list[n++])
158     ;
159   PetscCall(PetscMalloc1(n + 1, t));
160   for (PetscInt i = 0; i < n; i++) PetscCall(PetscStrallocpy(list[i], (*t) + i));
161   (*t)[n] = NULL;
162   PetscFunctionReturn(PETSC_SUCCESS);
163 }
164 
165 /*@C
166    PetscStrArrayDestroy - Frees array of strings created with `PetscStrArrayallocpy()`.
167 
168    Not Collective; No Fortran Support
169 
170    Output Parameter:
171 .   list - array of strings
172 
173    Level: intermediate
174 
175 .seealso: `PetscStrArrayallocpy()`
176 @*/
177 PetscErrorCode PetscStrArrayDestroy(char ***list)
178 {
179   PetscInt n = 0;
180 
181   PetscFunctionBegin;
182   if (!*list) PetscFunctionReturn(PETSC_SUCCESS);
183   while ((*list)[n]) {
184     PetscCall(PetscFree((*list)[n]));
185     ++n;
186   }
187   PetscCall(PetscFree(*list));
188   PetscFunctionReturn(PETSC_SUCCESS);
189 }
190 
191 /*@C
192    PetscStrNArrayallocpy - Allocates space to hold a copy of an array of strings then copies the strings
193 
194    Not Collective; No Fortran Support
195 
196    Input Parameters:
197 +  n - the number of string entries
198 -  s - pointer to array of strings
199 
200    Output Parameter:
201 .  t - the copied array string
202 
203    Level: intermediate
204 
205 .seealso: `PetscStrallocpy()`, `PetscStrArrayallocpy()`, `PetscStrNArrayDestroy()`
206 @*/
207 PetscErrorCode PetscStrNArrayallocpy(PetscInt n, const char *const *list, char ***t)
208 {
209   PetscFunctionBegin;
210   PetscCall(PetscMalloc1(n, t));
211   for (PetscInt i = 0; i < n; i++) PetscCall(PetscStrallocpy(list[i], (*t) + i));
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 /*@C
216    PetscStrNArrayDestroy - Frees array of strings created with `PetscStrNArrayallocpy()`.
217 
218    Not Collective; No Fortran Support
219 
220    Output Parameters:
221 +   n - number of string entries
222 -   list - array of strings
223 
224    Level: intermediate
225 
226 .seealso: `PetscStrNArrayallocpy()`, `PetscStrArrayallocpy()`
227 @*/
228 PetscErrorCode PetscStrNArrayDestroy(PetscInt n, char ***list)
229 {
230   PetscFunctionBegin;
231   if (!*list) PetscFunctionReturn(PETSC_SUCCESS);
232   for (PetscInt i = 0; i < n; i++) PetscCall(PetscFree((*list)[i]));
233   PetscCall(PetscFree(*list));
234   PetscFunctionReturn(PETSC_SUCCESS);
235 }
236 
237 /*@C
238    PetscBasename - returns a pointer to the last entry of a / or \ separated directory path
239 
240    Not Collective; No Fortran Support
241 
242    Input Parameter:
243 .  a - pointer to string
244 
245    Level: intermediate
246 
247 .seealso: `PetscStrgrt()`, `PetscStrncmp()`, `PetscStrcasecmp()`, `PetscStrrchr()`, `PetscStrcmp()`, `PetscStrstr()`,
248           `PetscTokenCreate()`, `PetscStrToArray()`, `PetscStrInList()`
249 @*/
250 const char *PetscBasename(const char a[])
251 {
252   const char *ptr = NULL;
253 
254   (void)PetscStrrchr(a, '/', (char **)&ptr);
255   if (ptr == a) {
256     if (PetscStrrchr(a, '\\', (char **)&ptr)) ptr = NULL;
257   }
258   return ptr;
259 }
260 
261 /*@C
262    PetscStrcasecmp - Returns true if the two strings are the same
263      except possibly for case.
264 
265    Not Collective; No Fortran Support
266 
267    Input Parameters:
268 +  a - pointer to first string
269 -  b - pointer to second string
270 
271    Output Parameter:
272 .  flg - if the two strings are the same
273 
274    Level: intermediate
275 
276    Note:
277    `NULL` arguments are ok
278 
279 .seealso: `PetscStrcmp()`, `PetscStrncmp()`, `PetscStrgrt()`
280 @*/
281 PetscErrorCode PetscStrcasecmp(const char a[], const char b[], PetscBool *t)
282 {
283   int c;
284 
285   PetscFunctionBegin;
286   PetscValidBoolPointer(t, 3);
287   if (!a && !b) c = 0;
288   else if (!a || !b) c = 1;
289 #if defined(PETSC_HAVE_STRCASECMP)
290   else c = strcasecmp(a, b);
291 #elif defined(PETSC_HAVE_STRICMP)
292   else c = stricmp(a, b);
293 #else
294   else {
295     char *aa, *bb;
296 
297     PetscCall(PetscStrallocpy(a, &aa));
298     PetscCall(PetscStrallocpy(b, &bb));
299     PetscCall(PetscStrtolower(aa));
300     PetscCall(PetscStrtolower(bb));
301     PetscCall(PetscStrcmp(aa, bb, t));
302     PetscCall(PetscFree(aa));
303     PetscCall(PetscFree(bb));
304     PetscFunctionReturn(PETSC_SUCCESS);
305   }
306 #endif
307   *t = c ? PETSC_FALSE : PETSC_TRUE;
308   PetscFunctionReturn(PETSC_SUCCESS);
309 }
310 
311 /*@C
312    PetscStrendswithwhich - Determines if a string ends with one of several possible strings
313 
314    Not Collective; No Fortran Support
315 
316    Input Parameters:
317 +  a - pointer to string
318 -  bs - strings to end with (last entry must be `NULL`)
319 
320    Output Parameter:
321 .  cnt - the index of the string it ends with or the index of `NULL`
322 
323    Level: intermediate
324 
325 .seealso: `PetscStrbeginswithwhich()`, `PetscStrendswith()`, `PetscStrtoupper`, `PetscStrtolower()`, `PetscStrrchr()`, `PetscStrchr()`,
326           `PetscStrncmp()`, `PetscStrlen()`, `PetscStrncmp()`, `PetscStrcmp()`
327 @*/
328 PetscErrorCode PetscStrendswithwhich(const char a[], const char *const *bs, PetscInt *cnt)
329 {
330   PetscFunctionBegin;
331   PetscValidPointer(bs, 2);
332   PetscValidIntPointer(cnt, 3);
333   *cnt = 0;
334   while (bs[*cnt]) {
335     PetscBool flg;
336 
337     PetscCall(PetscStrendswith(a, bs[*cnt], &flg));
338     if (flg) PetscFunctionReturn(PETSC_SUCCESS);
339     ++(*cnt);
340   }
341   PetscFunctionReturn(PETSC_SUCCESS);
342 }
343 
344 struct _p_PetscToken {
345   char  token;
346   char *array;
347   char *current;
348 };
349 
350 /*@C
351    PetscTokenFind - Locates next "token" in a `PetscToken`
352 
353    Not Collective; No Fortran Support
354 
355    Input Parameter:
356 .  a - pointer to token
357 
358    Output Parameter:
359 .  result - location of occurrence, `NULL` if not found
360 
361    Level: intermediate
362 
363    Notes:
364    Treats all characters etc. inside a double quote "
365    as a single token.
366 
367      For example if the separator character is + and the string is xxxx+y then the first fine will return a pointer to a `NULL` terminated xxxx and the
368    second will return a `NULL` terminated y
369 
370      If the separator character is + and the string is xxxx then the first and only token found will be a pointer to a `NULL` terminated xxxx
371 
372 .seealso: `PetscToken`, `PetscTokenCreate()`, `PetscTokenDestroy()`
373 @*/
374 PetscErrorCode PetscTokenFind(PetscToken a, char *result[])
375 {
376   char *ptr, token;
377 
378   PetscFunctionBegin;
379   PetscValidPointer(a, 1);
380   PetscValidPointer(result, 2);
381   *result = ptr = a->current;
382   if (ptr && !*ptr) {
383     *result = NULL;
384     PetscFunctionReturn(PETSC_SUCCESS);
385   }
386   token = a->token;
387   if (ptr && (*ptr == '"')) {
388     token = '"';
389     (*result)++;
390     ptr++;
391   }
392   while (ptr) {
393     if (*ptr == token) {
394       *ptr++ = 0;
395       while (*ptr == a->token) ptr++;
396       a->current = ptr;
397       break;
398     }
399     if (!*ptr) {
400       a->current = NULL;
401       break;
402     }
403     ptr++;
404   }
405   PetscFunctionReturn(PETSC_SUCCESS);
406 }
407 
408 /*@C
409    PetscTokenCreate - Creates a `PetscToken` used to find tokens in a string
410 
411    Not Collective; No Fortran Support
412 
413    Input Parameters:
414 +  string - the string to look in
415 -  b - the separator character
416 
417    Output Parameter:
418 .  t - the token object
419 
420    Level: intermediate
421 
422    Note:
423      This version is different from the system version in that
424   it allows you to pass a read-only string into the function.
425 
426 .seealso: `PetscToken`, `PetscTokenFind()`, `PetscTokenDestroy()`
427 @*/
428 PetscErrorCode PetscTokenCreate(const char a[], char b, PetscToken *t)
429 {
430   PetscFunctionBegin;
431   PetscValidCharPointer(a, 1);
432   PetscValidPointer(t, 3);
433   PetscCall(PetscNew(t));
434   PetscCall(PetscStrallocpy(a, &(*t)->array));
435 
436   (*t)->current = (*t)->array;
437   (*t)->token   = b;
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 /*@C
442    PetscTokenDestroy - Destroys a `PetscToken`
443 
444    Not Collective; No Fortran Support
445 
446    Input Parameter:
447 .  a - pointer to token
448 
449    Level: intermediate
450 
451 .seealso: `PetscToken`, `PetscTokenCreate()`, `PetscTokenFind()`
452 @*/
453 PetscErrorCode PetscTokenDestroy(PetscToken *a)
454 {
455   PetscFunctionBegin;
456   if (!*a) PetscFunctionReturn(PETSC_SUCCESS);
457   PetscCall(PetscFree((*a)->array));
458   PetscCall(PetscFree(*a));
459   PetscFunctionReturn(PETSC_SUCCESS);
460 }
461 
462 /*@C
463    PetscStrInList - search for a string in character-delimited list
464 
465    Not Collective; No Fortran Support
466 
467    Input Parameters:
468 +  str - the string to look for
469 .  list - the list to search in
470 -  sep - the separator character
471 
472    Output Parameter:
473 .  found - whether `str` is in `list`
474 
475    Level: intermediate
476 
477 .seealso: `PetscTokenCreate()`, `PetscTokenFind()`, `PetscStrcmp()`
478 @*/
479 PetscErrorCode PetscStrInList(const char str[], const char list[], char sep, PetscBool *found)
480 {
481   PetscToken token;
482   char      *item;
483 
484   PetscFunctionBegin;
485   PetscValidBoolPointer(found, 4);
486   *found = PETSC_FALSE;
487   PetscCall(PetscTokenCreate(list, sep, &token));
488   PetscCall(PetscTokenFind(token, &item));
489   while (item) {
490     PetscCall(PetscStrcmp(str, item, found));
491     if (*found) break;
492     PetscCall(PetscTokenFind(token, &item));
493   }
494   PetscCall(PetscTokenDestroy(&token));
495   PetscFunctionReturn(PETSC_SUCCESS);
496 }
497 
498 /*@C
499    PetscGetPetscDir - Gets the directory PETSc is installed in
500 
501    Not Collective; No Fortran Support
502 
503    Output Parameter:
504 .  dir - the directory
505 
506    Level: developer
507 
508 @*/
509 PetscErrorCode PetscGetPetscDir(const char *dir[])
510 {
511   PetscFunctionBegin;
512   PetscValidPointer(dir, 1);
513   *dir = PETSC_DIR;
514   PetscFunctionReturn(PETSC_SUCCESS);
515 }
516 
517 /*@C
518    PetscStrreplace - Replaces substrings in string with other substrings
519 
520    Not Collective; No Fortran Support
521 
522    Input Parameters:
523 +   comm - `MPI_Comm` of processors that are processing the string
524 .   aa - the string to look in
525 .   b - the resulting copy of a with replaced strings (`b` can be the same as `a`)
526 -   len - the length of `b`
527 
528    Level: developer
529 
530    Notes:
531       Replaces ${PETSC_ARCH},${PETSC_DIR},${PETSC_LIB_DIR},${DISPLAY},
532       ${HOMEDIRECTORY},${WORKINGDIRECTORY},${USERNAME}, ${HOSTNAME}, ${PETSC_MAKE} with appropriate values
533       as well as any environmental variables.
534 
535       `PETSC_LIB_DIR` uses the environmental variable if it exists. `PETSC_ARCH` and `PETSC_DIR` use what
536       PETSc was built with and do not use environmental variables.
537 
538 @*/
539 PetscErrorCode PetscStrreplace(MPI_Comm comm, const char aa[], char b[], size_t len)
540 {
541   int           i = 0;
542   size_t        l, l1, l2, l3;
543   char         *work, *par, *epar = NULL, env[1024], *tfree, *a = (char *)aa;
544   const char   *s[] = {"${PETSC_ARCH}", "${PETSC_DIR}", "${PETSC_LIB_DIR}", "${DISPLAY}", "${HOMEDIRECTORY}", "${WORKINGDIRECTORY}", "${USERNAME}", "${HOSTNAME}", "${PETSC_MAKE}", NULL};
545   char         *r[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
546   PetscBool     flag;
547   static size_t DISPLAY_LENGTH = 265, USER_LENGTH = 256, HOST_LENGTH = 256;
548 
549   PetscFunctionBegin;
550   PetscValidCharPointer(aa, 2);
551   PetscValidCharPointer(b, 3);
552   if (aa == b) PetscCall(PetscStrallocpy(aa, (char **)&a));
553   PetscCall(PetscMalloc1(len, &work));
554 
555   /* get values for replaced variables */
556   PetscCall(PetscStrallocpy(PETSC_ARCH, &r[0]));
557   PetscCall(PetscStrallocpy(PETSC_DIR, &r[1]));
558   PetscCall(PetscStrallocpy(PETSC_LIB_DIR, &r[2]));
559   PetscCall(PetscMalloc1(DISPLAY_LENGTH, &r[3]));
560   PetscCall(PetscMalloc1(PETSC_MAX_PATH_LEN, &r[4]));
561   PetscCall(PetscMalloc1(PETSC_MAX_PATH_LEN, &r[5]));
562   PetscCall(PetscMalloc1(USER_LENGTH, &r[6]));
563   PetscCall(PetscMalloc1(HOST_LENGTH, &r[7]));
564   PetscCall(PetscGetDisplay(r[3], DISPLAY_LENGTH));
565   PetscCall(PetscGetHomeDirectory(r[4], PETSC_MAX_PATH_LEN));
566   PetscCall(PetscGetWorkingDirectory(r[5], PETSC_MAX_PATH_LEN));
567   PetscCall(PetscGetUserName(r[6], USER_LENGTH));
568   PetscCall(PetscGetHostName(r[7], HOST_LENGTH));
569   PetscCall(PetscStrallocpy(PETSC_OMAKE, &r[8]));
570 
571   /* replace that are in environment */
572   PetscCall(PetscOptionsGetenv(comm, "PETSC_LIB_DIR", env, sizeof(env), &flag));
573   if (flag) {
574     PetscCall(PetscFree(r[2]));
575     PetscCall(PetscStrallocpy(env, &r[2]));
576   }
577 
578   /* replace the requested strings */
579   PetscCall(PetscStrncpy(b, a, len));
580   while (s[i]) {
581     PetscCall(PetscStrlen(s[i], &l));
582     PetscCall(PetscStrstr(b, s[i], &par));
583     while (par) {
584       *par = 0;
585       par += l;
586 
587       PetscCall(PetscStrlen(b, &l1));
588       PetscCall(PetscStrlen(r[i], &l2));
589       PetscCall(PetscStrlen(par, &l3));
590       PetscCheck(l1 + l2 + l3 < len, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "b len is not long enough to hold new values");
591       PetscCall(PetscStrncpy(work, b, len));
592       PetscCall(PetscStrlcat(work, r[i], len));
593       PetscCall(PetscStrlcat(work, par, len));
594       PetscCall(PetscStrncpy(b, work, len));
595       PetscCall(PetscStrstr(b, s[i], &par));
596     }
597     i++;
598   }
599   i = 0;
600   while (r[i]) {
601     tfree = (char *)r[i];
602     PetscCall(PetscFree(tfree));
603     i++;
604   }
605 
606   /* look for any other ${xxx} strings to replace from environmental variables */
607   PetscCall(PetscStrstr(b, "${", &par));
608   while (par) {
609     *par = 0;
610     par += 2;
611     PetscCall(PetscStrncpy(work, b, len));
612     PetscCall(PetscStrstr(par, "}", &epar));
613     *epar = 0;
614     epar += 1;
615     PetscCall(PetscOptionsGetenv(comm, par, env, sizeof(env), &flag));
616     PetscCheck(flag, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Substitution string ${%s} not found as environmental variable", par);
617     PetscCall(PetscStrlcat(work, env, len));
618     PetscCall(PetscStrlcat(work, epar, len));
619     PetscCall(PetscStrncpy(b, work, len));
620     PetscCall(PetscStrstr(b, "${", &par));
621   }
622   PetscCall(PetscFree(work));
623   if (aa == b) PetscCall(PetscFree(a));
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 /*@C
628    PetscStrcmpAny - Determines whether a string matches any of a list of strings.
629 
630    Not Collective
631 
632    Input Parameters:
633 +  src - pointer to input the string
634 -  cmp - list of non-null and non-empty strings to be compared against, pass the empty string "" to terminate the list
635 
636    Output Parameter:
637 .  match - `PETSC_TRUE` if the input string matches any in the list, else `PETSC_FALSE`
638 
639    Level: intermediate
640 
641 .seealso: `PetscStrcmp`
642 @*/
643 PetscErrorCode PetscStrcmpAny(const char src[], PetscBool *match, const char cmp[], ...)
644 {
645   va_list Argp;
646 
647   PetscFunctionBegin;
648   PetscValidBoolPointer(match, 2);
649   *match = PETSC_FALSE;
650   if (!src) PetscFunctionReturn(PETSC_SUCCESS);
651   va_start(Argp, cmp);
652   while (cmp && cmp[0]) {
653     PetscBool found;
654     PetscCall(PetscStrcmp(src, cmp, &found));
655     if (found) {
656       *match = PETSC_TRUE;
657       break;
658     }
659     cmp = va_arg(Argp, const char *);
660   }
661   va_end(Argp);
662   PetscFunctionReturn(PETSC_SUCCESS);
663 }
664 
665 /*@C
666    PetscEListFind - searches list of strings for given string, using case insensitive matching
667 
668    Not Collective; No Fortran Support
669 
670    Input Parameters:
671 +  n - number of strings in
672 .  list - list of strings to search
673 -  str - string to look for, empty string "" accepts default (first entry in list)
674 
675    Output Parameters:
676 +  value - index of matching string (if found)
677 -  found - boolean indicating whether string was found (can be `NULL`)
678 
679    Level: developer
680 
681 .seealso: `PetscEnumFind()`
682 @*/
683 PetscErrorCode PetscEListFind(PetscInt n, const char *const *list, const char *str, PetscInt *value, PetscBool *found)
684 {
685   PetscFunctionBegin;
686   if (found) {
687     PetscValidBoolPointer(found, 5);
688     *found = PETSC_FALSE;
689   }
690   for (PetscInt i = 0; i < n; ++i) {
691     PetscBool matched;
692 
693     PetscCall(PetscStrcasecmp(str, list[i], &matched));
694     if (matched || !str[0]) {
695       if (found) *found = PETSC_TRUE;
696       *value = i;
697       break;
698     }
699   }
700   PetscFunctionReturn(PETSC_SUCCESS);
701 }
702 
703 /*@C
704    PetscEnumFind - searches enum list of strings for given string, using case insensitive matching
705 
706    Not Collective; No Fortran Support
707 
708    Input Parameters:
709 +  enumlist - list of strings to search, followed by enum name, then enum prefix, then `NULL`
710 -  str - string to look for
711 
712    Output Parameters:
713 +  value - index of matching string (if found)
714 -  found - boolean indicating whether string was found (can be `NULL`)
715 
716    Level: advanced
717 
718 .seealso: `PetscEListFind()`
719 @*/
720 PetscErrorCode PetscEnumFind(const char *const *enumlist, const char *str, PetscEnum *value, PetscBool *found)
721 {
722   PetscInt  n = 0, evalue;
723   PetscBool efound;
724 
725   PetscFunctionBegin;
726   PetscValidPointer(enumlist, 1);
727   while (enumlist[n++]) PetscCheck(n <= 50, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "List argument appears to be wrong or have more than 50 entries");
728   PetscCheck(n >= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "List argument must have at least two entries: typename and type prefix");
729   n -= 3; /* drop enum name, prefix, and null termination */
730   PetscCall(PetscEListFind(n, enumlist, str, &evalue, &efound));
731   if (efound) {
732     PetscValidPointer(value, 3);
733     *value = (PetscEnum)evalue;
734   }
735   if (found) {
736     PetscValidBoolPointer(found, 4);
737     *found = efound;
738   }
739   PetscFunctionReturn(PETSC_SUCCESS);
740 }
741 
742 /*@C
743   PetscCIFilename - returns the basename of a file name when the PETSc CI portable error output mode is enabled.
744 
745   Not Collective; No Fortran Support
746 
747   Input Parameter:
748 . file - the file name
749 
750   Level: developer
751 
752   Note:
753   PETSc CI mode is a mode of running PETSc where output (both error and non-error) is made portable across all systems
754   so that comparisons of output between runs are easy to make.
755 
756   This mode is used for all tests in the test harness, it applies to both debug and optimized builds.
757 
758   Use the option `-petsc_ci` to turn on PETSc CI mode. It changes certain output in non-error situations to be portable for
759   all systems, mainly the output of options. It is passed to all PETSc programs automatically by the test harness.
760 
761   Always uses the Unix / as the file separate even on Microsoft Windows systems
762 
763   The option `-petsc_ci_portable_error_output` attempts to output the same error messages on all systems for the test harness.
764   In particular the output of filenames and line numbers in PETSc stacks. This is to allow (limited) checking of PETSc
765   error handling by the test harness. This options also causes PETSc to attempt to return an error code of 0 so that the test
766   harness can process the output for differences in the usual manner as for successful runs. It should be provided to the test
767   harness in the args: argument for specific examples. It will not necessarily produce portable output if different errors
768   (or no errors) occur on a subset of the MPI ranks.
769 
770 .seealso: `PetscCILinenumber()`
771 @*/
772 const char *PetscCIFilename(const char *file)
773 {
774   if (!PetscCIEnabledPortableErrorOutput) return file;
775   return PetscBasename(file);
776 }
777 
778 /*@C
779   PetscCILinenumber - returns a line number except if `PetscCIEnablePortableErrorOutput` is set when it returns 0
780 
781   Not Collective; No Fortran Support
782 
783   Input Parameter:
784 . linenumber - the initial line number
785 
786   Level: developer
787 
788   Note:
789   See `PetscCIFilename()` for details on usage
790 
791 .seealso: `PetscCIFilename()`
792 @*/
793 int PetscCILinenumber(int linenumber)
794 {
795   if (!PetscCIEnabledPortableErrorOutput) return linenumber;
796   return 0;
797 }
798 
799 /*@C
800   PetscStrcat - Concatenates a string onto a given string
801 
802   Not Collective, No Fortran Support
803 
804   Input Parameters:
805 + s - string to be added to
806 - t - pointer to string to be added to end
807 
808   Level: deprecated (since 3.18.5)
809 
810   Notes:
811   It is recommended you use `PetscStrlcat()` instead of this routine.
812 
813 .seealso: `PetscStrlcat()`
814 @*/
815 PetscErrorCode PetscStrcat(char s[], const char t[])
816 {
817   PetscFunctionBegin;
818   if (!t) PetscFunctionReturn(PETSC_SUCCESS);
819   PetscValidCharPointer(s, 1);
820   strcat(s, t);
821   PetscFunctionReturn(PETSC_SUCCESS);
822 }
823 
824 /*@C
825   PetscStrcpy - Copies a string
826 
827   Not Collective, No Fortran Support
828 
829   Input Parameter:
830 . t - pointer to string
831 
832   Output Parameter:
833 . s - the copied string
834 
835   Level: deprecated (since 3.18.5)
836 
837   Notes:
838   It is recommended you use `PetscStrncpy()` (equivalently `PetscArraycpy()` or
839   `PetscMemcpy()`) instead of this routine.
840 
841   `NULL` strings returns a string starting with zero.
842 
843 .seealso: `PetscStrncpy()`
844 @*/
845 PetscErrorCode PetscStrcpy(char s[], const char t[])
846 {
847   PetscFunctionBegin;
848   if (t) {
849     PetscValidCharPointer(s, 1);
850     PetscValidCharPointer(t, 2);
851     strcpy(s, t);
852   } else if (s) {
853     s[0] = '\0';
854   }
855   PetscFunctionReturn(PETSC_SUCCESS);
856 }
857