be more helpful when raising rb_eTypeError in string_arr string_arr_new(VALUE rb_stri...
[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 <rrd.h>
8
9 typedef struct string_arr_t {
10     int       len;
11     char    **strings;
12 } string_arr;
13
14 VALUE     mRRD;
15 VALUE     rb_eRRDError;
16
17 typedef int (
18     *RRDFUNC) (
19     int argc,
20     char **argv);
21
22 #define RRD_CHECK_ERROR  \
23     if (rrd_test_error()) \
24       rb_raise(rb_eRRDError, rrd_get_error()); \
25     rrd_clear_error();
26
27 string_arr string_arr_new(
28     VALUE rb_strings)
29 {
30     string_arr a;
31     char      buf[64];
32     int       i;
33
34     Check_Type(rb_strings, T_ARRAY);
35     a.len = RARRAY(rb_strings)->len + 1;
36
37     a.strings = malloc(a.len * sizeof(char *));
38     a.strings[0] = "dummy"; /* first element is a dummy element */
39
40     for (i = 0; i < a.len - 1; i++) {
41         VALUE     v = rb_ary_entry(rb_strings, i);
42
43         switch (TYPE(v)) {
44         case T_STRING:
45             a.strings[i + 1] = strdup(STR2CSTR(v));
46             break;
47         case T_FIXNUM:
48             snprintf(buf, 63, "%d", FIX2INT(v));
49             a.strings[i + 1] = strdup(buf);
50             break;
51         default:
52             rb_raise(rb_eTypeError, "invalid argument - %s, expected T_STRING or T_FIXNUM on index %d", rb_class2name(CLASS_OF(v)), i);
53             break;
54         }
55     }
56
57     return a;
58 }
59
60 void string_arr_delete(
61     string_arr a)
62 {
63     int       i;
64
65     /* skip dummy first entry */
66     for (i = 1; i < a.len; i++) {
67         free(a.strings[i]);
68     }
69
70     free(a.strings);
71 }
72
73 void reset_rrd_state(
74     )
75 {
76     optind = 0;
77     opterr = 0;
78     rrd_clear_error();
79 }
80
81 VALUE rrd_call(
82     RRDFUNC func,
83     VALUE args)
84 {
85     string_arr a;
86
87     a = string_arr_new(args);
88     reset_rrd_state();
89     func(a.len, a.strings);
90     string_arr_delete(a);
91
92     RRD_CHECK_ERROR return Qnil;
93 }
94
95 VALUE rb_rrd_create(
96     VALUE self,
97     VALUE args)
98 {
99     return rrd_call(rrd_create, args);
100 }
101
102 VALUE rb_rrd_dump(
103     VALUE self,
104     VALUE args)
105 {
106     return rrd_call(rrd_dump, args);
107 }
108
109 VALUE rb_rrd_fetch(
110     VALUE self,
111     VALUE args)
112 {
113     string_arr a;
114     unsigned long i, j, k, step, ds_cnt;
115     rrd_value_t *raw_data;
116     char    **raw_names;
117     VALUE     data, names, result;
118     time_t    start, end;
119
120     a = string_arr_new(args);
121     reset_rrd_state();
122     rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names,
123               &raw_data);
124     string_arr_delete(a);
125
126     RRD_CHECK_ERROR names = rb_ary_new();
127
128     for (i = 0; i < ds_cnt; i++) {
129         rb_ary_push(names, rb_str_new2(raw_names[i]));
130         free(raw_names[i]);
131     }
132     free(raw_names);
133
134     k = 0;
135     data = rb_ary_new();
136     for (i = start; i <= end; i += step) {
137         VALUE     line = rb_ary_new2(ds_cnt);
138
139         for (j = 0; j < ds_cnt; j++) {
140             rb_ary_store(line, j, rb_float_new(raw_data[k]));
141             k++;
142         }
143         rb_ary_push(data, line);
144     }
145     free(raw_data);
146
147     result = rb_ary_new2(4);
148     rb_ary_store(result, 0, INT2FIX(start));
149     rb_ary_store(result, 1, INT2FIX(end));
150     rb_ary_store(result, 2, names);
151     rb_ary_store(result, 2, data);
152     return result;
153 }
154
155 VALUE rb_rrd_graph(
156     VALUE self,
157     VALUE args)
158 {
159     string_arr a;
160     char    **calcpr, **p;
161     VALUE     result, print_results;
162     int       xsize, ysize;
163     double    ymin, ymax;
164
165     a = string_arr_new(args);
166     reset_rrd_state();
167     rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
168     string_arr_delete(a);
169
170     RRD_CHECK_ERROR result = rb_ary_new2(3);
171
172     print_results = rb_ary_new();
173     p = calcpr;
174     for (p = calcpr; p && *p; p++) {
175         rb_ary_push(print_results, rb_str_new2(*p));
176         free(*p);
177     }
178     free(calcpr);
179     rb_ary_store(result, 0, print_results);
180     rb_ary_store(result, 1, INT2FIX(xsize));
181     rb_ary_store(result, 2, INT2FIX(ysize));
182     return result;
183 }
184
185 /*
186 VALUE rb_rrd_info(VALUE self, VALUE args)
187 {
188     string_arr a;
189     info_t *p;
190     VALUE result;
191
192     a = string_arr_new(args);
193     data = rrd_info(a.len, a.strings);
194     string_arr_delete(a);
195
196     RRD_CHECK_ERROR
197
198     result = rb_hash_new();
199     while (data) {
200         VALUE key = rb_str_new2(data->key);
201         switch (data->type) {
202         case RD_I_VAL:
203             if (isnan(data->u_val)) {
204                 rb_hash_aset(result, key, Qnil);
205             }
206             else {
207                 rb_hash_aset(result, key, rb_float_new(data->u_val));
208             }
209             break;
210         case RD_I_CNT:
211             rb_hash_aset(result, key, INT2FIX(data->u_cnt));
212             break;
213         case RD_I_STR:
214             rb_hash_aset(result, key, rb_str_new2(data->u_str));
215             free(data->u_str);
216             break;
217         }
218         p = data;
219         data = data->next;
220         free(p);
221     }
222     return result;
223 }
224 */
225
226 VALUE rb_rrd_last(
227     VALUE self,
228     VALUE args)
229 {
230     string_arr a;
231     time_t    last;
232
233     a = string_arr_new(args);
234     reset_rrd_state();
235     last = rrd_last(a.len, a.strings);
236     string_arr_delete(a);
237
238     RRD_CHECK_ERROR
239         return rb_funcall(rb_cTime, rb_intern("at"), 1, INT2FIX(last));
240 }
241
242 VALUE rb_rrd_resize(
243     VALUE self,
244     VALUE args)
245 {
246     return rrd_call(rrd_resize, args);
247 }
248
249 VALUE rb_rrd_restore(
250     VALUE self,
251     VALUE args)
252 {
253     return rrd_call(rrd_restore, args);
254 }
255
256 VALUE rb_rrd_tune(
257     VALUE self,
258     VALUE args)
259 {
260     return rrd_call(rrd_tune, args);
261 }
262
263 VALUE rb_rrd_update(
264     VALUE self,
265     VALUE args)
266 {
267     return rrd_call(rrd_update, args);
268 }
269
270 void Init_RRD(
271     )
272 {
273     mRRD = rb_define_module("RRD");
274     rb_eRRDError = rb_define_class("RRDError", rb_eStandardError);
275
276     rb_define_module_function(mRRD, "create", rb_rrd_create, -2);
277     rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2);
278     rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2);
279     rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2);
280     rb_define_module_function(mRRD, "last", rb_rrd_last, -2);
281     rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2);
282     rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2);
283     rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2);
284     rb_define_module_function(mRRD, "update", rb_rrd_update, -2);
285 }