/*! \brief * Prints a brief list of keywords (selection methods) available. * * \param[in] fp Where to write the list. * \param[in] symtab Symbol table to use to find available keywords. * \param[in] type Only methods that return this type are printed. * \param[in] bMod If false, \ref SMETH_MODIFIER methods are excluded, otherwise * only them are printed. */ static void print_keyword_list(FILE *fp, gmx_sel_symtab_t *symtab, e_selvalue_t type, bool bMod) { gmx_sel_symrec_t *symbol; symbol = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD); while (symbol) { gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(symbol); bool bShow; bShow = (method->type == type) && ((bMod && (method->flags & SMETH_MODIFIER)) || (!bMod && !(method->flags & SMETH_MODIFIER))); if (bShow) { fprintf(fp, " %c ", (method->help.nlhelp > 0 && method->help.help) ? '*' : ' '); if (method->help.syntax) { fprintf(fp, "%s\n", method->help.syntax); } else { const char *symname = _gmx_sel_sym_name(symbol); fprintf(fp, "%s", symname); if (strcmp(symname, method->name) != 0) { fprintf(fp, " (synonym for %s)", method->name); } fprintf(fp, "\n"); } } symbol = _gmx_sel_next_symbol(symbol, SYMBOL_METHOD); } }
/*! \brief * Checks the validity of parameters. * * \param[in] fp File handle to use for diagnostic messages * (can be NULL). * \param[in] name Name of the method (used for error messages). * \param[in] nparams Number of parameters in \p param. * \param[in,out] param Parameter array * (only the \c flags field of gmx_boolean parameters may be modified). * \param[in] symtab Symbol table (used for checking overlaps). * \returns TRUE if there are no problems with the parameters, * FALSE otherwise. * * This function performs some checks common to both check_method() and * check_modifier(). * The purpose of these checks is to ensure that the selection parser does not * need to check for the validity of the parameters at each turn, and to * report programming errors as early as possible. * If you remove a check, make sure that the parameter parser can handle the * resulting parameters. */ static gmx_bool check_params(FILE *fp, const char *name, int nparams, gmx_ana_selparam_t param[], gmx_sel_symtab_t *symtab) { gmx_bool bOk = TRUE; gmx_sel_symrec_t *sym; int i, j; if (nparams > 0 && !param) { report_error(fp, name, "error: missing parameter data"); return FALSE; } if (nparams == 0 && param) { report_error(fp, name, "warning: parameter data unused because nparams=0"); } /* Check each parameter */ for (i = 0; i < nparams; ++i) { /* Check that there is at most one NULL name, in the beginning */ if (param[i].name == NULL && i > 0) { report_error(fp, name, "error: NULL parameter should be the first one"); bOk = FALSE; continue; } /* Check for duplicates */ for (j = 0; j < i; ++j) { if (param[j].name == NULL) { continue; } if (!gmx_strcasecmp(param[i].name, param[j].name)) { report_error(fp, name, "error: duplicate parameter name '%s'", param[i].name); bOk = FALSE; break; } } /* Check flags */ if (param[i].flags & SPAR_SET) { report_param_error(fp, name, param[i].name, "warning: flag SPAR_SET is set"); param[i].flags &= ~SPAR_SET; } if (param[i].flags & SPAR_RANGES) { if (param[i].val.type != INT_VALUE && param[i].val.type != REAL_VALUE) { report_param_error(fp, name, param[i].name, "error: SPAR_RANGES cannot be set for a non-numeric parameter"); bOk = FALSE; } if (param[i].flags & SPAR_DYNAMIC) { report_param_error(fp, name, param[i].name, "warning: SPAR_DYNAMIC does not have effect with SPAR_RANGES"); param[i].flags &= ~SPAR_DYNAMIC; } if (!(param[i].flags & SPAR_VARNUM) && param[i].val.nr != 1) { report_param_error(fp, name, param[i].name, "error: range should take either one or an arbitrary number of values"); bOk = FALSE; } if (param[i].flags & SPAR_ATOMVAL) { report_param_error(fp, name, param[i].name, "error: SPAR_RANGES and SPAR_ATOMVAL both set"); bOk = FALSE; } } if ((param[i].flags & SPAR_VARNUM) && (param[i].flags & SPAR_ATOMVAL)) { report_param_error(fp, name, param[i].name, "error: SPAR_VARNUM and SPAR_ATOMVAL both set"); bOk = FALSE; } if (param[i].flags & SPAR_ENUMVAL) { if (param[i].val.type != STR_VALUE) { report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL can only be set for string parameters"); bOk = FALSE; } if (param[i].val.nr != 1) { report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL parameters should take exactly one value"); bOk = FALSE; } if (param[i].flags & (SPAR_DYNAMIC | SPAR_VARNUM | SPAR_ATOMVAL)) { report_param_error(fp, name, param[i].name, "error: only SPAR_OPTIONAL supported with SPAR_ENUMVAL"); bOk = FALSE; } } /* Check gmx_boolean parameters */ if (param[i].val.type == NO_VALUE) { if (param[i].val.nr != 0) { report_param_error(fp, name, param[i].name, "error: number of values should be zero for gmx_boolean parameters"); bOk = FALSE; } /* The gmx_boolean parameters should always be optional, so set the * flag for convenience. */ param[i].flags |= SPAR_OPTIONAL; /* Any other flags should not be specified */ if (param[i].flags & ~SPAR_OPTIONAL) { report_param_error(fp, name, param[i].name, "error: gmx_boolean parameter should not have any flags set"); bOk = FALSE; } } /* Check val.nr */ if (param[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)) { if (param[i].val.nr != -1) { report_param_error(fp, name, param[i].name, "warning: val.nr is not -1 although SPAR_VARNUM/SPAR_ATOMVAL is set"); } param[i].val.nr = -1; } else if (param[i].val.type != NO_VALUE) { if (param[i].val.nr <= 0) { report_param_error(fp, name, param[i].name, "error: val.nr <= 0"); bOk = FALSE; } } /* Check that the value pointer is NULL */ if (param[i].nvalptr != NULL) { report_param_error(fp, name, param[i].name, "warning: nvalptr is set"); } if (param[i].val.u.ptr != NULL && !(param[i].flags & SPAR_ENUMVAL)) { report_param_error(fp, name, param[i].name, "warning: value pointer is set"); } /* Check that the name contains only valid characters */ if (param[i].name == NULL) { continue; } if (!isalpha(param[i].name[0])) { report_param_error(fp, name, param[i].name, "error: name does not begin with a letter"); bOk = FALSE; continue; } for (j = 1; param[i].name[j] != 0; ++j) { if (param[i].name[j] != '_' && !isalnum(param[i].name[j])) { report_param_error(fp, name, param[i].name, "error: name contains non-alphanumeric characters"); bOk = FALSE; break; } } if (param[i].name[j] != 0) { continue; } /* Check that the name does not conflict with a method */ if (_gmx_sel_find_symbol(symtab, param[i].name, TRUE)) { report_param_error(fp, name, param[i].name, "error: name conflicts with another method or a keyword"); bOk = FALSE; } } /* End of parameter loop */ /* Check parameters of existing methods */ sym = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD); while (sym) { gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(sym); gmx_ana_selparam_t *param = gmx_ana_selmethod_find_param(name, method); if (param) { report_param_error(fp, method->name, param->name, "error: name conflicts with another method or a keyword"); bOk = FALSE; } sym = _gmx_sel_next_symbol(sym, SYMBOL_METHOD); } return bOk; }
/*! * \param[in] fp Where to write the help. * \param[in] symtab Symbol table to use to find available keywords. * \param[in] topic Topic to print help on, or NULL for general help. * * \p symtab is used to get information on which keywords are available in the * present context. */ void _gmx_sel_print_help(FILE *fp, gmx_sel_symtab_t *symtab, const char *topic) { const t_selection_help_item *item = NULL; size_t i; /* Find the item for the topic */ if (!topic) { item = &helpitems[0]; } else if (strcmp(topic, "all") == 0) { for (i = 0; i < asize(helpitems); ++i) { item = &helpitems[i]; _gmx_sel_print_help(fp, symtab, item->topic); if (i != asize(helpitems) - 1) { fprintf(fp, "\n\n"); } } return; } else { for (i = 1; i < asize(helpitems); ++i) { if (strncmp(helpitems[i].topic, topic, strlen(topic)) == 0) { item = &helpitems[i]; break; } } } /* If the topic is not found, check the available methods. * If they don't provide any help either, tell the user and exit. */ if (!item) { gmx_sel_symrec_t *symbol; symbol = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD); while (symbol) { gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(symbol); if (method->help.nlhelp > 0 && method->help.help && strncmp(method->name, topic, strlen(topic)) == 0) { print_tty_formatted(fp, method->help.nlhelp, method->help.help, 0, NULL, NULL, false); return; } symbol = _gmx_sel_next_symbol(symbol, SYMBOL_METHOD); } fprintf(fp, "No help available for '%s'.\n", topic); return; } /* Print the help */ print_tty_formatted(fp, item->nl, item->text, 0, NULL, NULL, false); /* Special handling of certain pages */ if (!topic) { int len = 0; /* Print the subtopics on the main page */ fprintf(fp, "\nAvailable subtopics:\n"); for (i = 1; i < asize(helpitems); ++i) { int len1 = strlen(helpitems[i].topic) + 2; len += len1; if (len > 79) { fprintf(fp, "\n"); len = len1; } fprintf(fp, " %s", helpitems[i].topic); } fprintf(fp, "\n"); } else if (strcmp(item->topic, "keywords") == 0) { /* Print the list of keywords */ fprintf(fp, "\nKeywords that select atoms by an integer property:\n"); fprintf(fp, "(use in expressions or like \"atomnr 1 to 5 7 9\")\n"); print_keyword_list(fp, symtab, INT_VALUE, false); fprintf(fp, "\nKeywords that select atoms by a numeric property:\n"); fprintf(fp, "(use in expressions or like \"occupancy 0.5 to 1\")\n"); print_keyword_list(fp, symtab, REAL_VALUE, false); fprintf(fp, "\nKeywords that select atoms by a string property:\n"); fprintf(fp, "(use like \"name PATTERN [PATTERN] ...\")\n"); print_keyword_list(fp, symtab, STR_VALUE, false); fprintf(fp, "\nAdditional keywords that directly select atoms:\n"); print_keyword_list(fp, symtab, GROUP_VALUE, false); fprintf(fp, "\nKeywords that directly evaluate to positions:\n"); fprintf(fp, "(see also \"help positions\")\n"); print_keyword_list(fp, symtab, POS_VALUE, false); fprintf(fp, "\nAdditional keywords:\n"); print_keyword_list(fp, symtab, POS_VALUE, true); print_keyword_list(fp, symtab, NO_VALUE, true); } }