/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/gvc/gvdevice.c

Go to the documentation of this file.
00001 /* $Id: gvdevice.c,v 1.42 2008/02/02 06:42:23 glenlow Exp $ $Revision: 1.42 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 /*
00018  *  graphics code generator wrapper
00019  *
00020  *  This library forms the socket for run-time loadable device plugins.  
00021  */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026 
00027 #include <stdarg.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 
00031 #ifdef HAVE_UNISTD_H
00032 #include <unistd.h>
00033 #endif
00034 
00035 #ifdef WIN32
00036 #include <fcntl.h>
00037 #include <io.h>
00038 #include "compat.h"
00039 #endif
00040 
00041 #ifdef HAVE_LIBZ
00042 #include <zlib.h>
00043 #endif
00044 
00045 #include "logic.h"
00046 #include "const.h"
00047 #include "gvplugin_device.h"
00048 #include "gvcjob.h"
00049 #include "gvcint.h"
00050 #include "gvcproc.h"
00051 
00052 static const int PAGE_ALIGN = 4095;             /* align to a 4K boundary (less one), typical for Linux, Mac OS X and Windows memory allocation */
00053 
00054 size_t gvdevice_write (GVJ_t * job, const unsigned char *s, unsigned int len)
00055 {
00056     if (job->gvc->write_fn && job->output_file == stdout)   /* externally provided write dicipline */
00057         return (job->gvc->write_fn)((char*)s, len);
00058     if (job->flags & GVDEVICE_COMPRESSED_FORMAT) {
00059 #ifdef HAVE_LIBZ
00060         return gzwrite((gzFile *) (job->output_file), s, len);
00061 #endif
00062     }
00063     else if (job->output_data) {
00064         if (len > job->output_data_allocated - job->output_data_position) {
00065             job->output_data_allocated = (job->output_data_position + len + PAGE_ALIGN) & ~PAGE_ALIGN;
00066             job->output_data = realloc(job->output_data, job->output_data_allocated);
00067             if (!job->output_data) {
00068                 fprintf(stderr, "failure realloc'ing for result string\n");
00069                 return 0;
00070             }
00071         }
00072         memcpy(job->output_data + job->output_data_position, s, len);
00073         job->output_data_position += len;
00074         return len;
00075     }
00076     else
00077         return fwrite(s, sizeof(char), len, job->output_file);
00078     return 0;
00079 }
00080 
00081 void gvdevice_fputs(GVJ_t * job, char *s)
00082 {
00083     gvdevice_write (job, (unsigned char*)s, strlen(s));
00084 }
00085 
00086 /* gvdevice_printf:
00087  * Note that this function is unsafe due to the fixed buffer size.
00088  * It should only be used when the caller is sure the input will not
00089  * overflow the buffer. In particular, it should be avoided for
00090  * input coming from users. Also, if vsnprintf is available, the
00091  * code should check for return values to use it safely.
00092  */
00093 void gvdevice_printf(GVJ_t * job, const char *format, ...)
00094 {
00095     unsigned char buf[BUFSIZ];
00096     unsigned int len;
00097     va_list argp;
00098 
00099     va_start(argp, format);
00100 #ifdef HAVE_VSNPRINTF
00101     len = vsnprintf((char *)buf, sizeof(buf), format, argp);
00102 #else
00103     len = vsprintf((char *)buf, format, argp);
00104 #endif
00105     va_end(argp);
00106 
00107     gvdevice_write(job, buf, len);
00108 }
00109 
00110 
00111 /* Test with:
00112  *      cc -DGVPRINTNUM_TEST gvprintnum.c -o gvprintnum
00113  */
00114 
00115 #define DECPLACES 2
00116 #define DECPLACES_SCALE 100
00117 
00118 /* use macro so maxnegnum is stated just once for both double and string versions */
00119 #define val_str(n, x) static double n = x; static unsigned char n##str[] = #x;
00120 val_str(maxnegnum, -999999999999999.99)
00121 
00122 /* we use len and don't need the string to be terminated */
00123 /* #define TERMINATED_NUMBER_STRING */
00124 
00125 /* Note.  Returned string is only good until the next call to gvprintnum */
00126 static unsigned char * gvprintnum (int *len, double number)
00127 {
00128     static unsigned char tmpbuf[sizeof(maxnegnumstr)];   /* buffer big enough for worst case */
00129     unsigned char *result = tmpbuf+sizeof(maxnegnumstr); /* init result to end of tmpbuf */
00130     long int N;
00131     bool showzeros, negative;
00132     int digit, i;
00133 
00134     /*
00135         number limited to a working range: maxnegnum >= n >= -maxnegnum
00136         N = number * DECPLACES_SCALE rounded towards zero,
00137         printing to buffer in reverse direction,
00138         printing "." after DECPLACES
00139         suppressing trailing "0" and "."
00140      */
00141 
00142     if (number < maxnegnum) {           /* -ve limit */
00143         *len = sizeof(maxnegnumstr)-1;  /* len doesn't include terminator */
00144         return maxnegnumstr;;
00145     }
00146     if (number > -maxnegnum) {          /* +ve limit */
00147         *len = sizeof(maxnegnumstr)-2;  /* len doesn't include terminator or sign */
00148         return maxnegnumstr+1;          /* +1 to skip the '-' sign */
00149     }
00150     number *= DECPLACES_SCALE;          /* scale by DECPLACES_SCALE */
00151     if (number < 0.0)                   /* round towards zero */
00152         N = number - 0.5;
00153     else
00154         N = number + 0.5;
00155     if (N == 0) {                       /* special case for exactly 0 */
00156         *len = 1;
00157         return (unsigned char *)"0";
00158     }
00159     if ((negative = (N < 0)))           /* avoid "-0" by testing rounded int */
00160         N = -N;                         /* make number +ve */
00161 #ifdef TERMINATED_NUMBER_STRING
00162     *--result = '\0';                   /* terminate the result string */
00163 #endif
00164     showzeros = false;                  /* don't print trailing zeros */
00165     for (i = DECPLACES; N || i > 0; i--) {  /* non zero remainder,
00166                                                 or still in fractional part */
00167         digit = N % 10;                 /* next least-significant digit */
00168         N /= 10;
00169         if (digit || showzeros) {       /* if digit is non-zero,
00170                                                 or if we are printing zeros */
00171             *--result = digit | '0';    /* convert digit to ascii */
00172             showzeros = true;           /* from now on we must print zeros */
00173         }
00174         if (i == 1) {                   /* if completed fractional part */
00175             if (showzeros)              /* if there was a non-zero fraction */
00176                 *--result = '.';        /* print decimal point */
00177             showzeros = true;           /* print all digits in int part */
00178         }
00179     }
00180     if (negative)                       /* print "-" if needed */
00181         *--result = '-';
00182 #ifdef TERMINATED_NUMBER_STRING
00183     *len = tmpbuf+sizeof(maxnegnumstr)-1 - result;
00184 #else
00185     *len = tmpbuf+sizeof(maxnegnumstr) - result;
00186 #endif
00187     return result;                              
00188 }
00189 
00190 
00191 #ifdef GVPRINTNUM_TEST
00192 int main (int argc, char *argv[])
00193 {
00194     unsigned char *buf;
00195     int len;
00196 
00197     double test[] = {
00198         -maxnegnum*1.1, -maxnegnum*.9,
00199         1e8, 10.008, 10, 1, .1, .01,
00200         .006, .005, .004, .001, 1e-8, 
00201         0, -0,
00202         -1e-8, -.001, -.004, -.005, -.006,
00203         -.01, -.1, -1, -10, -10.008, -1e8,
00204         maxnegnum*.9, maxnegnum*1.1
00205     };
00206     int i = sizeof(test) / sizeof(test[0]);
00207 
00208     while (i--) {
00209         buf = gvprintnum(&len, test[i]);
00210         fprintf (stdout, "%g = %s %d\n", test[i], buf, len);
00211     }
00212 
00213     return 0;
00214 }
00215 #endif
00216 
00217 void gvdevice_printnum(GVJ_t * job, double num)
00218 {
00219     unsigned char *buf;
00220     int len;
00221 
00222     buf = gvprintnum(&len, num);
00223     gvdevice_write(job, buf, len);
00224 } 
00225 
00226 void gvdevice_printpointf(GVJ_t * job, pointf p)
00227 {
00228     unsigned char *buf;
00229     int len;
00230 
00231     buf = gvprintnum(&len, p.x);
00232     gvdevice_write(job, buf, len);
00233     gvdevice_write(job, (unsigned char*)" ", 1);
00234     buf = gvprintnum(&len, p.y);
00235     gvdevice_write(job, buf, len);
00236 } 
00237 
00238 void gvdevice_printpointflist(GVJ_t * job, pointf *p, int n)
00239 {
00240     int i = 0;
00241 
00242     while (TRUE) {
00243         gvdevice_printpointf(job, p[i]);
00244         if (++i >= n) break;
00245         gvdevice_write(job, (unsigned char*)" ", 1);
00246     }
00247 } 
00248 
00249 static void auto_output_filename(GVJ_t *job)
00250 {
00251     static char *buf;
00252     static int bufsz;
00253     char gidx[100];  /* large enough for '.' plus any integer */
00254     char *fn, *p;
00255     int len;
00256 
00257     if (job->graph_index)
00258         sprintf(gidx, ".%d", job->graph_index + 1);
00259     else
00260         gidx[0] = '\0';
00261     if (!(fn = job->input_filename))
00262         fn = "noname.dot";
00263     len = strlen(fn)                    /* typically "something.dot" */
00264         + strlen(gidx)                  /* "", ".2", ".3", ".4", ... */
00265         + 1                             /* "." */
00266         + strlen(job->output_langname)  /* e.g. "png" */
00267         + 1;                            /* null terminaor */
00268     if (bufsz < len) {
00269             bufsz = len + 10;
00270             buf = realloc(buf, bufsz * sizeof(char));
00271     }
00272     strcpy(buf, fn);
00273     strcat(buf, gidx);
00274     strcat(buf, ".");
00275     if ((p = strchr(job->output_langname, ':'))) {
00276         strcat(buf, p+1);
00277         strcat(buf, ".");
00278         strncat(buf, job->output_langname, (p - job->output_langname));
00279     }
00280     else {
00281         strcat(buf, job->output_langname);
00282     }
00283 
00284     job->output_filename = buf;
00285 }
00286 
00287 #ifdef WITH_CODEGENS
00288 extern FILE* Output_file;
00289 #endif
00290 
00291 void gvdevice_initialize(GVJ_t * job)
00292 {
00293     gvdevice_engine_t *gvde = job->device.engine;
00294     GVC_t *gvc = job->gvc;
00295 
00296     if (gvde && gvde->initialize) {
00297         gvde->initialize(job);
00298     }
00299     else if (job->output_data) {
00300     }
00301     /* if the device has no initialization then it uses file output */
00302     else if (!job->output_file) {        /* if not yet opened */
00303         if (gvc->common.auto_outfile_names)
00304             auto_output_filename(job);
00305         if (job->output_filename) {
00306             job->output_file = fopen(job->output_filename, "w");
00307             if (job->output_file == NULL) {
00308                 perror(job->output_filename);
00309                 exit(1);
00310             }
00311         }
00312         else
00313             job->output_file = stdout;
00314 
00315 #ifdef WITH_CODEGENS
00316         Output_file = job->output_file;
00317 #endif
00318 
00319 #ifdef HAVE_SETMODE
00320 #ifdef O_BINARY
00321         if (job->flags & GVDEVICE_BINARY_FORMAT)
00322             setmode(fileno(job->output_file), O_BINARY);
00323 #endif
00324 #endif
00325 
00326         if (job->flags & GVDEVICE_COMPRESSED_FORMAT) {
00327 #if HAVE_LIBZ
00328             int fd;
00329 
00330             /* open dup so can gzclose independent of FILE close */
00331             fd = dup(fileno(job->output_file));
00332             job->output_file = (FILE *) (gzdopen(fd, "wb"));
00333             if (!job->output_file) {
00334                 (job->common->errorfn) ("Error initializing compression on output file\n");
00335                 exit(1);
00336             }
00337 #else
00338             (job->common->errorfn) ("No libz support.\n");
00339             exit(1);
00340 #endif
00341         }
00342     }
00343 }
00344 
00345 void gvdevice_format(GVJ_t * job)
00346 {
00347     gvdevice_engine_t *gvde = job->device.engine;
00348 
00349     if (gvde && gvde->format)
00350         gvde->format(job);
00351     if (job->output_file
00352       && ! job->external_context
00353       && job->output_lang != TK
00354       && ! (job->flags & GVDEVICE_COMPRESSED_FORMAT))
00355         fflush(job->output_file);
00356 }
00357 
00358 void gvdevice_finalize(GVJ_t * job)
00359 {
00360     gvdevice_engine_t *gvde = job->device.engine;
00361     boolean finalized_p = FALSE;
00362 
00363     if (gvde) {
00364         if (gvde->finalize) {
00365             gvde->finalize(job);
00366             finalized_p = TRUE;
00367         }
00368     }
00369 #ifdef WITH_CODEGENS
00370     else {
00371         codegen_t *cg = job->codegen;
00372 
00373         if (cg && cg->reset)
00374             cg->reset();
00375     }
00376 #endif
00377 
00378     if (! finalized_p) {
00379         /* if the device has no finalization then it uses file output */
00380         if (job->flags & GVDEVICE_COMPRESSED_FORMAT) {
00381 #ifdef HAVE_LIBZ
00382             gzclose((gzFile *) (job->output_file));
00383             job->output_file = NULL;
00384 #else
00385             (job->common->errorfn) ("No libz support\n");
00386             exit(1);
00387 #endif
00388         }
00389         if (job->output_filename
00390           && job->output_file != stdout 
00391           && ! job->external_context) {
00392             if (job->output_file) {
00393                 fclose(job->output_file);
00394                 job->output_file = NULL;
00395             }
00396             job->output_filename = NULL;
00397         }
00398     }
00399 }

Generated on Mon Mar 31 19:03:26 2008 for Graphviz by  doxygen 1.5.1