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