[gnumeric] sstest: move function test and dump code to here from func.c
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] sstest: move function test and dump code to here from func.c
- Date: Thu, 30 Nov 2017 00:43:21 +0000 (UTC)
commit d9c565391771048a9f1408dcb6d3040788fa782d
Author: Morten Welinder <terra gnome org>
Date: Wed Nov 29 19:42:11 2017 -0500
sstest: move function test and dump code to here from func.c
That gets it out of libgnumeric. Hence we won't install it.
ChangeLog | 4 +
src/func.c | 796 +--------------------------------------------------------
src/func.h | 3 +-
src/sstest.c | 805 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 823 insertions(+), 785 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 3a3f915..e01cdc2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2017-11-29 Morten Welinder <terra gnome org>
+ * src/sstest.c (function_dump_defs): Move from func.c with all
+ support code.
+ (gnm_func_sanity_check): Ditto.
+
* src/libgnumeric.c (gnm_dump_func_defs): Not needed anymore.
* src/main-application.c (pathetic_qt_workaround): Not needed
diff --git a/src/func.c b/src/func.c
index 8bb88ce..350e617 100644
--- a/src/func.c
+++ b/src/func.c
@@ -35,7 +35,6 @@
#include <string.h>
#include <stdlib.h>
-#define UNICODE_ELLIPSIS "\xe2\x80\xa6"
#define F2(func,s) dgettext ((func)->textdomain->str, (s))
static GList *categories;
@@ -79,56 +78,25 @@ functions_shutdown (void)
functions_by_localized_name = NULL;
}
-inline void
-gnm_func_load_if_stub (GnmFunc *func)
-{
- if (func->fn_type == GNM_FUNC_TYPE_STUB)
- gnm_func_load_stub (func);
-}
-
-static void
-copy_hash_table_to_ptr_array (gpointer key, gpointer value, gpointer array)
-{
- GnmFunc *fd = value;
-
- if (fd->name == NULL ||
- strcmp (fd->name, "perl_adder") == 0 ||
- strcmp (fd->name, "perl_date") == 0 ||
- strcmp (fd->name, "perl_sed") == 0 ||
- strcmp (fd->name, "py_capwords") == 0 ||
- strcmp (fd->name, "py_printf") == 0 ||
- strcmp (fd->name, "py_bitand") == 0)
- return;
-
- gnm_func_load_if_stub (fd);
- if (fd->help != NULL)
- g_ptr_array_add (array, fd);
-}
-
-static int
-func_def_cmp (gconstpointer a, gconstpointer b)
+GPtrArray *
+gnm_func_enumerate (void)
{
- GnmFunc const *fda = *(GnmFunc const **)a ;
- GnmFunc const *fdb = *(GnmFunc const **)b ;
-
- g_return_val_if_fail (fda->name != NULL, 0);
- g_return_val_if_fail (fdb->name != NULL, 0);
+ GPtrArray *res = g_ptr_array_new ();
+ GHashTableIter hiter;
+ gpointer value;
- if (fda->fn_group != NULL && fdb->fn_group != NULL) {
- int res = go_string_cmp (fda->fn_group->display_name,
- fdb->fn_group->display_name);
- if (res != 0)
- return res;
- }
+ g_hash_table_iter_init (&hiter, functions_by_name);
+ while (g_hash_table_iter_next (&hiter, NULL, &value))
+ g_ptr_array_add (res, value);
- return g_ascii_strcasecmp (fda->name, fdb->name);
+ return res;
}
-static void
-cb_dump_usage (gpointer key, GnmFunc const *fd, FILE *out)
+inline void
+gnm_func_load_if_stub (GnmFunc *func)
{
- if (fd->usage_count > 0)
- fprintf (out, "%d,%s\n", fd->usage_count, fd->name);
+ if (func->fn_type == GNM_FUNC_TYPE_STUB)
+ gnm_func_load_stub (func);
}
static char *
@@ -145,744 +113,6 @@ split_at_colon (char const *s, char **rest)
return dup;
}
-static void
-dump_externals (GPtrArray *defs, FILE *out)
-{
- unsigned int ui;
-
- fprintf (out, "<!--#set var=\"title\" value=\"Gnumeric Web Documentation\" -->");
- fprintf (out, "<!--#set var=\"rootdir\" value=\".\" -->");
- fprintf (out, "<!--#include virtual=\"header-begin.shtml\" -->");
- fprintf (out, "<link rel=\"stylesheet\" href=\"style/index.css\" type=\"text/css\"/>");
- fprintf (out, "<!--#include virtual=\"header-end.shtml\" -->");
- fprintf (out, "<!--#set var=\"wolfram\" value=\"none\" -->");
- fprintf (out, "<!--#set var=\"wiki\" value=\"none\" -->");
- fprintf (out, "<!--\n\n-->");
-
- for (ui = 0; ui < defs->len; ui++) {
- GnmFunc const *fd = g_ptr_array_index (defs, ui);
- gboolean any = FALSE;
- int j;
-
- for (j = 0; fd->help[j].type != GNM_FUNC_HELP_END; j++) {
- const char *s = F2(fd, fd->help[j].text);
-
- switch (fd->help[j].type) {
- case GNM_FUNC_HELP_EXTREF:
- if (!any) {
- any = TRUE;
- fprintf (out, "<!--#if expr=\"${QUERY_STRING} = %s\" -->", fd->name);
- }
-
- if (strncmp (s, "wolfram:", 8) == 0) {
- fprintf (out, "<!--#set var=\"wolfram\" value=\"%s\" -->", s + 8);
- }
- if (strncmp (s, "wiki:", 5) == 0) {
- char *lang, *page;
- lang = split_at_colon (s + 5, &page);
- fprintf (out, "<!--#set var=\"wiki_lang\" value=\"%s\" -->", lang);
- fprintf (out, "<!--#set var=\"wiki\" value=\"%s\" -->", page);
- g_free (lang);
- }
- break;
- default:
- break;
- }
- }
-
- if (any)
- fprintf (out, "<!--#endif\n\n-->");
- }
-
- fprintf (out, "<div class=\"floatflush\">\n");
- fprintf (out, "<h1>Online Documentation for \"<!--#echo var=\"QUERY_STRING\" -->\"</h1>\n");
- fprintf (out, "<p>When last checked, these sources provided useful information about\n");
- fprintf (out, "this function. However, since the links are not controlled by the\n");
- fprintf (out, "Gnumeric Team, we cannot guarantee that the links still work. If\n");
- fprintf (out, "you find that they do not work, please drop us a line.</p>\n");
- fprintf (out, "<ul>");
- fprintf (out, "<!--#if expr=\"${wolfram} != none\"-->");
- fprintf (out, "<li><a href=\"http://mathworld.wolfram.com/<!--#echo var=\"wolfram\" -->\">Wolfram
Mathworld\nentry</a>.</li><!--#endif-->");
- fprintf (out, "<!--#if expr=\"${wiki} != none\"--><li><a href=\"http://<!--#echo var=\"wiki_lang\"
-->.wikipedia.org/wiki/<!--#echo var=\"wiki\" -->\">Wikipedia\nentry</a>.</li><!--#endif-->");
- fprintf (out, "<li><a href=\"http://www.google.com/#q=<!--#echo var=\"QUERY_STRING\" -->\">Google
Search</a>.</li>");
- fprintf (out, "</ul>");
- fprintf (out, "</div>\n");
-
- fprintf (out, "<!--#include virtual=\"footer.shtml\" -->\n");
-}
-
-static void
-csv_quoted_print (FILE *out, const char *s)
-{
- char quote = '"';
- fputc (quote, out);
- while (*s) {
- if (*s == quote) {
- fputc (quote, out);
- fputc (quote, out);
- s++;
- } else {
- int len = g_utf8_skip[(unsigned char)*s];
- fprintf (out, "%-.*s", len, s);
- s += len;
- }
- }
- fputc ('"', out);
-}
-
-static void
-dump_samples (GPtrArray *defs, FILE *out)
-{
- unsigned ui;
- GnmFuncGroup *last_group = NULL;
-
- for (ui = 0; ui < defs->len; ui++) {
- GnmFunc const *fd = g_ptr_array_index (defs, ui);
- int j;
- const char *last = NULL;
- gboolean has_sample = FALSE;
-
- if (last_group != fd->fn_group) {
- last_group = fd->fn_group;
- csv_quoted_print (out, last_group->display_name->str);
- fputc ('\n', out);
- }
-
- for (j = 0; fd->help[j].type != GNM_FUNC_HELP_END; j++) {
- const char *s = fd->help[j].text;
-
- if (fd->help[j].type != GNM_FUNC_HELP_EXAMPLES)
- continue;
-
- has_sample = TRUE;
-
- /*
- * Some of the random numbers functions have duplicate
- * samples. We don't want the duplicates here.
- */
- if (s[0] != '=' || (last && strcmp (last, s) == 0))
- continue;
-
- fputc (',', out);
- if (!last)
- csv_quoted_print (out, fd->name);
- last = s;
-
- fputc (',', out);
- csv_quoted_print (out, s);
- fputc ('\n', out);
- }
-
- if (!has_sample)
- g_printerr ("No samples for %s\n", fd->name);
- }
-}
-
-/**
- * function_dump_defs :
- * @filename:
- * @dump_type:
- *
- * A generic utility routine to operate on all funtion defs
- * in various ways. @dump_type will change/extend as needed
- * Right now
- * 0 : www.gnumeric.org's function.shtml page
- * 1 :
- * 2 : (obsolete)
- * 3 : dump function usage count
- * 4 : external refs
- * 5 : all sample expressions
- **/
-void
-function_dump_defs (char const *filename, int dump_type)
-{
- FILE *output_file;
- char *up, *catname;
- unsigned i;
- GPtrArray *ordered;
- GnmFuncGroup const *group = NULL;
-
- g_return_if_fail (filename != NULL);
-
- if ((output_file = g_fopen (filename, "w")) == NULL){
- g_printerr (_("Cannot create file %s\n"), filename);
- exit (1);
- }
-
- if (dump_type == 3) {
- g_hash_table_foreach (functions_by_name,
- (GHFunc) cb_dump_usage,
- output_file);
- fclose (output_file);
- return;
- }
-
- /* TODO : Use the translated names and split by fn_group. */
- ordered = g_ptr_array_new ();
- g_hash_table_foreach (functions_by_name,
- copy_hash_table_to_ptr_array, ordered);
-
- if (ordered->len > 0)
- qsort (&g_ptr_array_index (ordered, 0),
- ordered->len, sizeof (gpointer),
- func_def_cmp);
-
- if (dump_type == 4) {
- dump_externals (ordered, output_file);
- g_ptr_array_free (ordered, TRUE);
- fclose (output_file);
- return;
- }
-
- if (dump_type == 5) {
- dump_samples (ordered, output_file);
- g_ptr_array_free (ordered, TRUE);
- fclose (output_file);
- return;
- }
-
- if (dump_type == 0) {
- int unique = 0;
- for (i = 0; i < ordered->len; i++) {
- GnmFunc const *fd = g_ptr_array_index (ordered, i);
- switch (fd->impl_status) {
- case GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC:
- unique++;
- break;
- default: ;
- }
- }
-
- fprintf (output_file,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
- "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
- "<!-- DEFINE current=Home -->\n"
- "<!-- MARKER: start-header -->\n"
- "<head>\n"
- "<title>Gnumeric</title>\n"
- "<link rel=\"stylesheet\" href=\"style/style.css\" type=\"text/css\" />\n"
- "<link rel=\"icon\" type=\"image/png\" href=\"logo.png\" />\n"
- "<style type=\"text/css\"><!--\n"
- " div.functiongroup {\n"
- " margin-top: 1em;\n"
- " margin-bottom: 1em;\n"
- " }\n"
- " table.functiongroup {\n"
- " border-style: solid;\n"
- " border-width: 1px;\n"
- " border-spacing: 0px;\n"
- " }\n"
- " tr.header td {\n"
- " font-weight: bold;\n"
- " font-size: 14pt;\n"
- " border-style: solid;\n"
- " border-width: 1px;\n"
- " text-align: center;\n"
- " }\n"
- " tr.function td {\n"
- " border: solid 1px;\n"
- " }\n"
- " td.testing-unknown { background: #ffffff; }\n"
- " td.testing-nosuite { background: #ff7662; }\n"
- " td.testing-basic { background: #fff79d; }\n"
- " td.testing-exhaustive { background: #aef8b5; }\n"
- " td.testing-devel { background: #ff6c00; }\n"
- " td.imp-exists { background: #ffffff; }\n"
- " td.imp-no { background: #ff7662; }\n"
- " td.imp-subset { background: #fff79d; }\n"
- " td.imp-complete { background: #aef8b5; }\n"
- " td.imp-superset { background: #16e49e; }\n"
- " td.imp-subsetext { background: #59fff2; }\n"
- " td.imp-devel { background: #ff6c00; }\n"
- " td.imp-gnumeric { background: #44be18; }\n"
- "--></style>\n"
- "</head>\n"
- "<body>\n"
- "<div id=\"wrap\">\n"
- " <a href=\"/\"><div id=\"header\">\n"
- " <h1 id=\"logo-text\"><span>Gnumeric</span></h1>\n"
- " <p id=\"slogan\">Free, Fast, Accurate — Pick Any Three!</p>\n"
- " <img id=\"logo\" src=\"gnumeric.png\" alt=\"logo\" class=\"float-right\"/>\n"
- " </div></a>\n"
- "\n"
- " <div id=\"nav\">\n"
- " <ul>\n"
- " <li id=\"current\"><a href=\"/\">Home</a></li>\n"
- " <li><a href=\"development.html\">Development</a></li>\n"
- " <li><a href=\"contact.html\">Contact</a></li>\n"
- " </ul>\n"
- " </div>\n"
- "\n"
- " <div id=\"content-wrap\">\n"
- " <!-- MARKER: start-main -->\n"
- " <div id=\"main\">\n"
- " <div class=\"generalitem\">\n"
- " <h2><span class=\"gnumeric-bullet\"></span>Gnumeric Sheet Functions</h2>\n"
- " <p>Gnumeric currently has %d functions for use in spreadsheets.\n"
- " %d of these are unique to Gnumeric.</p>\n",
- ordered->len, unique);
- }
-
- for (i = 0; i < ordered->len; i++) {
- GnmFunc const *fd = g_ptr_array_index (ordered, i);
- if (dump_type == 1) {
- int i;
- gboolean first_arg = TRUE;
- GString *syntax = g_string_new (NULL);
- GString *arg_desc = g_string_new (NULL);
- GString *desc = g_string_new (NULL);
- GString *odf = g_string_new (NULL);
- GString *excel = g_string_new (NULL);
- GString *note = g_string_new (NULL);
- GString *seealso = g_string_new (NULL);
- gint min, max;
-
- fprintf (output_file, "@CATEGORY=%s\n",
- F2(fd, fd->fn_group->display_name->str));
- for (i = 0;
- fd->help[i].type != GNM_FUNC_HELP_END;
- i++) {
- switch (fd->help[i].type) {
- case GNM_FUNC_HELP_NAME: {
- char *short_desc;
- char *name = split_at_colon (F2(fd, fd->help[i].text), &short_desc);
- fprintf (output_file,
- "@FUNCTION=%s\n",
- name);
- fprintf (output_file,
- "@SHORTDESC=%s\n",
- short_desc);
- g_string_append (syntax, name);
- g_string_append_c (syntax, '(');
- g_free (name);
- break;
- }
- case GNM_FUNC_HELP_SEEALSO:
- if (seealso->len > 0)
- g_string_append (seealso, ",");
- g_string_append (seealso, F2(fd, fd->help[i].text));
- break;
- case GNM_FUNC_HELP_DESCRIPTION:
- if (desc->len > 0)
- g_string_append (desc, "\n");
- g_string_append (desc, F2(fd, fd->help[i].text));
- break;
- case GNM_FUNC_HELP_NOTE:
- if (note->len > 0)
- g_string_append (note, " ");
- g_string_append (note, F2(fd, fd->help[i].text));
- break;
- case GNM_FUNC_HELP_ARG: {
- char *argdesc;
- char *name = split_at_colon (F2(fd, fd->help[i].text), &argdesc);
- if (first_arg)
- first_arg = FALSE;
- else
- g_string_append_c (syntax, go_locale_get_arg_sep ());
- g_string_append (syntax, name);
- if (argdesc) {
- g_string_append_printf (arg_desc,
- "@{%s}: %s\n",
- name,
- argdesc);
- }
- g_free (name);
- /* FIXME: Optional args? */
- break;
- }
- case GNM_FUNC_HELP_ODF:
- if (odf->len > 0)
- g_string_append (odf, " ");
- g_string_append (odf, F2(fd, fd->help[i].text));
- break;
- case GNM_FUNC_HELP_EXCEL:
- if (excel->len > 0)
- g_string_append (excel, " ");
- g_string_append (excel, F2(fd, fd->help[i].text));
- break;
-
- case GNM_FUNC_HELP_EXTREF:
- /* FIXME! */
- case GNM_FUNC_HELP_EXAMPLES:
- /* FIXME! */
- case GNM_FUNC_HELP_END:
- break;
- }
- }
-
- function_def_count_args (fd, &min, &max);
- if (max == G_MAXINT)
- fprintf (output_file,
- "@SYNTAX=%s," UNICODE_ELLIPSIS ")\n",
- syntax->str);
- else
- fprintf (output_file, "@SYNTAX=%s)\n",
- syntax->str);
-
- if (arg_desc->len > 0)
- fprintf (output_file, "@ARGUMENTDESCRIPTION=%s", arg_desc->str);
- if (desc->len > 0)
- fprintf (output_file, "@DESCRIPTION=%s\n", desc->str);
- if (note->len > 0)
- fprintf (output_file, "@NOTE=%s\n", note->str);
- if (excel->len > 0)
- fprintf (output_file, "@EXCEL=%s\n", excel->str);
- if (odf->len > 0)
- fprintf (output_file, "@ODF=%s\n", odf->str);
- if (seealso->len > 0)
- fprintf (output_file, "@SEEALSO=%s\n", seealso->str);
-
- g_string_free (syntax, TRUE);
- g_string_free (arg_desc, TRUE);
- g_string_free (desc, TRUE);
- g_string_free (odf, TRUE);
- g_string_free (excel, TRUE);
- g_string_free (note, TRUE);
- g_string_free (seealso, TRUE);
-
- fputc ('\n', output_file);
- } else if (dump_type == 0) {
- static struct {
- char const *name;
- char const *klass;
- } const testing [] = {
- { "Unknown", "testing-unknown" },
- { "No Testsuite", "testing-nosuite" },
- { "Basic", "testing-basic" },
- { "Exhaustive", "testing-exhaustive" },
- { "Under Development", "testing-devel" }
- };
- static struct {
- char const *name;
- char const *klass;
- } const implementation [] = {
- { "Exists", "imp-exists" },
- { "Unimplemented", "imp-no" },
- { "Subset", "imp-subset" },
- { "Complete", "imp-complete" },
- { "Superset", "imp-superset" },
- { "Subset with_extensions", "imp-subsetext" },
- { "Under development", "imp-devel" },
- { "Unique to Gnumeric", "imp-gnumeric" },
- };
- if (group != fd->fn_group) {
- if (group) fprintf (output_file, "</table></div>\n");
- group = fd->fn_group;
- fprintf (output_file,
- "<h2>%s</h2>\n"
- "<div class=\"functiongroup\"><table class=\"functiongroup\">\n"
- "<tr class=\"header\">"
- "<td>Function</td>"
- "<td>Implementation</td>"
- "<td>Testing</td>"
- "</tr>\n",
- group->display_name->str);
- }
- up = g_ascii_strup (fd->name, -1);
- catname = g_strdup (group->display_name->str);
- while (strchr (catname, ' '))
- *strchr (catname, ' ') = '_';
- fprintf (output_file, "<tr class=\"function\">\n");
- fprintf (output_file,
- "<td><a href
=\"https://help.gnome.org/users/gnumeric/stable/gnumeric.html#gnumeric-function-%s\">%s</a></td>\n",
- up, fd->name);
- g_free (up);
- g_free (catname);
- fprintf (output_file,
- "<td class=\"%s\"><a href=\"mailto:gnumeric-list gnome org?subject=Re: %s
implementation\">%s</a></td>\n",
- implementation[fd->impl_status].klass,
- fd->name,
- implementation[fd->impl_status].name);
- fprintf (output_file,
- "<td class=\"%s\"><a href=\"mailto:gnumeric-list gnome org?subject=Re: %s
testing\">%s</a></td>\n",
- testing[fd->test_status].klass,
- fd->name,
- testing[fd->test_status].name);
- fprintf (output_file,"</tr>\n");
- }
- }
- if (dump_type == 0) {
- if (group) fprintf (output_file, "</table></div>\n");
- fprintf (output_file,
- " </div>\n"
- " </div>\n"
- " <!-- MARKER: end-main -->\n"
- " <!-- MARKER: start-sidebar -->\n"
- " <!-- MARKER: end-sidebar -->\n"
- " </div>\n"
- "</div>\n"
- "</body>\n"
- "</html>\n");
- }
-
- g_ptr_array_free (ordered, TRUE);
- fclose (output_file);
-}
-
-/* ------------------------------------------------------------------------- */
-
-static gboolean
-check_help_expression (const char *text, GnmFunc const *fd)
-{
- GnmConventions const *convs = gnm_conventions_default;
- GnmParsePos pp;
- GnmExprTop const *texpr;
- Workbook *wb;
- GnmParseError perr;
-
- /* Create a dummy workbook with no sheets for interesting effects. */
- wb = workbook_new ();
- parse_pos_init (&pp, wb, NULL, 0, 0);
-
- parse_error_init (&perr);
-
- texpr = gnm_expr_parse_str (text, &pp,
- GNM_EXPR_PARSE_DEFAULT,
- convs,
- &perr);
- if (perr.err) {
- g_printerr ("Error parsing %s: %s\n",
- text, perr.err->message);
- }
- parse_error_free (&perr);
- g_object_unref (wb);
-
- if (!texpr)
- return TRUE;
-
- gnm_expr_top_unref (texpr);
- return FALSE;
-}
-
-static gboolean
-check_argument_refs (const char *text, GnmFunc const *fd)
-{
- if (fd->fn_type != GNM_FUNC_TYPE_ARGS)
- return FALSE;
-
- while (1) {
- const char *at = strchr (text, '@');
- char *argname;
- int i;
-
- if (!at)
- return FALSE;
- if (at[1] != '{')
- return TRUE;
- text = strchr (at + 2, '}');
- if (!text)
- return FALSE;
- argname = g_strndup (at + 2, text - at - 2);
-
- for (i = 0; TRUE; i++) {
- char *thisarg = function_def_get_arg_name (fd, i);
- gboolean found;
- if (!thisarg) {
- g_free (argname);
- return TRUE;
- }
- found = strcmp (argname, thisarg) == 0;
- g_free (thisarg);
- if (found)
- break;
- }
- g_free (argname);
- }
-}
-
-
-static int
-gnm_func_sanity_check1 (GnmFunc const *fd)
-{
- GnmFuncHelp const *h;
- int counts[(int)GNM_FUNC_HELP_ODF + 1];
- int res = 0;
- size_t nlen = strlen (fd->name);
- GHashTable *allargs;
-
- allargs = g_hash_table_new_full
- (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
-
- memset (counts, 0, sizeof (counts));
- for (h = fd->help; h->type != GNM_FUNC_HELP_END; h++) {
- g_assert (h->type <= GNM_FUNC_HELP_ODF);
- counts[h->type]++;
-
- if (!g_utf8_validate (h->text, -1, NULL)) {
- g_printerr ("%s: Invalid UTF-8 in type %i\n",
- fd->name, h->type);
- res = 1;
- continue;
- }
-
- switch (h->type) {
- case GNM_FUNC_HELP_NAME:
- if (g_ascii_strncasecmp (fd->name, h->text, nlen) ||
- h->text[nlen] != ':') {
- g_printerr ("%s: Invalid NAME record\n",
- fd->name);
- res = 1;
- } else if (h->text[nlen + 1] == ' ') {
- g_printerr ("%s: Unwanted space in NAME record\n",
- fd->name);
- res = 1;
- } else if (h->text[strlen (h->text) - 1] == '.') {
- g_printerr ("%s: Unwanted period in NAME record\n",
- fd->name);
- res = 1;
- }
- break;
- case GNM_FUNC_HELP_ARG: {
- const char *aend = strchr (h->text, ':');
- char *argname;
-
- if (aend == NULL || aend == h->text) {
- g_printerr ("%s: Invalid ARG record\n",
- fd->name);
- res = 1;
- break;
- }
-
- if (aend[1] == ' ') {
- g_printerr ("%s: Unwanted space in ARG record\n",
- fd->name);
- res = 1;
- }
- if (aend[1] == '\0') {
- g_printerr ("%s: Empty ARG record\n",
- fd->name);
- res = 1;
- }
- if (h->text[strlen (h->text) - 1] == '.') {
- g_printerr ("%s: Unwanted period in ARG record\n",
- fd->name);
- res = 1;
- }
- if (check_argument_refs (aend + 1, fd)) {
- g_printerr ("%s: Invalid argument reference in argument\n",
- fd->name);
- res = 1;
- }
- argname = g_strndup (h->text, aend - h->text);
- if (g_hash_table_lookup (allargs, argname)) {
- g_printerr ("%s: Duplicate argument name %s\n",
- fd->name, argname);
- res = 1;
- g_free (argname);
- g_printerr ("%s\n", h->text);
- } else
- g_hash_table_insert (allargs, argname, argname);
- break;
- }
- case GNM_FUNC_HELP_DESCRIPTION: {
- const char *p;
-
- if (check_argument_refs (h->text, fd)) {
- g_printerr ("%s: Invalid argument reference in description\n",
- fd->name);
- res = 1;
- }
-
- p = h->text;
- while (g_ascii_isupper (*p) ||
- (p != h->text && (*p == '_' ||
- *p == '.' ||
- g_ascii_isdigit (*p))))
- p++;
- if (*p == ' ' &&
- p - h->text >= 2 &&
- strncmp (h->text, "CP1252", 6) != 0) {
- if (g_ascii_strncasecmp (h->text, fd->name, nlen)) {
- g_printerr ("%s: Wrong function name in description\n",
- fd->name);
- res = 1;
- }
- }
- break;
- }
-
- case GNM_FUNC_HELP_EXAMPLES:
- if (h->text[0] == '=') {
- if (check_help_expression (h->text + 1, fd)) {
- g_printerr ("%s: Invalid EXAMPLES record\n",
- fd->name);
- res = 1;
- }
- }
- break;
- default:
- ; /* Nothing */
- }
- }
-
- g_hash_table_destroy (allargs);
-
- if (fd->fn_type == GNM_FUNC_TYPE_ARGS) {
- int n = counts[GNM_FUNC_HELP_ARG];
- if (n != fd->fn.args.max_args) {
- g_printerr ("%s: Help for %d args, but takes %d-%d\n",
- fd->name, n,
- fd->fn.args.min_args, fd->fn.args.max_args);
- res = 1;
- }
- }
-
-#if 0
- if (counts[GNM_FUNC_HELP_DESCRIPTION] != 1) {
- g_printerr ("%s: Help has %d descriptions.\n",
- fd->name, counts[GNM_FUNC_HELP_DESCRIPTION]);
- res = 1;
- }
-#endif
-
- if (counts[GNM_FUNC_HELP_NAME] != 1) {
- g_printerr ("%s: Help has %d NAME records.\n",
- fd->name, counts[GNM_FUNC_HELP_NAME]);
- res = 1;
- }
-
- if (counts[GNM_FUNC_HELP_EXCEL] > 1) {
- g_printerr ("%s: Help has %d Excel notes.\n",
- fd->name, counts[GNM_FUNC_HELP_EXCEL]);
- res = 1;
- }
-
- if (counts[GNM_FUNC_HELP_ODF] > 1) {
- g_printerr ("%s: Help has %d ODF notes.\n",
- fd->name, counts[GNM_FUNC_HELP_ODF]);
- res = 1;
- }
-
- return res;
-}
-
-int
-gnm_func_sanity_check (void)
-{
- int res = 0;
- GPtrArray *ordered;
- unsigned ui;
-
- ordered = g_ptr_array_new ();
- g_hash_table_foreach (functions_by_name,
- copy_hash_table_to_ptr_array, ordered);
- if (ordered->len > 0)
- qsort (&g_ptr_array_index (ordered, 0),
- ordered->len, sizeof (gpointer),
- func_def_cmp);
-
- for (ui = 0; ui < ordered->len; ui++) {
- GnmFunc const *fd = g_ptr_array_index (ordered, ui);
- if (gnm_func_sanity_check1 (fd))
- res = 1;
- }
-
- g_ptr_array_free (ordered, TRUE);
-
- return res;
-}
-
/* ------------------------------------------------------------------------- */
static void
diff --git a/src/func.h b/src/func.h
index 3ec536e..a6bcd56 100644
--- a/src/func.h
+++ b/src/func.h
@@ -11,8 +11,7 @@ G_BEGIN_DECLS
void functions_init (void);
void functions_shutdown (void);
-void function_dump_defs (char const *filename, int dump_type);
-int gnm_func_sanity_check (void);
+GPtrArray *gnm_func_enumerate (void);
/******************************************************************************/
/* Function group support */
diff --git a/src/sstest.c b/src/sstest.c
index e85f137..33ba43a 100644
--- a/src/sstest.c
+++ b/src/sstest.c
@@ -30,6 +30,7 @@
#include <gsf/gsf-input-stdio.h>
#include <gsf/gsf-input-textline.h>
+#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <string.h>
#include <errno.h>
@@ -89,6 +90,553 @@ static GOptionEntry const sstest_options [] = {
/* ------------------------------------------------------------------------- */
+#define UNICODE_ELLIPSIS "\xe2\x80\xa6"
+#define F2(func,s) dgettext ((func)->textdomain->str, (s))
+
+static char *
+split_at_colon (char const *s, char **rest)
+{
+ char *dup = g_strdup (s);
+ char *colon = strchr (dup, ':');
+ if (colon) {
+ *colon = 0;
+ if (rest) *rest = colon + 1;
+ } else {
+ if (rest) *rest = NULL;
+ }
+ return dup;
+}
+
+
+static void
+dump_externals (GPtrArray *defs, FILE *out)
+{
+ unsigned int ui;
+
+ fprintf (out, "<!--#set var=\"title\" value=\"Gnumeric Web Documentation\" -->");
+ fprintf (out, "<!--#set var=\"rootdir\" value=\".\" -->");
+ fprintf (out, "<!--#include virtual=\"header-begin.shtml\" -->");
+ fprintf (out, "<link rel=\"stylesheet\" href=\"style/index.css\" type=\"text/css\"/>");
+ fprintf (out, "<!--#include virtual=\"header-end.shtml\" -->");
+ fprintf (out, "<!--#set var=\"wolfram\" value=\"none\" -->");
+ fprintf (out, "<!--#set var=\"wiki\" value=\"none\" -->");
+ fprintf (out, "<!--\n\n-->");
+
+ for (ui = 0; ui < defs->len; ui++) {
+ GnmFunc const *fd = g_ptr_array_index (defs, ui);
+ gboolean any = FALSE;
+ int j;
+
+ for (j = 0; fd->help[j].type != GNM_FUNC_HELP_END; j++) {
+ const char *s = F2(fd, fd->help[j].text);
+
+ switch (fd->help[j].type) {
+ case GNM_FUNC_HELP_EXTREF:
+ if (!any) {
+ any = TRUE;
+ fprintf (out, "<!--#if expr=\"${QUERY_STRING} = %s\" -->", fd->name);
+ }
+
+ if (strncmp (s, "wolfram:", 8) == 0) {
+ fprintf (out, "<!--#set var=\"wolfram\" value=\"%s\" -->", s + 8);
+ }
+ if (strncmp (s, "wiki:", 5) == 0) {
+ char *lang, *page;
+ lang = split_at_colon (s + 5, &page);
+ fprintf (out, "<!--#set var=\"wiki_lang\" value=\"%s\" -->", lang);
+ fprintf (out, "<!--#set var=\"wiki\" value=\"%s\" -->", page);
+ g_free (lang);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (any)
+ fprintf (out, "<!--#endif\n\n-->");
+ }
+
+ fprintf (out, "<div class=\"floatflush\">\n");
+ fprintf (out, "<h1>Online Documentation for \"<!--#echo var=\"QUERY_STRING\" -->\"</h1>\n");
+ fprintf (out, "<p>When last checked, these sources provided useful information about\n");
+ fprintf (out, "this function. However, since the links are not controlled by the\n");
+ fprintf (out, "Gnumeric Team, we cannot guarantee that the links still work. If\n");
+ fprintf (out, "you find that they do not work, please drop us a line.</p>\n");
+ fprintf (out, "<ul>");
+ fprintf (out, "<!--#if expr=\"${wolfram} != none\"-->");
+ fprintf (out, "<li><a href=\"http://mathworld.wolfram.com/<!--#echo var=\"wolfram\" -->\">Wolfram
Mathworld\nentry</a>.</li><!--#endif-->");
+ fprintf (out, "<!--#if expr=\"${wiki} != none\"--><li><a href=\"http://<!--#echo var=\"wiki_lang\"
-->.wikipedia.org/wiki/<!--#echo var=\"wiki\" -->\">Wikipedia\nentry</a>.</li><!--#endif-->");
+ fprintf (out, "<li><a href=\"http://www.google.com/#q=<!--#echo var=\"QUERY_STRING\" -->\">Google
Search</a>.</li>");
+ fprintf (out, "</ul>");
+ fprintf (out, "</div>\n");
+
+ fprintf (out, "<!--#include virtual=\"footer.shtml\" -->\n");
+}
+
+static void
+csv_quoted_print (FILE *out, const char *s)
+{
+ char quote = '"';
+ fputc (quote, out);
+ while (*s) {
+ if (*s == quote) {
+ fputc (quote, out);
+ fputc (quote, out);
+ s++;
+ } else {
+ int len = g_utf8_skip[(unsigned char)*s];
+ fprintf (out, "%-.*s", len, s);
+ s += len;
+ }
+ }
+ fputc ('"', out);
+}
+
+static void
+dump_samples (GPtrArray *defs, FILE *out)
+{
+ unsigned ui;
+ GnmFuncGroup *last_group = NULL;
+
+ for (ui = 0; ui < defs->len; ui++) {
+ GnmFunc const *fd = g_ptr_array_index (defs, ui);
+ int j;
+ const char *last = NULL;
+ gboolean has_sample = FALSE;
+
+ if (last_group != fd->fn_group) {
+ last_group = fd->fn_group;
+ csv_quoted_print (out, last_group->display_name->str);
+ fputc ('\n', out);
+ }
+
+ for (j = 0; fd->help[j].type != GNM_FUNC_HELP_END; j++) {
+ const char *s = fd->help[j].text;
+
+ if (fd->help[j].type != GNM_FUNC_HELP_EXAMPLES)
+ continue;
+
+ has_sample = TRUE;
+
+ /*
+ * Some of the random numbers functions have duplicate
+ * samples. We don't want the duplicates here.
+ */
+ if (s[0] != '=' || (last && strcmp (last, s) == 0))
+ continue;
+
+ fputc (',', out);
+ if (!last)
+ csv_quoted_print (out, fd->name);
+ last = s;
+
+ fputc (',', out);
+ csv_quoted_print (out, s);
+ fputc ('\n', out);
+ }
+
+ if (!has_sample)
+ g_printerr ("No samples for %s\n", fd->name);
+ }
+}
+
+static void
+cb_dump_usage (GnmFunc const *fd, FILE *out)
+{
+ if (fd->usage_count > 0)
+ fprintf (out, "%d,%s\n", fd->usage_count, fd->name);
+}
+
+
+
+static int
+func_def_cmp (gconstpointer a, gconstpointer b)
+{
+ GnmFunc const *fda = *(GnmFunc const **)a ;
+ GnmFunc const *fdb = *(GnmFunc const **)b ;
+
+ g_return_val_if_fail (fda->name != NULL, 0);
+ g_return_val_if_fail (fdb->name != NULL, 0);
+
+ if (fda->fn_group != NULL && fdb->fn_group != NULL) {
+ int res = go_string_cmp (fda->fn_group->display_name,
+ fdb->fn_group->display_name);
+ if (res != 0)
+ return res;
+ }
+
+ return g_ascii_strcasecmp (fda->name, fdb->name);
+}
+
+static GPtrArray *
+enumerate_functions (gboolean filter)
+{
+ GPtrArray *res = gnm_func_enumerate ();
+
+ if (filter) {
+ unsigned ui;
+ for (ui = 0; ui < res->len; ui++) {
+ GnmFunc *fd = g_ptr_array_index (res, ui);
+
+ if (fd->name == NULL ||
+ strcmp (fd->name, "perl_adder") == 0 ||
+ strcmp (fd->name, "perl_date") == 0 ||
+ strcmp (fd->name, "perl_sed") == 0 ||
+ strcmp (fd->name, "py_capwords") == 0 ||
+ strcmp (fd->name, "py_printf") == 0 ||
+ strcmp (fd->name, "py_bitand") == 0) {
+ g_ptr_array_remove_index_fast (res, ui);
+ ui--;
+ }
+ }
+ }
+
+ if (res->len > 0)
+ qsort (&g_ptr_array_index (res, 0),
+ res->len, sizeof (gpointer),
+ func_def_cmp);
+
+ return res;
+}
+
+/**
+ * function_dump_defs :
+ * @filename:
+ * @dump_type:
+ *
+ * A generic utility routine to operate on all funtion defs
+ * in various ways. @dump_type will change/extend as needed
+ * Right now
+ * 0 : www.gnumeric.org's function.shtml page
+ * 1 :
+ * 2 : (obsolete)
+ * 3 : dump function usage count
+ * 4 : external refs
+ * 5 : all sample expressions
+ **/
+static void
+function_dump_defs (char const *filename, int dump_type)
+{
+ FILE *output_file;
+ char *up, *catname;
+ unsigned i;
+ GPtrArray *ordered;
+ GnmFuncGroup const *group = NULL;
+
+ g_return_if_fail (filename != NULL);
+
+ if ((output_file = g_fopen (filename, "w")) == NULL){
+ g_printerr (_("Cannot create file %s\n"), filename);
+ exit (1);
+ }
+
+ if (dump_type == 3) {
+ GPtrArray *funcs = enumerate_functions (FALSE);
+ g_ptr_array_foreach (funcs, (GFunc)cb_dump_usage, output_file);
+ g_ptr_array_free (funcs, TRUE);
+ fclose (output_file);
+ return;
+ }
+
+ /* TODO : Use the translated names and split by fn_group. */
+ ordered = enumerate_functions (TRUE);
+
+ if (dump_type == 4) {
+ dump_externals (ordered, output_file);
+ g_ptr_array_free (ordered, TRUE);
+ fclose (output_file);
+ return;
+ }
+
+ if (dump_type == 5) {
+ dump_samples (ordered, output_file);
+ g_ptr_array_free (ordered, TRUE);
+ fclose (output_file);
+ return;
+ }
+
+ if (dump_type == 0) {
+ int unique = 0;
+ for (i = 0; i < ordered->len; i++) {
+ GnmFunc const *fd = g_ptr_array_index (ordered, i);
+ switch (fd->impl_status) {
+ case GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC:
+ unique++;
+ break;
+ default: ;
+ }
+ }
+
+ fprintf (output_file,
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
+ "<!-- DEFINE current=Home -->\n"
+ "<!-- MARKER: start-header -->\n"
+ "<head>\n"
+ "<title>Gnumeric</title>\n"
+ "<link rel=\"stylesheet\" href=\"style/style.css\" type=\"text/css\" />\n"
+ "<link rel=\"icon\" type=\"image/png\" href=\"logo.png\" />\n"
+ "<style type=\"text/css\"><!--\n"
+ " div.functiongroup {\n"
+ " margin-top: 1em;\n"
+ " margin-bottom: 1em;\n"
+ " }\n"
+ " table.functiongroup {\n"
+ " border-style: solid;\n"
+ " border-width: 1px;\n"
+ " border-spacing: 0px;\n"
+ " }\n"
+ " tr.header td {\n"
+ " font-weight: bold;\n"
+ " font-size: 14pt;\n"
+ " border-style: solid;\n"
+ " border-width: 1px;\n"
+ " text-align: center;\n"
+ " }\n"
+ " tr.function td {\n"
+ " border: solid 1px;\n"
+ " }\n"
+ " td.testing-unknown { background: #ffffff; }\n"
+ " td.testing-nosuite { background: #ff7662; }\n"
+ " td.testing-basic { background: #fff79d; }\n"
+ " td.testing-exhaustive { background: #aef8b5; }\n"
+ " td.testing-devel { background: #ff6c00; }\n"
+ " td.imp-exists { background: #ffffff; }\n"
+ " td.imp-no { background: #ff7662; }\n"
+ " td.imp-subset { background: #fff79d; }\n"
+ " td.imp-complete { background: #aef8b5; }\n"
+ " td.imp-superset { background: #16e49e; }\n"
+ " td.imp-subsetext { background: #59fff2; }\n"
+ " td.imp-devel { background: #ff6c00; }\n"
+ " td.imp-gnumeric { background: #44be18; }\n"
+ "--></style>\n"
+ "</head>\n"
+ "<body>\n"
+ "<div id=\"wrap\">\n"
+ " <a href=\"/\"><div id=\"header\">\n"
+ " <h1 id=\"logo-text\"><span>Gnumeric</span></h1>\n"
+ " <p id=\"slogan\">Free, Fast, Accurate — Pick Any Three!</p>\n"
+ " <img id=\"logo\" src=\"gnumeric.png\" alt=\"logo\" class=\"float-right\"/>\n"
+ " </div></a>\n"
+ "\n"
+ " <div id=\"nav\">\n"
+ " <ul>\n"
+ " <li id=\"current\"><a href=\"/\">Home</a></li>\n"
+ " <li><a href=\"development.html\">Development</a></li>\n"
+ " <li><a href=\"contact.html\">Contact</a></li>\n"
+ " </ul>\n"
+ " </div>\n"
+ "\n"
+ " <div id=\"content-wrap\">\n"
+ " <!-- MARKER: start-main -->\n"
+ " <div id=\"main\">\n"
+ " <div class=\"generalitem\">\n"
+ " <h2><span class=\"gnumeric-bullet\"></span>Gnumeric Sheet Functions</h2>\n"
+ " <p>Gnumeric currently has %d functions for use in spreadsheets.\n"
+ " %d of these are unique to Gnumeric.</p>\n",
+ ordered->len, unique);
+ }
+
+ for (i = 0; i < ordered->len; i++) {
+ GnmFunc const *fd = g_ptr_array_index (ordered, i);
+ if (dump_type == 1) {
+ int i;
+ gboolean first_arg = TRUE;
+ GString *syntax = g_string_new (NULL);
+ GString *arg_desc = g_string_new (NULL);
+ GString *desc = g_string_new (NULL);
+ GString *odf = g_string_new (NULL);
+ GString *excel = g_string_new (NULL);
+ GString *note = g_string_new (NULL);
+ GString *seealso = g_string_new (NULL);
+ gint min, max;
+
+ fprintf (output_file, "@CATEGORY=%s\n",
+ F2(fd, fd->fn_group->display_name->str));
+ for (i = 0;
+ fd->help[i].type != GNM_FUNC_HELP_END;
+ i++) {
+ switch (fd->help[i].type) {
+ case GNM_FUNC_HELP_NAME: {
+ char *short_desc;
+ char *name = split_at_colon (F2(fd, fd->help[i].text), &short_desc);
+ fprintf (output_file,
+ "@FUNCTION=%s\n",
+ name);
+ fprintf (output_file,
+ "@SHORTDESC=%s\n",
+ short_desc);
+ g_string_append (syntax, name);
+ g_string_append_c (syntax, '(');
+ g_free (name);
+ break;
+ }
+ case GNM_FUNC_HELP_SEEALSO:
+ if (seealso->len > 0)
+ g_string_append (seealso, ",");
+ g_string_append (seealso, F2(fd, fd->help[i].text));
+ break;
+ case GNM_FUNC_HELP_DESCRIPTION:
+ if (desc->len > 0)
+ g_string_append (desc, "\n");
+ g_string_append (desc, F2(fd, fd->help[i].text));
+ break;
+ case GNM_FUNC_HELP_NOTE:
+ if (note->len > 0)
+ g_string_append (note, " ");
+ g_string_append (note, F2(fd, fd->help[i].text));
+ break;
+ case GNM_FUNC_HELP_ARG: {
+ char *argdesc;
+ char *name = split_at_colon (F2(fd, fd->help[i].text), &argdesc);
+ if (first_arg)
+ first_arg = FALSE;
+ else
+ g_string_append_c (syntax, go_locale_get_arg_sep ());
+ g_string_append (syntax, name);
+ if (argdesc) {
+ g_string_append_printf (arg_desc,
+ "@{%s}: %s\n",
+ name,
+ argdesc);
+ }
+ g_free (name);
+ /* FIXME: Optional args? */
+ break;
+ }
+ case GNM_FUNC_HELP_ODF:
+ if (odf->len > 0)
+ g_string_append (odf, " ");
+ g_string_append (odf, F2(fd, fd->help[i].text));
+ break;
+ case GNM_FUNC_HELP_EXCEL:
+ if (excel->len > 0)
+ g_string_append (excel, " ");
+ g_string_append (excel, F2(fd, fd->help[i].text));
+ break;
+
+ case GNM_FUNC_HELP_EXTREF:
+ /* FIXME! */
+ case GNM_FUNC_HELP_EXAMPLES:
+ /* FIXME! */
+ case GNM_FUNC_HELP_END:
+ break;
+ }
+ }
+
+ function_def_count_args (fd, &min, &max);
+ if (max == G_MAXINT)
+ fprintf (output_file,
+ "@SYNTAX=%s," UNICODE_ELLIPSIS ")\n",
+ syntax->str);
+ else
+ fprintf (output_file, "@SYNTAX=%s)\n",
+ syntax->str);
+
+ if (arg_desc->len > 0)
+ fprintf (output_file, "@ARGUMENTDESCRIPTION=%s", arg_desc->str);
+ if (desc->len > 0)
+ fprintf (output_file, "@DESCRIPTION=%s\n", desc->str);
+ if (note->len > 0)
+ fprintf (output_file, "@NOTE=%s\n", note->str);
+ if (excel->len > 0)
+ fprintf (output_file, "@EXCEL=%s\n", excel->str);
+ if (odf->len > 0)
+ fprintf (output_file, "@ODF=%s\n", odf->str);
+ if (seealso->len > 0)
+ fprintf (output_file, "@SEEALSO=%s\n", seealso->str);
+
+ g_string_free (syntax, TRUE);
+ g_string_free (arg_desc, TRUE);
+ g_string_free (desc, TRUE);
+ g_string_free (odf, TRUE);
+ g_string_free (excel, TRUE);
+ g_string_free (note, TRUE);
+ g_string_free (seealso, TRUE);
+
+ fputc ('\n', output_file);
+ } else if (dump_type == 0) {
+ static struct {
+ char const *name;
+ char const *klass;
+ } const testing [] = {
+ { "Unknown", "testing-unknown" },
+ { "No Testsuite", "testing-nosuite" },
+ { "Basic", "testing-basic" },
+ { "Exhaustive", "testing-exhaustive" },
+ { "Under Development", "testing-devel" }
+ };
+ static struct {
+ char const *name;
+ char const *klass;
+ } const implementation [] = {
+ { "Exists", "imp-exists" },
+ { "Unimplemented", "imp-no" },
+ { "Subset", "imp-subset" },
+ { "Complete", "imp-complete" },
+ { "Superset", "imp-superset" },
+ { "Subset with_extensions", "imp-subsetext" },
+ { "Under development", "imp-devel" },
+ { "Unique to Gnumeric", "imp-gnumeric" },
+ };
+ if (group != fd->fn_group) {
+ if (group) fprintf (output_file, "</table></div>\n");
+ group = fd->fn_group;
+ fprintf (output_file,
+ "<h2>%s</h2>\n"
+ "<div class=\"functiongroup\"><table class=\"functiongroup\">\n"
+ "<tr class=\"header\">"
+ "<td>Function</td>"
+ "<td>Implementation</td>"
+ "<td>Testing</td>"
+ "</tr>\n",
+ group->display_name->str);
+ }
+ up = g_ascii_strup (fd->name, -1);
+ catname = g_strdup (group->display_name->str);
+ while (strchr (catname, ' '))
+ *strchr (catname, ' ') = '_';
+ fprintf (output_file, "<tr class=\"function\">\n");
+ fprintf (output_file,
+ "<td><a href
=\"https://help.gnome.org/users/gnumeric/stable/gnumeric.html#gnumeric-function-%s\">%s</a></td>\n",
+ up, fd->name);
+ g_free (up);
+ g_free (catname);
+ fprintf (output_file,
+ "<td class=\"%s\"><a href=\"mailto:gnumeric-list gnome org?subject=Re: %s
implementation\">%s</a></td>\n",
+ implementation[fd->impl_status].klass,
+ fd->name,
+ implementation[fd->impl_status].name);
+ fprintf (output_file,
+ "<td class=\"%s\"><a href=\"mailto:gnumeric-list gnome org?subject=Re: %s
testing\">%s</a></td>\n",
+ testing[fd->test_status].klass,
+ fd->name,
+ testing[fd->test_status].name);
+ fprintf (output_file,"</tr>\n");
+ }
+ }
+ if (dump_type == 0) {
+ if (group) fprintf (output_file, "</table></div>\n");
+ fprintf (output_file,
+ " </div>\n"
+ " </div>\n"
+ " <!-- MARKER: end-main -->\n"
+ " <!-- MARKER: start-sidebar -->\n"
+ " <!-- MARKER: end-sidebar -->\n"
+ " </div>\n"
+ "</div>\n"
+ "</body>\n"
+ "</html>\n");
+ }
+
+ g_ptr_array_free (ordered, TRUE);
+ fclose (output_file);
+}
+
+/* ------------------------------------------------------------------------- */
+
static void
mark_test_start (const char *name)
{
@@ -350,6 +898,263 @@ test_insert_delete (void)
/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+
+static gboolean
+check_help_expression (const char *text, GnmFunc const *fd)
+{
+ GnmConventions const *convs = gnm_conventions_default;
+ GnmParsePos pp;
+ GnmExprTop const *texpr;
+ Workbook *wb;
+ GnmParseError perr;
+
+ /* Create a dummy workbook with no sheets for interesting effects. */
+ wb = workbook_new ();
+ parse_pos_init (&pp, wb, NULL, 0, 0);
+
+ parse_error_init (&perr);
+
+ texpr = gnm_expr_parse_str (text, &pp,
+ GNM_EXPR_PARSE_DEFAULT,
+ convs,
+ &perr);
+ if (perr.err) {
+ g_printerr ("Error parsing %s: %s\n",
+ text, perr.err->message);
+ }
+ parse_error_free (&perr);
+ g_object_unref (wb);
+
+ if (!texpr)
+ return TRUE;
+
+ gnm_expr_top_unref (texpr);
+ return FALSE;
+}
+
+static gboolean
+check_argument_refs (const char *text, GnmFunc const *fd)
+{
+ if (fd->fn_type != GNM_FUNC_TYPE_ARGS)
+ return FALSE;
+
+ while (1) {
+ const char *at = strchr (text, '@');
+ char *argname;
+ int i;
+
+ if (!at)
+ return FALSE;
+ if (at[1] != '{')
+ return TRUE;
+ text = strchr (at + 2, '}');
+ if (!text)
+ return FALSE;
+ argname = g_strndup (at + 2, text - at - 2);
+
+ for (i = 0; TRUE; i++) {
+ char *thisarg = function_def_get_arg_name (fd, i);
+ gboolean found;
+ if (!thisarg) {
+ g_free (argname);
+ return TRUE;
+ }
+ found = strcmp (argname, thisarg) == 0;
+ g_free (thisarg);
+ if (found)
+ break;
+ }
+ g_free (argname);
+ }
+}
+
+
+static int
+gnm_func_sanity_check1 (GnmFunc const *fd)
+{
+ GnmFuncHelp const *h;
+ int counts[(int)GNM_FUNC_HELP_ODF + 1];
+ int res = 0;
+ size_t nlen = strlen (fd->name);
+ GHashTable *allargs;
+
+ allargs = g_hash_table_new_full
+ (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
+
+ memset (counts, 0, sizeof (counts));
+ for (h = fd->help; h->type != GNM_FUNC_HELP_END; h++) {
+ g_assert (h->type <= GNM_FUNC_HELP_ODF);
+ counts[h->type]++;
+
+ if (!g_utf8_validate (h->text, -1, NULL)) {
+ g_printerr ("%s: Invalid UTF-8 in type %i\n",
+ fd->name, h->type);
+ res = 1;
+ continue;
+ }
+
+ switch (h->type) {
+ case GNM_FUNC_HELP_NAME:
+ if (g_ascii_strncasecmp (fd->name, h->text, nlen) ||
+ h->text[nlen] != ':') {
+ g_printerr ("%s: Invalid NAME record\n",
+ fd->name);
+ res = 1;
+ } else if (h->text[nlen + 1] == ' ') {
+ g_printerr ("%s: Unwanted space in NAME record\n",
+ fd->name);
+ res = 1;
+ } else if (h->text[strlen (h->text) - 1] == '.') {
+ g_printerr ("%s: Unwanted period in NAME record\n",
+ fd->name);
+ res = 1;
+ }
+ break;
+ case GNM_FUNC_HELP_ARG: {
+ const char *aend = strchr (h->text, ':');
+ char *argname;
+
+ if (aend == NULL || aend == h->text) {
+ g_printerr ("%s: Invalid ARG record\n",
+ fd->name);
+ res = 1;
+ break;
+ }
+
+ if (aend[1] == ' ') {
+ g_printerr ("%s: Unwanted space in ARG record\n",
+ fd->name);
+ res = 1;
+ }
+ if (aend[1] == '\0') {
+ g_printerr ("%s: Empty ARG record\n",
+ fd->name);
+ res = 1;
+ }
+ if (h->text[strlen (h->text) - 1] == '.') {
+ g_printerr ("%s: Unwanted period in ARG record\n",
+ fd->name);
+ res = 1;
+ }
+ if (check_argument_refs (aend + 1, fd)) {
+ g_printerr ("%s: Invalid argument reference in argument\n",
+ fd->name);
+ res = 1;
+ }
+ argname = g_strndup (h->text, aend - h->text);
+ if (g_hash_table_lookup (allargs, argname)) {
+ g_printerr ("%s: Duplicate argument name %s\n",
+ fd->name, argname);
+ res = 1;
+ g_free (argname);
+ g_printerr ("%s\n", h->text);
+ } else
+ g_hash_table_insert (allargs, argname, argname);
+ break;
+ }
+ case GNM_FUNC_HELP_DESCRIPTION: {
+ const char *p;
+
+ if (check_argument_refs (h->text, fd)) {
+ g_printerr ("%s: Invalid argument reference in description\n",
+ fd->name);
+ res = 1;
+ }
+
+ p = h->text;
+ while (g_ascii_isupper (*p) ||
+ (p != h->text && (*p == '_' ||
+ *p == '.' ||
+ g_ascii_isdigit (*p))))
+ p++;
+ if (*p == ' ' &&
+ p - h->text >= 2 &&
+ strncmp (h->text, "CP1252", 6) != 0) {
+ if (g_ascii_strncasecmp (h->text, fd->name, nlen)) {
+ g_printerr ("%s: Wrong function name in description\n",
+ fd->name);
+ res = 1;
+ }
+ }
+ break;
+ }
+
+ case GNM_FUNC_HELP_EXAMPLES:
+ if (h->text[0] == '=') {
+ if (check_help_expression (h->text + 1, fd)) {
+ g_printerr ("%s: Invalid EXAMPLES record\n",
+ fd->name);
+ res = 1;
+ }
+ }
+ break;
+ default:
+ ; /* Nothing */
+ }
+ }
+
+ g_hash_table_destroy (allargs);
+
+ if (fd->fn_type == GNM_FUNC_TYPE_ARGS) {
+ int n = counts[GNM_FUNC_HELP_ARG];
+ if (n != fd->fn.args.max_args) {
+ g_printerr ("%s: Help for %d args, but takes %d-%d\n",
+ fd->name, n,
+ fd->fn.args.min_args, fd->fn.args.max_args);
+ res = 1;
+ }
+ }
+
+#if 0
+ if (counts[GNM_FUNC_HELP_DESCRIPTION] != 1) {
+ g_printerr ("%s: Help has %d descriptions.\n",
+ fd->name, counts[GNM_FUNC_HELP_DESCRIPTION]);
+ res = 1;
+ }
+#endif
+
+ if (counts[GNM_FUNC_HELP_NAME] != 1) {
+ g_printerr ("%s: Help has %d NAME records.\n",
+ fd->name, counts[GNM_FUNC_HELP_NAME]);
+ res = 1;
+ }
+
+ if (counts[GNM_FUNC_HELP_EXCEL] > 1) {
+ g_printerr ("%s: Help has %d Excel notes.\n",
+ fd->name, counts[GNM_FUNC_HELP_EXCEL]);
+ res = 1;
+ }
+
+ if (counts[GNM_FUNC_HELP_ODF] > 1) {
+ g_printerr ("%s: Help has %d ODF notes.\n",
+ fd->name, counts[GNM_FUNC_HELP_ODF]);
+ res = 1;
+ }
+
+ return res;
+}
+
+static int
+gnm_func_sanity_check (void)
+{
+ int res = 0;
+ GPtrArray *ordered;
+ unsigned ui;
+
+ ordered = enumerate_functions (TRUE);
+
+ for (ui = 0; ui < ordered->len; ui++) {
+ GnmFunc const *fd = g_ptr_array_index (ordered, ui);
+ if (gnm_func_sanity_check1 (fd))
+ res = 1;
+ }
+
+ g_ptr_array_free (ordered, TRUE);
+
+ return res;
+}
+
static void
test_func_help (void)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]