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