xref: /petsc/src/sys/fileio/mprint.c (revision ee1cef2c4f92b4b45b8aa3ceac59cb944062fab9)
1 #define PETSC_DLL
2 /*
3       Utilites routines to add simple ASCII IO capability.
4 */
5 #include "../src/sys/fileio/mprint.h"
6 /*
7    If petsc_history is on, then all Petsc*Printf() results are saved
8    if the appropriate (usually .petschistory) file.
9 */
10 extern 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 = 0;
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 = 0;
23 /*
24      Used to output to Zope
25 */
26 FILE *PETSC_ZOPEFD = 0;
27 
28 #undef __FUNCT__
29 #define __FUNCT__ "PetscFormatConvert"
30 PetscErrorCode PETSC_DLLEXPORT PetscFormatConvert(const char *format,char *newformat,PetscInt size)
31 {
32   PetscInt i = 0,j = 0;
33 
34   while (format[i] && i < size-1) {
35     if (format[i] == '%' && format[i+1] == 'D') {
36       newformat[j++] = '%';
37 #if !defined(PETSC_USE_64BIT_INDICES)
38       newformat[j++] = 'd';
39 #else
40       newformat[j++] = 'l';
41       newformat[j++] = 'l';
42       newformat[j++] = 'd';
43 #endif
44       i += 2;
45     } else if (format[i] == '%' && format[i+1] >= '1' && format[i+1] <= '9' && format[i+2] == 'D') {
46       newformat[j++] = '%';
47       newformat[j++] = format[i+1];
48 #if !defined(PETSC_USE_64BIT_INDICES)
49       newformat[j++] = 'd';
50 #else
51       newformat[j++] = 'l';
52       newformat[j++] = 'l';
53       newformat[j++] = 'd';
54 #endif
55       i += 3;
56     } else if (format[i] == '%' && format[i+1] == 'G') {
57       newformat[j++] = '%';
58 #if defined(PETSC_USE_INT)
59       newformat[j++] = 'd';
60 #elif !defined(PETSC_USE_LONG_DOUBLE)
61       newformat[j++] = 'g';
62 #else
63       newformat[j++] = 'L';
64       newformat[j++] = 'g';
65 #endif
66       i += 2;
67     }else {
68       newformat[j++] = format[i++];
69     }
70   }
71   newformat[j] = 0;
72   return 0;
73 }
74 
75 #undef __FUNCT__
76 #define __FUNCT__ "PetscVSNPrintf"
77 /*
78    No error handling because may be called by error handler
79 */
80 PetscErrorCode PETSC_DLLEXPORT PetscVSNPrintf(char *str,size_t len,const char *format,int *fullLength,va_list Argp)
81 {
82   /* no malloc since may be called by error handler */
83   char          *newformat;
84   char           formatbuf[8*1024];
85   size_t         oldLength,length;
86   PetscErrorCode ierr;
87 
88   ierr = PetscStrlen(format, &oldLength);CHKERRQ(ierr);
89   if (oldLength < 8*1024) {
90     newformat = formatbuf;
91   } else {
92     ierr = PetscMalloc((oldLength+1) * sizeof(char), &newformat);CHKERRQ(ierr);
93   }
94   PetscFormatConvert(format,newformat,oldLength+1);
95   ierr = PetscStrlen(newformat, &length);CHKERRQ(ierr);
96 #if 0
97   if (length > len) {
98     newformat[len] = '\0';
99   }
100 #endif
101 #if defined(PETSC_HAVE_VSNPRINTF_CHAR)
102   *fullLength = vsnprintf(str,len,newformat,(char *)Argp);
103 #elif defined(PETSC_HAVE_VSNPRINTF)
104   *fullLength = vsnprintf(str,len,newformat,Argp);
105 #elif defined(PETSC_HAVE__VSNPRINTF)
106   *fullLength = _vsnprintf(str,len,newformat,Argp);
107 #else
108 #error "vsnprintf not found"
109 #endif
110   if (oldLength >= 8*1024) {
111     ierr = PetscFree(newformat);CHKERRQ(ierr);
112   }
113   return 0;
114 }
115 
116 #undef __FUNCT__
117 #define __FUNCT__ "PetscZopeLog"
118 
119 PetscErrorCode PETSC_DLLEXPORT PetscZopeLog(const char *format,va_list Argp){
120   /* no malloc since may be called by error handler */
121   char     newformat[8*1024];
122   char     log[8*1024];
123 
124   extern FILE * PETSC_ZOPEFD;
125   char logstart[] = " <<<log>>>";
126   size_t len;
127   size_t formatlen;
128   PetscFormatConvert(format,newformat,8*1024);
129   PetscStrlen(logstart, &len);
130   PetscMemcpy(log, logstart, len);
131   PetscStrlen(newformat, &formatlen);
132   PetscMemcpy(&(log[len]), newformat, formatlen);
133   if(PETSC_ZOPEFD != NULL){
134 #if defined(PETSC_HAVE_VFPRINTF_CHAR)
135   vfprintf(PETSC_ZOPEFD,log,(char *)Argp);
136 #else
137   vfprintf(PETSC_ZOPEFD,log,Argp);
138 #endif
139   fflush(PETSC_ZOPEFD);
140 }
141   return 0;
142 }
143 
144 #undef __FUNCT__
145 #define __FUNCT__ "PetscVFPrintf"
146 /*
147    All PETSc standard out and error messages are sent through this function; so, in theory, this can
148    can be replaced with something that does not simply write to a file.
149 
150    Note: For error messages this may be called by a process, for regular standard out it is
151    called only by process 0 of a given communicator
152 
153    No error handling because may be called by error handler
154 */
155 PetscErrorCode PETSC_DLLEXPORT PetscVFPrintfDefault(FILE *fd,const char *format,va_list Argp)
156 {
157   /* no malloc since may be called by error handler (assume no long messages in errors) */
158   char        *newformat;
159   char         formatbuf[8*1024];
160   size_t       oldLength;
161   extern FILE *PETSC_ZOPEFD;
162 
163   PetscStrlen(format, &oldLength);
164   if (oldLength < 8*1024) {
165     newformat = formatbuf;
166   } else {
167     PetscMalloc((oldLength+1) * sizeof(char), &newformat);
168   }
169   PetscFormatConvert(format,newformat,oldLength+1);
170   if(PETSC_ZOPEFD != NULL && PETSC_ZOPEFD != PETSC_STDOUT){
171     va_list s;
172 #if defined(PETSC_HAVE_VA_COPY)
173     va_copy(s, Argp);
174 #elif defined(PETSC_HAVE___VA_COPY)
175     __va_copy(s, Argp);
176 #else
177     SETERRQ(PETSC_ERR_SUP_SYS,"Zope not supported due to missing va_copy()");
178 #endif
179 
180 #if defined(PETSC_HAVE_VA_COPY) || defined(PETSC_HAVE___VA_COPY)
181 #if defined(PETSC_HAVE_VFPRINTF_CHAR)
182     vfprintf(PETSC_ZOPEFD,newformat,(char *)s);
183 #else
184     vfprintf(PETSC_ZOPEFD,newformat,s);
185 #endif
186     fflush(PETSC_ZOPEFD);
187 #endif
188   }
189 
190 #if defined(PETSC_HAVE_VFPRINTF_CHAR)
191   vfprintf(fd,newformat,(char *)Argp);
192 #else
193   vfprintf(fd,newformat,Argp);
194 #endif
195   fflush(fd);
196   if (oldLength >= 8*1024) {
197     if (PetscFree(newformat)) {};
198   }
199   return 0;
200 }
201 
202 #undef __FUNCT__
203 #define __FUNCT__ "PetscSNPrintf"
204 /*@C
205     PetscSNPrintf - Prints to a string of given length
206 
207     Not Collective
208 
209     Input Parameters:
210 +   str - the string to print to
211 .   len - the length of str
212 .   format - the usual printf() format string
213 -   any arguments
214 
215    Level: intermediate
216 
217 .seealso: PetscSynchronizedFlush(), PetscSynchronizedFPrintf(), PetscFPrintf(), PetscVSNPrintf(),
218           PetscPrintf(), PetscViewerASCIIPrintf(), PetscViewerASCIISynchronizedPrintf()
219 @*/
220 PetscErrorCode PETSC_DLLEXPORT PetscSNPrintf(char *str,size_t len,const char format[],...)
221 {
222   PetscErrorCode ierr;
223   int            fullLength;
224   va_list        Argp;
225 
226   PetscFunctionBegin;
227   va_start(Argp,format);
228   ierr = PetscVSNPrintf(str,len,format,&fullLength,Argp);CHKERRQ(ierr);
229   PetscFunctionReturn(0);
230 }
231 
232 /* ----------------------------------------------------------------------- */
233 
234 PrintfQueue queue       = 0,queuebase = 0;
235 int         queuelength = 0;
236 FILE        *queuefile  = PETSC_NULL;
237 
238 #undef __FUNCT__
239 #define __FUNCT__ "PetscSynchronizedPrintf"
240 /*@C
241     PetscSynchronizedPrintf - Prints synchronized output from several processors.
242     Output of the first processor is followed by that of the second, etc.
243 
244     Not Collective
245 
246     Input Parameters:
247 +   comm - the communicator
248 -   format - the usual printf() format string
249 
250    Level: intermediate
251 
252     Notes:
253     REQUIRES a intervening call to PetscSynchronizedFlush() for the information
254     from all the processors to be printed.
255 
256     Fortran Note:
257     The call sequence is PetscSynchronizedPrintf(MPI_Comm, character(*), PetscErrorCode ierr) from Fortran.
258     That is, you can only pass a single character string from Fortran.
259 
260 .seealso: PetscSynchronizedFlush(), PetscSynchronizedFPrintf(), PetscFPrintf(),
261           PetscPrintf(), PetscViewerASCIIPrintf(), PetscViewerASCIISynchronizedPrintf()
262 @*/
263 PetscErrorCode PETSC_DLLEXPORT PetscSynchronizedPrintf(MPI_Comm comm,const char format[],...)
264 {
265   PetscErrorCode ierr;
266   PetscMPIInt    rank;
267 
268   PetscFunctionBegin;
269   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
270 
271   /* First processor prints immediately to stdout */
272   if (!rank) {
273     va_list Argp;
274     va_start(Argp,format);
275     ierr = (*PetscVFPrintf)(PETSC_STDOUT,format,Argp);CHKERRQ(ierr);
276     if (petsc_history) {
277       ierr = (*PetscVFPrintf)(petsc_history,format,Argp);CHKERRQ(ierr);
278     }
279     va_end(Argp);
280   } else { /* other processors add to local queue */
281     va_list     Argp;
282     PrintfQueue next;
283     int         fullLength = 8191;
284 
285     ierr = PetscNew(struct _PrintfQueue,&next);CHKERRQ(ierr);
286     if (queue) {queue->next = next; queue = next; queue->next = 0;}
287     else       {queuebase   = queue = next;}
288     queuelength++;
289     next->size = -1;
290     while(fullLength >= next->size) {
291       next->size = fullLength+1;
292       ierr = PetscMalloc(next->size * sizeof(char), &next->string);CHKERRQ(ierr);
293       va_start(Argp,format);
294       ierr = PetscMemzero(next->string,next->size);CHKERRQ(ierr);
295       ierr = PetscVSNPrintf(next->string,next->size,format, &fullLength,Argp);CHKERRQ(ierr);
296       va_end(Argp);
297     }
298   }
299 
300   PetscFunctionReturn(0);
301 }
302 
303 #undef __FUNCT__
304 #define __FUNCT__ "PetscSynchronizedFPrintf"
305 /*@C
306     PetscSynchronizedFPrintf - Prints synchronized output to the specified file from
307     several processors.  Output of the first processor is followed by that of the
308     second, etc.
309 
310     Not Collective
311 
312     Input Parameters:
313 +   comm - the communicator
314 .   fd - the file pointer
315 -   format - the usual printf() format string
316 
317     Level: intermediate
318 
319     Notes:
320     REQUIRES a intervening call to PetscSynchronizedFlush() for the information
321     from all the processors to be printed.
322 
323     Contributed by: Matthew Knepley
324 
325 .seealso: PetscSynchronizedPrintf(), PetscSynchronizedFlush(), PetscFPrintf(),
326           PetscFOpen(), PetscViewerASCIISynchronizedPrintf(), PetscViewerASCIIPrintf()
327 
328 @*/
329 PetscErrorCode PETSC_DLLEXPORT PetscSynchronizedFPrintf(MPI_Comm comm,FILE* fp,const char format[],...)
330 {
331   PetscErrorCode ierr;
332   PetscMPIInt    rank;
333 
334   PetscFunctionBegin;
335   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
336 
337   /* First processor prints immediately to fp */
338   if (!rank) {
339     va_list Argp;
340     va_start(Argp,format);
341     ierr = (*PetscVFPrintf)(fp,format,Argp);CHKERRQ(ierr);
342     queuefile = fp;
343     if (petsc_history) {
344       ierr = (*PetscVFPrintf)(petsc_history,format,Argp);CHKERRQ(ierr);
345     }
346     va_end(Argp);
347   } else { /* other processors add to local queue */
348     va_list     Argp;
349     PrintfQueue next;
350     int         fullLength = 8191;
351     ierr = PetscNew(struct _PrintfQueue,&next);CHKERRQ(ierr);
352     if (queue) {queue->next = next; queue = next; queue->next = 0;}
353     else       {queuebase   = queue = next;}
354     queuelength++;
355     next->size = -1;
356     while(fullLength >= next->size) {
357       next->size = fullLength+1;
358       ierr = PetscMalloc(next->size * sizeof(char), &next->string);CHKERRQ(ierr);
359       va_start(Argp,format);
360       ierr = PetscMemzero(next->string,next->size);CHKERRQ(ierr);
361       ierr = PetscVSNPrintf(next->string,next->size,format,&fullLength,Argp);CHKERRQ(ierr);
362       va_end(Argp);
363     }
364   }
365   PetscFunctionReturn(0);
366 }
367 
368 #undef __FUNCT__
369 #define __FUNCT__ "PetscSynchronizedFlush"
370 /*@
371     PetscSynchronizedFlush - Flushes to the screen output from all processors
372     involved in previous PetscSynchronizedPrintf() calls.
373 
374     Collective on MPI_Comm
375 
376     Input Parameters:
377 .   comm - the communicator
378 
379     Level: intermediate
380 
381     Notes:
382     Usage of PetscSynchronizedPrintf() and PetscSynchronizedFPrintf() with
383     different MPI communicators REQUIRES an intervening call to PetscSynchronizedFlush().
384 
385 .seealso: PetscSynchronizedPrintf(), PetscFPrintf(), PetscPrintf(), PetscViewerASCIIPrintf(),
386           PetscViewerASCIISynchronizedPrintf()
387 @*/
388 PetscErrorCode PETSC_DLLEXPORT PetscSynchronizedFlush(MPI_Comm comm)
389 {
390   PetscErrorCode ierr;
391   PetscMPIInt    rank,size,tag,i,j,n;
392   char          *message;
393   MPI_Status     status;
394   FILE           *fd;
395 
396   PetscFunctionBegin;
397   ierr = PetscCommDuplicate(comm,&comm,&tag);CHKERRQ(ierr);
398   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
399   ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
400 
401   /* First processor waits for messages from all other processors */
402   if (!rank) {
403     if (queuefile) {
404       fd = queuefile;
405     } else {
406       fd = PETSC_STDOUT;
407     }
408     for (i=1; i<size; i++) {
409       ierr = MPI_Recv(&n,1,MPI_INT,i,tag,comm,&status);CHKERRQ(ierr);
410       for (j=0; j<n; j++) {
411         int size;
412 
413         ierr = MPI_Recv(&size,1,MPI_INT,i,tag,comm,&status);CHKERRQ(ierr);
414         ierr = PetscMalloc(size * sizeof(char), &message);CHKERRQ(ierr);
415         ierr = MPI_Recv(message,size,MPI_CHAR,i,tag,comm,&status);CHKERRQ(ierr);
416         ierr = PetscFPrintf(comm,fd,"%s",message);
417         ierr = PetscFree(message);CHKERRQ(ierr);
418       }
419     }
420     queuefile = PETSC_NULL;
421   } else { /* other processors send queue to processor 0 */
422     PrintfQueue next = queuebase,previous;
423 
424     ierr = MPI_Send(&queuelength,1,MPI_INT,0,tag,comm);CHKERRQ(ierr);
425     for (i=0; i<queuelength; i++) {
426       ierr     = MPI_Send(&next->size,1,MPI_INT,0,tag,comm);CHKERRQ(ierr);
427       ierr     = MPI_Send(next->string,next->size,MPI_CHAR,0,tag,comm);CHKERRQ(ierr);
428       previous = next;
429       next     = next->next;
430       ierr     = PetscFree(previous->string);CHKERRQ(ierr);
431       ierr     = PetscFree(previous);CHKERRQ(ierr);
432     }
433     queue       = 0;
434     queuelength = 0;
435   }
436   ierr = PetscCommDestroy(&comm);CHKERRQ(ierr);
437   PetscFunctionReturn(0);
438 }
439 
440 /* ---------------------------------------------------------------------------------------*/
441 
442 #undef __FUNCT__
443 #define __FUNCT__ "PetscFPrintf"
444 /*@C
445     PetscFPrintf - Prints to a file, only from the first
446     processor in the communicator.
447 
448     Not Collective
449 
450     Input Parameters:
451 +   comm - the communicator
452 .   fd - the file pointer
453 -   format - the usual printf() format string
454 
455     Level: intermediate
456 
457     Fortran Note:
458     This routine is not supported in Fortran.
459 
460    Concepts: printing^in parallel
461    Concepts: printf^in parallel
462 
463 .seealso: PetscPrintf(), PetscSynchronizedPrintf(), PetscViewerASCIIPrintf(),
464           PetscViewerASCIISynchronizedPrintf(), PetscSynchronizedFlush()
465 @*/
466 PetscErrorCode PETSC_DLLEXPORT PetscFPrintf(MPI_Comm comm,FILE* fd,const char format[],...)
467 {
468   PetscErrorCode ierr;
469   PetscMPIInt    rank;
470 
471   PetscFunctionBegin;
472   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
473   if (!rank) {
474     va_list Argp;
475     va_start(Argp,format);
476     ierr = (*PetscVFPrintf)(fd,format,Argp);CHKERRQ(ierr);
477     if (petsc_history) {
478       ierr = (*PetscVFPrintf)(petsc_history,format,Argp);CHKERRQ(ierr);
479     }
480     va_end(Argp);
481   }
482   PetscFunctionReturn(0);
483 }
484 
485 #undef __FUNCT__
486 #define __FUNCT__ "PetscPrintf"
487 /*@C
488     PetscPrintf - Prints to standard out, only from the first
489     processor in the communicator.
490 
491     Not Collective
492 
493     Input Parameters:
494 +   comm - the communicator
495 -   format - the usual printf() format string
496 
497    Level: intermediate
498 
499     Fortran Note:
500     The call sequence is PetscPrintf(MPI_Comm, character(*), PetscErrorCode ierr) from Fortran.
501     That is, you can only pass a single character string from Fortran.
502 
503    Notes: %A is replace with %g unless the value is < 1.e-12 when it is
504           replaced with < 1.e-12
505 
506    Concepts: printing^in parallel
507    Concepts: printf^in parallel
508 
509 .seealso: PetscFPrintf(), PetscSynchronizedPrintf()
510 @*/
511 PetscErrorCode PETSC_DLLEXPORT PetscPrintf(MPI_Comm comm,const char format[],...)
512 {
513   PetscErrorCode ierr;
514   PetscMPIInt    rank;
515   size_t         len;
516   char           *nformat,*sub1,*sub2;
517   PetscReal      value;
518 
519   PetscFunctionBegin;
520   if (!comm) comm = PETSC_COMM_WORLD;
521   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
522   if (!rank) {
523     va_list Argp;
524     va_start(Argp,format);
525 
526     ierr = PetscStrstr(format,"%A",&sub1);CHKERRQ(ierr);
527     if (sub1) {
528       ierr = PetscStrstr(format,"%",&sub2);CHKERRQ(ierr);
529       if (sub1 != sub2) SETERRQ(PETSC_ERR_ARG_WRONG,"%%A format must be first in format string");
530       ierr    = PetscStrlen(format,&len);CHKERRQ(ierr);
531       ierr    = PetscMalloc((len+16)*sizeof(char),&nformat);CHKERRQ(ierr);
532       ierr    = PetscStrcpy(nformat,format);CHKERRQ(ierr);
533       ierr    = PetscStrstr(nformat,"%",&sub2);CHKERRQ(ierr);
534       sub2[0] = 0;
535       value   = (double)va_arg(Argp,double);
536       if (PetscAbsReal(value) < 1.e-12) {
537         ierr    = PetscStrcat(nformat,"< 1.e-12");CHKERRQ(ierr);
538       } else {
539         ierr    = PetscStrcat(nformat,"%g");CHKERRQ(ierr);
540         va_end(Argp);
541         va_start(Argp,format);
542       }
543       ierr    = PetscStrcat(nformat,sub1+2);CHKERRQ(ierr);
544     } else {
545       nformat = (char*)format;
546     }
547     ierr = (*PetscVFPrintf)(PETSC_STDOUT,nformat,Argp);CHKERRQ(ierr);
548     if (petsc_history) {
549       ierr = (*PetscVFPrintf)(petsc_history,nformat,Argp);CHKERRQ(ierr);
550     }
551     va_end(Argp);
552     if (sub1) {ierr = PetscFree(nformat);CHKERRQ(ierr);}
553   }
554   PetscFunctionReturn(0);
555 }
556 
557 /* ---------------------------------------------------------------------------------------*/
558 #undef __FUNCT__
559 #define __FUNCT__ "PetscHelpPrintfDefault"
560 PetscErrorCode PETSC_DLLEXPORT PetscHelpPrintfDefault(MPI_Comm comm,const char format[],...)
561 {
562   PetscErrorCode ierr;
563   PetscMPIInt    rank;
564 
565   PetscFunctionBegin;
566   if (!comm) comm = PETSC_COMM_WORLD;
567   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
568   if (!rank) {
569     va_list Argp;
570     va_start(Argp,format);
571     ierr = (*PetscVFPrintf)(PETSC_STDOUT,format,Argp);CHKERRQ(ierr);
572     if (petsc_history) {
573       ierr = (*PetscVFPrintf)(petsc_history,format,Argp);CHKERRQ(ierr);
574     }
575     va_end(Argp);
576   }
577   PetscFunctionReturn(0);
578 }
579 
580 /* ---------------------------------------------------------------------------------------*/
581 
582 
583 #undef __FUNCT__
584 #define __FUNCT__ "PetscSynchronizedFGets"
585 /*@C
586     PetscSynchronizedFGets - Several processors all get the same line from a file.
587 
588     Collective on MPI_Comm
589 
590     Input Parameters:
591 +   comm - the communicator
592 .   fd - the file pointer
593 -   len - the length of the output buffer
594 
595     Output Parameter:
596 .   string - the line read from the file
597 
598     Level: intermediate
599 
600 .seealso: PetscSynchronizedPrintf(), PetscSynchronizedFlush(),
601           PetscFOpen(), PetscViewerASCIISynchronizedPrintf(), PetscViewerASCIIPrintf()
602 
603 @*/
604 PetscErrorCode PETSC_DLLEXPORT PetscSynchronizedFGets(MPI_Comm comm,FILE* fp,size_t len,char string[])
605 {
606   PetscErrorCode ierr;
607   PetscMPIInt    rank;
608 
609   PetscFunctionBegin;
610   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
611 
612   if (!rank) {
613     fgets(string,len,fp);
614   }
615   ierr = MPI_Bcast(string,len,MPI_BYTE,0,comm);CHKERRQ(ierr);
616   PetscFunctionReturn(0);
617 }
618