fix ruby bindings to be compatible with curent ruby implementations fix for #279
[rrdtool.git] / bindings / ruby / main.c
1 /* $Id$
2  * Substantial penalty for early withdrawal.
3  */
4
5 #include <unistd.h>
6 #include <ruby.h>
7 #include <math.h>
8 #include "../../src/rrd_tool.h"
9
10 typedef struct string_arr_t {
11     int       len;
12     char    **strings;
13 } string_arr;
14
15 VALUE     mRRD;
16 VALUE     rb_eRRDError;
17
18 typedef int (
19     *RRDFUNC) (
20     int argc,
21     char **argv);
22
23 typedef rrd_info_t *(
24     *RRDINFOFUNC) (
25     int argc,
26     char **argv);
27
28 #define RRD_CHECK_ERROR  \
29     if (rrd_test_error()) \
30       rb_raise(rb_eRRDError, rrd_get_error()); \
31     rrd_clear_error();
32
33 string_arr string_arr_new(
34     VALUE rb_strings)
35 {
36     string_arr a;
37     char      buf[64];
38     int       i;
39
40     Check_Type(rb_strings, T_ARRAY);
41     a.len = RARRAY_LEN(rb_strings) + 1;
42
43     a.strings = malloc(a.len * sizeof(char *));
44     a.strings[0] = "dummy"; /* first element is a dummy element */
45
46     for (i = 0; i < a.len - 1; i++) {
47         VALUE     v = rb_ary_entry(rb_strings, i);
48
49         switch (TYPE(v)) {
50         case T_STRING:
51             a.strings[i + 1] = strdup(StringValuePtr(v));
52             break;
53         case T_FIXNUM:
54             snprintf(buf, 63, "%d", FIX2INT(v));
55             a.strings[i + 1] = strdup(buf);
56             break;
57         default:
58             rb_raise(rb_eTypeError,
59                      "invalid argument - %s, expected T_STRING or T_FIXNUM on index %d",
60                      rb_class2name(CLASS_OF(v)), i);
61             break;
62         }
63     }
64
65     return a;
66 }
67
68 void string_arr_delete(
69     string_arr a)
70 {
71     int       i;
72
73     /* skip dummy first entry */
74     for (i = 1; i < a.len; i++) {
75         free(a.strings[i]);
76     }
77
78     free(a.strings);
79 }
80
81 void reset_rrd_state(
82     )
83 {
84     optind = 0;
85     opterr = 0;
86     rrd_clear_error();
87 }
88
89 /* Simple Calls */
90
91 VALUE rrd_call(
92     RRDFUNC func,
93     VALUE args)
94 {
95     string_arr a;
96
97     a = string_arr_new(args);
98     reset_rrd_state();
99     func(a.len, a.strings);
100     string_arr_delete(a);
101
102     RRD_CHECK_ERROR return Qnil;
103 }
104
105 VALUE rb_rrd_create(
106     VALUE self,
107     VALUE args)
108 {
109     return rrd_call(rrd_create, args);
110 }
111
112 VALUE rb_rrd_dump(
113     VALUE self,
114     VALUE args)
115 {
116     return rrd_call(rrd_dump, args);
117 }
118
119 VALUE rb_rrd_resize(
120     VALUE self,
121     VALUE args)
122 {
123     return rrd_call(rrd_resize, args);
124 }
125
126 VALUE rb_rrd_restore(
127     VALUE self,
128     VALUE args)
129 {
130     return rrd_call(rrd_restore, args);
131 }
132
133 VALUE rb_rrd_tune(
134     VALUE self,
135     VALUE args)
136 {
137     return rrd_call(rrd_tune, args);
138 }
139
140 VALUE rb_rrd_update(
141     VALUE self,
142     VALUE args)
143 {
144     return rrd_call(rrd_update, args);
145 }
146
147 VALUE rb_rrd_flushcached(
148     VALUE self,
149     VALUE args)
150 {
151     return rrd_call(rrd_flushcached, args);
152 }
153
154
155 /* Calls Returning Data via the Info Interface */
156
157 VALUE rb_rrd_infocall(
158     RRDINFOFUNC func,
159     VALUE args)
160 {
161     string_arr a;
162     rrd_info_t *p, *data;
163     VALUE     result;
164
165     a = string_arr_new(args);
166     reset_rrd_state();
167     data = func(a.len, a.strings);
168     string_arr_delete(a);
169
170     RRD_CHECK_ERROR result = rb_hash_new();
171
172     p = data;
173     while (data) {
174         VALUE     key = rb_str_new2(data->key);
175
176         switch (data->type) {
177         case RD_I_VAL:
178             if (isnan(data->value.u_val)) {
179                 rb_hash_aset(result, key, Qnil);
180             } else {
181                 rb_hash_aset(result, key, rb_float_new(data->value.u_val));
182             }
183             break;
184         case RD_I_CNT:
185             rb_hash_aset(result, key, INT2FIX(data->value.u_cnt));
186             break;
187         case RD_I_STR:
188             rb_hash_aset(result, key, rb_str_new2(data->value.u_str));
189             break;
190         case RD_I_INT:
191             rb_hash_aset(result, key, INT2FIX(data->value.u_int));
192             break;
193         case RD_I_BLO:
194             rb_hash_aset(result, key,
195                          rb_str_new((char *)data->value.u_blo.ptr,
196                                     data->value.u_blo.size));
197             break;
198         }
199         data = data->next;
200     }
201     rrd_info_free(p);
202     return result;
203 }
204
205 VALUE rb_rrd_info(
206     VALUE self,
207     VALUE args)
208 {
209     return rb_rrd_infocall(rrd_info, args);
210 }
211
212 VALUE rb_rrd_updatev(
213     VALUE self,
214     VALUE args)
215 {
216     return rb_rrd_infocall(rrd_update_v, args);
217 }
218
219 VALUE rb_rrd_graphv(
220     VALUE self,
221     VALUE args)
222 {
223     return rb_rrd_infocall(rrd_graph_v, args);
224 }
225
226
227 /* Other Calls */
228
229 VALUE rb_rrd_fetch(
230     VALUE self,
231     VALUE args)
232 {
233     string_arr a;
234     unsigned long i, j, k, step, ds_cnt;
235     rrd_value_t *raw_data;
236     char    **raw_names;
237     VALUE     data, names, result;
238     time_t    start, end;
239
240     a = string_arr_new(args);
241     reset_rrd_state();
242     rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names,
243               &raw_data);
244     string_arr_delete(a);
245
246     RRD_CHECK_ERROR names = rb_ary_new();
247
248     for (i = 0; i < ds_cnt; i++) {
249         rb_ary_push(names, rb_str_new2(raw_names[i]));
250         rrd_freemem(raw_names[i]);
251     }
252     rrd_freemem(raw_names);
253
254     k = 0;
255     data = rb_ary_new();
256     for (i = start; i <= end; i += step) {
257         VALUE     line = rb_ary_new2(ds_cnt);
258
259         for (j = 0; j < ds_cnt; j++) {
260             rb_ary_store(line, j, rb_float_new(raw_data[k]));
261             k++;
262         }
263         rb_ary_push(data, line);
264     }
265     rrd_freemem(raw_data);
266
267     result = rb_ary_new2(5);
268     rb_ary_store(result, 0, INT2NUM(start));
269     rb_ary_store(result, 1, INT2NUM(end));
270     rb_ary_store(result, 2, names);
271     rb_ary_store(result, 3, data);
272     rb_ary_store(result, 4, INT2FIX(step));
273     return result;
274 }
275
276 VALUE rb_rrd_graph(
277     VALUE self,
278     VALUE args)
279 {
280     string_arr a;
281     char    **calcpr, **p;
282     VALUE     result, print_results;
283     int       xsize, ysize;
284     double    ymin, ymax;
285
286     a = string_arr_new(args);
287     reset_rrd_state();
288     rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
289     string_arr_delete(a);
290
291     RRD_CHECK_ERROR result = rb_ary_new2(3);
292
293     print_results = rb_ary_new();
294     p = calcpr;
295     for (p = calcpr; p && *p; p++) {
296         rb_ary_push(print_results, rb_str_new2(*p));
297         rrd_freemem(*p);
298     }
299     rrd_freemem(calcpr);
300     rb_ary_store(result, 0, print_results);
301     rb_ary_store(result, 1, INT2FIX(xsize));
302     rb_ary_store(result, 2, INT2FIX(ysize));
303     return result;
304 }
305
306
307 VALUE rb_rrd_last(
308     VALUE self,
309     VALUE args)
310 {
311     string_arr a;
312     time_t    last;
313
314     a = string_arr_new(args);
315     reset_rrd_state();
316     last = rrd_last(a.len, a.strings);
317     string_arr_delete(a);
318
319     RRD_CHECK_ERROR
320         return rb_funcall(rb_cTime, rb_intern("at"), 1, UINT2NUM(last));
321 }
322
323 VALUE rb_rrd_xport(
324     VALUE self,
325     VALUE args)
326 {
327     string_arr a;
328     unsigned long i, j, k, step, col_cnt;
329     int xxsize;
330     rrd_value_t *data;
331     char **legend_v;
332     VALUE legend, result, rdata;
333     time_t start, end;
334
335     a = string_arr_new(args);
336     rrd_xport(a.len, a.strings, &xxsize, &start, &end, &step, &col_cnt, &legend_v, &data);
337     string_arr_delete(a);
338
339     RRD_CHECK_ERROR;
340             
341     legend = rb_ary_new();
342     for (i = 0; i < col_cnt; i++) {
343         rb_ary_push(legend, rb_str_new2(legend_v[i]));
344         free(legend_v[i]);
345     }
346     free(legend_v);
347
348     k = 0;
349     rdata = rb_ary_new();
350     for (i = start; i <= end; i += step) {
351         VALUE line = rb_ary_new2(col_cnt);
352         for (j = 0; j < col_cnt; j++) {
353             rb_ary_store(line, j, rb_float_new(data[k]));
354             k++;
355         }
356         rb_ary_push(rdata, line);
357     }
358     free(data);
359
360     result = rb_ary_new2(6);
361     rb_ary_store(result, 0, INT2FIX(start));
362     rb_ary_store(result, 1, INT2FIX(end));
363     rb_ary_store(result, 2, INT2FIX(step));
364     rb_ary_store(result, 3, INT2FIX(col_cnt));
365     rb_ary_store(result, 4, legend);
366     rb_ary_store(result, 5, rdata);
367     return result;
368 }
369
370 void Init_RRD(
371     )
372 {
373     mRRD = rb_define_module("RRD");
374     rb_eRRDError = rb_define_class("RRDError", rb_eStandardError);
375
376     rb_define_module_function(mRRD, "create", rb_rrd_create, -2);
377     rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2);
378     rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2);
379     rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2);
380     rb_define_module_function(mRRD, "last", rb_rrd_last, -2);
381     rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2);
382     rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2);
383     rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2);
384     rb_define_module_function(mRRD, "update", rb_rrd_update, -2);
385     rb_define_module_function(mRRD, "flushcached", rb_rrd_flushcached, -2);
386     rb_define_module_function(mRRD, "info", rb_rrd_info, -2);
387     rb_define_module_function(mRRD, "updatev", rb_rrd_updatev, -2);
388     rb_define_module_function(mRRD, "graphv", rb_rrd_graphv, -2);
389     rb_define_module_function(mRRD, "xport", rb_rrd_xport, -2);
390 }