b70df1d25697a2bc0240b862c3f1e72ea4d811d1
[alexxy/gromacs.git] / src / gromacs / gmxlib / statutil.cpp
1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
2  *
3  *
4  *                This source code is part of
5  *
6  *                 G   R   O   M   A   C   S
7  *
8  *          GROningen MAchine for Chemical Simulations
9  *
10  *                        VERSION 3.2.0
11  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
12  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
13  * Copyright (c) 2001-2004, The GROMACS development team,
14  * check out http://www.gromacs.org for more information.
15
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * If you want to redistribute modifications, please consider that
22  * scientific software is very special. Version control is crucial -
23  * bugs must be traceable. We will be happy to consider code for
24  * inclusion in the official distribution, but derived work must not
25  * be called official GROMACS. Details are found in the README & COPYING
26  * files - if they are missing, get the official version at www.gromacs.org.
27  *
28  * To help us fund GROMACS development, we humbly ask that you cite
29  * the papers on the package - you can find them in the top README file.
30  *
31  * For more info, check our website at http://www.gromacs.org
32  *
33  * And Hey:
34  * GROningen Mixture of Alchemy and Childrens' Stories
35  */
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 #include <cctype>
41 #include <cmath>
42 #include <cstdlib>
43
44 #include "sysstuff.h"
45 #include "macros.h"
46 #include "string2.h"
47 #include "smalloc.h"
48 #include "statutil.h"
49 #include "tpxio.h"
50 #include "gmx_fatal.h"
51 #include "network.h"
52 #include "gmxfio.h"
53
54 #include "gromacs/onlinehelp/wman.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/programinfo.h"
58
59 #include "thread_mpi/threads.h"
60
61 #ifdef HAVE_UNISTD_H
62 #include <unistd.h>
63 #endif
64
65 /* used for npri */
66 #ifdef __sgi
67 #include <sys/schedctl.h>
68 #include <sys/sysmp.h>
69 #endif
70
71 /* The source code in this file should be thread-safe.
72       Please keep it that way. */
73
74 /******************************************************************
75  *
76  *             T R A J E C T O R Y   S T U F F
77  *
78  ******************************************************************/
79
80 /****************************************************************
81  *
82  *            E X P O R T E D   F U N C T I O N S
83  *
84  ****************************************************************/
85
86
87 /* progam names, etc. */
88
89 const char *ShortProgram(void)
90 {
91     try
92     {
93         // TODO: Use the display name once it doesn't break anything.
94         return gmx::ProgramInfo::getInstance().programName().c_str();
95     }
96     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
97 }
98
99 const char *Program(void)
100 {
101     try
102     {
103         return gmx::ProgramInfo::getInstance().programNameWithPath().c_str();
104     }
105     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
106 }
107
108 /* utility functions */
109
110 gmx_bool bRmod_fd(double a, double b, double c, gmx_bool bDouble)
111 {
112     int    iq;
113     double tol;
114
115     tol = 2*(bDouble ? GMX_DOUBLE_EPS : GMX_FLOAT_EPS);
116
117     iq = static_cast<int>((a - b + tol*a)/c);
118
119     if (std::fabs(a - b - c*iq) <= tol*std::fabs(a))
120     {
121         return TRUE;
122     }
123     else
124     {
125         return FALSE;
126     }
127 }
128
129 int check_times2(real t, real t0, gmx_bool bDouble)
130 {
131     int  r;
132
133 #ifndef GMX_DOUBLE
134     /* since t is float, we can not use double precision for bRmod */
135     bDouble = FALSE;
136 #endif
137
138     r = -1;
139     if ((!bTimeSet(TBEGIN) || (t >= rTimeValue(TBEGIN)))  &&
140         (!bTimeSet(TEND)   || (t <= rTimeValue(TEND))))
141     {
142         if (bTimeSet(TDELTA) && !bRmod_fd(t, t0, rTimeValue(TDELTA), bDouble))
143         {
144             r = -1;
145         }
146         else
147         {
148             r = 0;
149         }
150     }
151     else if (bTimeSet(TEND) && (t >= rTimeValue(TEND)))
152     {
153         r = 1;
154     }
155     if (debug)
156     {
157         fprintf(debug, "t=%g, t0=%g, b=%g, e=%g, dt=%g: r=%d\n",
158                 t, t0, rTimeValue(TBEGIN), rTimeValue(TEND), rTimeValue(TDELTA), r);
159     }
160     return r;
161 }
162
163 int check_times(real t)
164 {
165     return check_times2(t, t, FALSE);
166 }
167
168
169
170
171 static void set_default_time_unit(const char *time_list[], gmx_bool bCanTime)
172 {
173     int         i      = 0;
174     const char *select = NULL;
175
176     if (bCanTime)
177     {
178         select = getenv("GMXTIMEUNIT");
179         if (select != NULL)
180         {
181             i = 1;
182             while (time_list[i] && strcmp(time_list[i], select) != 0)
183             {
184                 i++;
185             }
186         }
187     }
188     if (!bCanTime || select == NULL ||
189         time_list[i] == NULL || strcmp(time_list[i], select) != 0)
190     {
191         /* Set it to the default: ps */
192         i = 1;
193         while (time_list[i] && strcmp(time_list[i], "ps") != 0)
194         {
195             i++;
196         }
197
198     }
199     time_list[0] = time_list[i];
200 }
201
202
203 static void set_default_xvg_format(const char *xvg_list[])
204 {
205     int         i;
206     const char *select;
207
208     select = getenv("GMX_VIEW_XVG");
209     if (select == NULL)
210     {
211         /* The default is the first option */
212         xvg_list[0] = xvg_list[1];
213     }
214     else
215     {
216         i = 1;
217         while (xvg_list[i] && strcmp(xvg_list[i], select) != 0)
218         {
219             i++;
220         }
221         if (xvg_list[i] != NULL)
222         {
223             xvg_list[0] = xvg_list[i];
224         }
225         else
226         {
227             xvg_list[0] = xvg_list[exvgNONE];
228         }
229     }
230 }
231
232
233 /***** T O P O L O G Y   S T U F F ******/
234
235 t_topology *read_top(const char *fn, int *ePBC)
236 {
237     int         epbc, natoms;
238     t_topology *top;
239
240     snew(top, 1);
241     epbc = read_tpx_top(fn, NULL, NULL, &natoms, NULL, NULL, NULL, top);
242     if (ePBC)
243     {
244         *ePBC = epbc;
245     }
246
247     return top;
248 }
249
250 /*************************************************************
251  *
252  *           P A R S I N G   S T U F F
253  *
254  *************************************************************/
255
256 static void usage(const char *type, const char *arg)
257 {
258     GMX_ASSERT(arg != NULL, "NULL command-line argument should not occur");
259     gmx_fatal(FARGS, "Expected %s argument for option %s\n", type, arg);
260 }
261
262 int iscan(int argc, char *argv[], int *i)
263 {
264     const char *const arg = argv[*i];
265     if (argc <= (*i)+1)
266     {
267         usage("an integer", arg);
268     }
269     const char *const value = argv[++(*i)];
270     char             *endptr;
271     int               var = std::strtol(value, &endptr, 10);
272     if (*value == '\0' || *endptr != '\0')
273     {
274         usage("an integer", arg);
275     }
276     return var;
277 }
278
279 gmx_large_int_t istepscan(int argc, char *argv[], int *i)
280 {
281     const char *const arg = argv[*i];
282     if (argc <= (*i)+1)
283     {
284         usage("an integer", arg);
285     }
286     const char *const value = argv[++(*i)];
287     char             *endptr;
288     gmx_large_int_t   var = str_to_large_int_t(value, &endptr);
289     if (*value == '\0' || *endptr != '\0')
290     {
291         usage("an integer", arg);
292     }
293     return var;
294 }
295
296 double dscan(int argc, char *argv[], int *i)
297 {
298     const char *const arg = argv[*i];
299     if (argc <= (*i)+1)
300     {
301         usage("a real", arg);
302     }
303     const char *const value = argv[++(*i)];
304     char             *endptr;
305     double            var = std::strtod(value, &endptr);
306     if (*value == '\0' || *endptr != '\0')
307     {
308         usage("a real", arg);
309     }
310     return var;
311 }
312
313 char *sscan(int argc, char *argv[], int *i)
314 {
315     if (argc > (*i)+1)
316     {
317         if ( (argv[(*i)+1][0] == '-') && (argc > (*i)+2) &&
318              (argv[(*i)+2][0] != '-') )
319         {
320             fprintf(stderr, "Possible missing string argument for option %s\n\n",
321                     argv[*i]);
322         }
323     }
324     else
325     {
326         usage("a string", argv[*i]);
327     }
328
329     return argv[++(*i)];
330 }
331
332 int nenum(const char *const enumc[])
333 {
334     int i;
335
336     i = 1;
337     /* we *can* compare pointers directly here! */
338     while (enumc[i] && enumc[0] != enumc[i])
339     {
340         i++;
341     }
342
343     return i;
344 }
345
346 static void pdesc(char *desc)
347 {
348     char *ptr, *nptr;
349
350     ptr = desc;
351     if ((int)strlen(ptr) < 70)
352     {
353         fprintf(stderr, "\t%s\n", ptr);
354     }
355     else
356     {
357         for (nptr = ptr+70; (nptr != ptr) && (!std::isspace(*nptr)); nptr--)
358         {
359             ;
360         }
361         if (nptr == ptr)
362         {
363             fprintf(stderr, "\t%s\n", ptr);
364         }
365         else
366         {
367             *nptr = '\0';
368             nptr++;
369             fprintf(stderr, "\t%s\n", ptr);
370             pdesc(nptr);
371         }
372     }
373 }
374
375 static int add_parg(int npargs, t_pargs *pa, t_pargs *pa_add)
376 {
377     memcpy(&(pa[npargs]), pa_add, sizeof(*pa_add));
378
379     return npargs+1;
380 }
381
382 static char *mk_desc(t_pargs *pa, const char *time_unit_str)
383 {
384     char      *newdesc = NULL, *ndesc = NULL, *nptr = NULL;
385     const char*ptr     = NULL;
386     int        len, k;
387
388     /* First compute length for description */
389     len = strlen(pa->desc)+1;
390     if ((ptr = strstr(pa->desc, "HIDDEN")) != NULL)
391     {
392         len += 4;
393     }
394     if (pa->type == etENUM)
395     {
396         len += 10;
397         for (k = 1; (pa->u.c[k] != NULL); k++)
398         {
399             len += strlen(pa->u.c[k])+12;
400         }
401     }
402     snew(newdesc, len);
403
404     /* add label for hidden options */
405     if (is_hidden(pa))
406     {
407         sprintf(newdesc, "[hidden] %s", ptr+6);
408     }
409     else
410     {
411         strcpy(newdesc, pa->desc);
412     }
413
414     /* change '%t' into time_unit */
415 #define TUNITLABEL "%t"
416 #define NTUNIT strlen(TUNITLABEL)
417     if (pa->type == etTIME)
418     {
419         while ( (nptr = strstr(newdesc, TUNITLABEL)) != NULL)
420         {
421             nptr[0] = '\0';
422             nptr   += NTUNIT;
423             len    += strlen(time_unit_str)-NTUNIT;
424             snew(ndesc, len);
425             strcpy(ndesc, newdesc);
426             strcat(ndesc, time_unit_str);
427             strcat(ndesc, nptr);
428             sfree(newdesc);
429             newdesc = ndesc;
430             ndesc   = NULL;
431         }
432     }
433 #undef TUNITLABEL
434 #undef NTUNIT
435
436     /* Add extra comment for enumerateds */
437     if (pa->type == etENUM)
438     {
439         strcat(newdesc, ": ");
440         for (k = 1; (pa->u.c[k] != NULL); k++)
441         {
442             strcat(newdesc, "[TT]");
443             strcat(newdesc, pa->u.c[k]);
444             strcat(newdesc, "[tt]");
445             /* Print a comma everywhere but at the last one */
446             if (pa->u.c[k+1] != NULL)
447             {
448                 if (pa->u.c[k+2] == NULL)
449                 {
450                     strcat(newdesc, " or ");
451                 }
452                 else
453                 {
454                     strcat(newdesc, ", ");
455                 }
456             }
457         }
458     }
459     return newdesc;
460 }
461
462
463 gmx_bool parse_common_args(int *argc, char *argv[], unsigned long Flags,
464                            int nfile, t_filenm fnm[], int npargs, t_pargs *pa,
465                            int ndesc, const char **desc,
466                            int nbugs, const char **bugs,
467                            output_env_t *oenv)
468 {
469     const char *manstr[] = {
470         NULL, "no", "help", "html", "nroff", "completion", NULL
471     };
472     /* This array should match the order of the enum in oenv.h */
473     const char *xvg_format[] = { NULL, "xmgrace", "xmgr", "none", NULL };
474     /* This array should match the order of the enum in oenv.h */
475     const char *time_units[] = {
476         NULL, "fs", "ps", "ns", "us", "ms", "s",
477         NULL
478     };
479     int         nicelevel = 0, debug_level = 0;
480     char       *deffnm    = NULL;
481     real        tbegin    = 0, tend = 0, tdelta = 0;
482     gmx_bool    bView     = FALSE;
483
484     t_pargs    *all_pa = NULL;
485
486 #ifdef __sgi
487     int     npri      = 0;
488     t_pargs npri_pa   = {
489         "-npri", FALSE, etINT,   {&npri},
490         "HIDDEN Set non blocking priority (try 128)"
491     };
492 #endif
493     t_pargs nice_pa   = {
494         "-nice", FALSE, etINT,   {&nicelevel},
495         "Set the nicelevel"
496     };
497     t_pargs deffnm_pa = {
498         "-deffnm", FALSE, etSTR, {&deffnm},
499         "Set the default filename for all file options"
500     };
501     t_pargs begin_pa  = {
502         "-b",    FALSE, etTIME,  {&tbegin},
503         "First frame (%t) to read from trajectory"
504     };
505     t_pargs end_pa    = {
506         "-e",    FALSE, etTIME,  {&tend},
507         "Last frame (%t) to read from trajectory"
508     };
509     t_pargs dt_pa     = {
510         "-dt",   FALSE, etTIME,  {&tdelta},
511         "Only use frame when t MOD dt = first time (%t)"
512     };
513     t_pargs view_pa   = {
514         "-w",    FALSE, etBOOL,  {&bView},
515         "View output [TT].xvg[tt], [TT].xpm[tt], [TT].eps[tt] and [TT].pdb[tt] files"
516     };
517     t_pargs xvg_pa    = {
518         "-xvg",  FALSE, etENUM,  {xvg_format},
519         "xvg plot formatting"
520     };
521     t_pargs time_pa   = {
522         "-tu",   FALSE, etENUM,  {time_units},
523         "Time unit"
524     };
525     /* Maximum number of extra arguments */
526 #define EXTRA_PA 16
527
528     t_pargs  pca_pa[] = {
529         { "-man",  FALSE, etENUM,  {manstr},
530           "HIDDENWrite manual and quit" },
531         { "-debug", FALSE, etINT, {&debug_level},
532           "HIDDENWrite file with debug information, 1: short, 2: also x and f" },
533     };
534 #define NPCA_PA asize(pca_pa)
535     gmx_bool bExit, bXvgr;
536     int      i, j, k, npall, max_pa;
537
538     // Handle the flags argument, which is a bit field
539     // The FF macro returns whether or not the bit is set
540 #define FF(arg) ((Flags & arg) == arg)
541
542     /* Check for double arguments */
543     for (i = 1; (i < *argc); i++)
544     {
545         if (argv[i] && (strlen(argv[i]) > 1) && (!std::isdigit(argv[i][1])))
546         {
547             for (j = i+1; (j < *argc); j++)
548             {
549                 if ( (argv[i][0] == '-') && (argv[j][0] == '-') &&
550                      (strcmp(argv[i], argv[j]) == 0) )
551                 {
552                     if (FF(PCA_NOEXIT_ON_ARGS))
553                     {
554                         fprintf(stderr, "Double command line argument %s\n",
555                                 argv[i]);
556                     }
557                     else
558                     {
559                         gmx_fatal(FARGS, "Double command line argument %s\n",
560                                   argv[i]);
561                     }
562                 }
563             }
564         }
565     }
566     debug_gmx();
567
568     /* Check ALL the flags ... */
569     max_pa = NPCA_PA + EXTRA_PA + npargs+1;
570     snew(all_pa, max_pa);
571
572     for (i = npall = 0; (i < static_cast<int>(NPCA_PA)); i++)
573     {
574         npall = add_parg(npall, all_pa, &(pca_pa[i]));
575     }
576
577 #ifdef __sgi
578     const char *envstr = getenv("GMXNPRIALL");
579     if (envstr)
580     {
581         npri = strtol(envstr, NULL, 10);
582     }
583     if (FF(PCA_BE_NICE))
584     {
585         envstr = getenv("GMXNPRI");
586         if (envstr)
587         {
588             npri = strtol(envstr, NULL, 10);
589         }
590     }
591     npall = add_parg(npall, all_pa, &npri_pa);
592 #endif
593
594     if (FF(PCA_BE_NICE))
595     {
596         nicelevel = 19;
597     }
598     npall = add_parg(npall, all_pa, &nice_pa);
599
600     if (FF(PCA_CAN_SET_DEFFNM))
601     {
602         npall = add_parg(npall, all_pa, &deffnm_pa);
603     }
604     if (FF(PCA_CAN_BEGIN))
605     {
606         npall = add_parg(npall, all_pa, &begin_pa);
607     }
608     if (FF(PCA_CAN_END))
609     {
610         npall = add_parg(npall, all_pa, &end_pa);
611     }
612     if (FF(PCA_CAN_DT))
613     {
614         npall = add_parg(npall, all_pa, &dt_pa);
615     }
616     if (FF(PCA_TIME_UNIT))
617     {
618         npall = add_parg(npall, all_pa, &time_pa);
619     }
620     if (FF(PCA_CAN_VIEW))
621     {
622         npall = add_parg(npall, all_pa, &view_pa);
623     }
624
625     bXvgr = FALSE;
626     for (i = 0; (i < nfile); i++)
627     {
628         bXvgr = bXvgr ||  (fnm[i].ftp == efXVG);
629     }
630     if (bXvgr)
631     {
632         npall = add_parg(npall, all_pa, &xvg_pa);
633     }
634
635     /* Now append the program specific arguments */
636     for (i = 0; (i < npargs); i++)
637     {
638         npall = add_parg(npall, all_pa, &(pa[i]));
639     }
640
641     /* set etENUM options to default */
642     for (i = 0; (i < npall); i++)
643     {
644         if (all_pa[i].type == etENUM)
645         {
646             all_pa[i].u.c[0] = all_pa[i].u.c[1];
647         }
648     }
649     set_default_time_unit(time_units, FF(PCA_TIME_UNIT));
650     set_default_xvg_format(xvg_format);
651
652     /* Now parse all the command-line options */
653     get_pargs(argc, argv, npall, all_pa, FF(PCA_KEEP_ARGS));
654
655     /* set program name, command line, and default values for output options */
656     output_env_init(oenv, *argc, argv, (time_unit_t)nenum(time_units), bView,
657                     (xvg_format_t)nenum(xvg_format), 0, debug_level);
658
659     if (FF(PCA_CAN_SET_DEFFNM) && (deffnm != NULL))
660     {
661         set_default_file_name(deffnm);
662     }
663
664     /* Parse the file args */
665     parse_file_args(argc, argv, nfile, fnm, FF(PCA_KEEP_ARGS), !FF(PCA_NOT_READ_NODE));
666
667     /* Open the debug file */
668     if (debug_level > 0)
669     {
670         char buf[256];
671
672         if (gmx_mpi_initialized())
673         {
674             sprintf(buf, "%s%d.debug", output_env_get_short_program_name(*oenv),
675                     gmx_node_rank());
676         }
677         else
678         {
679             sprintf(buf, "%s.debug", output_env_get_short_program_name(*oenv));
680         }
681
682         init_debug(debug_level, buf);
683         fprintf(stderr, "Opening debug file %s (src code file %s, line %d)\n",
684                 buf, __FILE__, __LINE__);
685     }
686
687     /* Now copy the results back... */
688     for (i = 0, k = npall-npargs; (i < npargs); i++, k++)
689     {
690         memcpy(&(pa[i]), &(all_pa[k]), (size_t)sizeof(pa[i]));
691     }
692
693
694     for (i = 0; (i < npall); i++)
695     {
696         all_pa[i].desc = mk_desc(&(all_pa[i]), output_env_get_time_unit(*oenv));
697     }
698
699     // To satisfy clang.
700     GMX_ASSERT(manstr[0] != NULL,
701                "Enum option default assignment should have changed this");
702     bExit = (strcmp(manstr[0], "no") != 0);
703
704 #if (defined __sgi && USE_SGI_FPE)
705     doexceptions();
706 #endif
707
708     /* Set the nice level */
709 #ifdef __sgi
710     if (npri != 0 && !bExit)
711     {
712         schedctl(MPTS_RTPRI, 0, npri);
713     }
714 #endif
715
716 #ifdef HAVE_UNISTD_H
717 #ifndef GMX_NO_NICE
718     /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
719     if (nicelevel != 0 && !bExit)
720     {
721         static gmx_bool            nice_set   = FALSE; /* only set it once */
722         static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
723         tMPI_Thread_mutex_lock(&init_mutex);
724         if (!nice_set)
725         {
726             if (nice(nicelevel) == -1)
727             {
728                 /* Do nothing, but use the return value to avoid warnings. */
729             }
730             nice_set = TRUE;
731         }
732         tMPI_Thread_mutex_unlock(&init_mutex);
733     }
734 #endif
735 #endif
736
737     if (strcmp(manstr[0], "no") != 0 && !(FF(PCA_QUIET)))
738     {
739         if (!strcmp(manstr[0], "completion"))
740         {
741             /* one file each for csh, bash and zsh if we do completions */
742             write_man("completion-zsh", output_env_get_short_program_name(*oenv),
743                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
744             write_man("completion-bash", output_env_get_short_program_name(*oenv),
745                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
746             write_man("completion-csh", output_env_get_short_program_name(*oenv),
747                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
748         }
749         else
750         {
751             write_man(manstr[0], output_env_get_short_program_name(*oenv),
752                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
753         }
754     }
755
756     /* convert time options, must be done after printing! */
757
758     for (i = 0; i < npall; i++)
759     {
760         if ((all_pa[i].type == etTIME) && (*all_pa[i].u.r >= 0))
761         {
762             *all_pa[i].u.r *= output_env_get_time_invfactor(*oenv);
763         }
764     }
765
766     /* Extract Time info from arguments */
767     if (FF(PCA_CAN_BEGIN) && opt2parg_bSet("-b", npall, all_pa))
768     {
769         setTimeValue(TBEGIN, opt2parg_real("-b", npall, all_pa));
770     }
771
772     if (FF(PCA_CAN_END) && opt2parg_bSet("-e", npall, all_pa))
773     {
774         setTimeValue(TEND, opt2parg_real("-e", npall, all_pa));
775     }
776
777     if (FF(PCA_CAN_DT) && opt2parg_bSet("-dt", npall, all_pa))
778     {
779         setTimeValue(TDELTA, opt2parg_real("-dt", npall, all_pa));
780     }
781
782     /* clear memory */
783     for (i = 0; i < npall; ++i)
784     {
785         sfree((void *)all_pa[i].desc);
786     }
787     sfree(all_pa);
788
789     if (!FF(PCA_NOEXIT_ON_ARGS))
790     {
791         if (*argc > 1)
792         {
793             gmx_cmd(argv[1]);
794         }
795     }
796     return !bExit;
797 #undef FF
798 }