Only accept exact matches for selection keywords
[alexxy/gromacs.git] / src / gromacs / selection / selmethod.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2009,2010,2011,2012,2014,2015, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements functions in selmethod.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_selection
41  */
42 #include "gmxpre.h"
43
44 #include "selmethod.h"
45
46 #include <ctype.h>
47 #include <stdarg.h>
48
49 #include "gromacs/legacyheaders/macros.h"
50 #include "gromacs/utility/cstringutil.h"
51 #include "gromacs/utility/exceptions.h"
52
53 #include "symrec.h"
54
55 /*
56  * These global variables cannot be const because gmx_ana_selmethod_register()
57  * modifies them to set some defaults. This is a small price to pay for the
58  * convenience of not having to remember exactly how the selection compiler
59  * expects the structures to be filled, and even more so if the expectations
60  * change. Also, even if the gmx_ana_selmethod_t structures were made const,
61  * the parameters could not be without typecasts somewhere, because the param
62  * field in gmx_ana_selmethod_t cannot be declared const.
63  *
64  * Even though the variables may be modified, this should be thread-safe as
65  * modifications are done only in gmx_ana_selmethod_register(), and it should
66  * work even if called more than once for the same structure, and even if
67  * called concurrently from multiple threads (as long as the selection
68  * collection is not the same).
69  *
70  * All of these problems should go away if/when the selection methods are
71  * implemented as C++ classes.
72  */
73
74 /* From sm_com.c */
75 extern gmx_ana_selmethod_t sm_cog;
76 extern gmx_ana_selmethod_t sm_com;
77 /* From sm_simple.c */
78 extern gmx_ana_selmethod_t sm_all;
79 extern gmx_ana_selmethod_t sm_none;
80 extern gmx_ana_selmethod_t sm_atomnr;
81 extern gmx_ana_selmethod_t sm_resnr;
82 extern gmx_ana_selmethod_t sm_resindex;
83 extern gmx_ana_selmethod_t sm_molindex;
84 extern gmx_ana_selmethod_t sm_atomname;
85 extern gmx_ana_selmethod_t sm_pdbatomname;
86 extern gmx_ana_selmethod_t sm_atomtype;
87 extern gmx_ana_selmethod_t sm_resname;
88 extern gmx_ana_selmethod_t sm_insertcode;
89 extern gmx_ana_selmethod_t sm_chain;
90 extern gmx_ana_selmethod_t sm_mass;
91 extern gmx_ana_selmethod_t sm_charge;
92 extern gmx_ana_selmethod_t sm_altloc;
93 extern gmx_ana_selmethod_t sm_occupancy;
94 extern gmx_ana_selmethod_t sm_betafactor;
95 extern gmx_ana_selmethod_t sm_x;
96 extern gmx_ana_selmethod_t sm_y;
97 extern gmx_ana_selmethod_t sm_z;
98 /* From sm_distance.c */
99 extern gmx_ana_selmethod_t sm_distance;
100 extern gmx_ana_selmethod_t sm_mindistance;
101 extern gmx_ana_selmethod_t sm_within;
102 /* From sm_insolidangle.c */
103 extern gmx_ana_selmethod_t sm_insolidangle;
104 /* From sm_same.c */
105 extern gmx_ana_selmethod_t sm_same;
106
107 /* From sm_merge.c */
108 extern gmx_ana_selmethod_t sm_merge;
109 extern gmx_ana_selmethod_t sm_plus;
110 /* From sm_permute.c */
111 extern gmx_ana_selmethod_t sm_permute;
112
113 /*! \internal \brief
114  * Helper structure for defining selection methods.
115  */
116 typedef struct {
117     /*! \brief
118      * Name to register the method under.
119      *
120      * If NULL, use the actual name of the method.
121      * This field is used for defining synonyms.
122      */
123     const char            *name;
124     /** Method data structure to register. */
125     gmx_ana_selmethod_t   *method;
126 } t_register_method;
127
128 /** Array of selection methods defined in the library. */
129 static const t_register_method smtable_def[] = {
130     {NULL,         &sm_cog},
131     {NULL,         &sm_com},
132
133     {NULL,         &sm_all},
134     {NULL,         &sm_none},
135     {NULL,         &sm_atomnr},
136     {NULL,         &sm_resnr},
137     {"resid",      &sm_resnr},
138     {NULL,         &sm_resindex},
139     {"residue",    &sm_resindex},
140     {NULL,         &sm_molindex},
141     {"mol",        &sm_molindex},
142     {"molecule",   &sm_molindex},
143     {NULL,         &sm_atomname},
144     {"name",       &sm_atomname},
145     {NULL,         &sm_pdbatomname},
146     {"pdbname",    &sm_pdbatomname},
147     {NULL,         &sm_atomtype},
148     {"type",       &sm_atomtype},
149     {NULL,         &sm_resname},
150     {NULL,         &sm_insertcode},
151     {NULL,         &sm_chain},
152     {NULL,         &sm_mass},
153     {NULL,         &sm_charge},
154     {NULL,         &sm_altloc},
155     {NULL,         &sm_occupancy},
156     {NULL,         &sm_betafactor},
157     {"beta",       &sm_betafactor},
158     {NULL,         &sm_x},
159     {NULL,         &sm_y},
160     {NULL,         &sm_z},
161
162     {NULL,         &sm_distance},
163     {"dist",       &sm_distance},
164     {NULL,         &sm_mindistance},
165     {"mindist",    &sm_mindistance},
166     {NULL,         &sm_within},
167     {NULL,         &sm_insolidangle},
168     {NULL,         &sm_same},
169
170     {NULL,         &sm_merge},
171     {NULL,         &sm_plus},
172     {NULL,         &sm_permute},
173 };
174
175 /*! \brief
176  * Convenience function for reporting errors found in selection methods.
177  */
178 static void
179 report_error(FILE *fp, const char *name, const char *fmt, ...)
180 {
181     va_list ap;
182     va_start(ap, fmt);
183     if (fp)
184     {
185         fprintf(fp, "selection method '%s': ", name);
186         vfprintf(fp, fmt, ap);
187         fprintf(fp, "\n");
188     }
189     va_end(ap);
190 }
191
192 /*! \brief
193  * Convenience function for reporting errors found in selection method parameters.
194  */
195 static void
196 report_param_error(FILE *fp, const char *mname, const char *pname,
197                    const char *fmt, ...)
198 {
199     va_list ap;
200     va_start(ap, fmt);
201     if (fp)
202     {
203         fprintf(fp, "selection method '%s': parameter '%s': ", mname, pname);
204         vfprintf(fp, fmt, ap);
205         fprintf(fp, "\n");
206     }
207     va_end(ap);
208 }
209
210 /*! \brief
211  * Checks the validity of parameters.
212  *
213  * \param[in]     fp      File handle to use for diagnostic messages
214  *   (can be NULL).
215  * \param[in]     name    Name of the method (used for error messages).
216  * \param[in]     nparams Number of parameters in \p param.
217  * \param[in,out] param   Parameter array
218  *   (only the \c flags field of boolean parameters may be modified).
219  * \param[in]     symtab  Symbol table (used for checking overlaps).
220  * \returns       true if there are no problems with the parameters,
221  *   false otherwise.
222  *
223  * This function performs some checks common to both check_method() and
224  * check_modifier().
225  * The purpose of these checks is to ensure that the selection parser does not
226  * need to check for the validity of the parameters at each turn, and to
227  * report programming errors as early as possible.
228  * If you remove a check, make sure that the parameter parser can handle the
229  * resulting parameters.
230  */
231 static bool
232 check_params(FILE *fp, const char *name, int nparams, gmx_ana_selparam_t param[],
233              const gmx::SelectionParserSymbolTable &symtab)
234 {
235     bool              bOk = true;
236     int               i, j;
237
238     if (nparams > 0 && !param)
239     {
240         report_error(fp, name, "error: missing parameter data");
241         return false;
242     }
243     if (nparams == 0 && param)
244     {
245         report_error(fp, name, "warning: parameter data unused because nparams=0");
246     }
247     /* Check each parameter */
248     for (i = 0; i < nparams; ++i)
249     {
250         /* Check that there is at most one NULL name, in the beginning */
251         if (param[i].name == NULL && i > 0)
252         {
253             report_error(fp, name, "error: NULL parameter should be the first one");
254             bOk = false;
255             continue;
256         }
257         /* Check for duplicates */
258         for (j = 0; j < i; ++j)
259         {
260             if (param[j].name == NULL)
261             {
262                 continue;
263             }
264             if (!gmx_strcasecmp(param[i].name, param[j].name))
265             {
266                 report_error(fp, name, "error: duplicate parameter name '%s'", param[i].name);
267                 bOk = false;
268                 break;
269             }
270         }
271         /* Check flags */
272         if (param[i].flags & SPAR_SET)
273         {
274             report_param_error(fp, name, param[i].name, "warning: flag SPAR_SET is set");
275             param[i].flags &= ~SPAR_SET;
276         }
277         if (param[i].flags & SPAR_RANGES)
278         {
279             if (param[i].val.type != INT_VALUE && param[i].val.type != REAL_VALUE)
280             {
281                 report_param_error(fp, name, param[i].name, "error: SPAR_RANGES cannot be set for a non-numeric parameter");
282                 bOk = false;
283             }
284             if (param[i].flags & SPAR_DYNAMIC)
285             {
286                 report_param_error(fp, name, param[i].name, "warning: SPAR_DYNAMIC does not have effect with SPAR_RANGES");
287                 param[i].flags &= ~SPAR_DYNAMIC;
288             }
289             if (!(param[i].flags & SPAR_VARNUM) && param[i].val.nr != 1)
290             {
291                 report_param_error(fp, name, param[i].name, "error: range should take either one or an arbitrary number of values");
292                 bOk = false;
293             }
294             if (param[i].flags & SPAR_ATOMVAL)
295             {
296                 report_param_error(fp, name, param[i].name, "error: SPAR_RANGES and SPAR_ATOMVAL both set");
297                 bOk = false;
298             }
299         }
300         if ((param[i].flags & SPAR_VARNUM) && (param[i].flags & SPAR_ATOMVAL))
301         {
302             report_param_error(fp, name, param[i].name, "error: SPAR_VARNUM and SPAR_ATOMVAL both set");
303             bOk = false;
304         }
305         if (param[i].flags & SPAR_ENUMVAL)
306         {
307             if (param[i].val.type != STR_VALUE)
308             {
309                 report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL can only be set for string parameters");
310                 bOk = false;
311             }
312             if (param[i].val.nr != 1)
313             {
314                 report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL parameters should take exactly one value");
315                 bOk = false;
316             }
317             if (param[i].flags & (SPAR_DYNAMIC | SPAR_VARNUM | SPAR_ATOMVAL))
318             {
319                 report_param_error(fp, name, param[i].name, "error: only SPAR_OPTIONAL supported with SPAR_ENUMVAL");
320                 bOk = false;
321             }
322         }
323         /* Check boolean parameters */
324         if (param[i].val.type == NO_VALUE)
325         {
326             if (param[i].val.nr != 0)
327             {
328                 report_param_error(fp, name, param[i].name, "error: number of values should be zero for boolean parameters");
329                 bOk = false;
330             }
331             /* The boolean parameters should always be optional, so set the
332              * flag for convenience. */
333             param[i].flags |= SPAR_OPTIONAL;
334             /* Any other flags should not be specified */
335             if (param[i].flags & ~SPAR_OPTIONAL)
336             {
337                 report_param_error(fp, name, param[i].name, "error: boolean parameter should not have any flags set");
338                 bOk = false;
339             }
340         }
341         /* Check val.nr */
342         if (param[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL))
343         {
344             if (param[i].val.nr != -1)
345             {
346                 report_param_error(fp, name, param[i].name, "warning: val.nr is not -1 although SPAR_VARNUM/SPAR_ATOMVAL is set");
347             }
348             param[i].val.nr = -1;
349         }
350         else if (param[i].val.type != NO_VALUE)
351         {
352             if (param[i].val.nr <= 0)
353             {
354                 report_param_error(fp, name, param[i].name, "error: val.nr <= 0");
355                 bOk = false;
356             }
357         }
358         /* Check that the value pointer is NULL */
359         if (param[i].nvalptr != NULL)
360         {
361             report_param_error(fp, name, param[i].name, "warning: nvalptr is set");
362         }
363         if (param[i].val.u.ptr != NULL && !(param[i].flags & SPAR_ENUMVAL))
364         {
365             report_param_error(fp, name, param[i].name, "warning: value pointer is set");
366         }
367         /* Check that the name contains only valid characters */
368         if (param[i].name == NULL)
369         {
370             continue;
371         }
372         if (!isalpha(param[i].name[0]))
373         {
374             report_param_error(fp, name, param[i].name, "error: name does not begin with a letter");
375             bOk = false;
376             continue;
377         }
378         for (j = 1; param[i].name[j] != 0; ++j)
379         {
380             if (param[i].name[j] != '_' && !isalnum(param[i].name[j]))
381             {
382                 report_param_error(fp, name, param[i].name, "error: name contains non-alphanumeric characters");
383                 bOk = false;
384                 break;
385             }
386         }
387         if (param[i].name[j] != 0)
388         {
389             continue;
390         }
391         /* Check that the name does not conflict with a method */
392         if (symtab.findSymbol(param[i].name))
393         {
394             report_param_error(fp, name, param[i].name, "error: name conflicts with another method or a keyword");
395             bOk = false;
396         }
397     } /* End of parameter loop */
398       /* Check parameters of existing methods */
399     gmx::SelectionParserSymbolIterator symbol
400         = symtab.beginIterator(gmx::SelectionParserSymbol::MethodSymbol);
401     while (symbol != symtab.endIterator())
402     {
403         gmx_ana_selmethod_t *method = symbol->methodValue();
404         gmx_ana_selparam_t  *param  =
405             gmx_ana_selmethod_find_param(name, method);
406         if (param)
407         {
408             report_param_error(fp, method->name, param->name, "error: name conflicts with another method or a keyword");
409             bOk = false;
410         }
411         ++symbol;
412     }
413     return bOk;
414 }
415
416 /*! \brief
417  * Checks the validity of selection method callback functions.
418  *
419  * \param[in] fp        File handle to use for diagnostic messages
420  *   (can be NULL).
421  * \param[in] method    The method to check.
422  * \returns   true if there are no problems, false otherwise.
423  *
424  * This function performs some checks common to both check_method() and
425  * check_modifier().
426  * This function checks that all the required callbacks are defined, i.e.,
427  * not NULL, to find programming errors.
428  */
429 static bool
430 check_callbacks(FILE *fp, gmx_ana_selmethod_t *method)
431 {
432     bool         bOk = true;
433     bool         bNeedInit;
434     int          i;
435
436     /* Make some checks on init_data and free */
437     if (method->nparams > 0 && !method->init_data)
438     {
439         report_error(fp, method->name, "error: init_data should be provided because the method has parameters");
440         bOk = false;
441     }
442     if (method->free && !method->init_data)
443     {
444         report_error(fp, method->name, "warning: free is not used because of missing init_data");
445     }
446     /* Check presence of outinit for position-valued methods */
447     if (method->type == POS_VALUE && !method->outinit)
448     {
449         report_error(fp, method->name, "error: outinit should be provided because the method has POS_VALUE");
450         bOk = false;
451     }
452     /* Check presence of outinit for variable output count methods */
453     if ((method->flags & SMETH_VARNUMVAL) && !method->outinit)
454     {
455         report_error(fp, method->name, "error: outinit should be provided because the method has SMETH_VARNUMVAL");
456         bOk = false;
457     }
458     /* Warn of dynamic callbacks in static methods */
459     if (!(method->flags & SMETH_MODIFIER))
460     {
461         if (method->pupdate && !(method->flags & SMETH_DYNAMIC))
462         {
463             report_error(fp, method->name, "warning: pupdate not used because the method is static");
464             method->pupdate = NULL;
465         }
466     }
467     /* Check that there is an evaluation function */
468     if (method->type != NO_VALUE && !method->update && !method->pupdate)
469     {
470         report_error(fp, method->name, "error: evaluation function missing");
471         bOk = false;
472     }
473     /* Loop through the parameters to determine if initialization callbacks
474      * are needed. */
475     bNeedInit = false;
476     for (i = 0; i < method->nparams; ++i)
477     {
478         if (method->param[i].val.type != POS_VALUE
479             && (method->param[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
480         {
481             bNeedInit = true;
482         }
483     }
484     /* Check that the callbacks required by the parameters are present */
485     if (bNeedInit && !method->init)
486     {
487         report_error(fp, method->name, "error: init should be provided");
488         bOk = false;
489     }
490     return bOk;
491 }
492
493 /*! \brief
494  * Checks the validity of a selection method.
495  *
496  * \param[in]     fp     File handle to use for diagnostic messages
497  *   (can be NULL).
498  * \param[in,out] method Method to check.
499  * \param[in]     symtab Symbol table (used for checking overlaps).
500  *
501  * Checks the validity of the given selection method data structure
502  * that does not have \ref SMETH_MODIFIER set.
503  * If you remove a check, please make sure that the selection parser,
504  * compiler, and evaluation functions can deal with the method.
505  */
506 static bool
507 check_method(FILE *fp, gmx_ana_selmethod_t *method,
508              const gmx::SelectionParserSymbolTable &symtab)
509 {
510     bool         bOk = true;
511
512     /* Check the type */
513     if (method->type == NO_VALUE)
514     {
515         report_error(fp, method->name, "error: no value type specified");
516         bOk = false;
517     }
518     if (method->type == STR_VALUE && method->nparams > 0)
519     {
520         report_error(fp, method->name, "error: evaluates to a string but is not a keyword");
521         bOk = false;
522     }
523     /* Check flags */
524     if (method->type == GROUP_VALUE)
525     {
526         /* Group methods should always have SMETH_SINGLEVAL,
527          * so set it for convenience. */
528         method->flags |= SMETH_SINGLEVAL;
529         /* Check that conflicting flags are not present. */
530         if (method->flags & SMETH_VARNUMVAL)
531         {
532             report_error(fp, method->name, "error: SMETH_VARNUMVAL cannot be set for group-valued methods");
533             bOk = false;
534         }
535     }
536     else
537     {
538         if ((method->flags & SMETH_SINGLEVAL)
539             && (method->flags & SMETH_VARNUMVAL))
540         {
541             report_error(fp, method->name, "error: SMETH_SINGLEVAL and SMETH_VARNUMVAL both set");
542             bOk = false;
543         }
544     }
545     if ((method->flags & SMETH_CHARVAL) && method->type != STR_VALUE)
546     {
547         report_error(fp, method->name, "error: SMETH_CHARVAL can only be specified for STR_VALUE methods");
548         bOk = false;
549     }
550     /* Check the parameters */
551     if (!check_params(fp, method->name, method->nparams, method->param, symtab))
552     {
553         bOk = false;
554     }
555     /* Check the callback pointers */
556     if (!check_callbacks(fp, method))
557     {
558         bOk = false;
559     }
560
561     return bOk;
562 }
563
564 /*! \brief
565  * Checks the validity of a selection modifier method.
566  *
567  * \param[in]     fp     File handle to use for diagnostic messages
568  *   (can be NULL).
569  * \param[in,out] method Method to check.
570  * \param[in]     symtab Symbol table (used for checking overlaps).
571  *
572  * Checks the validity of the given selection method data structure
573  * that has \ref SMETH_MODIFIER set.
574  * If you remove a check, please make sure that the selection parser,
575  * compiler, and evaluation functions can deal with the method.
576  */
577 static bool
578 check_modifier(FILE *fp, gmx_ana_selmethod_t *method,
579                const gmx::SelectionParserSymbolTable &symtab)
580 {
581     bool         bOk = true;
582
583     /* Check the type */
584     if (method->type != NO_VALUE && method->type != POS_VALUE)
585     {
586         report_error(fp, method->name, "error: modifier should have type POS_VALUE or NO_VALUE");
587         bOk = false;
588     }
589     /* Check flags */
590     if (method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
591     {
592         report_error(fp, method->name, "error: modifier should not have SMETH_SINGLEVAL or SMETH_VARNUMVAL set");
593         bOk = false;
594     }
595     /* Check the parameters */
596     /* The first parameter is skipped */
597     if (!check_params(fp, method->name, method->nparams-1, method->param+1, symtab))
598     {
599         bOk = false;
600     }
601     /* Check the callback pointers */
602     if (!check_callbacks(fp, method))
603     {
604         bOk = false;
605     }
606     if (method->update)
607     {
608         report_error(fp, method->name, "error: modifier should not have update");
609         bOk = false;
610     }
611     if (method->type == POS_VALUE && !method->pupdate)
612     {
613         report_error(fp, method->name, "error: evaluation function missing");
614         bOk = false;
615     }
616
617     return bOk;
618 }
619
620 /*!
621  * \param[in,out] symtab Symbol table to register the method to.
622  * \param[in]     name   Name under which the method should be registered.
623  * \param[in]     method Method to register.
624  * \returns       0 on success, EINVAL if there was something wrong with the
625  *   method.
626  *
627  * \p name does not need to match the name of the method, and the same
628  * method can be registered multiple times under different names.
629  * If \p name equals some previously registered name,
630  * an error message is printed and the method is not registered.
631  *
632  * The function also performs some sanity checking on the input method,
633  * and refuses to register it if there are problems.
634  * Some problems only generate warnings.
635  * All problems are described to \p stderr.
636  */
637 int
638 gmx_ana_selmethod_register(gmx::SelectionParserSymbolTable *symtab,
639                            const char *name, gmx_ana_selmethod_t *method)
640 {
641     bool bOk;
642
643     /* Check the method */
644     if (method->flags & SMETH_MODIFIER)
645     {
646         bOk = check_modifier(stderr, method, *symtab);
647     }
648     else
649     {
650         bOk = check_method(stderr, method, *symtab);
651     }
652     /* Try to register the method if everything is ok */
653     if (bOk)
654     {
655         try
656         {
657             symtab->addMethod(name, method);
658         }
659         catch (const gmx::APIError &ex)
660         {
661             report_error(stderr, name, ex.what());
662             bOk = false;
663         }
664     }
665     if (!bOk)
666     {
667         report_error(stderr, name, "warning: not registered");
668         return EINVAL;
669     }
670     return 0;
671 }
672
673 /*!
674  * \param[in,out] symtab Symbol table to register the methods to.
675  * \returns       0 on success, -1 if any of the default methods could not be
676  *   registered.
677  */
678 int
679 gmx_ana_selmethod_register_defaults(gmx::SelectionParserSymbolTable *symtab)
680 {
681     size_t i;
682     int    rc;
683     bool   bOk;
684
685     bOk = true;
686     for (i = 0; i < asize(smtable_def); ++i)
687     {
688         gmx_ana_selmethod_t *method = smtable_def[i].method;
689
690         if (smtable_def[i].name == NULL)
691         {
692             rc = gmx_ana_selmethod_register(symtab, method->name, method);
693         }
694         else
695         {
696             rc = gmx_ana_selmethod_register(symtab, smtable_def[i].name, method);
697         }
698         if (rc != 0)
699         {
700             bOk = false;
701         }
702     }
703     return bOk ? 0 : -1;
704 }
705
706 /*!
707  * \param[in] name   Name of the parameter to search.
708  * \param[in] method Method to search for the parameter.
709  * \returns   Pointer to the parameter in the
710  *   \ref gmx_ana_selmethod_t::param "method->param" array,
711  *   or NULL if no parameter with name \p name was found.
712  *
713  * This is a simple wrapper for gmx_ana_selparam_find().
714  */
715 gmx_ana_selparam_t *
716 gmx_ana_selmethod_find_param(const char *name, gmx_ana_selmethod_t *method)
717 {
718     return gmx_ana_selparam_find(name, method->nparams, method->param);
719 }