new command rrdtool xport integrated
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Tue, 7 May 2002 21:58:33 +0000 (21:58 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Tue, 7 May 2002 21:58:33 +0000 (21:58 +0000)
--  Wolfgang Schrimm <Wolfgang.Schrimm@urz.uni-heidelberg.de>

git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@135 a5681a0c-68f1-0310-ab6d-d61299d08faa

13 files changed:
CONTRIBUTORS
bindings/perl-shared/RRDs.xs
doc/Makefile.am
doc/rrdtool.pod
doc/rrdxport.pod [new file with mode: 0644]
examples/shared-demo.pl.in
src/Makefile.am
src/rrd.h
src/rrd_graph.c
src/rrd_graph.h
src/rrd_tool.c
src/rrd_xport.c [new file with mode: 0644]
src/rrd_xport.h [new file with mode: 0644]

index fd86627..7e6f1ac 100644 (file)
@@ -54,7 +54,7 @@ Debugging and code contributions
        Bruce Campbell <bruce.campbell@apnic.net>
        Sean Summers <sean@Fenstermaker.com> (RPM .spec)
        Christophe Van Ginneken <Christophe.VanGinneken@ubizen.com> (--no-legend)
-
+       Wolfgang Schrimm <wschrimm@uni-hd.de> xport function
 
 Documentation
 
index 4d871e9..a0001f7 100644 (file)
@@ -204,7 +204,7 @@ rrd_fetch(...)
                /* convert the data array into perl format */
                datai=data;
                retar=newAV();
-               for (i = start; i <= end; i += step){
+               for (i = start+step; i <= end; i += step){
                        line = newAV();
                        for (ii = 0; ii < ds_cnt; ii++){
                          av_push(line,(isnan(*datai) ? &PL_sv_undef : newSVnv(*datai)));
@@ -214,12 +214,71 @@ rrd_fetch(...)
                }
                free(data);
                EXTEND(sp,5);
-               PUSHs(sv_2mortal(newSViv(start)));
+               PUSHs(sv_2mortal(newSViv(start+step)));
                PUSHs(sv_2mortal(newSViv(step)));
                PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
                PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
 
 
+int
+rrd_xport(...)
+       PROTOTYPE: @    
+       PREINIT:
+                time_t start,end;              
+                int xsize;
+               unsigned long step, col_cnt,row_cnt,i,ii;
+               rrd_value_t *data,*ptr;
+                char **argv,**legend_v;
+               AV *retar,*line,*names;
+       PPCODE:
+               argv = (char **) malloc((items+1)*sizeof(char *));
+               argv[0] = "dummy";
+               for (i = 0; i < items; i++) { 
+                   STRLEN len;
+                   char *handle = SvPV(ST(i),len);
+                   /* actually copy the data to make sure possible modifications
+                      on the argv data does not backfire into perl */ 
+                   argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+                   strcpy(argv[i+1],handle);
+               }
+               optind=0; opterr=0; 
+               rrd_clear_error();
+               rrd_xport(items+1,argv,&xsize,&start,&end,&step,&col_cnt,&legend_v,&data); 
+               for (i=0; i < items; i++) {
+                   free(argv[i+1]);
+               }
+               free(argv);
+               if (rrd_test_error()) XSRETURN_UNDEF;
+
+                /* convert the legend_v into perl format */
+               names=newAV();
+               for (ii = 0; ii < col_cnt; ii++){
+                   av_push(names,newSVpv(legend_v[ii],0));
+                   free(legend_v[ii]);
+               }
+               free(legend_v);                 
+
+               /* convert the data array into perl format */
+               ptr=data;
+               retar=newAV();
+               for (i = start+step; i <= end; i += step){
+                       line = newAV();
+                       for (ii = 0; ii < col_cnt; ii++){
+                         av_push(line,(isnan(*ptr) ? &PL_sv_undef : newSVnv(*ptr)));
+                         ptr++;
+                       }
+                       av_push(retar,newRV_noinc((SV*)line));
+               }
+               free(data);
+
+               EXTEND(sp,7);
+               PUSHs(sv_2mortal(newSViv(start+step)));
+               PUSHs(sv_2mortal(newSViv(end)));
+               PUSHs(sv_2mortal(newSViv(step)));
+               PUSHs(sv_2mortal(newSViv(col_cnt)));
+               PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
+               PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+
 SV*
 rrd_info(...)
        PROTOTYPE: @    
index 824fad9..3022e64 100644 (file)
@@ -14,7 +14,7 @@ SRC = rrdgraph.src rrdgraph_examples.src  rrdgraph_rpn.src \
 POD = rrdtool.pod rrdlast.pod rrdcreate.pod rrdupdate.pod  rrdtutorial.es.pod \
        cdeftutorial.pod rpntutorial.pod rrdgraph-old.pod  bin_dec_hex.pod \
        rrdfetch.pod rrdrestore.pod rrddump.pod rrdtune.pod rrdresize.pod \
-       rrdcgi.pod rrdtutorial.pod rrdinfo.pod $(SRC:.src=.pod)
+       rrdxport.pod rrdcgi.pod rrdtutorial.pod rrdinfo.pod $(SRC:.src=.pod)
 
 PMP = RRDs.pm RRDp.pm
 
index fee6d35..7233592 100644 (file)
@@ -82,6 +82,10 @@ Find last update time of an RRD. Check L<rrdlast>.
 
 Change the size of individual RRAs ... Dangerous! Check L<rrdresize>.
 
+=item B<xport>
+
+Export data retrieved from one or several RRD. Check L<rrdxport>
+
 =item B<rrdcgi>
 
 This is a standalone tool for producing rrd graphs on the fly. Check
@@ -245,7 +249,7 @@ will not abort if possible, but follow the ERROR line with an OK line.
 
 =head1 SEE ALSO
 
-rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport
 
 =head1 BUGS
 
diff --git a/doc/rrdxport.pod b/doc/rrdxport.pod
new file mode 100644 (file)
index 0000000..fcaaa0d
--- /dev/null
@@ -0,0 +1,140 @@
+=head1 NAME
+
+rrdtool xport - Export data in XML format based on data from one or several RRD
+
+=for html <div align="right"><a href="rrdxport.pdf">PDF</a> version.</div> 
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<xport> 
+S<[B<-s>|B<--start> I<seconds>]> 
+S<[B<-e>|B<--end> I<seconds>]>
+S<[B<-m>|B<--maxrows> I<rows>]>
+S<[B<--step> I<value>]>
+S<[B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>]>
+S<[B<CDEF:>I<vname>B<=>I<rpn-expression>]>
+S<[B<XPORT>B<:>I<vname>[B<:>I<legend>]]>
+
+=head1 DESCRIPTION
+
+The B<xport> functions main purpose is to write XML formatted
+representation of the data stored in one or several B<RRD>s. It
+can also extract numerical reports. 
+
+If no I<XPORT> statements are found, there will be no output.
+
+=over
+
+=item B<-s>|B<--start> I<seconds> (default end-1day)
+
+The time when the exported range should begin. Time in seconds since
+epoch (1970-01-01) is required. Negative numbers are relative to the
+current time. By default one day worth of data will be printed.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation on how to specify time.
+
+=item B<-e>|B<--end> I<seconds> (default now)
+
+The time when the exported range should end. Time in seconds since epoch.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation of ways to specify time.
+
+=item B<-m>|B<--maxrows> I<rows> (default 400 rows)
+
+This works like the B<-w>|B<--width> parameter of I<rrdgraph>.
+In fact it is exactly the same, but the parameter was renamed to
+describe its purpose in this module. See I<rrdgraph> documentation
+for details.
+
+=item B<--step> I<value> (default automatic)
+
+See I<rrdgraph> documentation.
+
+=item B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>
+
+See I<rrdgraph> documentation.
+
+=item B<CDEF:>I<vname>B<=>I<rpn-expression>
+
+See I<rrdgraph> documentation.
+
+=item B<XPORT:>I<vname>B<:>B<:>I<legend>
+
+At least one I<XPORT> statement should be present. The values
+referenced by I<vname> are printed. Optionally add a legend.
+
+=over
+
+=head1 Output format
+
+The output is enclosed in a B<xport> element and contains two
+blocks. The first block is enclosed by a B<meta> element and
+contains some meta data. The second block is enclosed by a 
+B<data> element and contains the data rows.
+
+Let's assume that the I<xport> command looks like this:
+
+  rrdtool xport \
+          --start now-1h --end now \
+          DEF:xx=host-inout.lo.rrd:output:AVERAGE \
+         DEF:yy=host-inout.lo.rrd:input:AVERAGE \
+          CDEF:aa=xx,yy,+,8,* \
+          XPORT:xx:"out bytes" \
+          XPORT:aa:"in and out bits"
+
+The resulting meta data section (the values will depend on the
+RRD characteristics):
+  <meta>
+    <start>1020611700</start>
+    <step>300</step>
+    <end>1020615600</end>
+    <rows>14</rows>
+    <columns>2</columns>
+    <legend>
+      <entry>out bytes</entry>
+      <entry>in and out bits</entry>
+    </legend>
+  </meta>
+  
+The resulting data section:
+
+  <data>
+    <row><t>1020611700</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020612000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020612300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020612600</t><v>3.4113333333e+00</v><v>5.4581333333e+01</v></row>
+    <row><t>1020612900</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020613200</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020613500</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020613800</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020614100</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020614400</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020614700</t><v>3.7333333333e+00</v><v>5.9733333333e+01</v></row>
+    <row><t>1020615000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020615300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+    <row><t>1020615600</t><v>NaN</v><v>NaN</v></row>
+  </data>
+
+
+=head1 EXAMPLE 1
+
+  rrdtool xport \
+          DEF:out=if1-inouts.rrd:outoctets:AVERAGE \
+          XPORT:out:"out bytes"
+
+=head1 EXAMPLE 2
+
+  rrdtool xport \
+          DEF:out1=if1-inouts.rrd:outoctets:AVERAGE \
+          DEF:out2=if2-inouts.rrd:outoctets:AVERAGE \
+         CDEF:sum=out1,out2,+ \
+          XPORT:out1:"if1 out bytes" \
+          XPORT:out2:"if2 out bytes" \
+         XPORT:sum:"output sum"
+
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>oetiker@ee.ethz.chE<gt>
+
index 61df205..596fb06 100755 (executable)
@@ -173,3 +173,46 @@ foreach my $line (@$array){
 
 
 
+my ($start,$end,$step,$col_cnt,$legend,$data) = 
+  RRDs::xport ("-m", 400,
+              "--start", "now-1day",
+              "--end", "now",
+              "DEF:alpha=$RRD1:a:AVERAGE",
+              "DEF:beta=$RRD1:d:AVERAGE",
+              "CDEF:calc=alpha,beta,+,2,/,100,*,102,/",
+              "XPORT:alpha:original ds",
+              "XPORT:calc:calculated values",
+              );
+
+my $ERROR = RRDs::error;
+die "$0: unable to xport: $ERROR\n" if $ERROR;
+
+print "\nrrdxport test:\n\n";
+print "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n";
+print "<xport>\n";
+print "  <meta>\n";
+print "    <start>$start</start>\n";
+print "    <step>$step</step>\n";
+print "    <end>$end</end>\n";
+print "    <rows>", $#$data + 1, "</rows>\n";
+print "    <columns>$col_cnt</columns>\n";
+print "    <legend>\n";
+foreach my $entry (@$legend) {
+    print "      <entry>$entry</entry>\n";
+}
+print "    </legend>\n";
+print "  </meta>\n";
+print "  <data>\n";
+my $row_counter = 0;
+foreach my $row (@$data) {
+    $row_counter++;
+    print "    <row id=\"$row_counter\"><t is=\"", scalar localtime($start), "\">$start</t>";
+    $start += $step;
+    foreach my $val (@$row) {
+       printf ("<v>%1.10e</v>",$val) if $val ne '';
+       print "<v>NaN</v>" if  $val eq '';
+    }
+    print "</row>\n";
+}
+print "  </data>\n";
+print "</xport>\n";
index 5f8d27a..9e64c24 100644 (file)
@@ -62,9 +62,11 @@ RRD_C_FILES =                \
        rrd_rpncalc.c \
        rrd_tune.c      \
        rrd_update.c    \
+       rrd_xport.c     \
        rrd_gfx.c rrd_gfx.h \
        rrd_afm.c rrd_afm_data.c \
-       getopt.h ntconfig.h parsetime.h rrd_format.h rrd_tool.h rrd.h rrd_hw.h rrd_rpncalc.h
+       getopt.h ntconfig.h parsetime.h \
+       rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_hw.h rrd_rpncalc.h
 
 # Build two libraries.  One is a public one that gets installed in
 # $prefix/lib.  Libtool does not create an archive of the PIC compiled
index a327a1b..8172c7f 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -5,8 +5,12 @@
  *****************************************************************************
  * $Id$
  * $Log$
- * Revision 1.1  2001/02/25 22:25:05  oetiker
- * Initial revision
+ * Revision 1.2  2002/05/07 21:58:32  oetiker
+ * new command rrdtool xport integrated
+ * --  Wolfgang Schrimm <Wolfgang.Schrimm@urz.uni-heidelberg.de>
+ *
+ * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
+ * checkin
  *
  *****************************************************************************/
 #ifdef  __cplusplus
@@ -34,6 +38,9 @@ int    rrd_dump(int, char **);
 int    rrd_tune(int, char **);
 time_t rrd_last(int, char **);
 int    rrd_resize(int, char **);
+int    rrd_xport(int, char **, int *, time_t *, time_t *,
+                unsigned long *, unsigned long *,
+                char ***, rrd_value_t **);
 
 /* Transplanted from parsetime.h */
 typedef enum {
index 17ec03a..a73dbbc 100644 (file)
@@ -186,6 +186,7 @@ enum gf_en gf_conv(char *string){
     conv_if(CDEF,GF_CDEF)
     conv_if(VDEF,GF_VDEF)
     conv_if(PART,GF_PART)
+    conv_if(XPORT,GF_XPORT)
     
     return (-1);
 }
@@ -814,6 +815,8 @@ data_calc( image_desc_t *im){
         * so CDEFs can use VDEFs and vice versa
         */
        switch (im->gdes[gdi].gf) {
+           case GF_XPORT:
+             break;
            case GF_VDEF:
                /* A VDEF has no DS.  This also signals other parts
                 * of rrdtool that this is a VDEF value, not a CDEF.
@@ -1030,6 +1033,7 @@ data_proc( image_desc_t *im ){
            case GF_CDEF:
            case GF_VDEF:
            case GF_PART:
+           case GF_XPORT:
                break;
            }
        }
@@ -1311,6 +1315,7 @@ print_calc(image_desc_t *im, char ***prdata)
        case GF_CDEF:       
        case GF_VDEF:       
        case GF_PART:
+       case GF_XPORT:
            break;
        }
     }
@@ -2274,6 +2279,7 @@ graph_paint(image_desc_t *im, char ***calcpr)
     case GF_COMMENT:
     case GF_HRULE:
     case GF_VRULE:
+    case GF_XPORT:
       break;
     case GF_TICK:
       for (ii = 0; ii < im->xsize; ii++)
@@ -2999,6 +3005,8 @@ rrd_graph_script(int argc, char *argv[], image_desc_t *im)
 
        /* If the error string is set, we exit at the end of the switch */
        switch (gdp->gf) {
+           case GF_XPORT:
+               break;
            case GF_COMMENT:
                if (rrd_graph_legend(gdp,&line[argstart])==0)
                    rrd_set_error("Cannot parse comment in line: %s",line);
index e61d6b0..1f20a40 100644 (file)
@@ -23,7 +23,7 @@ enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
 enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE,
            GF_AREA,GF_STACK,GF_TICK,
            GF_DEF, GF_CDEF, GF_VDEF,
-           GF_PART};
+           GF_PART, GF_XPORT};
 
 enum vdef_op_en {
                 VDEF_MAXIMUM   /* like the MAX in (G)PRINT */
index a86e0ed..9a1f795 100644 (file)
@@ -5,6 +5,7 @@
  *****************************************************************************/
 
 #include "rrd_tool.h"
+#include "rrd_xport.h"
 
 void PrintUsage(char *cmd);
 int CountArgs(char *aLine);
@@ -24,7 +25,7 @@ void PrintUsage(char *cmd)
 
     char help_list[] =
           "Valid commands: create, update, graph, dump, restore,\n"
-          "\t\tlast, info, fetch, tune, resize\n\n";
+          "\t\tlast, info, fetch, tune, resize, xport\n\n";
 
     char help_create[] =
           "* create - create a new RRD\n\n"
@@ -113,6 +114,15 @@ void PrintUsage(char *cmd)
           " * resize - alter the lenght of one of the RRAs in an RRD\n\n"
           "\trrdtool resize filename rranum GROW|SHRINK rows\n\n";
 
+    char help_xport[] =
+          "* xport - generate XML dump from one or several RRD\n\n"
+          "\trrdtool xport [-s|--start seconds] [-e|--end seconds]\n"
+          "\t\t[-m|--maxrows rows]\n"
+          "\t\t[--step seconds]\n"        
+          "\t\t[DEF:vname=rrd:ds-name:CF]\n"
+          "\t\t[CDEF:vname=rpn-expression]\n"
+           "\t\t[XPORT:vname:legend]\n\n";
+
     char help_lic[] =
           "RRDtool is distributed under the Terms of the GNU General\n"
           "Public License Version 2. (www.gnu.org/copyleft/gpl.html)\n\n"
@@ -120,7 +130,7 @@ void PrintUsage(char *cmd)
           "For more information read the RRD manpages\n\n";
 
     enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_RESTORE, C_LAST,
-          C_UPDATE, C_FETCH, C_GRAPH, C_TUNE, C_RESIZE };
+          C_UPDATE, C_FETCH, C_GRAPH, C_TUNE, C_RESIZE, C_XPORT };
 
     int help_cmd = C_NONE;
 
@@ -146,6 +156,8 @@ void PrintUsage(char *cmd)
                help_cmd = C_TUNE;
            else if (!strcmp(cmd,"resize"))
                help_cmd = C_RESIZE;
+           else if (!strcmp(cmd,"xport"))
+               help_cmd = C_XPORT;
        }
     fputs(help_main, stdout);
     switch (help_cmd)
@@ -183,6 +195,9 @@ void PrintUsage(char *cmd)
            case C_RESIZE:
                fputs(help_resize, stdout);
                break;
+           case C_XPORT:
+               fputs(help_xport, stdout);
+               break;
        }
     fputs(help_lic, stdout);
 }
@@ -347,6 +362,54 @@ int HandleInputLine(int argc, char **argv, FILE* out)
            free(ds_namv);
            free (data);
        }
+    } else if (strcmp("xport", argv[1]) == 0) {
+       int xxsize;
+       int i = 0, j = 0;
+       time_t        start,end;
+       unsigned long step, col_cnt,row_cnt;
+       rrd_value_t   *data,*ptr;
+       char          **legend_v;
+       if(rrd_xport(argc-1, &argv[1], &xxsize,&start,&end,&step,&col_cnt,&legend_v,&data) != -1) {
+         row_cnt = (end-start)/step;
+         ptr = data;
+         printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n", XML_ENCODING);
+         printf("<%s>\n", ROOT_TAG);
+         printf("  <%s>\n", META_TAG);
+         printf("    <%s>%lu</%s>\n", META_START_TAG, start+step, META_START_TAG);
+         printf("    <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
+         printf("    <%s>%lu</%s>\n", META_END_TAG, end, META_END_TAG);
+         printf("    <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt, META_ROWS_TAG);
+         printf("    <%s>%lu</%s>\n", META_COLS_TAG, col_cnt, META_COLS_TAG);
+         printf("    <%s>\n", LEGEND_TAG);
+         for (j = 0; j < col_cnt; j++) {
+           char *entry = NULL;
+           entry = legend_v[j];
+           printf("      <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry, LEGEND_ENTRY_TAG);
+           free(entry);
+         }
+         free(legend_v);
+         printf("    </%s>\n", LEGEND_TAG);
+         printf("  </%s>\n", META_TAG);
+         printf("  <%s>\n", DATA_TAG);
+         for (i = start+step; i <= end; i += step) {
+           printf ("    <%s>", DATA_ROW_TAG);
+           printf ("<%s>%lu</%s>", COL_TIME_TAG, i, COL_TIME_TAG);
+           for (j = 0; j < col_cnt; j++) {
+             rrd_value_t newval = DNAN;
+             newval = *ptr;
+             if(isnan(newval)){
+               printf("<%s>NaN</%s>", COL_DATA_TAG, COL_DATA_TAG);
+             } else {
+               printf("<%s>%0.10e</%s>", COL_DATA_TAG, newval, COL_DATA_TAG);
+             };
+             ptr++;
+           }
+           printf("</%s>\n", DATA_ROW_TAG);
+         }
+         free(data);
+         printf("  </%s>\n", DATA_TAG);
+         printf("</%s>\n", ROOT_TAG);
+       }
     }
     else if (strcmp("graph", argv[1]) == 0) {
        char **calcpr;
diff --git a/src/rrd_xport.c b/src/rrd_xport.c
new file mode 100644 (file)
index 0000000..b943fe2
--- /dev/null
@@ -0,0 +1,425 @@
+/****************************************************************************
+ * RRDtool 1.0.37  Copyright Tobias Oetiker, 1997 - 2000
+ ****************************************************************************
+ * rrd_xport.c  export RRD data 
+ ****************************************************************************/
+
+#include <sys/stat.h>
+
+#include "rrd_tool.h"
+#include "rrd_graph.h"
+#include "rrd_xport.h"
+
+#ifdef WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+
+int rrd_xport(int, char **, int *,
+             time_t *, time_t *,
+             unsigned long *, unsigned long *,
+             char ***, rrd_value_t **);
+
+int rrd_xport_fn(image_desc_t *,
+                time_t *, time_t *,
+                unsigned long *, unsigned long *,
+                char ***, rrd_value_t **);
+
+
+
+
+int 
+rrd_xport(int argc, char **argv, int *xsize,
+         time_t         *start,
+         time_t         *end,        /* which time frame do you want ?
+                                      * will be changed to represent reality */
+         unsigned long  *step,       /* which stepsize do you want? 
+                                      * will be changed to represent reality */
+         unsigned long  *col_cnt,    /* number of data columns in the result */
+         char           ***legend_v, /* legend entries */
+         rrd_value_t    **data)      /* two dimensional array containing the data */
+
+{
+
+    image_desc_t   im;
+    int            i;
+    long           long_tmp;
+    time_t        start_tmp=0,end_tmp=0;
+    char           symname[100];
+    long           scancount;
+    struct time_value start_tv, end_tv;
+    char           *parsetime_error = NULL;
+
+    rrd_graph_init(&im);
+
+    parsetime("end-24h", &start_tv);
+    parsetime("now", &end_tv);
+
+    while (1){
+       static struct option long_options[] =
+       {
+           {"start",      required_argument, 0,  's'},
+           {"end",        required_argument, 0,  'e'},
+           {"maxrows",    required_argument, 0,  'm'},
+           {"step",       required_argument, 0,   261},
+           {0,0,0,0}
+       };
+       int option_index = 0;
+       int opt;
+       
+       opt = getopt_long(argc, argv, "s:e:m:",
+                         long_options, &option_index);
+
+       if (opt == EOF)
+           break;
+       
+       switch(opt) {
+       case 261:
+           im.step =  atoi(optarg);
+           break;
+       case 's':
+           if ((parsetime_error = parsetime(optarg, &start_tv))) {
+               rrd_set_error( "start time: %s", parsetime_error );
+               return -1;
+           }
+           break;
+       case 'e':
+           if ((parsetime_error = parsetime(optarg, &end_tv))) {
+               rrd_set_error( "end time: %s", parsetime_error );
+               return -1;
+           }
+           break;
+       case 'm':
+           long_tmp = atol(optarg);
+           if (long_tmp < 10) {
+               rrd_set_error("maxrows below 10 rows");
+               return -1;
+           }
+           im.xsize = long_tmp;
+           break;
+       case '?':
+           rrd_set_error("unknown option '%c'", optopt);
+            return -1;
+       }
+    }
+
+    if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+       return -1;
+    }  
+    
+    if (start_tmp < 3600*24*365*10){
+       rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
+       return -1;
+    }
+    
+    if (end_tmp < start_tmp) {
+       rrd_set_error("start (%ld) should be less than end (%ld)", 
+              start_tmp, end_tmp);
+       return -1;
+    }
+    
+    im.start = start_tmp;
+    im.end = end_tmp;
+
+    for(i=optind;i<argc;i++){
+       int   argstart=0;
+       int   strstart=0;
+       char  varname[30],*rpnex;
+       gdes_alloc(&im);
+       if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
+           if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
+               im_free(&im);
+               rrd_set_error("unknown function '%s'",symname);
+               return -1;
+           }
+       } else {
+           rrd_set_error("can't parse '%s'",argv[i]);
+           im_free(&im);
+           return -1;
+       }
+
+       switch(im.gdes[im.gdes_c-1].gf){
+       case GF_CDEF:
+           if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
+               rrd_set_error("malloc for CDEF");
+               return -1;
+           }
+           if(sscanf(
+                   &argv[i][argstart],
+                   DEF_NAM_FMT "=%[^: ]",
+                   im.gdes[im.gdes_c-1].vname,
+                   rpnex) != 2){
+               im_free(&im);
+               free(rpnex);
+               rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
+               return -1;
+           }
+           /* checking for duplicate DEF CDEFS */
+           if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
+               im_free(&im);
+               rrd_set_error("duplicate variable '%s'",
+                             im.gdes[im.gdes_c-1].vname);
+               return -1; 
+           }      
+           if((im.gdes[im.gdes_c-1].rpnp = rpn_parse(&im,rpnex,&find_var_wrapper))== NULL){
+               rrd_set_error("invalid rpn expression '%s'", rpnex);
+               im_free(&im);           
+               return -1;
+           }
+           free(rpnex);
+           break;
+       case GF_DEF:
+           if (sscanf(
+               &argv[i][argstart],
+               DEF_NAM_FMT "=%n",
+               im.gdes[im.gdes_c-1].vname,
+               &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */ 
+               if(sscanf(&argv[i][argstart
+                                 +strstart
+                                 +scan_for_col(&argv[i][argstart+strstart],
+                                               MAXPATH,im.gdes[im.gdes_c-1].rrd)],
+                         ":" DS_NAM_FMT ":" CF_NAM_FMT,
+                         im.gdes[im.gdes_c-1].ds_nam,
+                         symname) != 2){
+                   im_free(&im);
+                   rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
+                   return -1;
+               }
+           } else {
+               im_free(&im);
+               rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
+               return -1;
+           }
+           
+           /* checking for duplicate DEF CDEFS */
+           if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
+               im_free(&im);
+               rrd_set_error("duplicate variable '%s'",
+                         im.gdes[im.gdes_c-1].vname);
+               return -1; 
+           }      
+           if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
+               im_free(&im);
+               rrd_set_error("unknown cf '%s'",symname);
+               return -1;
+           }
+           break;
+       case GF_XPORT:
+           if((scancount=sscanf(
+               &argv[i][argstart],
+               "%29[^:]:%n",
+               varname,
+               &strstart))>=1){
+               if(strstart <= 0){
+                   im.gdes[im.gdes_c-1].legend[0] = '\0';
+               } else { 
+                   scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
+               }
+               if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
+                   im_free(&im);
+                   rrd_set_error("unknown variable '%s'",varname);
+                   return -1;
+               }               
+           } else {
+               im_free(&im);
+               rrd_set_error("can't parse '%s'",&argv[i][argstart]);
+               return -1;
+           }
+           break;
+       default:
+         break;
+       }
+       
+    }
+
+    if (im.gdes_c == 0){
+       rrd_set_error("can't make a graph without contents");
+       im_free(&im);
+       return(-1); 
+    }
+    
+    if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data) == -1){
+       im_free(&im);
+       return -1;
+    }
+
+    im_free(&im);
+    return 0;
+}
+
+
+
+int
+rrd_xport_fn(image_desc_t *im,
+            time_t         *start,
+            time_t         *end,        /* which time frame do you want ?
+                                         * will be changed to represent reality */
+            unsigned long  *step,       /* which stepsize do you want? 
+                                         * will be changed to represent reality */
+            unsigned long  *col_cnt,    /* number of data columns in the result */
+            char           ***legend_v, /* legend entries */
+            rrd_value_t    **data)      /* two dimensional array containing the data */
+{
+
+    int            i = 0, j = 0;
+    unsigned long  *ds_cnt;    /* number of data sources in file */
+    unsigned long  col, dst_row, row_cnt;
+    rrd_value_t    *srcptr, *dstptr;
+
+    unsigned long nof_xports = 0;
+    unsigned long xport_counter = 0;
+    unsigned long *ref_list;
+    rrd_value_t **srcptr_list;
+    char **legend_list;
+    int ii = 0;
+
+    time_t start_tmp = 0;
+    time_t end_tmp = 0;
+    unsigned long step_tmp = 1;
+
+    /* pull the data from the rrd files ... */
+    if(data_fetch(im)==-1)
+       return -1;
+
+    /* evaluate CDEF  operations ... */
+    if(data_calc(im)==-1)
+       return -1;
+
+    /* how many xports? */
+    for(i = 0; i < im->gdes_c; i++) {  
+       switch(im->gdes[i].gf) {
+       case GF_XPORT:
+         nof_xports++;
+         break;
+       default:
+         break;
+       }
+    }
+
+    if(nof_xports == 0) {
+      rrd_set_error("no XPORT found, nothing to do");
+      return -1;
+    }
+
+    /* a list of referenced gdes */
+    ref_list = malloc(sizeof(int) * nof_xports);
+    if(ref_list == NULL)
+      return -1;
+
+    /* a list to save pointers into each gdes data */
+    srcptr_list = malloc(sizeof(srcptr) * nof_xports);
+    if(srcptr_list == NULL) {
+      free(ref_list);
+      return -1;
+    }
+
+    /* a list to save pointers to the column's legend entry */
+    /* this is a return value! */
+    legend_list = malloc(sizeof(char *) * nof_xports);
+    if(legend_list == NULL) {
+      free(srcptr_list);
+      free(ref_list);
+      return -1;
+    }
+
+    /* find referenced gdes and save their index and */
+    /* a pointer into their data */
+    for(i = 0; i < im->gdes_c; i++) {  
+       switch(im->gdes[i].gf) {
+       case GF_XPORT:
+         ii = im->gdes[i].vidx;
+         if(xport_counter > nof_xports) {
+           rrd_set_error( "too many xports: should not happen. Hmmm");
+           free(srcptr_list);
+           free(ref_list);
+           free(legend_list);
+           return -1;
+         } 
+         srcptr_list[xport_counter] = im->gdes[ii].data;
+         ref_list[xport_counter++] = i;
+         break;
+       default:
+         break;
+       }
+    }
+
+    start_tmp = im->gdes[0].start;
+    end_tmp = im->gdes[0].end;
+    step_tmp = im->gdes[0].step;
+
+    /* fill some return values */
+    *col_cnt = nof_xports;
+    *start = start_tmp;
+    *end = end_tmp;
+    *step = step_tmp;
+
+    row_cnt = ((*end)-(*start))/(*step);
+
+    /* room for rearranged data */
+    /* this is a return value! */
+    if (((*data) = malloc((*col_cnt) * row_cnt * sizeof(rrd_value_t)))==NULL){
+        free(srcptr_list);
+        free(ref_list);
+       free(legend_list);
+       rrd_set_error("malloc xport data area");
+       return(-1);
+    }
+    dstptr = (*data);
+
+    j = 0;
+    for(i = 0; i < im->gdes_c; i++) {  
+       switch(im->gdes[i].gf) {
+       case GF_XPORT:
+         /* reserve room for one legend entry */
+         /* is FMT_LEG_LEN + 5 the correct size? */
+         if ((legend_list[j] = malloc(sizeof(char) * (FMT_LEG_LEN+5)))==NULL) {
+           free(srcptr_list);
+           free(ref_list);
+           free(legend_list);
+           rrd_set_error("malloc xprint legend entry");
+           return(-1);
+         }
+
+         if (im->gdes[i].legend)
+           /* omit bounds check, should have the same size */
+           strcpy (legend_list[j++], im->gdes[i].legend);
+         else
+           legend_list[j++][0] = '\0';
+
+         break;
+       default:
+         break;
+       }
+    }
+
+    /* fill data structure */
+    for(dst_row = 0; dst_row < row_cnt; dst_row++) {
+      for(i = 0; i < nof_xports; i++) {
+        j = ref_list[i];
+       ii = im->gdes[j].vidx;
+       ds_cnt = &im->gdes[ii].ds_cnt;
+
+       srcptr = srcptr_list[i];
+       for(col = 0; col < (*ds_cnt); col++) {
+         rrd_value_t newval = DNAN;
+         newval = srcptr[col];
+
+         if (im->gdes[ii].ds_namv && im->gdes[ii].ds_nam) {
+           if(strcmp(im->gdes[ii].ds_namv[col],im->gdes[ii].ds_nam) == 0)
+             (*dstptr++) = newval;
+         } else {
+           (*dstptr++) = newval;
+         }
+
+       }
+       srcptr_list[i] += (*ds_cnt);
+      }
+    }
+
+    *legend_v = legend_list;
+    free(srcptr_list);
+    free(ref_list);
+    return 0;
+
+}
diff --git a/src/rrd_xport.h b/src/rrd_xport.h
new file mode 100644 (file)
index 0000000..d8d76d4
--- /dev/null
@@ -0,0 +1,34 @@
+/****************************************************************************
+ * RRDtool 1.0.37  Copyright Tobias Oetiker, 1997 - 2000
+ ****************************************************************************
+ * rrd_xport.h  contains XML related constants
+ ****************************************************************************/
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifndef _RRD_XPORT_H
+#define _RRD_XPORT_H
+
+#define XML_ENCODING     "ISO-8859-1"
+#define ROOT_TAG         "xport"
+#define META_TAG         "meta"
+#define META_START_TAG   "start"
+#define META_STEP_TAG    "step"
+#define META_END_TAG     "end"
+#define META_ROWS_TAG    "rows"
+#define META_COLS_TAG    "columns"
+#define LEGEND_TAG       "legend"
+#define LEGEND_ENTRY_TAG "entry"
+#define DATA_TAG         "data"
+#define DATA_ROW_TAG     "row"
+#define COL_TIME_TAG     "t"
+#define COL_DATA_TAG     "v"
+
+
+#endif
+
+
+#ifdef  __cplusplus
+}
+#endif