xref: /petsc/src/sys/utils/str.c (revision 52c29a0d759b1922e7baf4f48548d20f97d09446)
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 _p_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 .seealso: `PetscToken`, `PetscTokenCreate()`, `PetscTokenDestroy()`
374 @*/
375 PetscErrorCode PetscTokenFind(PetscToken a, char *result[])
376 {
377   char *ptr, token;
378 
379   PetscFunctionBegin;
380   PetscAssertPointer(a, 1);
381   PetscAssertPointer(result, 2);
382   *result = ptr = a->current;
383   if (ptr && !*ptr) {
384     *result = NULL;
385     PetscFunctionReturn(PETSC_SUCCESS);
386   }
387   token = a->token;
388   if (ptr && (*ptr == '"')) {
389     token = '"';
390     (*result)++;
391     ptr++;
392   }
393   while (ptr) {
394     if (*ptr == token) {
395       *ptr++ = 0;
396       while (*ptr == a->token) ptr++;
397       a->current = ptr;
398       break;
399     }
400     if (!*ptr) {
401       a->current = NULL;
402       break;
403     }
404     ptr++;
405   }
406   PetscFunctionReturn(PETSC_SUCCESS);
407 }
408 
409 /*@C
410   PetscTokenCreate - Creates a `PetscToken` used to find tokens in a string
411 
412   Not Collective; No Fortran Support
413 
414   Input Parameters:
415 + a - the string to look in
416 - b - the separator character
417 
418   Output Parameter:
419 . t - the token object
420 
421   Level: intermediate
422 
423   Note:
424   This version is different from the system version in that
425   it allows you to pass a read-only string into the function.
426 
427 .seealso: `PetscToken`, `PetscTokenFind()`, `PetscTokenDestroy()`
428 @*/
429 PetscErrorCode PetscTokenCreate(const char a[], char b, PetscToken *t)
430 {
431   PetscFunctionBegin;
432   PetscAssertPointer(a, 1);
433   PetscAssertPointer(t, 3);
434   PetscCall(PetscNew(t));
435   PetscCall(PetscStrallocpy(a, &(*t)->array));
436 
437   (*t)->current = (*t)->array;
438   (*t)->token   = b;
439   PetscFunctionReturn(PETSC_SUCCESS);
440 }
441 
442 /*@C
443   PetscTokenDestroy - Destroys a `PetscToken`
444 
445   Not Collective; No Fortran Support
446 
447   Input Parameter:
448 . a - pointer to token
449 
450   Level: intermediate
451 
452 .seealso: `PetscToken`, `PetscTokenCreate()`, `PetscTokenFind()`
453 @*/
454 PetscErrorCode PetscTokenDestroy(PetscToken *a)
455 {
456   PetscFunctionBegin;
457   if (!*a) PetscFunctionReturn(PETSC_SUCCESS);
458   PetscCall(PetscFree((*a)->array));
459   PetscCall(PetscFree(*a));
460   PetscFunctionReturn(PETSC_SUCCESS);
461 }
462 
463 /*@C
464   PetscStrInList - search for a string in character-delimited list
465 
466   Not Collective; No Fortran Support
467 
468   Input Parameters:
469 + str  - the string to look for
470 . list - the list to search in
471 - sep  - the separator character
472 
473   Output Parameter:
474 . found - whether `str` is in `list`
475 
476   Level: intermediate
477 
478 .seealso: `PetscTokenCreate()`, `PetscTokenFind()`, `PetscStrcmp()`
479 @*/
480 PetscErrorCode PetscStrInList(const char str[], const char list[], char sep, PetscBool *found)
481 {
482   PetscToken token;
483   char      *item;
484 
485   PetscFunctionBegin;
486   PetscAssertPointer(found, 4);
487   *found = PETSC_FALSE;
488   PetscCall(PetscTokenCreate(list, sep, &token));
489   PetscCall(PetscTokenFind(token, &item));
490   while (item) {
491     PetscCall(PetscStrcmp(str, item, found));
492     if (*found) break;
493     PetscCall(PetscTokenFind(token, &item));
494   }
495   PetscCall(PetscTokenDestroy(&token));
496   PetscFunctionReturn(PETSC_SUCCESS);
497 }
498 
499 /*@C
500   PetscGetPetscDir - Gets the directory PETSc is installed in
501 
502   Not Collective; No Fortran Support
503 
504   Output Parameter:
505 . dir - the directory
506 
507   Level: developer
508 
509 .seealso: `PetscGetArchType()`
510 @*/
511 PetscErrorCode PetscGetPetscDir(const char *dir[])
512 {
513   PetscFunctionBegin;
514   PetscAssertPointer(dir, 1);
515   *dir = PETSC_DIR;
516   PetscFunctionReturn(PETSC_SUCCESS);
517 }
518 
519 /*@C
520   PetscStrreplace - Replaces substrings in string with other substrings
521 
522   Not Collective; No Fortran Support
523 
524   Input Parameters:
525 + comm - `MPI_Comm` of processors that are processing the string
526 . aa   - the string to look in
527 . b    - the resulting copy of a with replaced strings (`b` can be the same as `a`)
528 - len  - the length of `b`
529 
530   Level: developer
531 
532   Notes:
533   Replaces ${PETSC_ARCH},${PETSC_DIR},${PETSC_LIB_DIR},${DISPLAY},
534       ${HOMEDIRECTORY},${WORKINGDIRECTORY},${USERNAME}, ${HOSTNAME}, ${PETSC_MAKE} with appropriate values
535   as well as any environmental variables.
536 
537   `PETSC_LIB_DIR` uses the environmental variable if it exists. `PETSC_ARCH` and `PETSC_DIR` use what
538   PETSc was built with and do not use environmental variables.
539 
540 .seealso: `PetscStrcmp()`
541 @*/
542 PetscErrorCode PetscStrreplace(MPI_Comm comm, const char aa[], char b[], size_t len)
543 {
544   int           i = 0;
545   size_t        l, l1, l2, l3;
546   char         *work, *par, *epar = NULL, env[1024], *tfree, *a = (char *)aa;
547   const char   *s[] = {"${PETSC_ARCH}", "${PETSC_DIR}", "${PETSC_LIB_DIR}", "${DISPLAY}", "${HOMEDIRECTORY}", "${WORKINGDIRECTORY}", "${USERNAME}", "${HOSTNAME}", "${PETSC_MAKE}", NULL};
548   char         *r[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
549   PetscBool     flag;
550   static size_t DISPLAY_LENGTH = 265, USER_LENGTH = 256, HOST_LENGTH = 256;
551 
552   PetscFunctionBegin;
553   PetscAssertPointer(aa, 2);
554   PetscAssertPointer(b, 3);
555   if (aa == b) PetscCall(PetscStrallocpy(aa, (char **)&a));
556   PetscCall(PetscMalloc1(len, &work));
557 
558   /* get values for replaced variables */
559   PetscCall(PetscStrallocpy(PETSC_ARCH, &r[0]));
560   PetscCall(PetscStrallocpy(PETSC_DIR, &r[1]));
561   PetscCall(PetscStrallocpy(PETSC_LIB_DIR, &r[2]));
562   PetscCall(PetscMalloc1(DISPLAY_LENGTH, &r[3]));
563   PetscCall(PetscMalloc1(PETSC_MAX_PATH_LEN, &r[4]));
564   PetscCall(PetscMalloc1(PETSC_MAX_PATH_LEN, &r[5]));
565   PetscCall(PetscMalloc1(USER_LENGTH, &r[6]));
566   PetscCall(PetscMalloc1(HOST_LENGTH, &r[7]));
567   PetscCall(PetscGetDisplay(r[3], DISPLAY_LENGTH));
568   PetscCall(PetscGetHomeDirectory(r[4], PETSC_MAX_PATH_LEN));
569   PetscCall(PetscGetWorkingDirectory(r[5], PETSC_MAX_PATH_LEN));
570   PetscCall(PetscGetUserName(r[6], USER_LENGTH));
571   PetscCall(PetscGetHostName(r[7], HOST_LENGTH));
572   PetscCall(PetscStrallocpy(PETSC_OMAKE, &r[8]));
573 
574   /* replace that are in environment */
575   PetscCall(PetscOptionsGetenv(comm, "PETSC_LIB_DIR", env, sizeof(env), &flag));
576   if (flag) {
577     PetscCall(PetscFree(r[2]));
578     PetscCall(PetscStrallocpy(env, &r[2]));
579   }
580 
581   /* replace the requested strings */
582   PetscCall(PetscStrncpy(b, a, len));
583   while (s[i]) {
584     PetscCall(PetscStrlen(s[i], &l));
585     PetscCall(PetscStrstr(b, s[i], &par));
586     while (par) {
587       *par = 0;
588       par += l;
589 
590       PetscCall(PetscStrlen(b, &l1));
591       PetscCall(PetscStrlen(r[i], &l2));
592       PetscCall(PetscStrlen(par, &l3));
593       PetscCheck(l1 + l2 + l3 < len, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "b len is not long enough to hold new values");
594       PetscCall(PetscStrncpy(work, b, len));
595       PetscCall(PetscStrlcat(work, r[i], len));
596       PetscCall(PetscStrlcat(work, par, len));
597       PetscCall(PetscStrncpy(b, work, len));
598       PetscCall(PetscStrstr(b, s[i], &par));
599     }
600     i++;
601   }
602   i = 0;
603   while (r[i]) {
604     tfree = (char *)r[i];
605     PetscCall(PetscFree(tfree));
606     i++;
607   }
608 
609   /* look for any other ${xxx} strings to replace from environmental variables */
610   PetscCall(PetscStrstr(b, "${", &par));
611   while (par) {
612     *par = 0;
613     par += 2;
614     PetscCall(PetscStrncpy(work, b, len));
615     PetscCall(PetscStrstr(par, "}", &epar));
616     *epar = 0;
617     epar += 1;
618     PetscCall(PetscOptionsGetenv(comm, par, env, sizeof(env), &flag));
619     PetscCheck(flag, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Substitution string ${%s} not found as environmental variable", par);
620     PetscCall(PetscStrlcat(work, env, len));
621     PetscCall(PetscStrlcat(work, epar, len));
622     PetscCall(PetscStrncpy(b, work, len));
623     PetscCall(PetscStrstr(b, "${", &par));
624   }
625   PetscCall(PetscFree(work));
626   if (aa == b) PetscCall(PetscFree(a));
627   PetscFunctionReturn(PETSC_SUCCESS);
628 }
629 
630 /*@C
631   PetscStrcmpAny - Determines whether a string matches any of a list of strings.
632 
633   Not Collective, No Fortran Support
634 
635   Input Parameters:
636 + src - pointer to input the string
637 - cmp - list of non-null and non-empty strings to be compared against, pass the empty string "" to terminate the list
638 
639   Output Parameter:
640 . match - `PETSC_TRUE` if the input string matches any in the list, else `PETSC_FALSE`
641 
642   Level: intermediate
643 
644 .seealso: `PetscStrcmp()`
645 @*/
646 PetscErrorCode PetscStrcmpAny(const char src[], PetscBool *match, const char cmp[], ...)
647 {
648   va_list Argp;
649 
650   PetscFunctionBegin;
651   PetscAssertPointer(match, 2);
652   *match = PETSC_FALSE;
653   if (!src) PetscFunctionReturn(PETSC_SUCCESS);
654   va_start(Argp, cmp);
655   while (cmp && cmp[0]) {
656     PetscBool found;
657     PetscCall(PetscStrcmp(src, cmp, &found));
658     if (found) {
659       *match = PETSC_TRUE;
660       break;
661     }
662     cmp = va_arg(Argp, const char *);
663   }
664   va_end(Argp);
665   PetscFunctionReturn(PETSC_SUCCESS);
666 }
667 
668 /*@C
669   PetscEListFind - searches list of strings for given string, using case insensitive matching
670 
671   Not Collective; No Fortran Support
672 
673   Input Parameters:
674 + n    - number of strings in
675 . list - list of strings to search
676 - str  - string to look for, empty string "" accepts default (first entry in list)
677 
678   Output Parameters:
679 + value - index of matching string (if found)
680 - found - boolean indicating whether string was found (can be `NULL`)
681 
682   Level: developer
683 
684 .seealso: `PetscEnumFind()`
685 @*/
686 PetscErrorCode PetscEListFind(PetscInt n, const char *const *list, const char *str, PetscInt *value, PetscBool *found)
687 {
688   PetscFunctionBegin;
689   if (found) {
690     PetscAssertPointer(found, 5);
691     *found = PETSC_FALSE;
692   }
693   for (PetscInt i = 0; i < n; ++i) {
694     PetscBool matched;
695 
696     PetscCall(PetscStrcasecmp(str, list[i], &matched));
697     if (matched || !str[0]) {
698       if (found) *found = PETSC_TRUE;
699       *value = i;
700       break;
701     }
702   }
703   PetscFunctionReturn(PETSC_SUCCESS);
704 }
705 
706 /*@C
707   PetscEnumFind - searches enum list of strings for given string, using case insensitive matching
708 
709   Not Collective; No Fortran Support
710 
711   Input Parameters:
712 + enumlist - list of strings to search, followed by enum name, then enum prefix, then `NULL`
713 - str      - string to look for
714 
715   Output Parameters:
716 + value - index of matching string (if found)
717 - found - boolean indicating whether string was found (can be `NULL`)
718 
719   Level: advanced
720 
721 .seealso: `PetscEListFind()`
722 @*/
723 PetscErrorCode PetscEnumFind(const char *const *enumlist, const char *str, PetscEnum *value, PetscBool *found)
724 {
725   PetscInt  n = 0, evalue;
726   PetscBool efound;
727 
728   PetscFunctionBegin;
729   PetscAssertPointer(enumlist, 1);
730   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");
731   PetscCheck(n >= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "List argument must have at least two entries: typename and type prefix");
732   n -= 3; /* drop enum name, prefix, and null termination */
733   PetscCall(PetscEListFind(n, enumlist, str, &evalue, &efound));
734   if (efound) {
735     PetscAssertPointer(value, 3);
736     *value = (PetscEnum)evalue;
737   }
738   if (found) {
739     PetscAssertPointer(found, 4);
740     *found = efound;
741   }
742   PetscFunctionReturn(PETSC_SUCCESS);
743 }
744 
745 /*@C
746   PetscCIFilename - returns the basename of a file name when the PETSc CI portable error output mode is enabled.
747 
748   Not Collective; No Fortran Support
749 
750   Input Parameter:
751 . file - the file name
752 
753   Level: developer
754 
755   Note:
756   PETSc CI mode is a mode of running PETSc where output (both error and non-error) is made portable across all systems
757   so that comparisons of output between runs are easy to make.
758 
759   This mode is used for all tests in the test harness, it applies to both debug and optimized builds.
760 
761   Use the option `-petsc_ci` to turn on PETSc CI mode. It changes certain output in non-error situations to be portable for
762   all systems, mainly the output of options. It is passed to all PETSc programs automatically by the test harness.
763 
764   Always uses the Unix / as the file separate even on Microsoft Windows systems
765 
766   The option `-petsc_ci_portable_error_output` attempts to output the same error messages on all systems for the test harness.
767   In particular the output of filenames and line numbers in PETSc stacks. This is to allow (limited) checking of PETSc
768   error handling by the test harness. This options also causes PETSc to attempt to return an error code of 0 so that the test
769   harness can process the output for differences in the usual manner as for successful runs. It should be provided to the test
770   harness in the args: argument for specific examples. It will not necessarily produce portable output if different errors
771   (or no errors) occur on a subset of the MPI ranks.
772 
773 .seealso: `PetscCILinenumber()`
774 @*/
775 const char *PetscCIFilename(const char *file)
776 {
777   if (!PetscCIEnabledPortableErrorOutput) return file;
778   return PetscBasename(file);
779 }
780 
781 /*@C
782   PetscCILinenumber - returns a line number except if `PetscCIEnablePortableErrorOutput` is set when it returns 0
783 
784   Not Collective; No Fortran Support
785 
786   Input Parameter:
787 . linenumber - the initial line number
788 
789   Level: developer
790 
791   Note:
792   See `PetscCIFilename()` for details on usage
793 
794 .seealso: `PetscCIFilename()`
795 @*/
796 int PetscCILinenumber(int linenumber)
797 {
798   if (!PetscCIEnabledPortableErrorOutput) return linenumber;
799   return 0;
800 }
801 
802 /*@C
803   PetscStrcat - Concatenates a string onto a given string
804 
805   Not Collective, No Fortran Support
806 
807   Input Parameters:
808 + s - string to be added to
809 - t - pointer to string to be added to end
810 
811   Level: deprecated (since 3.18.5)
812 
813   Notes:
814   It is recommended you use `PetscStrlcat()` instead of this routine.
815 
816 .seealso: `PetscStrlcat()`
817 @*/
818 PetscErrorCode PetscStrcat(char s[], const char t[])
819 {
820   PetscFunctionBegin;
821   if (!t) PetscFunctionReturn(PETSC_SUCCESS);
822   PetscAssertPointer(s, 1);
823   strcat(s, t);
824   PetscFunctionReturn(PETSC_SUCCESS);
825 }
826 
827 /*@C
828   PetscStrcpy - Copies a string
829 
830   Not Collective, No Fortran Support
831 
832   Input Parameter:
833 . t - pointer to string
834 
835   Output Parameter:
836 . s - the copied string
837 
838   Level: deprecated (since 3.18.5)
839 
840   Notes:
841   It is recommended you use `PetscStrncpy()` (equivalently `PetscArraycpy()` or
842   `PetscMemcpy()`) instead of this routine.
843 
844   `NULL` strings returns a string starting with zero.
845 
846 .seealso: `PetscStrncpy()`
847 @*/
848 PetscErrorCode PetscStrcpy(char s[], const char t[])
849 {
850   PetscFunctionBegin;
851   if (t) {
852     PetscAssertPointer(s, 1);
853     PetscAssertPointer(t, 2);
854     strcpy(s, t);
855   } else if (s) {
856     s[0] = '\0';
857   }
858   PetscFunctionReturn(PETSC_SUCCESS);
859 }
860