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