xref: /petsc/src/sys/utils/str.c (revision c5563ed3d46fd4a2074e7fad74cb8c322d44c032)
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 Parameters:
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 Parameters:
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 Parameters:
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 Parameters:
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    Notes:
529       Replaces ${PETSC_ARCH},${PETSC_DIR},${PETSC_LIB_DIR},${DISPLAY},
530       ${HOMEDIRECTORY},${WORKINGDIRECTORY},${USERNAME}, ${HOSTNAME} with appropriate values
531       as well as any environmental variables.
532 
533       `PETSC_LIB_DIR` uses the environmental variable if it exists. `PETSC_ARCH` and `PETSC_DIR` use what
534       PETSc was built with and do not use environmental variables.
535 
536    Level: developer
537 @*/
538 PetscErrorCode PetscStrreplace(MPI_Comm comm, const char aa[], char b[], size_t len)
539 {
540   int           i = 0;
541   size_t        l, l1, l2, l3;
542   char         *work, *par, *epar = NULL, env[1024], *tfree, *a = (char *)aa;
543   const char   *s[] = {"${PETSC_ARCH}", "${PETSC_DIR}", "${PETSC_LIB_DIR}", "${DISPLAY}", "${HOMEDIRECTORY}", "${WORKINGDIRECTORY}", "${USERNAME}", "${HOSTNAME}", NULL};
544   char         *r[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
545   PetscBool     flag;
546   static size_t DISPLAY_LENGTH = 265, USER_LENGTH = 256, HOST_LENGTH = 256;
547 
548   PetscFunctionBegin;
549   PetscValidCharPointer(aa, 2);
550   PetscValidCharPointer(b, 3);
551   if (aa == b) PetscCall(PetscStrallocpy(aa, (char **)&a));
552   PetscCall(PetscMalloc1(len, &work));
553 
554   /* get values for replaced variables */
555   PetscCall(PetscStrallocpy(PETSC_ARCH, &r[0]));
556   PetscCall(PetscStrallocpy(PETSC_DIR, &r[1]));
557   PetscCall(PetscStrallocpy(PETSC_LIB_DIR, &r[2]));
558   PetscCall(PetscMalloc1(DISPLAY_LENGTH, &r[3]));
559   PetscCall(PetscMalloc1(PETSC_MAX_PATH_LEN, &r[4]));
560   PetscCall(PetscMalloc1(PETSC_MAX_PATH_LEN, &r[5]));
561   PetscCall(PetscMalloc1(USER_LENGTH, &r[6]));
562   PetscCall(PetscMalloc1(HOST_LENGTH, &r[7]));
563   PetscCall(PetscGetDisplay(r[3], DISPLAY_LENGTH));
564   PetscCall(PetscGetHomeDirectory(r[4], PETSC_MAX_PATH_LEN));
565   PetscCall(PetscGetWorkingDirectory(r[5], PETSC_MAX_PATH_LEN));
566   PetscCall(PetscGetUserName(r[6], USER_LENGTH));
567   PetscCall(PetscGetHostName(r[7], HOST_LENGTH));
568 
569   /* replace that are in environment */
570   PetscCall(PetscOptionsGetenv(comm, "PETSC_LIB_DIR", env, sizeof(env), &flag));
571   if (flag) {
572     PetscCall(PetscFree(r[2]));
573     PetscCall(PetscStrallocpy(env, &r[2]));
574   }
575 
576   /* replace the requested strings */
577   PetscCall(PetscStrncpy(b, a, len));
578   while (s[i]) {
579     PetscCall(PetscStrlen(s[i], &l));
580     PetscCall(PetscStrstr(b, s[i], &par));
581     while (par) {
582       *par = 0;
583       par += l;
584 
585       PetscCall(PetscStrlen(b, &l1));
586       PetscCall(PetscStrlen(r[i], &l2));
587       PetscCall(PetscStrlen(par, &l3));
588       PetscCheck(l1 + l2 + l3 < len, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "b len is not long enough to hold new values");
589       PetscCall(PetscStrncpy(work, b, len));
590       PetscCall(PetscStrlcat(work, r[i], len));
591       PetscCall(PetscStrlcat(work, par, len));
592       PetscCall(PetscStrncpy(b, work, len));
593       PetscCall(PetscStrstr(b, s[i], &par));
594     }
595     i++;
596   }
597   i = 0;
598   while (r[i]) {
599     tfree = (char *)r[i];
600     PetscCall(PetscFree(tfree));
601     i++;
602   }
603 
604   /* look for any other ${xxx} strings to replace from environmental variables */
605   PetscCall(PetscStrstr(b, "${", &par));
606   while (par) {
607     *par = 0;
608     par += 2;
609     PetscCall(PetscStrncpy(work, b, len));
610     PetscCall(PetscStrstr(par, "}", &epar));
611     *epar = 0;
612     epar += 1;
613     PetscCall(PetscOptionsGetenv(comm, par, env, sizeof(env), &flag));
614     PetscCheck(flag, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Substitution string ${%s} not found as environmental variable", par);
615     PetscCall(PetscStrlcat(work, env, len));
616     PetscCall(PetscStrlcat(work, epar, len));
617     PetscCall(PetscStrncpy(b, work, len));
618     PetscCall(PetscStrstr(b, "${", &par));
619   }
620   PetscCall(PetscFree(work));
621   if (aa == b) PetscCall(PetscFree(a));
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 /*@C
626    PetscEListFind - searches list of strings for given string, using case insensitive matching
627 
628    Not Collective; No Fortran Support
629 
630    Input Parameters:
631 +  n - number of strings in
632 .  list - list of strings to search
633 -  str - string to look for, empty string "" accepts default (first entry in list)
634 
635    Output Parameters:
636 +  value - index of matching string (if found)
637 -  found - boolean indicating whether string was found (can be `NULL`)
638 
639    Level: developer
640 
641 .seealso: `PetscEnumFind()`
642 @*/
643 PetscErrorCode PetscEListFind(PetscInt n, const char *const *list, const char *str, PetscInt *value, PetscBool *found)
644 {
645   PetscFunctionBegin;
646   if (found) {
647     PetscValidBoolPointer(found, 5);
648     *found = PETSC_FALSE;
649   }
650   for (PetscInt i = 0; i < n; ++i) {
651     PetscBool matched;
652 
653     PetscCall(PetscStrcasecmp(str, list[i], &matched));
654     if (matched || !str[0]) {
655       if (found) *found = PETSC_TRUE;
656       *value = i;
657       break;
658     }
659   }
660   PetscFunctionReturn(PETSC_SUCCESS);
661 }
662 
663 /*@C
664    PetscEnumFind - searches enum list of strings for given string, using case insensitive matching
665 
666    Not Collective; No Fortran Support
667 
668    Input Parameters:
669 +  enumlist - list of strings to search, followed by enum name, then enum prefix, then `NULL`
670 -  str - string to look for
671 
672    Output Parameters:
673 +  value - index of matching string (if found)
674 -  found - boolean indicating whether string was found (can be `NULL`)
675 
676    Level: advanced
677 
678 .seealso: `PetscEListFind()`
679 @*/
680 PetscErrorCode PetscEnumFind(const char *const *enumlist, const char *str, PetscEnum *value, PetscBool *found)
681 {
682   PetscInt  n = 0, evalue;
683   PetscBool efound;
684 
685   PetscFunctionBegin;
686   PetscValidPointer(enumlist, 1);
687   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");
688   PetscCheck(n >= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "List argument must have at least two entries: typename and type prefix");
689   n -= 3; /* drop enum name, prefix, and null termination */
690   PetscCall(PetscEListFind(n, enumlist, str, &evalue, &efound));
691   if (efound) {
692     PetscValidPointer(value, 3);
693     *value = (PetscEnum)evalue;
694   }
695   if (found) {
696     PetscValidBoolPointer(found, 4);
697     *found = efound;
698   }
699   PetscFunctionReturn(PETSC_SUCCESS);
700 }
701 
702 /*@C
703   PetscCIFilename - returns the basename of a file name when the PETSc CI portable error output mode is enabled.
704 
705   Not collective; No Fortran Support
706 
707   Input Parameter:
708 . file - the file name
709 
710   Level: developer
711 
712   Note:
713   PETSc CI mode is a mode of running PETSc where output (both error and non-error) is made portable across all systems
714   so that comparisons of output between runs are easy to make.
715 
716   This mode is used for all tests in the test harness, it applies to both debug and optimized builds.
717 
718   Use the option `-petsc_ci` to turn on PETSc CI mode. It changes certain output in non-error situations to be portable for
719   all systems, mainly the output of options. It is passed to all PETSc programs automatically by the test harness.
720 
721   Always uses the Unix / as the file separate even on Microsoft Windows systems
722 
723   The option `-petsc_ci_portable_error_output` attempts to output the same error messages on all systems for the test harness.
724   In particular the output of filenames and line numbers in PETSc stacks. This is to allow (limited) checking of PETSc
725   error handling by the test harness. This options also causes PETSc to attempt to return an error code of 0 so that the test
726   harness can process the output for differences in the usual manner as for successful runs. It should be provided to the test
727   harness in the args: argument for specific examples. It will not necessarily produce portable output if different errors
728   (or no errors) occur on a subset of the MPI ranks.
729 
730 .seealso: `PetscCILinenumber()`
731 @*/
732 const char *PetscCIFilename(const char *file)
733 {
734   if (!PetscCIEnabledPortableErrorOutput) return file;
735   return PetscBasename(file);
736 }
737 
738 /*@C
739   PetscCILinenumber - returns a line number except if `PetscCIEnablePortableErrorOutput` is set when it returns 0
740 
741   Not Collective; No Fortran Support
742 
743   Input Parameter:
744 . linenumber - the initial line number
745 
746   Level: developer
747 
748   Note:
749   See `PetscCIFilename()` for details on usage
750 
751 .seealso: `PetscCIFilename()`
752 @*/
753 int PetscCILinenumber(int linenumber)
754 {
755   if (!PetscCIEnabledPortableErrorOutput) return linenumber;
756   return 0;
757 }
758