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