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