File Coverage

blib/lib/Chart/Graph/Xrt2d.pm
Criterion Covered Total %
statement 26 175 14.8
branch 1 94 1.0
condition 0 18 0.0
subroutine 7 8 87.5
pod 0 1 0.0
total 34 296 11.4


line stmt bran cond sub pod time code
1             ## Xrt2d.pm is a sub-module of Graph.pm. It has all the subroutines
2             ## needed for the Xrt2d part of the package.
3             ##
4             ## $Id: Xrt2d.pm,v 1.26 2006/06/07 21:09:33 emile Exp $ $Name: $
5             ##
6             ## This software product is developed by Michael Young and David Moore,
7             ## and copyrighted(C) 1998 by the University of California, San Diego
8             ## (UCSD), with all rights reserved. UCSD administers the CAIDA grant,
9             ## NCR-9711092, under which part of this code was developed.
10             ##
11             ## There is no charge for this software. You can redistribute it and/or
12             ## modify it under the terms of the GNU General Public License, v. 2 dated
13             ## June 1991 which is incorporated by reference herein. This software is
14             ## distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF MERCHANTABILITY
15             ## OR FITNESS FOR A PARTICULAR PURPOSE or that the use of it will not
16             ## infringe on any third party's intellectual property rights.
17             ##
18             ## You should have received a copy of the GNU GPL along with this program.
19             ##
20             ##
21             ## IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY
22             ## PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
23             ## DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS
24             ## SOFTWARE, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF
25             ## THE POSSIBILITY OF SUCH DAMAGE.
26             ##
27             ## THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND THE
28             ## UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
29             ## SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. THE UNIVERSITY
30             ## OF CALIFORNIA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES
31             ## OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED
32             ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
33             ## PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE
34             ## ANY PATENT, TRADEMARK OR OTHER RIGHTS.
35             ##
36             ##
37             ## Contact: graph-dev@caida.org
38             ##
39             ##
40             package Chart::Graph::Xrt2d;
41 4     4   24 use Exporter ();
  4         9  
  4         275  
42              
43             @ISA = qw(Exporter);
44             @EXPORT = qw();
45             @EXPORT_OK = qw(&xrt2d);
46              
47 4     4   20 use FileHandle; # to create generic filehandles
  4         8  
  4         33  
48 4     4   1728 use Carp; # for carp() and croak()
  4         73  
  4         420  
49             #use POSIX ":sys_wait_h"; # for waitpid()
50 4     4   26 use Chart::Graph::Utils qw(:UTILS); # get global subs and variables
  4         7  
  4         1098  
51 4     4   2714 use Chart::Graph::XrtUtils qw(:UTILS); # get Xrt subroutines
  4         12  
  4         1137  
52              
53             $cvs_Id = '$Id: Xrt2d.pm,v 1.26 2006/06/07 21:09:33 emile Exp $';
54             $cvs_Author = '$Author: emile $';
55             $cvs_Name = '$Name: $';
56             $cvs_Revision = '$Revision: 1.26 $';
57              
58             $VERSION = 3.2;
59              
60 4     4   26 use strict;
  4         8  
  4         6994  
