xref: /petsc/src/sys/fileio/mprint.c (revision d2522c19e8fa9bca20aaca277941d9a63e71db6a)
1 /*
2       Utilites routines to add simple ASCII IO capability.
3 */
4 #include <../src/sys/fileio/mprint.h>
5 #include <errno.h>
6 /*
7    If petsc_history is on, then all Petsc*Printf() results are saved
8    if the appropriate (usually .petschistory) file.
9 */
10 PETSC_INTERN FILE *petsc_history;
11 /*
12      Allows one to overwrite where standard out is sent. For example
13      PETSC_STDOUT = fopen("/dev/ttyXX","w") will cause all standard out
14      writes to go to terminal XX; assuming you have write permission there
15 */
16 FILE              *PETSC_STDOUT = NULL;
17 /*
18      Allows one to overwrite where standard error is sent. For example
19      PETSC_STDERR = fopen("/dev/ttyXX","w") will cause all standard error
20      writes to go to terminal XX; assuming you have write permission there
21 */
22 FILE              *PETSC_STDERR = NULL;
23 
24 /*@C
25      PetscFormatConvertGetSize - Gets the length of a string needed to hold format converted with PetscFormatConvert()
26 
27    Input Parameter:
28 .   format - the PETSc format string
29 
30    Output Parameter:
31 .   size - the needed length of the new format
32 
33  Level: developer
34 
35 .seealso: `PetscFormatConvert()`, `PetscVSNPrintf()`, `PetscVFPrintf()`
36 
37 @*/
38 PetscErrorCode PetscFormatConvertGetSize(const char *format, size_t *size) {
39   size_t   sz = 0;
40   PetscInt i  = 0;
41 
42   PetscFunctionBegin;
43   PetscValidCharPointer(format, 1);
44   PetscValidPointer(size, 2);
45   while (format[i]) {
46     if (format[i] == '%') {
47       if (format[i + 1] == '%') {
48         i += 2;
49         sz += 2;
50         continue;
51       }
52       /* Find the letter */
53       while (format[i] && (format[i] <= '9')) {
54         ++i;
55         ++sz;
56       }
57       switch (format[i]) {
58 #if PetscDefined(USE_64BIT_INDICES)
59       case 'D': sz += 2; break;
60 #endif
61       case 'g': sz += 4;
62       default: break;
63       }
64     }
65     ++i;
66     ++sz;
67   }
68   *size = sz + 1; /* space for NULL character */
69   PetscFunctionReturn(0);
70 }
71 
72 /*@C
73      PetscFormatConvert - Takes a PETSc format string and converts the %D to %d for 32 bit PETSc indices and %lld for 64 bit PETSc indices. Also
74                         converts %g to [|%g|] so that PetscVSNPrintf() can easily insure all %g formatted numbers have a decimal point when printed.
75 
76    Input Parameters:
77 +   format - the PETSc format string
78 .   newformat - the location to put the new format
79 -   size - the length of newformat, you can use PetscFormatConvertGetSize() to compute the needed size
80 
81     Note: this exists so we can have the same code when PetscInt is either int or long long int
82 
83  Level: developer
84 
85 .seealso: `PetscFormatConvertGetSize()`, `PetscVSNPrintf()`, `PetscVFPrintf()`
86 
87 @*/
88 PetscErrorCode PetscFormatConvert(const char *format, char *newformat) {
89   PetscInt i = 0, j = 0;
90 
91   PetscFunctionBegin;
92   while (format[i]) {
93     if (format[i] == '%' && format[i + 1] == '%') {
94       newformat[j++] = format[i++];
95       newformat[j++] = format[i++];
96     } else if (format[i] == '%') {
97       if (format[i + 1] == 'g') {
98         newformat[j++] = '[';
99         newformat[j++] = '|';
100       }
101       /* Find the letter */
102       for (; format[i] && format[i] <= '9'; i++) newformat[j++] = format[i];
103       switch (format[i]) {
104       case 'D':
105 #if !defined(PETSC_USE_64BIT_INDICES)
106         newformat[j++] = 'd';
107 #else
108         newformat[j++] = 'l';
109         newformat[j++] = 'l';
110         newformat[j++] = 'd';
111 #endif
112         break;
113       case 'g':
114         newformat[j++] = format[i];
115         if (format[i - 1] == '%') {
116           newformat[j++] = '|';
117           newformat[j++] = ']';
118         }
119         break;
120       case 'G': SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "%%G format is no longer supported, use %%g and cast the argument to double");
121       case 'F': SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "%%F format is no longer supported, use %%f and cast the argument to double");
122       default: newformat[j++] = format[i]; break;
123       }
124       i++;
125     } else newformat[j++] = format[i++];
126   }
127   newformat[j] = 0;
128   PetscFunctionReturn(0);
129 }
130 
131 #define PETSCDEFAULTBUFFERSIZE 8 * 1024
132 
133 /*@C
134      PetscVSNPrintf - The PETSc version of vsnprintf(). Converts a PETSc format string into a standard C format string and then puts all the
135        function arguments into a string using the format statement.
136 
137    Input Parameters:
138 +   str - location to put result
139 .   len - the amount of space in str
140 +   format - the PETSc format string
141 -   fullLength - the amount of space in str actually used.
142 
143     Developer Notes:
144     this function may be called from an error handler, if an error occurs when it is called by the error handler than likely
145       a recursion will occur and possible crash.
146 
147  Level: developer
148 
149 .seealso: `PetscVSNPrintf()`, `PetscErrorPrintf()`, `PetscVPrintf()`
150 
151 @*/
152 PetscErrorCode PetscVSNPrintf(char *str, size_t len, const char *format, size_t *fullLength, va_list Argp) {
153   char  *newformat = NULL;
154   char   formatbuf[PETSCDEFAULTBUFFERSIZE];
155   size_t newLength;
156   int    flen;
157 
158   PetscFunctionBegin;
159   PetscCall(PetscFormatConvertGetSize(format, &newLength));
160   if (newLength < sizeof(formatbuf)) {
161     newformat = formatbuf;
162     newLength = sizeof(formatbuf) - 1;
163   } else {
164     PetscCall(PetscMalloc1(newLength, &newformat));
165   }
166   PetscCall(PetscFormatConvert(format, newformat));
167 #if defined(PETSC_HAVE_VSNPRINTF)
168   flen = vsnprintf(str, len, newformat, Argp);
169 #else
170 #error "vsnprintf not found"
171 #endif
172   if (newLength > sizeof(formatbuf) - 1) { PetscCall(PetscFree(newformat)); }
173   {
174     PetscBool foundedot;
175     size_t    cnt = 0, ncnt = 0, leng;
176     PetscCall(PetscStrlen(str, &leng));
177     if (leng > 4) {
178       for (cnt = 0; cnt < leng - 4; cnt++) {
179         if (str[cnt] == '[' && str[cnt + 1] == '|') {
180           flen -= 4;
181           cnt++;
182           cnt++;
183           foundedot = PETSC_FALSE;
184           for (; cnt < leng - 1; cnt++) {
185             if (str[cnt] == '|' && str[cnt + 1] == ']') {
186               cnt++;
187               if (!foundedot) str[ncnt++] = '.';
188               ncnt--;
189               break;
190             } else {
191               if (str[cnt] == 'e' || str[cnt] == '.') foundedot = PETSC_TRUE;
192               str[ncnt++] = str[cnt];
193             }
194           }
195         } else {
196           str[ncnt] = str[cnt];
197         }
198         ncnt++;
199       }
200       while (cnt < leng) {
201         str[ncnt] = str[cnt];
202         ncnt++;
203         cnt++;
204       }
205       str[ncnt] = 0;
206     }
207   }
208 #if defined(PETSC_HAVE_WINDOWS_H) && !defined(PETSC_HAVE__SET_OUTPUT_FORMAT)
209   /* older Windows OS always produces e-+0np for floating point output; remove the extra 0 */
210   {
211     size_t cnt = 0, ncnt = 0, leng;
212     PetscCall(PetscStrlen(str, &leng));
213     if (leng > 5) {
214       for (cnt = 0; cnt < leng - 4; cnt++) {
215         if (str[cnt] == 'e' && (str[cnt + 1] == '-' || str[cnt + 1] == '+') && str[cnt + 2] == '0' && str[cnt + 3] >= '0' && str[cnt + 3] <= '9' && str[cnt + 4] >= '0' && str[cnt + 4] <= '9') {
216           str[ncnt] = str[cnt];
217           ncnt++;
218           cnt++;
219           str[ncnt] = str[cnt];
220           ncnt++;
221           cnt++;
222           cnt++;
223           str[ncnt] = str[cnt];
224         } else {
225           str[ncnt] = str[cnt];
226         }
227         ncnt++;
228       }
229       while (cnt < leng) {
230         str[ncnt] = str[cnt];
231         ncnt++;
232         cnt++;
233       }
234       str[ncnt] = 0;
235     }
236   }
237 #endif
238   if (fullLength) *fullLength = 1 + (size_t)flen;
239   PetscFunctionReturn(0);
240 }
241 
242 /*@C
243      PetscVFPrintf -  All PETSc standard out and error messages are sent through this function; so, in theory, this can
244         can be replaced with something that does not simply write to a file.
245 
246       To use, write your own function for example,
247 $PetscErrorCode mypetscvfprintf(FILE *fd,const char format[],va_list Argp)
248 ${
249 $  PetscErrorCode ierr;
250 $
251 $  PetscFunctionBegin;
252 $   if (fd != stdout && fd != stderr) {  handle regular files
253 $      CHKERR(PetscVFPrintfDefault(fd,format,Argp));
254 $  } else {
255 $     char   buff[BIG];
256 $     size_t length;
257 $     PetscCall(PetscVSNPrintf(buff,BIG,format,&length,Argp));
258 $     now send buff to whatever stream or whatever you want
259 $ }
260 $ PetscFunctionReturn(0);
261 $}
262 then before the call to PetscInitialize() do the assignment
263 $    PetscVFPrintf = mypetscvfprintf;
264 
265       Notes:
266     For error messages this may be called by any process, for regular standard out it is
267           called only by process 0 of a given communicator
268 
269       Developer Notes:
270     this could be called by an error handler, if that happens then a recursion of the error handler may occur
271                        and a crash
272 
273   Level:  developer
274 
275 .seealso: `PetscVSNPrintf()`, `PetscErrorPrintf()`
276 
277 @*/
278 PetscErrorCode PetscVFPrintfDefault(FILE *fd, const char *format, va_list Argp) {
279   char   str[PETSCDEFAULTBUFFERSIZE];
280   char  *buff = str;
281   size_t fullLength;
282 #if defined(PETSC_HAVE_VA_COPY)
283   va_list Argpcopy;
284 #endif
285 
286   PetscFunctionBegin;
287 #if defined(PETSC_HAVE_VA_COPY)
288   va_copy(Argpcopy, Argp);
289 #endif
290   PetscCall(PetscVSNPrintf(str, sizeof(str), format, &fullLength, Argp));
291   if (fullLength > sizeof(str)) {
292     PetscCall(PetscMalloc1(fullLength, &buff));
293 #if defined(PETSC_HAVE_VA_COPY)
294     PetscCall(PetscVSNPrintf(buff, fullLength, format, NULL, Argpcopy));
295 #else
296     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "C89 does not support va_copy() hence cannot print long strings with PETSc printing routines");
297 #endif
298   }
299   fprintf(fd, "%s", buff);
300   fflush(fd);
301   if (buff != str) { PetscCall(PetscFree(buff)); }
302   PetscFunctionReturn(0);
303 }
304 
305 /*@C
306     PetscSNPrintf - Prints to a string of given length
307 
308     Not Collective
309 
310     Input Parameters:
311 +   str - the string to print to
312 .   len - the length of str
313 .   format - the usual printf() format string
314 -   ... - any arguments that are to be printed, each much have an appropriate symbol in the format argument
315 
316    Level: intermediate
317 
318 .seealso: `PetscSynchronizedFlush()`, `PetscSynchronizedFPrintf()`, `PetscFPrintf()`, `PetscVSNPrintf()`,
319           `PetscPrintf()`, `PetscViewerASCIIPrintf()`, `PetscViewerASCIISynchronizedPrintf()`, `PetscVFPrintf()`
320 @*/
321 PetscErrorCode PetscSNPrintf(char *str, size_t len, const char format[], ...) {
322   size_t  fullLength;
323   va_list Argp;
324 
325   PetscFunctionBegin;
326   va_start(Argp, format);
327   PetscCall(PetscVSNPrintf(str, len, format, &fullLength, Argp));
328   PetscFunctionReturn(0);
329 }
330 
331 /*@C
332     PetscSNPrintfCount - Prints to a string of given length, returns count
333 
334     Not Collective
335 
336     Input Parameters:
337 +   str - the string to print to
338 .   len - the length of str
339 .   format - the usual printf() format string
340 -   ... - any arguments that are to be printed, each much have an appropriate symbol in the format argument
341 
342     Output Parameter:
343 .   countused - number of characters used
344 
345    Level: intermediate
346 
347 .seealso: `PetscSynchronizedFlush()`, `PetscSynchronizedFPrintf()`, `PetscFPrintf()`, `PetscVSNPrintf()`,
348           `PetscPrintf()`, `PetscViewerASCIIPrintf()`, `PetscViewerASCIISynchronizedPrintf()`, `PetscSNPrintf()`, `PetscVFPrintf()`
349 @*/
350 PetscErrorCode PetscSNPrintfCount(char *str, size_t len, const char format[], size_t *countused, ...) {
351   va_list Argp;
352 
353   PetscFunctionBegin;
354   va_start(Argp, countused);
355   PetscCall(PetscVSNPrintf(str, len, format, countused, Argp));
356   PetscFunctionReturn(0);
357 }
358 
359 /* ----------------------------------------------------------------------- */
360 
361 PrintfQueue petsc_printfqueue = NULL, petsc_printfqueuebase = NULL;
362 int         petsc_printfqueuelength = 0;
363 
364 /*@C
365     PetscSynchronizedPrintf - Prints synchronized output from several processors.
366     Output of the first processor is followed by that of the second, etc.
367 
368     Not Collective
369 
370     Input Parameters:
371 +   comm - the communicator
372 -   format - the usual printf() format string
373 
374    Level: intermediate
375 
376     Notes:
377     REQUIRES a call to PetscSynchronizedFlush() by all the processes after the completion of the calls to PetscSynchronizedPrintf() for the information
378     from all the processors to be printed.
379 
380     Fortran Note:
381     The call sequence is PetscSynchronizedPrintf(MPI_Comm, character(*), PetscErrorCode ierr) from Fortran.
382     That is, you can only pass a single character string from Fortran.
383 
384 .seealso: `PetscSynchronizedFlush()`, `PetscSynchronizedFPrintf()`, `PetscFPrintf()`,
385           `PetscPrintf()`, `PetscViewerASCIIPrintf()`, `PetscViewerASCIISynchronizedPrintf()`
386 @*/
387 PetscErrorCode PetscSynchronizedPrintf(MPI_Comm comm, const char format[], ...) {
388   PetscMPIInt rank;
389 
390   PetscFunctionBegin;
391   PetscCheck(comm != MPI_COMM_NULL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Called with MPI_COMM_NULL, likely PetscObjectComm() failed");
392   PetscCallMPI(MPI_Comm_rank(comm, &rank));
393 
394   /* First processor prints immediately to stdout */
395   if (rank == 0) {
396     va_list Argp;
397     va_start(Argp, format);
398     PetscCall((*PetscVFPrintf)(PETSC_STDOUT, format, Argp));
399     if (petsc_history) {
400       va_start(Argp, format);
401       PetscCall((*PetscVFPrintf)(petsc_history, format, Argp));
402     }
403     va_end(Argp);
404   } else { /* other processors add to local queue */
405     va_list     Argp;
406     PrintfQueue next;
407     size_t      fullLength = PETSCDEFAULTBUFFERSIZE;
408 
409     PetscCall(PetscNew(&next));
410     if (petsc_printfqueue) {
411       petsc_printfqueue->next = next;
412       petsc_printfqueue       = next;
413       petsc_printfqueue->next = NULL;
414     } else petsc_printfqueuebase = petsc_printfqueue = next;
415     petsc_printfqueuelength++;
416     next->size   = 0;
417     next->string = NULL;
418     while (fullLength >= next->size) {
419       next->size = fullLength + 1;
420       PetscCall(PetscFree(next->string));
421       PetscCall(PetscMalloc1(next->size, &next->string));
422       va_start(Argp, format);
423       PetscCall(PetscArrayzero(next->string, next->size));
424       PetscCall(PetscVSNPrintf(next->string, next->size, format, &fullLength, Argp));
425       va_end(Argp);
426     }
427   }
428   PetscFunctionReturn(0);
429 }
430 
431 /*@C
432     PetscSynchronizedFPrintf - Prints synchronized output to the specified file from
433     several processors.  Output of the first processor is followed by that of the
434     second, etc.
435 
436     Not Collective
437 
438     Input Parameters:
439 +   comm - the communicator
440 .   fd - the file pointer
441 -   format - the usual printf() format string
442 
443     Level: intermediate
444 
445     Notes:
446     REQUIRES a intervening call to PetscSynchronizedFlush() for the information
447     from all the processors to be printed.
448 
449 .seealso: `PetscSynchronizedPrintf()`, `PetscSynchronizedFlush()`, `PetscFPrintf()`,
450           `PetscFOpen()`, `PetscViewerASCIISynchronizedPrintf()`, `PetscViewerASCIIPrintf()`
451 
452 @*/
453 PetscErrorCode PetscSynchronizedFPrintf(MPI_Comm comm, FILE *fp, const char format[], ...) {
454   PetscMPIInt rank;
455 
456   PetscFunctionBegin;
457   PetscCheck(comm != MPI_COMM_NULL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Called with MPI_COMM_NULL, likely PetscObjectComm() failed");
458   PetscCallMPI(MPI_Comm_rank(comm, &rank));
459 
460   /* First processor prints immediately to fp */
461   if (rank == 0) {
462     va_list Argp;
463     va_start(Argp, format);
464     PetscCall((*PetscVFPrintf)(fp, format, Argp));
465     if (petsc_history && (fp != petsc_history)) {
466       va_start(Argp, format);
467       PetscCall((*PetscVFPrintf)(petsc_history, format, Argp));
468     }
469     va_end(Argp);
470   } else { /* other processors add to local queue */
471     va_list     Argp;
472     PrintfQueue next;
473     size_t      fullLength = PETSCDEFAULTBUFFERSIZE;
474 
475     PetscCall(PetscNew(&next));
476     if (petsc_printfqueue) {
477       petsc_printfqueue->next = next;
478       petsc_printfqueue       = next;
479       petsc_printfqueue->next = NULL;
480     } else petsc_printfqueuebase = petsc_printfqueue = next;
481     petsc_printfqueuelength++;
482     next->size   = 0;
483     next->string = NULL;
484     while (fullLength >= next->size) {
485       next->size = fullLength + 1;
486       PetscCall(PetscFree(next->string));
487       PetscCall(PetscMalloc1(next->size, &next->string));
488       va_start(Argp, format);
489       PetscCall(PetscArrayzero(next->string, next->size));
490       PetscCall(PetscVSNPrintf(next->string, next->size, format, &fullLength, Argp));
491       va_end(Argp);
492     }
493   }
494   PetscFunctionReturn(0);
495 }
496 
497 /*@C
498     PetscSynchronizedFlush - Flushes to the screen output from all processors
499     involved in previous PetscSynchronizedPrintf()/PetscSynchronizedFPrintf() calls.
500 
501     Collective
502 
503     Input Parameters:
504 +   comm - the communicator
505 -   fd - the file pointer (valid on process 0 of the communicator)
506 
507     Level: intermediate
508 
509     Notes:
510     If PetscSynchronizedPrintf() and/or PetscSynchronizedFPrintf() are called with
511     different MPI communicators there must be an intervening call to PetscSynchronizedFlush() between the calls with different MPI communicators.
512 
513     From Fortran pass PETSC_STDOUT if the flush is for standard out; otherwise pass a value obtained from PetscFOpen()
514 
515 .seealso: `PetscSynchronizedPrintf()`, `PetscFPrintf()`, `PetscPrintf()`, `PetscViewerASCIIPrintf()`,
516           `PetscViewerASCIISynchronizedPrintf()`
517 @*/
518 PetscErrorCode PetscSynchronizedFlush(MPI_Comm comm, FILE *fd) {
519   PetscMPIInt rank, size, tag, i, j, n = 0, dummy = 0;
520   char       *message;
521   MPI_Status  status;
522 
523   PetscFunctionBegin;
524   PetscCall(PetscCommDuplicate(comm, &comm, &tag));
525   PetscCallMPI(MPI_Comm_rank(comm, &rank));
526   PetscCallMPI(MPI_Comm_size(comm, &size));
527 
528   /* First processor waits for messages from all other processors */
529   if (rank == 0) {
530     if (!fd) fd = PETSC_STDOUT;
531     for (i = 1; i < size; i++) {
532       /* to prevent a flood of messages to process zero, request each message separately */
533       PetscCallMPI(MPI_Send(&dummy, 1, MPI_INT, i, tag, comm));
534       PetscCallMPI(MPI_Recv(&n, 1, MPI_INT, i, tag, comm, &status));
535       for (j = 0; j < n; j++) {
536         PetscMPIInt size = 0;
537 
538         PetscCallMPI(MPI_Recv(&size, 1, MPI_INT, i, tag, comm, &status));
539         PetscCall(PetscMalloc1(size, &message));
540         PetscCallMPI(MPI_Recv(message, size, MPI_CHAR, i, tag, comm, &status));
541         PetscCall(PetscFPrintf(comm, fd, "%s", message));
542         PetscCall(PetscFree(message));
543       }
544     }
545   } else { /* other processors send queue to processor 0 */
546     PrintfQueue next = petsc_printfqueuebase, previous;
547 
548     PetscCallMPI(MPI_Recv(&dummy, 1, MPI_INT, 0, tag, comm, &status));
549     PetscCallMPI(MPI_Send(&petsc_printfqueuelength, 1, MPI_INT, 0, tag, comm));
550     for (i = 0; i < petsc_printfqueuelength; i++) {
551       PetscCallMPI(MPI_Send(&next->size, 1, MPI_INT, 0, tag, comm));
552       PetscCallMPI(MPI_Send(next->string, next->size, MPI_CHAR, 0, tag, comm));
553       previous = next;
554       next     = next->next;
555       PetscCall(PetscFree(previous->string));
556       PetscCall(PetscFree(previous));
557     }
558     petsc_printfqueue       = NULL;
559     petsc_printfqueuelength = 0;
560   }
561   PetscCall(PetscCommDestroy(&comm));
562   PetscFunctionReturn(0);
563 }
564 
565 /* ---------------------------------------------------------------------------------------*/
566 
567 /*@C
568     PetscFPrintf - Prints to a file, only from the first
569     processor in the communicator.
570 
571     Not Collective
572 
573     Input Parameters:
574 +   comm - the communicator
575 .   fd - the file pointer
576 -   format - the usual printf() format string
577 
578     Level: intermediate
579 
580     Fortran Note:
581     This routine is not supported in Fortran.
582 
583 .seealso: `PetscPrintf()`, `PetscSynchronizedPrintf()`, `PetscViewerASCIIPrintf()`,
584           `PetscViewerASCIISynchronizedPrintf()`, `PetscSynchronizedFlush()`
585 @*/
586 PetscErrorCode PetscFPrintf(MPI_Comm comm, FILE *fd, const char format[], ...) {
587   PetscMPIInt rank;
588 
589   PetscFunctionBegin;
590   PetscCheck(comm != MPI_COMM_NULL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Called with MPI_COMM_NULL, likely PetscObjectComm() failed");
591   PetscCallMPI(MPI_Comm_rank(comm, &rank));
592   if (rank == 0) {
593     va_list Argp;
594     va_start(Argp, format);
595     PetscCall((*PetscVFPrintf)(fd, format, Argp));
596     if (petsc_history && (fd != petsc_history)) {
597       va_start(Argp, format);
598       PetscCall((*PetscVFPrintf)(petsc_history, format, Argp));
599     }
600     va_end(Argp);
601   }
602   PetscFunctionReturn(0);
603 }
604 
605 /*@C
606     PetscPrintf - Prints to standard out, only from the first
607     processor in the communicator. Calls from other processes are ignored.
608 
609     Not Collective
610 
611     Input Parameters:
612 +   comm - the communicator
613 -   format - the usual printf() format string
614 
615     Level: intermediate
616 
617     Notes:
618     PetscPrintf() supports some format specifiers that are unique to PETSc.
619     See the manual page for PetscFormatConvert() for details.
620 
621     Fortran Note:
622     The call sequence is PetscPrintf(MPI_Comm, character(*), PetscErrorCode ierr) from Fortran.
623     That is, you can only pass a single character string from Fortran.
624 
625 .seealso: `PetscFPrintf()`, `PetscSynchronizedPrintf()`, `PetscFormatConvert()`
626 @*/
627 PetscErrorCode PetscPrintf(MPI_Comm comm, const char format[], ...) {
628   PetscMPIInt rank;
629 
630   PetscFunctionBegin;
631   PetscCheck(comm != MPI_COMM_NULL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Called with MPI_COMM_NULL, likely PetscObjectComm() failed");
632   PetscCallMPI(MPI_Comm_rank(comm, &rank));
633   if (rank == 0) {
634     va_list Argp;
635     va_start(Argp, format);
636     PetscCall((*PetscVFPrintf)(PETSC_STDOUT, format, Argp));
637     if (petsc_history) {
638       va_start(Argp, format);
639       PetscCall((*PetscVFPrintf)(petsc_history, format, Argp));
640     }
641     va_end(Argp);
642   }
643   PetscFunctionReturn(0);
644 }
645 
646 PetscErrorCode PetscHelpPrintfDefault(MPI_Comm comm, const char format[], ...) {
647   PetscMPIInt rank;
648 
649   PetscFunctionBegin;
650   PetscCheck(comm != MPI_COMM_NULL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Called with MPI_COMM_NULL, likely PetscObjectComm() failed");
651   PetscCallMPI(MPI_Comm_rank(comm, &rank));
652   if (rank == 0) {
653     va_list Argp;
654     va_start(Argp, format);
655     PetscCall((*PetscVFPrintf)(PETSC_STDOUT, format, Argp));
656     if (petsc_history) {
657       va_start(Argp, format);
658       PetscCall((*PetscVFPrintf)(petsc_history, format, Argp));
659     }
660     va_end(Argp);
661   }
662   PetscFunctionReturn(0);
663 }
664 
665 /* ---------------------------------------------------------------------------------------*/
666 
667 /*@C
668     PetscSynchronizedFGets - Several processors all get the same line from a file.
669 
670     Collective
671 
672     Input Parameters:
673 +   comm - the communicator
674 .   fd - the file pointer
675 -   len - the length of the output buffer
676 
677     Output Parameter:
678 .   string - the line read from the file, at end of file string[0] == 0
679 
680     Level: intermediate
681 
682 .seealso: `PetscSynchronizedPrintf()`, `PetscSynchronizedFlush()`,
683           `PetscFOpen()`, `PetscViewerASCIISynchronizedPrintf()`, `PetscViewerASCIIPrintf()`
684 
685 @*/
686 PetscErrorCode PetscSynchronizedFGets(MPI_Comm comm, FILE *fp, size_t len, char string[]) {
687   PetscMPIInt rank;
688 
689   PetscFunctionBegin;
690   PetscCallMPI(MPI_Comm_rank(comm, &rank));
691 
692   if (rank == 0) {
693     char *ptr = fgets(string, len, fp);
694 
695     if (!ptr) {
696       string[0] = 0;
697       PetscCheck(feof(fp), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from file: %d", errno);
698     }
699   }
700   PetscCallMPI(MPI_Bcast(string, len, MPI_BYTE, 0, comm));
701   PetscFunctionReturn(0);
702 }
703 
704 #if defined(PETSC_HAVE_CLOSURE)
705 int (^SwiftClosure)(const char *) = 0;
706 
707 PetscErrorCode PetscVFPrintfToString(FILE *fd, const char format[], va_list Argp) {
708   PetscFunctionBegin;
709   if (fd != stdout && fd != stderr) { /* handle regular files */
710     PetscCall(PetscVFPrintfDefault(fd, format, Argp));
711   } else {
712     size_t length;
713     char   buff[PETSCDEFAULTBUFFERSIZE];
714 
715     PetscCall(PetscVSNPrintf(buff, sizeof(buff), format, &length, Argp));
716     PetscCall(SwiftClosure(buff));
717   }
718   PetscFunctionReturn(0);
719 }
720 
721 /*
722    Provide a Swift function that processes all the PETSc calls to PetscVFPrintf()
723 */
724 PetscErrorCode PetscVFPrintfSetClosure(int (^closure)(const char *)) {
725   PetscVFPrintf = PetscVFPrintfToString;
726   SwiftClosure  = closure;
727   return 0;
728 }
729 #endif
730 
731 /*@C
732      PetscFormatStrip - Takes a PETSc format string and removes all numerical modifiers to % operations
733 
734    Input Parameters:
735 .   format - the PETSc format string
736 
737  Level: developer
738 
739 @*/
740 PetscErrorCode PetscFormatStrip(char *format) {
741   size_t loc1 = 0, loc2 = 0;
742 
743   PetscFunctionBegin;
744   while (format[loc2]) {
745     if (format[loc2] == '%') {
746       format[loc1++] = format[loc2++];
747       while (format[loc2] && ((format[loc2] >= '0' && format[loc2] <= '9') || format[loc2] == '.')) loc2++;
748     }
749     format[loc1++] = format[loc2++];
750   }
751   PetscFunctionReturn(0);
752 }
753 
754 PetscErrorCode PetscFormatRealArray(char buf[], size_t len, const char *fmt, PetscInt n, const PetscReal x[]) {
755   PetscInt i;
756   size_t   left, count;
757   char    *p;
758 
759   PetscFunctionBegin;
760   for (i = 0, p = buf, left = len; i < n; i++) {
761     PetscCall(PetscSNPrintfCount(p, left, fmt, &count, (double)x[i]));
762     PetscCheck(count < left, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Insufficient space in buffer");
763     left -= count;
764     p += count - 1;
765     *p++ = ' ';
766   }
767   p[i ? 0 : -1] = 0;
768   PetscFunctionReturn(0);
769 }
770