2 * sample_datasource.c: A prototype for handling external data sources
4 * Copyright (C) 2002 Jody Goldberg (jody@gnome.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gnumeric-config.h>
28 #include <application.h>
32 #include <goffice/goffice.h>
33 #include <gnm-plugin.h>
35 #include <sys/types.h>
43 #include <glib/gstdio.h>
46 #define G_LOG_DOMAIN "gnumeric:atl"
48 GNM_PLUGIN_MODULE_HEADER
;
50 static gboolean debug
;
51 static int atl_fd
= -1;
52 static char * atl_filename
= NULL
;
53 static FILE *atl_file
= NULL
;
54 static guint atl_source
= 0;
55 static GHashTable
*watched_values
= NULL
;
56 static GHashTable
*watchers
= NULL
;
66 GnmExprFunction
const *node
; /* Expression node that calls us */
67 GnmDependent
*dep
; /* GnmDependent containing that node */
73 watcher_hash (Watcher
const *w
)
75 return (GPOINTER_TO_INT(w
->node
) << 16) + GPOINTER_TO_INT(w
->dep
);
78 watcher_equal (Watcher
const *w1
, Watcher
const *w2
)
80 return w1
->node
== w2
->node
&& w1
->dep
== w2
->dep
;
84 watched_value_fetch (char const *tag
)
86 WatchedValue
*val
= g_hash_table_lookup (watched_values
, tag
);
88 val
= g_new (WatchedValue
, 1);
89 val
->name
= g_strdup (tag
);
92 val
->deps
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
93 g_hash_table_insert (watched_values
, val
->name
, val
);
98 /***************************************************************************/
101 cb_watcher_queue_recalc (gpointer key
, gpointer value
, gpointer closure
)
103 Watcher
const *w
= key
;
104 dependent_queue_recalc (w
->dep
);
109 cb_atl_input (GIOChannel
*gioc
, GIOCondition cond
, gpointer ignored
)
113 /* quick format ticker:value\n
114 * there is no notion of a field for now.
116 while (fgets (buf
, sizeof (buf
), atl_file
) != NULL
) {
118 char *value_str
= strchr (buf
, ':');
120 if (value_str
!= NULL
) {
125 val
= gnm_strto (value_str
, &end
);
126 if (sym
!= end
&& errno
== 0) {
127 WatchedValue
*wv
= watched_value_fetch (sym
);
131 g_hash_table_foreach (wv
->deps
,
132 cb_watcher_queue_recalc
, NULL
);
133 g_printerr ("'%s' <= %" GNM_FORMAT_f
"\n", sym
, val
);
142 atl_last (GnmFuncEvalInfo
*ei
, GnmValue
const * const argv
[])
144 WatchedValue
*val
= watched_value_fetch (value_peek_string (argv
[0]));
147 key
.node
= ei
->func_call
;
148 key
.dep
= ei
->pos
->dep
;
150 g_return_val_if_fail (val
!= NULL
,
151 value_new_error_NA (ei
->pos
));
153 /* If caller wants to be notified of updates */
154 if (key
.node
!= NULL
&& key
.dep
!= NULL
) {
155 Watcher
*w
= g_hash_table_lookup (watchers
, &key
);
157 w
= g_new (Watcher
, 1);
160 g_hash_table_insert (watchers
, w
, w
);
161 g_hash_table_insert (w
->value
->deps
, w
, w
);
162 } else if (w
->value
!= val
) {
163 g_hash_table_remove (w
->value
->deps
, w
);
165 g_hash_table_insert (w
->value
->deps
, w
, w
);
170 return value_new_error_NA (ei
->pos
);
171 return value_new_float (val
->value
);
174 static int // GnmDependentFlags
175 atl_last_link (GnmFunc
*func
, GnmFuncEvalInfo
*ei
, gboolean qlink
)
179 g_printerr ("link atl_last\n");
182 key
.node
= ei
->func_call
;
183 key
.dep
= ei
->pos
->dep
;
185 w
= g_hash_table_lookup (watchers
, &key
);
187 if (w
->value
!= NULL
)
188 g_hash_table_remove (w
->value
->deps
, w
);
192 g_printerr ("unlink atl_last\n");
194 return DEPENDENT_NO_FLAG
;
197 static GnmFuncHelp
const help_atl_last
[] = {
198 { GNM_FUNC_HELP_NAME
, F_("ATL_LAST:sample real-time data source")},
199 { GNM_FUNC_HELP_ARG
, F_("tag:tag to watch")},
200 { GNM_FUNC_HELP_DESCRIPTION
, F_("ATL_LAST is a sample implementation of a real time data source. It takes a string tag and monitors the named pipe ~/atl for changes to the value of that tag.") },
201 { GNM_FUNC_HELP_NOTE
, F_("This is not intended to be generally enabled and is OFF by default.") },
202 { GNM_FUNC_HELP_END
}
205 GnmFuncDescriptor
const ATL_functions
[] = {
206 {"atl_last", "s", help_atl_last
, atl_last
, NULL
,
207 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
214 go_plugin_init (GOPlugin
*plugin
, GOCmdContext
*cc
)
216 GIOChannel
*channel
= NULL
;
218 GnmFunc
*atl_last
= gnm_func_lookup ("atl_last", NULL
);
220 g_signal_connect (atl_last
, "link-dep", G_CALLBACK (atl_last_link
), NULL
);
222 debug
= gnm_debug_flag ("datasource");
225 g_printerr (">>>>>>>>>>>>>>>>>>>>>>>>>>>> LOAD ATL\n");
226 g_return_if_fail (atl_fd
< 0);
228 filename
= g_build_filename (g_get_home_dir (), "atl", NULL
);
230 /* NOTE : better to use popen here, but this is fine for testing */
232 #warning "If gstdio.h had g_mkfifo, that's what we should use here"
233 if (mkfifo (filename
, S_IRUSR
| S_IWUSR
) == 0) {
234 atl_filename
= filename
;
235 atl_fd
= g_open (atl_filename
, O_RDWR
|O_NONBLOCK
, 0);
237 #endif /* HAVE_MKFIFO */
241 atl_file
= fdopen (atl_fd
, "rb");
242 channel
= g_io_channel_unix_new (atl_fd
);
243 atl_source
= g_io_add_watch (channel
,
244 G_IO_IN
| G_IO_ERR
| G_IO_HUP
| G_IO_NVAL
,
246 g_io_channel_unref (channel
);
248 watched_values
= g_hash_table_new (
249 (GHashFunc
) g_str_hash
,
250 (GEqualFunc
) g_str_equal
);
251 watchers
= g_hash_table_new (
252 (GHashFunc
) watcher_hash
,
253 (GEqualFunc
) watcher_equal
);
256 /* TODO : init and cleanup should be given CommandContexts
257 * to make things tidier
260 go_plugin_shutdown (GOPlugin
*plugin
, GOCmdContext
*cc
)
263 g_printerr ("UNLOAD ATL >>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
266 g_source_remove (atl_source
);
271 g_unlink (atl_filename
);
272 g_free (atl_filename
);
281 if (atl_file
!= NULL
) {
286 g_hash_table_destroy (watched_values
);
287 watched_values
= NULL
;
288 g_hash_table_destroy (watchers
);