61              
62             #
63             # xrt graphing package
64             #
65              
66             my %def_xrt_global_opts = (
67             "output file" => "untitled-xrt2d.gif",
68             "output type" => "gif",
69             "x-axis title" => "x-axis",
70             "y-axis title" => "y-axis",
71             "set labels" => undef,
72             "point labels" => undef,
73             "header" => [],
74             "footer" => [],
75             "misc labels" => undef,
76             "invert" => 0,
77             "x time" => 0, # x axis labels are time indicators
78             "style" => "bar",
79             );
80              
81             my %def_xrt_data_opts = (
82             "color" => undef,
83             );
84              
85             #
86             #
87             # Subroutine: xrt2d()
88             #
89             # Description: this is the main function you will be calling from
90             # our scripts. please see
91             # www.caida.org/Tools/Graph/ for a full description
92             # and how-to of this subroutine
93             #
94              
95             sub xrt2d {
96 2     2 0 123 my ($user_global_opts_ref, @data_sets) = @_;
97 2         4 my (%global_opts, %data_opts);
98 0         0 my $data_set_ref;
99            
100             # variables to be written to the command file
101 0         0 my ($plot_file, $x_axis, $y_axis);
102 0         0 my ($header, $footer, $misc_labels, $set_labels, $point_labels);
103 0         0 my ($x_cnt, $y_cnt, $hdr_cnt, $ftr_cnt, $label_cnt);
104 0         0 my ($output_file, $output_type);
105 2         4 my $xrt_options = "";
106 2         99 my $x_timestamps = 0;
107              
108 2         9 _make_tmpdir("_Xrt2d_");
109              
110             # set paths for external programs
111 2 50       8 if (not _set_xrtpaths("xrt2d")) {
112 2         7 _cleanup_tmpdir();
113 2         8 return 0;
114             }
115            
116            
117             # check first arg for hash
118 0 0         if (ref($user_global_opts_ref) ne "HASH") {
119 0           carp "Global options must be a hash.";
120 0           _cleanup_tmpdir();
121 0           return 0;
122             }
123              
124             # call to combine user options with default options
125 0           %global_opts = _mesh_opts($user_global_opts_ref, \%def_xrt_global_opts);
126            
127             # check for values in command file
128 0           while (my ($key, $value) = each %global_opts) {
129            
130 0 0         if ($key eq "output file") {
131 0 0         if (defined($value)) {
132 0           $output_file = $value;
133 0 0         unless (defined $global_opts{"output type"}) {
134 0           carp "Must have an output type defined";
135 0           _cleanup_tmpdir();
136 0           return 0;
137             }
138              
139             # If the file is PostScript ... what XRT makes is PostScript
140 0 0 0       if ($global_opts{"output type"} eq "ps") {
    0 0        
      0        
141 0           $plot_file = _make_tmpfile("plot", "ps");
142             }
143             # For all raster formats XRT starts out with X-Windows XWD format.
144             elsif (($global_opts{"output type"} eq "gif") or
145             ($global_opts{"output type"} eq "xwd") or
146             ($global_opts{"output type"} eq "png") or
147             ($global_opts{"output type"} eq "jpg")
148             ) {
149 0           $plot_file = _make_tmpfile("plot", "xwd");
150             } else {
151             # Default is XWD
152 0           carp "Unknown output type, defaulting to xwd";
153 0           $plot_file = _make_tmpfile("plot", "xwd");
154             }
155             }
156             }
157            
158 0 0         if ($key eq "x-axis title") {
159 0 0         if(defined($value)) {
160 0           $x_axis = $value;
161             }
162             }
163            
164 0 0         if ($key eq "y-axis title") {
165 0 0         if(defined($value)) {
166 0           $y_axis = $value;
167             }
168             }
169            
170 0 0         if ($key eq "header") {
171 0 0         if(defined($value)) {
172 0           $header = $value;
173             }
174             }
175            
176 0 0         if ($key eq "footer") {
177 0 0         if(defined($value)) {
178 0           $footer = $value;
179             }
180             }
181              
182 0 0         if ($key eq "misc labels") {
183 0 0         if(defined($value)) {
184 0           $misc_labels = $value;
185             }
186             }
187              
188 0 0         if ($key eq "set labels") {
189 0 0         if(defined($value)) {
190 0           $set_labels = $value;
191             }
192             }
193              
194 0 0         if ($key eq "point labels") {
195 0 0         if(defined($value)) {
196 0           $point_labels = $value;
197             }
198             }
199              
200 0 0         if ($key eq "invert") {
201 0 0 0       if(defined($value) and $value) {
202 0           $xrt_options .= " -xrm '*xrtInvertOrientation: True'";
203             }
204             }
205              
206 0 0         if ($key eq "x time") {
207 0 0 0       if(defined($value) and $value) {
208 0           $xrt_options .= " -xrm '*xrtXAnnotationMethod: ANNOTIMELABELS'";
209 0           $x_timestamps = 1;
210             }
211             }
212              
213 0 0         if ($key eq "style") {
214 0 0         if(defined($value)) {
215 0 0         if ($value eq "stackedbar") {
    0          
    0          
    0          
    0          
216 0           $xrt_options .= " -xrm '*xrtType: TYPESTACKINGBAR'";
217             } elsif ($value eq "bar") {
218 0           $xrt_options .= " -xrm '*xrtType: TYPEBAR'";
219             } elsif ($value eq "pie") {
220 0           $xrt_options .= " -xrm '*xrtType: TYPEPIE'";
221             } elsif ($value eq "area") {
222 0           $xrt_options .= " -xrm '*xrtType: TYPEAREA'";
223             } elsif ($value eq "stackedarea") {
224 0           $xrt_options .= " -xrm '*xrtLegendReversed: True'";
225 0           $xrt_options .= " -xrm '*xrtType: TYPEAREA'" .
226             " -xrm '*xrtIsStacked: True'";
227             } else {
228 0           carp "No graph type defined, defaulting to bar";
229 0           $xrt_options .= " -xrm '*xrtType: TYPEBAR'";
230             }
231             }
232             }
233             }
234             # Only reverse legend if NOT inverting bars.
235 0 0 0       if (not $global_opts{"invert"} and $global_opts{"style"} eq "stackedbar") {
236 0           $xrt_options .= " -xrm '*xrtLegendReversed: True'";
237             }
238            
239             # get the number of columns and number of rows
240            
241 0           $x_cnt = @{$point_labels};
  0            
242 0           $y_cnt = @{$set_labels};
  0            
243            
244             # because xrt allows multiline headers
245             # get the length of the header array
246             # each line of the header is one index
247             # in the array
248 0           $hdr_cnt = $#{$header} + 1;
  0            
249 0           $ftr_cnt = $#{$footer} + 1;
  0            
250 0           $label_cnt = $#{$misc_labels} + 1;
  0            
251              
252 0           my @colors;
253             my @all_data;
254 0           foreach $data_set_ref (@data_sets) {
255 0 0         if (ref($data_set_ref) ne "ARRAY") {
256 0           carp "Data set must be an array";
257 0           _cleanup_tmpdir();
258 0           return 0;
259             }
260 0           my ($user_data_opts_ref, $data_ref) = @$data_set_ref;
261 0           my (%data_opts);
262            
263             ## check first arg for hash
264 0 0         if (ref($user_data_opts_ref) ne "HASH") {
265 0           carp "Data options must be a hash.";
266 0           _cleanup_tmpdir();
267 0           return 0;
268             }
269              
270 0           push @all_data, $data_ref;
271              
272             # call to combine user options with default options
273 0           %data_opts = _mesh_opts($user_data_opts_ref, \%def_xrt_data_opts);
274              
275             # write data options to command file
276 0           while (my ($key, $value) = each %data_opts) {
277 0 0         if ($key eq "color") {
278 0 0         if (defined $value) {
279 0           push @colors, $value;
280             }
281             }
282             }
283             }
284              
285 0 0         if (@colors) {
286 0           my $resource_string;
287 0           foreach my $color (@colors) {
288 0           $resource_string .=
289             "(LpatSolid FpatSolid \"$color\" 1 PointNone \"$color\" 7)";
290             }
291 0           $xrt_options .= " -xrm '*xrtDataStyles: ($resource_string)'";
292             }
293             ##
294             ## print command file using this format (for bar graphs)
295             ## data separated by tabs
296             ##
297            
298             # output.file
299             # x_cnt (number of points of data)
300             # y_cnt (number of sets of info per point)
301             # point1title set1 set2 ... sety
302             # point2title set1 set2 ...
303             # .
304             # .
305             # pointxtitle set1 set2 ... sety
306             # Number of header lines (multiple header lines available)
307             # header1
308             # header2
309             # ...
310             # Number of header lines (multiple header lines available)
311             # foot1
312             # foot2
313             # ...
314             # Title of x-axis
315             # Title of y-axis
316             # label_cnt (number of extra data labels)
317             # label1title point# set#
318            
319             # create command file and open file handle
320 0           my $command_file = _make_tmpfile("command");
321 0           my $handle = new FileHandle;
322 0 0         if (not $handle->open(">$command_file")) {
323 0           carp "could not open $command_file";
324 0           _cleanup_tmpdir();
325 0           return 0;
326             }
327              
328 0           print $handle "$plot_file\n";
329 0           print $handle "$x_timestamps\n";
330 0           print $handle "$x_cnt\n";
331 0           print $handle "$y_cnt\n";
332 0           _print_array($handle, @{$set_labels});
  0            
333 0           _print_array($handle, @{$point_labels});
  0            
334 0           _print_matrix($handle, @all_data);
335 0           print $handle "$hdr_cnt\n";
336 0           _print_array($handle, @{$header});
  0            
337 0           print $handle "$ftr_cnt\n";
338 0           _print_array($handle, @{$footer});
  0            
339 0           print $handle "$x_axis\n";
340 0           print $handle "$y_axis\n";
341 0           print $handle "$label_cnt\n";
342 0           _print_matrix($handle, @$misc_labels);
343 0           $handle->close();
344              
345             # call xrt and convert file to gif
346 0 0         if (not _exec_xrt2d($command_file, $xrt_options)) {
347 0           _cleanup_tmpdir();
348 0           return 0;
349             }
350              
351 0           my $graph_format = $global_opts{"output type"};
352 0 0         if ($graph_format eq "ps") {
    0          
353 0 0         if (not _chk_status(system("cp $plot_file $output_file"))) {
354 0           _cleanup_tmpdir();
355 0           return 0;
356             }
357             } elsif ($graph_format eq "xwd") {
358 0 0         if (not _chk_status(system("cp $plot_file $output_file"))) {
359 0           _cleanup_tmpdir();
360 0           return 0;
361             }
362             } else {
363 0 0         if(not _convert_raster($graph_format, $plot_file, $output_file)) {
364 0           _cleanup_tmpdir();
365 0           return 0;
366             }
367             }
368              
369 0           _cleanup_tmpdir();
370 0           return 1;
371             }
372              
373             sub _xrt_data_check {
374 0     0     my ($user_data_opts_ref, @data) = @_;
375 0           my (%data_opts);
376            
377 0           my ($result, $color);
378            
379             ## check first arg for hash
380 0 0         if (ref($user_data_opts_ref) ne "HASH") {
381 0           carp "Data options must be a hash.";
382 0           return 0;
383             }
384              
385             # call to combine user options with default options
386 0           %data_opts = _mesh_opts($user_data_opts_ref, \%def_xrt_data_opts);
387              
388             # write data options to command file
389 0           while (my ($key, $value) = each %data_opts) {
390 0 0         if ($key eq "color") {
391 0           $color = $value;
392             }
393             }
394             }
395              
396             # #
397             # # Subroutine: set_xrtpaths()
398             # #
399             # # Description: set paths for external programs required by xrt()
400             # # if they are not defined already
401             # #
402             # sub _set_xrtpaths {
403              
404             # my $xrt = shift;
405              
406             # if (not defined($ppmtogif)) {
407             # if (not $ppmtogif = _get_path("ppmtogif")) {
408             # return 0;
409             # }
410             # }
411              
412             # if (not defined($xrt)) {
413             # if (not $xrt = _get_path("graph_2d")) {
414             # return 0;
415             # }
416             # }
417              
418             # if (not defined($xwdtopnm)) {
419             # if (!($xwdtopnm = _get_path("xwdtopnm"))) {
420             # return 0;
421             # }
422             # }
423              
424             # if (not defined($xvfb)) {
425             # if (not $xvfb = _get_path("Xvfb")) {
426             # return 0;
427             # }
428             # }
429              
430             # # make sure /usr/dt/lib is in the library path
431             # _set_ldpath("/usr/dt/lib");
432              
433             # return 1;
434             # }
435              
436             # #
437             # # Subroutine: set_ldpath()
438             # #
439             # # Description: Xvfb has trouble finding libMrm, so we have to add
440             # # /usr/dt/lib to LD_LIBRARY_PATH
441             # #
442              
443             # sub _set_ldpath {
444             # my ($libpath) = @_;
445            
446             # if (not defined($ENV{LD_LIBRARY_PATH})) {
447             # $ENV{LD_LIBRARY_PATH} = "$libpath";
448             # return 1;
449             # }
450              
451             # my @ldpath = split (/:/, $ENV{LD_LIBRARY_PATH});
452              
453             # # make sure library path isn't already defined
454             # foreach my $i(@ldpath){
455             # if ($i eq $libpath) {
456             # return 1;
457             # }
458             # }
459              
460             # # add library path to LD_LIBRARY_PATH
461             # $ENV{LD_LIBRARY_PATH} = "$libpath:$ENV{LD_LIBRARY_PATH}";
462             # return 1;
463             # }
464              
465             # #
466             # # Subroutine: print_matrix()
467             # #
468             # # Description: print out all the elements
469             # # in a X by Y matrix, row by row
470             # #
471              
472             # sub _print_matrix {
473             # my ($handle, @matrix) = @_;
474            
475             # foreach my $row (@matrix){
476             # foreach my $i (@{$row}){
477             # print $handle "$i\t";
478             # }
479             # print $handle "\n";
480             # }
481             # return 1;
482             # }
483             # #
484             # # Subroutine: print_array()
485             # #
486             # # Description: print out each element of array, one per line
487             # #
488              
489             # sub _print_array {
490             # my ($handle, @array) = @_;
491             # my $i;
492            
493             # foreach $i (@array) {
494             # print $handle "$i\n";
495             # }
496             # return 1;
497             # }
498              
499             # #
500             # # Subroutine: verify_ticks();
501             # #
502             # # Description: check that the number of tick labels is the same
503             # # as the number of xy rows and columns. we can only have
504             # # as many ticks as the number of rows or columns
505             # # we make this subroutine so that the calling subroutine
506             # # is kept cleaner.
507              
508             # sub _verify_ticks {
509             # my ($cnt, $ticks_ref) = @_;
510              
511             # # if no ticks are given then just
512             # # give the xrt binary "1, 2,..."
513             # if (not defined($ticks_ref)) {
514             # my @def_ticks;
515             # for (my $i = 0; $i < $cnt; $i++) {
516             # $def_ticks[$i] = $i + 1;
517             # }
518             # $ticks_ref = \@def_ticks;
519             # }
520              
521             # my $tick_cnt = @{$ticks_ref};
522              
523             # if ($cnt ne $tick_cnt){
524             # carp "number of tick labels must equal the number of xy rows and columns";
525             # return 0;
526             # }
527             # return 1;
528             # }
529              
530             # #
531             # # Subroutine: exec_xrt()
532             # #
533             # # Description: execute the xrt program on the command file.
534             # # xrt generates a xwd file.
535             # #
536             # sub _exec_xrt {
537             # my ($command_file, $options) = @_;
538             # my ($output);
539             # my ($childpid, $port);
540             # my $display_env = $ENV{DISPLAY};
541             # my $status;
542              
543             # if ($use_xvfb) {
544             # # start the virtual X server
545             # ($childpid, $port) = _exec_xvfb();
546             # printf STDERR "\tXRT is $xrt\n";
547             # my $status = system("$xrt -display ipn:$port.0 < $command_file $options");
548             # } else {
549             # # use the local X server
550             # # warning: colors might be messed up
551             # # depending on your current setup
552             # $status = system("$xrt -display $display_env < $command_file $options");
553             # }
554              
555             # if (not _chk_status($status)) {
556             # return 0;
557             # }
558              
559             # kill('KILL', $childpid);
560             # return 1;
561             # }
562            
563             # #
564             # # Subroutine: exec_xwdtogif
565             # #
566             # # Description: convert the xwd file generated by xrt into a gif
567             # # this is a 2-step process. the xwd must be converted into
568             # # a pnm and then into a gif.
569             # sub _exec_xwdtogif {
570             # my ($xwd_file, $gif_file) = @_;
571             # my ($status);
572            
573             # if ($Chart::Graph::debug) {
574             # $status = system("$xwdtopnm $xwd_file | $ppmtogif > $gif_file");
575             # } else {
576             # $status = system("( $xwdtopnm $xwd_file | $ppmtogif > $gif_file; ) 2> /dev/null");
577             # }
578            
579             # if (not _chk_status($status)) {
580             # return 0;
581             # }
582             # return 1;
583             # }
584              
585             # #
586             # # Subroutine: exec_xvfb()
587             # #
588             # # Description: this starts the vitualX server(X is required by xrt, so
589             # # we fake out xrt with Xvfb, for speed and compatability)
590             # #
591             # #
592             # sub _exec_xvfb {
593             # my $port = 99;
594             # my $childpid;
595             # my $sleep_time = 1;
596            
597              
598             # # starting with port 100, we try to start
599             # # the virtual server until we find an open port
600             # # because of the nature of the virtual x server
601             # # we use, in order to know if we have found an
602             # # open port, we have to sleep.
603             # # we check the pid of the virtual x process we started
604             # # and see if it died or not.
605             # while (_childpid_dead($childpid)) {
606             # $port++;
607             # $childpid = _try_port($port);
608             # sleep($sleep_time);
609             # }
610            
611             # # save the childpid so we can stop the virtual server later
612             # # save the $port so we can tell xrt where the virtual server is.
613             # return ($childpid, $port);
614             # }
615             # #
616             # # Subroutine: try_port();
617             # #
618             # # Description: will try to start Xvfb on specified port
619             # sub _try_port {
620              
621             # my ($port) = @_;
622             # my ($childpid);
623            
624             # #fork a process
625             # if (not defined($childpid = fork())){
626             # # the fork failed
627             # carp "cannot fork: $!";
628             # return 0;
629             # } elsif ($childpid == 0) {
630             # # we are in the child process
631             # if ($Chart::Graph::debug) {
632             # exec "$xvfb :$port";
633             # }
634             # else {
635             # exec "exec $xvfb :$port 2> /dev/null";
636             # }
637              
638             # die "should never reach here\n";
639             # } else {
640             # # we are in the parent, return the childpid
641             # # so re can kill it later.
642             # return $childpid;
643             # }
644            
645             # }
646              
647             # #
648             # # Subroutine: childpid_dead
649             # #
650             # # Description: check to see if a PID has died or not
651             # #
652             # #
653             # sub _childpid_dead {
654             # my ($childpid) = @_;
655            
656             # if (not defined($childpid)) {
657             # return 1;
658             # }
659              
660             # # WNOHANG: waitpid() will not suspend execution of
661             # # the calling process if status is not
662             # # immediately available for one of the
663             # # child processes specified by pid.
664             # return waitpid($childpid, &WNOHANG);
665             # }
666              
667             1;
668              
669              
670             __END__