xref: /petsc/src/sys/utils/str.c (revision 0d821f603638cf319655b464fce8824689c6444b)
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 @*/
PetscStrToArray(const char s[],char sp,int * argc,char *** args)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 @*/
PetscStrToArrayDestroy(int argc,char ** args)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 @*/
PetscStrArrayallocpy(const char * const * list,char *** t)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 @*/
PetscStrArrayDestroy(char *** list)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 @*/
PetscStrNArrayallocpy(PetscInt n,const char * const * list,char *** t)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 @*/
PetscStrNArrayDestroy(PetscInt n,char *** list)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 @*/
PetscBasename(const char a[])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 @*/
PetscStrcasecmp(const char a[],const char b[],PetscBool * t)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 @*/
PetscStrendswithwhich(const char a[],const char * const * bs,PetscInt * cnt)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 @*/
PetscTokenFind(PetscToken a,const char * result[])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 @*/
PetscTokenCreate(const char a[],char b,PetscToken * t)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 @*/
PetscTokenDestroy(PetscToken * a)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 @*/
PetscStrInList(const char str[],const char list[],char sep,PetscBool * found)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 @*/
PetscGetPetscDir(const char * dir[])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 @*/
PetscStrreplace(MPI_Comm comm,const char aa[],char b[],size_t len)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(PetscStrcmp(s[i], r[i], &flag));
590     if (flag) {
591       i++;
592       continue;
593     }
594     PetscCall(PetscStrlen(s[i], &l));
595     PetscCall(PetscStrstr(b, s[i], &par));
596     while (par) {
597       *par = 0;
598       par += l;
599 
600       PetscCall(PetscStrlen(b, &l1));
601       PetscCall(PetscStrlen(r[i], &l2));
602       PetscCall(PetscStrlen(par, &l3));
603       PetscCheck(l1 + l2 + l3 < len, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "b len is not long enough to hold new values");
604       PetscCall(PetscStrncpy(work, b, len));
605       PetscCall(PetscStrlcat(work, r[i], len));
606       PetscCall(PetscStrlcat(work, par, len));
607       PetscCall(PetscStrncpy(b, work, len));
608       PetscCall(PetscStrstr(b, s[i], &par));
609     }
610     i++;
611   }
612   i = 0;
613   while (r[i]) {
614     tfree = r[i];
615     PetscCall(PetscFree(tfree));
616     i++;
617   }
618 
619   /* look for any other ${xxx} strings to replace from environmental variables */
620   PetscCall(PetscStrstr(b, "${", &par));
621   while (par) {
622     *par = 0;
623     par += 2;
624     PetscCall(PetscStrncpy(work, b, len));
625     PetscCall(PetscStrstr(par, "}", &epar));
626     *epar = 0;
627     epar += 1;
628     PetscCall(PetscOptionsGetenv(comm, par, env, sizeof(env), &flag));
629     PetscCheck(flag, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Substitution string ${%s} not found as environmental variable", par);
630     PetscCall(PetscStrlcat(work, env, len));
631     PetscCall(PetscStrlcat(work, epar, len));
632     PetscCall(PetscStrncpy(b, work, len));
633     PetscCall(PetscStrstr(b, "${", &par));
634   }
635   PetscCall(PetscFree(work));
636   if (aa == b) PetscCall(PetscFree(a));
637   PetscFunctionReturn(PETSC_SUCCESS);
638 }
639 
640 /*@C
641   PetscStrcmpAny - Determines whether a string matches any of a list of strings.
642 
643   Not Collective, No Fortran Support
644 
645   Input Parameters:
646 + src - pointer to input the string
647 - cmp - list of non-null and non-empty strings to be compared against, pass the empty string "" to terminate the list
648 
649   Output Parameter:
650 . match - `PETSC_TRUE` if the input string matches any in the list, else `PETSC_FALSE`
651 
652   Level: intermediate
653 
654 .seealso: `PetscStrcmp()`
655 @*/
PetscStrcmpAny(const char src[],PetscBool * match,const char cmp[],...)656 PetscErrorCode PetscStrcmpAny(const char src[], PetscBool *match, const char cmp[], ...)
657 {
658   va_list Argp;
659 
660   PetscFunctionBegin;
661   PetscAssertPointer(match, 2);
662   *match = PETSC_FALSE;
663   if (!src) PetscFunctionReturn(PETSC_SUCCESS);
664   va_start(Argp, cmp);
665   while (cmp && cmp[0]) {
666     PetscBool found;
667     PetscCall(PetscStrcmp(src, cmp, &found));
668     if (found) {
669       *match = PETSC_TRUE;
670       break;
671     }
672     cmp = va_arg(Argp, const char *);
673   }
674   va_end(Argp);
675   PetscFunctionReturn(PETSC_SUCCESS);
676 }
677 
678 /*@C
679   PetscEListFind - searches list of strings for given string, using case insensitive matching
680 
681   Not Collective; No Fortran Support
682 
683   Input Parameters:
684 + n    - number of strings in
685 . list - list of strings to search
686 - str  - string to look for, empty string "" accepts default (first entry in list)
687 
688   Output Parameters:
689 + value - index of matching string (if found)
690 - found - boolean indicating whether string was found (can be `NULL`)
691 
692   Level: developer
693 
694 .seealso: `PetscEnumFind()`
695 @*/
PetscEListFind(PetscInt n,const char * const * list,const char * str,PetscInt * value,PetscBool * found)696 PetscErrorCode PetscEListFind(PetscInt n, const char *const *list, const char *str, PetscInt *value, PetscBool *found)
697 {
698   PetscFunctionBegin;
699   if (found) {
700     PetscAssertPointer(found, 5);
701     *found = PETSC_FALSE;
702   }
703   for (PetscInt i = 0; i < n; ++i) {
704     PetscBool matched;
705 
706     PetscCall(PetscStrcasecmp(str, list[i], &matched));
707     if (matched || !str[0]) {
708       if (found) *found = PETSC_TRUE;
709       *value = i;
710       break;
711     }
712   }
713   PetscFunctionReturn(PETSC_SUCCESS);
714 }
715 
716 /*@C
717   PetscEnumFind - searches enum list of strings for given string, using case insensitive matching
718 
719   Not Collective; No Fortran Support
720 
721   Input Parameters:
722 + enumlist - list of strings to search, followed by enum name, then enum prefix, then `NULL`
723 - str      - string to look for
724 
725   Output Parameters:
726 + value - index of matching string (if found)
727 - found - boolean indicating whether string was found (can be `NULL`)
728 
729   Level: advanced
730 
731 .seealso: `PetscEListFind()`
732 @*/
PetscEnumFind(const char * const * enumlist,const char * str,PetscEnum * value,PetscBool * found)733 PetscErrorCode PetscEnumFind(const char *const *enumlist, const char *str, PetscEnum *value, PetscBool *found)
734 {
735   PetscInt  n = 0, evalue;
736   PetscBool efound;
737 
738   PetscFunctionBegin;
739   PetscAssertPointer(enumlist, 1);
740   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");
741   PetscCheck(n >= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "List argument must have at least two entries: typename and type prefix");
742   n -= 3; /* drop enum name, prefix, and null termination */
743   PetscCall(PetscEListFind(n, enumlist, str, &evalue, &efound));
744   if (efound) {
745     PetscAssertPointer(value, 3);
746     *value = (PetscEnum)evalue;
747   }
748   if (found) {
749     PetscAssertPointer(found, 4);
750     *found = efound;
751   }
752   PetscFunctionReturn(PETSC_SUCCESS);
753 }
754 
755 /*@C
756   PetscCIFilename - returns the basename of a file name when the PETSc CI portable error output mode is enabled.
757 
758   Not Collective; No Fortran Support
759 
760   Input Parameter:
761 . file - the file name
762 
763   Level: developer
764 
765   Note:
766   PETSc CI mode is a mode of running PETSc where output (both error and non-error) is made portable across all systems
767   so that comparisons of output between runs are easy to make.
768 
769   This mode is used for all tests in the test harness, it applies to both debug and optimized builds.
770 
771   Use the option `-petsc_ci` to turn on PETSc CI mode. It changes certain output in non-error situations to be portable for
772   all systems, mainly the output of options. It is passed to all PETSc programs automatically by the test harness.
773 
774   Always uses the Unix / as the file separate even on Microsoft Windows systems
775 
776   The option `-petsc_ci_portable_error_output` attempts to output the same error messages on all systems for the test harness.
777   In particular the output of filenames and line numbers in PETSc stacks. This is to allow (limited) checking of PETSc
778   error handling by the test harness. This options also causes PETSc to attempt to return an error code of 0 so that the test
779   harness can process the output for differences in the usual manner as for successful runs. It should be provided to the test
780   harness in the args: argument for specific examples. It will not necessarily produce portable output if different errors
781   (or no errors) occur on a subset of the MPI ranks.
782 
783 .seealso: `PetscCILinenumber()`
784 @*/
PetscCIFilename(const char * file)785 const char *PetscCIFilename(const char *file)
786 {
787   if (!PetscCIEnabledPortableErrorOutput) return file;
788   return PetscBasename(file);
789 }
790 
791 /*@C
792   PetscCILinenumber - returns a line number except if `PetscCIEnablePortableErrorOutput` is set when it returns 0
793 
794   Not Collective; No Fortran Support
795 
796   Input Parameter:
797 . linenumber - the initial line number
798 
799   Level: developer
800 
801   Note:
802   See `PetscCIFilename()` for details on usage
803 
804 .seealso: `PetscCIFilename()`
805 @*/
PetscCILinenumber(int linenumber)806 int PetscCILinenumber(int linenumber)
807 {
808   if (!PetscCIEnabledPortableErrorOutput) return linenumber;
809   return 0;
810 }
811 
812 /*@C
813   PetscStrcat - Concatenates a string onto a given string
814 
815   Not Collective, No Fortran Support
816 
817   Input Parameters:
818 + s - string to be added to
819 - t - pointer to string to be added to end
820 
821   Level: deprecated (since 3.18.5)
822 
823   Notes:
824   It is recommended you use `PetscStrlcat()` instead of this routine.
825 
826 .seealso: `PetscStrlcat()`
827 @*/
PetscStrcat(char s[],const char t[])828 PetscErrorCode PetscStrcat(char s[], const char t[])
829 {
830   PetscFunctionBegin;
831   if (!t) PetscFunctionReturn(PETSC_SUCCESS);
832   PetscAssertPointer(s, 1);
833   strcat(s, t);
834   PetscFunctionReturn(PETSC_SUCCESS);
835 }
836 
837 /*@C
838   PetscStrcpy - Copies a string
839 
840   Not Collective, No Fortran Support
841 
842   Input Parameter:
843 . t - pointer to string
844 
845   Output Parameter:
846 . s - the copied string
847 
848   Level: deprecated (since 3.18.5)
849 
850   Notes:
851   It is recommended you use `PetscStrncpy()` (equivalently `PetscArraycpy()` or
852   `PetscMemcpy()`) instead of this routine.
853 
854   `NULL` strings returns a string starting with zero.
855 
856 .seealso: `PetscStrncpy()`
857 @*/
PetscStrcpy(char s[],const char t[])858 PetscErrorCode PetscStrcpy(char s[], const char t[])
859 {
860   PetscFunctionBegin;
861   if (t) {
862     PetscAssertPointer(s, 1);
863     PetscAssertPointer(t, 2);
864     strcpy(s, t);
865   } else if (s) {
866     s[0] = '\0';
867   }
868   PetscFunctionReturn(PETSC_SUCCESS);
869 }
870