|
Graphviz
2.31.20130618.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 /* 00015 * This library forms the socket for run-time loadable device plugins. 00016 */ 00017 00018 #ifdef HAVE_CONFIG_H 00019 #include "config.h" 00020 #endif 00021 00022 #include <stdarg.h> 00023 #include <stdlib.h> 00024 #include <string.h> 00025 00026 #ifdef HAVE_ERRNO_H 00027 #include <errno.h> 00028 #endif 00029 #ifdef HAVE_UNISTD_H 00030 #include <unistd.h> 00031 #endif 00032 00033 #ifdef WIN32 00034 #include <fcntl.h> 00035 #include <io.h> 00036 #include "compat.h" 00037 #endif 00038 00039 #ifdef HAVE_LIBZ 00040 #include <zlib.h> 00041 00042 #ifndef OS_CODE 00043 # define OS_CODE 0x03 /* assume Unix */ 00044 #endif 00045 static char z_file_header[] = 00046 {0x1f, 0x8b, /*magic*/ Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE}; 00047 00048 static z_stream z_strm; 00049 static unsigned char *df; 00050 static unsigned int dfallocated; 00051 static unsigned long int crc; 00052 #endif /* HAVE_LIBZ */ 00053 00054 #include "const.h" 00055 #include "memory.h" 00056 #include "gvplugin_device.h" 00057 #include "gvcjob.h" 00058 #include "gvcint.h" 00059 #include "gvcproc.h" 00060 #include "logic.h" 00061 #include "gvio.h" 00062 00063 static const int PAGE_ALIGN = 4095; /* align to a 4K boundary (less one), typical for Linux, Mac OS X and Windows memory allocation */ 00064 00065 static size_t gvwrite_no_z (GVJ_t * job, const char *s, size_t len) 00066 { 00067 if (job->gvc->write_fn) /* externally provided write dicipline */ 00068 return (job->gvc->write_fn)(job, (char*)s, len); 00069 if (job->output_data) { 00070 if (len > job->output_data_allocated - (job->output_data_position + 1)) { 00071 /* ensure enough allocation for string = null terminator */ 00072 job->output_data_allocated = (job->output_data_position + len + 1 + PAGE_ALIGN) & ~PAGE_ALIGN; 00073 job->output_data = realloc(job->output_data, job->output_data_allocated); 00074 if (!job->output_data) { 00075 (job->common->errorfn) ("memory allocation failure\n"); 00076 exit(1); 00077 } 00078 } 00079 memcpy(job->output_data + job->output_data_position, s, len); 00080 job->output_data_position += len; 00081 job->output_data[job->output_data_position] = '\0'; /* keep null termnated */ 00082 return len; 00083 } 00084 else 00085 return fwrite(s, sizeof(char), len, job->output_file); 00086 return 0; 00087 } 00088 00089 static void auto_output_filename(GVJ_t *job) 00090 { 00091 static char *buf; 00092 static size_t bufsz; 00093 char gidx[100]; /* large enough for '.' plus any integer */ 00094 char *fn, *p, *q; 00095 size_t len; 00096 00097 if (job->graph_index) 00098 sprintf(gidx, ".%d", job->graph_index + 1); 00099 else 00100 gidx[0] = '\0'; 00101 if (!(fn = job->input_filename)) 00102 fn = "noname.gv"; 00103 len = strlen(fn) /* typically "something.gv" */ 00104 + strlen(gidx) /* "", ".2", ".3", ".4", ... */ 00105 + 1 /* "." */ 00106 + strlen(job->output_langname) /* e.g. "png" */ 00107 + 1; /* null terminaor */ 00108 if (bufsz < len) { 00109 bufsz = len + 10; 00110 buf = realloc(buf, bufsz * sizeof(char)); 00111 } 00112 strcpy(buf, fn); 00113 strcat(buf, gidx); 00114 strcat(buf, "."); 00115 p = strdup(job->output_langname); 00116 while ((q = strrchr(p, ':'))) { 00117 strcat(buf, q+1); 00118 strcat(buf, "."); 00119 *q = '\0'; 00120 } 00121 strcat(buf, p); 00122 free(p); 00123 00124 job->output_filename = buf; 00125 } 00126 00127 /* gvdevice_initialize: 00128 * Return 0 on success, non-zero on failure 00129 */ 00130 int gvdevice_initialize(GVJ_t * job) 00131 { 00132 gvdevice_engine_t *gvde = job->device.engine; 00133 GVC_t *gvc = job->gvc; 00134 00135 if (gvde && gvde->initialize) { 00136 gvde->initialize(job); 00137 } 00138 else if (job->output_data) { 00139 } 00140 /* if the device has no initialization then it uses file output */ 00141 else if (!job->output_file) { /* if not yet opened */ 00142 if (gvc->common.auto_outfile_names) 00143 auto_output_filename(job); 00144 if (job->output_filename) { 00145 job->output_file = fopen(job->output_filename, "w"); 00146 if (job->output_file == NULL) { 00147 (job->common->errorfn) ("Could not open \"%s\" for writing : %s\n", 00148 job->output_filename, strerror(errno)); 00149 /* perror(job->output_filename); */ 00150 return(1); 00151 } 00152 } 00153 else 00154 job->output_file = stdout; 00155 00156 #ifdef HAVE_SETMODE 00157 #ifdef O_BINARY 00158 if (job->flags & GVDEVICE_BINARY_FORMAT) 00159 #ifdef WIN32 00160 _setmode(fileno(job->output_file), O_BINARY); 00161 #else 00162 setmode(fileno(job->output_file), O_BINARY); 00163 #endif 00164 #endif 00165 #endif 00166 } 00167 00168 if (job->flags & GVDEVICE_COMPRESSED_FORMAT) { 00169 #ifdef HAVE_LIBZ 00170 z_stream *z = &z_strm; 00171 00172 z->zalloc = 0; 00173 z->zfree = 0; 00174 z->opaque = 0; 00175 z->next_in = NULL; 00176 z->next_out = NULL; 00177 z->avail_in = 0; 00178 00179 crc = crc32(0L, Z_NULL, 0); 00180 00181 if (deflateInit2(z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) { 00182 (job->common->errorfn) ("Error initializing for deflation\n"); 00183 return(1); 00184 } 00185 gvwrite_no_z(job, z_file_header, sizeof(z_file_header)); 00186 #else 00187 (job->common->errorfn) ("No libz support.\n"); 00188 return(1); 00189 #endif 00190 } 00191 return 0; 00192 } 00193 00194 size_t gvwrite (GVJ_t * job, const char *s, size_t len) 00195 { 00196 size_t ret, olen; 00197 00198 if (!len || !s) 00199 return 0; 00200 00201 if (job->flags & GVDEVICE_COMPRESSED_FORMAT) { 00202 #ifdef HAVE_LIBZ 00203 z_streamp z = &z_strm; 00204 size_t dflen; 00205 00206 #ifdef HAVE_DEFLATEBOUND 00207 dflen = deflateBound(z, len); 00208 #else 00209 /* deflateBound() is not available in older libz, e.g. from centos3 */ 00210 dflen = 2 * len + dfallocated - z->avail_out; 00211 #endif 00212 if (dfallocated < dflen) { 00213 dfallocated = (dflen + 1 + PAGE_ALIGN) & ~PAGE_ALIGN; 00214 df = realloc(df, dfallocated); 00215 if (! df) { 00216 (job->common->errorfn) ("memory allocation failure\n"); 00217 exit(1); 00218 } 00219 } 00220 00221 crc = crc32(crc, (unsigned char*)s, len); 00222 00223 z->next_in = (unsigned char*)s; 00224 z->avail_in = len; 00225 while (z->avail_in) { 00226 z->next_out = df; 00227 z->avail_out = dfallocated; 00228 ret=deflate (z, Z_NO_FLUSH); 00229 if (ret != Z_OK) { 00230 (job->common->errorfn) ("deflation problem %d\n", ret); 00231 exit(1); 00232 } 00233 00234 if ((olen = z->next_out - df)) { 00235 ret = gvwrite_no_z (job, (char*)df, olen); 00236 if (ret != olen) { 00237 (job->common->errorfn) ("gvwrite_no_z problem %d\n", ret); 00238 exit(1); 00239 } 00240 } 00241 } 00242 00243 #else 00244 (job->common->errorfn) ("No libz support.\n"); 00245 exit(1); 00246 #endif 00247 } 00248 else { /* uncompressed write */ 00249 ret = gvwrite_no_z (job, s, len); 00250 if (ret != len) { 00251 (job->common->errorfn) ("gvwrite_no_z problem %d\n", len); 00252 exit(1); 00253 } 00254 } 00255 return len; 00256 } 00257 00258 int gvferror (FILE* stream) 00259 { 00260 GVJ_t *job = (GVJ_t*)stream; 00261 00262 if (!job->gvc->write_fn && !job->output_data) 00263 return ferror(job->output_file); 00264 00265 return 0; 00266 } 00267 00268 size_t gvfwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream) 00269 { 00270 assert(size = sizeof(char)); 00271 return gvwrite((GVJ_t*)stream, ptr, nmemb); 00272 } 00273 00274 int gvputs(GVJ_t * job, const char *s) 00275 { 00276 size_t len = strlen(s); 00277 00278 if (gvwrite (job, s, len) != len) { 00279 return EOF; 00280 } 00281 return +1; 00282 } 00283 00284 int gvputc(GVJ_t * job, int c) 00285 { 00286 const char cc = c; 00287 00288 if (gvwrite (job, &cc, 1) != 1) { 00289 return EOF; 00290 } 00291 return c; 00292 } 00293 00294 int gvflush (GVJ_t * job) 00295 { 00296 if (job->output_file 00297 && ! job->external_context 00298 && ! job->gvc->write_fn) { 00299 return fflush(job->output_file); 00300 } 00301 else 00302 return 0; 00303 } 00304 00305 static void gvdevice_close(GVJ_t * job) 00306 { 00307 if (job->output_filename 00308 && job->output_file != stdout 00309 && ! job->external_context) { 00310 if (job->output_file) { 00311 fclose(job->output_file); 00312 job->output_file = NULL; 00313 } 00314 job->output_filename = NULL; 00315 } 00316 } 00317 00318 void gvdevice_format(GVJ_t * job) 00319 { 00320 gvdevice_engine_t *gvde = job->device.engine; 00321 00322 if (gvde && gvde->format) 00323 gvde->format(job); 00324 gvflush (job); 00325 } 00326 00327 void gvdevice_finalize(GVJ_t * job) 00328 { 00329 gvdevice_engine_t *gvde = job->device.engine; 00330 boolean finalized_p = FALSE; 00331 00332 if (job->flags & GVDEVICE_COMPRESSED_FORMAT) { 00333 #ifdef HAVE_LIBZ 00334 z_streamp z = &z_strm; 00335 unsigned char out[8] = ""; 00336 int ret; 00337 int cnt = 0; 00338 00339 z->next_in = out; 00340 z->avail_in = 0; 00341 z->next_out = df; 00342 z->avail_out = dfallocated; 00343 while ((ret = deflate (z, Z_FINISH)) == Z_OK && (cnt++ <= 100)) { 00344 gvwrite_no_z(job, (char*)df, z->next_out - df); 00345 z->next_out = df; 00346 z->avail_out = dfallocated; 00347 } 00348 if (ret != Z_STREAM_END) { 00349 (job->common->errorfn) ("deflation finish problem %d cnt=%d\n", ret, cnt); 00350 exit(1); 00351 } 00352 gvwrite_no_z(job, (char*)df, z->next_out - df); 00353 00354 ret = deflateEnd(z); 00355 if (ret != Z_OK) { 00356 (job->common->errorfn) ("deflation end problem %d\n", ret); 00357 exit(1); 00358 } 00359 out[0] = crc; 00360 out[1] = crc >> 8; 00361 out[2] = crc >> 16; 00362 out[3] = crc >> 24; 00363 out[4] = z->total_in; 00364 out[5] = z->total_in >> 8; 00365 out[6] = z->total_in >> 16; 00366 out[7] = z->total_in >> 24; 00367 gvwrite_no_z(job, (char*)out, sizeof(out)); 00368 #else 00369 (job->common->errorfn) ("No libz support\n"); 00370 exit(1); 00371 #endif 00372 } 00373 00374 if (gvde) { 00375 if (gvde->finalize) { 00376 gvde->finalize(job); 00377 finalized_p = TRUE; 00378 } 00379 } 00380 00381 if (! finalized_p) { 00382 /* if the device has no finalization then it uses file output */ 00383 gvflush (job); 00384 gvdevice_close(job); 00385 } 00386 } 00387 /* gvprintf: 00388 * Unless vsnprintf is available, this function is unsafe due to the fixed buffer size. 00389 * It should only be used when the caller is sure the input will not 00390 * overflow the buffer. In particular, it should be avoided for 00391 * input coming from users. 00392 */ 00393 void gvprintf(GVJ_t * job, const char *format, ...) 00394 { 00395 char buf[BUFSIZ]; 00396 size_t len; 00397 va_list argp; 00398 char* bp = buf; 00399 00400 va_start(argp, format); 00401 #ifdef HAVE_VSNPRINTF 00402 len = vsnprintf((char *)buf, BUFSIZ, format, argp); 00403 if (len < 0) { 00404 agerr (AGERR, "gvprintf: %s\n", strerror(errno)); 00405 return; 00406 } 00407 else if (len >= BUFSIZ) { 00408 /* C99 vsnprintf returns the length that would be required 00409 * to write the string without truncation. 00410 */ 00411 bp = gmalloc(len + 1); 00412 va_end(argp); 00413 va_start(argp, format); 00414 len = vsprintf(bp, format, argp); 00415 } 00416 #else 00417 len = vsprintf((char *)buf, format, argp); 00418 #endif 00419 va_end(argp); 00420 00421 gvwrite(job, bp, len); 00422 if (bp != buf) 00423 free (bp); 00424 } 00425 00426 00427 /* Test with: 00428 * cc -DGVPRINTNUM_TEST gvprintnum.c -o gvprintnum 00429 */ 00430 00431 #define DECPLACES 2 00432 #define DECPLACES_SCALE 100 00433 00434 /* use macro so maxnegnum is stated just once for both double and string versions */ 00435 #define val_str(n, x) static double n = x; static char n##str[] = #x; 00436 val_str(maxnegnum, -999999999999999.99) 00437 00438 /* we use len and don't need the string to be terminated */ 00439 /* #define TERMINATED_NUMBER_STRING */ 00440 00441 /* Note. Returned string is only good until the next call to gvprintnum */ 00442 static char * gvprintnum (size_t *len, double number) 00443 { 00444 static char tmpbuf[sizeof(maxnegnumstr)]; /* buffer big enough for worst case */ 00445 char *result = tmpbuf+sizeof(maxnegnumstr); /* init result to end of tmpbuf */ 00446 long int N; 00447 boolean showzeros, negative; 00448 int digit, i; 00449 00450 /* 00451 number limited to a working range: maxnegnum >= n >= -maxnegnum 00452 N = number * DECPLACES_SCALE rounded towards zero, 00453 printing to buffer in reverse direction, 00454 printing "." after DECPLACES 00455 suppressing trailing "0" and "." 00456 */ 00457 00458 if (number < maxnegnum) { /* -ve limit */ 00459 *len = sizeof(maxnegnumstr)-1; /* len doesn't include terminator */ 00460 return maxnegnumstr;; 00461 } 00462 if (number > -maxnegnum) { /* +ve limit */ 00463 *len = sizeof(maxnegnumstr)-2; /* len doesn't include terminator or sign */ 00464 return maxnegnumstr+1; /* +1 to skip the '-' sign */ 00465 } 00466 number *= DECPLACES_SCALE; /* scale by DECPLACES_SCALE */ 00467 if (number < 0.0) /* round towards zero */ 00468 N = number - 0.5; 00469 else 00470 N = number + 0.5; 00471 if (N == 0) { /* special case for exactly 0 */ 00472 *len = 1; 00473 return "0"; 00474 } 00475 if ((negative = (N < 0))) /* avoid "-0" by testing rounded int */ 00476 N = -N; /* make number +ve */ 00477 #ifdef TERMINATED_NUMBER_STRING 00478 *--result = '\0'; /* terminate the result string */ 00479 #endif 00480 showzeros = FALSE; /* don't print trailing zeros */ 00481 for (i = DECPLACES; N || i > 0; i--) { /* non zero remainder, 00482 or still in fractional part */ 00483 digit = N % 10; /* next least-significant digit */ 00484 N /= 10; 00485 if (digit || showzeros) { /* if digit is non-zero, 00486 or if we are printing zeros */ 00487 *--result = digit | '0'; /* convert digit to ascii */ 00488 showzeros = TRUE; /* from now on we must print zeros */ 00489 } 00490 if (i == 1) { /* if completed fractional part */ 00491 if (showzeros) /* if there was a non-zero fraction */ 00492 *--result = '.'; /* print decimal point */ 00493 showzeros = TRUE; /* print all digits in int part */ 00494 } 00495 } 00496 if (negative) /* print "-" if needed */ 00497 *--result = '-'; 00498 #ifdef TERMINATED_NUMBER_STRING 00499 *len = tmpbuf+sizeof(maxnegnumstr)-1 - result; 00500 #else 00501 *len = tmpbuf+sizeof(maxnegnumstr) - result; 00502 #endif 00503 return result; 00504 } 00505 00506 00507 #ifdef GVPRINTNUM_TEST 00508 int main (int argc, char *argv[]) 00509 { 00510 char *buf; 00511 size_t len; 00512 00513 double test[] = { 00514 -maxnegnum*1.1, -maxnegnum*.9, 00515 1e8, 10.008, 10, 1, .1, .01, 00516 .006, .005, .004, .001, 1e-8, 00517 0, -0, 00518 -1e-8, -.001, -.004, -.005, -.006, 00519 -.01, -.1, -1, -10, -10.008, -1e8, 00520 maxnegnum*.9, maxnegnum*1.1 00521 }; 00522 int i = sizeof(test) / sizeof(test[0]); 00523 00524 while (i--) { 00525 buf = gvprintnum(&len, test[i]); 00526 fprintf (stdout, "%g = %s %d\n", test[i], buf, len); 00527 } 00528 00529 return 0; 00530 } 00531 #endif 00532 00533 void gvprintdouble(GVJ_t * job, double num) 00534 { 00535 char *buf; 00536 size_t len; 00537 00538 buf = gvprintnum(&len, num); 00539 gvwrite(job, buf, len); 00540 } 00541 00542 void gvprintpointf(GVJ_t * job, pointf p) 00543 { 00544 char *buf; 00545 size_t len; 00546 00547 buf = gvprintnum(&len, p.x); 00548 gvwrite(job, buf, len); 00549 gvwrite(job, " ", 1); 00550 buf = gvprintnum(&len, p.y); 00551 gvwrite(job, buf, len); 00552 } 00553 00554 void gvprintpointflist(GVJ_t * job, pointf *p, int n) 00555 { 00556 int i = 0; 00557 00558 while (TRUE) { 00559 gvprintpointf(job, p[i]); 00560 if (++i >= n) break; 00561 gvwrite(job, " ", 1); 00562 } 00563 } 00564
1.7.5