Graphviz  2.41.20171026.1811
gvdevice.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 /*
15  * This library forms the socket for run-time loadable device plugins.
16  */
17 
18 #include "config.h"
19 
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <inttypes.h>
24 #include <errno.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 
29 #ifdef _WIN32
30 #include <fcntl.h>
31 #include <io.h>
32 #include "compat.h"
33 #endif
34 
35 #ifdef HAVE_LIBZ
36 #include <zlib.h>
37 
38 #ifndef OS_CODE
39 # define OS_CODE 0x03 /* assume Unix */
40 #endif
41 static char z_file_header[] =
42  {0x1f, 0x8b, /*magic*/ Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE};
43 
44 static z_stream z_strm;
45 static unsigned char *df;
46 static unsigned int dfallocated;
47 static uint64_t crc;
48 #endif /* HAVE_LIBZ */
49 
50 #include "const.h"
51 #include "memory.h"
52 #include "gvplugin_device.h"
53 #include "gvcjob.h"
54 #include "gvcint.h"
55 #include "gvcproc.h"
56 #include "logic.h"
57 #include "gvio.h"
58 
59 static const int PAGE_ALIGN = 4095; /* align to a 4K boundary (less one), typical for Linux, Mac OS X and Windows memory allocation */
60 
61 static size_t gvwrite_no_z (GVJ_t * job, const char *s, size_t len)
62 {
63  if (job->gvc->write_fn) /* externally provided write dicipline */
64  return (job->gvc->write_fn)(job, (char*)s, len);
65  if (job->output_data) {
66  if (len > job->output_data_allocated - (job->output_data_position + 1)) {
67  /* ensure enough allocation for string = null terminator */
68  job->output_data_allocated = (job->output_data_position + len + 1 + PAGE_ALIGN) & ~PAGE_ALIGN;
69  job->output_data = realloc(job->output_data, job->output_data_allocated);
70  if (!job->output_data) {
71  (job->common->errorfn) ("memory allocation failure\n");
72  exit(1);
73  }
74  }
75  memcpy(job->output_data + job->output_data_position, s, len);
76  job->output_data_position += len;
77  job->output_data[job->output_data_position] = '\0'; /* keep null termnated */
78  return len;
79  }
80  else
81  return fwrite(s, sizeof(char), len, job->output_file);
82  return 0;
83 }
84 
85 static void auto_output_filename(GVJ_t *job)
86 {
87  static char *buf;
88  static size_t bufsz;
89  char gidx[100]; /* large enough for '.' plus any integer */
90  char *fn, *p, *q;
91  size_t len;
92 
93  if (job->graph_index)
94  sprintf(gidx, ".%d", job->graph_index + 1);
95  else
96  gidx[0] = '\0';
97  if (!(fn = job->input_filename))
98  fn = "noname.gv";
99  len = strlen(fn) /* typically "something.gv" */
100  + strlen(gidx) /* "", ".2", ".3", ".4", ... */
101  + 1 /* "." */
102  + strlen(job->output_langname) /* e.g. "png" */
103  + 1; /* null terminaor */
104  if (bufsz < len) {
105  bufsz = len + 10;
106  buf = realloc(buf, bufsz * sizeof(char));
107  }
108  strcpy(buf, fn);
109  strcat(buf, gidx);
110  strcat(buf, ".");
111  p = strdup(job->output_langname);
112  while ((q = strrchr(p, ':'))) {
113  strcat(buf, q+1);
114  strcat(buf, ".");
115  *q = '\0';
116  }
117  strcat(buf, p);
118  free(p);
119 
120  job->output_filename = buf;
121 }
122 
123 /* gvdevice_initialize:
124  * Return 0 on success, non-zero on failure
125  */
127 {
128  gvdevice_engine_t *gvde = job->device.engine;
129  GVC_t *gvc = job->gvc;
130 
131  if (gvde && gvde->initialize) {
132  gvde->initialize(job);
133  }
134  else if (job->output_data) {
135  }
136  /* if the device has no initialization then it uses file output */
137  else if (!job->output_file) { /* if not yet opened */
138  if (gvc->common.auto_outfile_names)
139  auto_output_filename(job);
140  if (job->output_filename) {
141  job->output_file = fopen(job->output_filename, "w");
142  if (job->output_file == NULL) {
143  (job->common->errorfn) ("Could not open \"%s\" for writing : %s\n",
144  job->output_filename, strerror(errno));
145  /* perror(job->output_filename); */
146  return(1);
147  }
148  }
149  else
150  job->output_file = stdout;
151 
152 #ifdef HAVE_SETMODE
153 #ifdef O_BINARY
154  if (job->flags & GVDEVICE_BINARY_FORMAT)
155 #ifdef _WIN32
156  _setmode(fileno(job->output_file), O_BINARY);
157 #else
158  setmode(fileno(job->output_file), O_BINARY);
159 #endif
160 #endif
161 #endif
162  }
163 
164  if (job->flags & GVDEVICE_COMPRESSED_FORMAT) {
165 #ifdef HAVE_LIBZ
166  z_stream *z = &z_strm;
167 
168  z->zalloc = 0;
169  z->zfree = 0;
170  z->opaque = 0;
171  z->next_in = NULL;
172  z->next_out = NULL;
173  z->avail_in = 0;
174 
175  crc = crc32(0L, Z_NULL, 0);
176 
177  if (deflateInit2(z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
178  (job->common->errorfn) ("Error initializing for deflation\n");
179  return(1);
180  }
181  gvwrite_no_z(job, z_file_header, sizeof(z_file_header));
182 #else
183  (job->common->errorfn) ("No libz support.\n");
184  return(1);
185 #endif
186  }
187  return 0;
188 }
189 
190 size_t gvwrite (GVJ_t * job, const char *s, size_t len)
191 {
192  size_t ret, olen;
193 
194  if (!len || !s)
195  return 0;
196 
197  if (job->flags & GVDEVICE_COMPRESSED_FORMAT) {
198 #ifdef HAVE_LIBZ
199  z_streamp z = &z_strm;
200  size_t dflen;
201 
202 #ifdef HAVE_DEFLATEBOUND
203  dflen = deflateBound(z, len);
204 #else
205  /* deflateBound() is not available in older libz, e.g. from centos3 */
206  dflen = 2 * len + dfallocated - z->avail_out;
207 #endif
208  if (dfallocated < dflen) {
209  dfallocated = (dflen + 1 + PAGE_ALIGN) & ~PAGE_ALIGN;
210  df = realloc(df, dfallocated);
211  if (! df) {
212  (job->common->errorfn) ("memory allocation failure\n");
213  exit(1);
214  }
215  }
216 
217  crc = crc32(crc, (unsigned char*)s, len);
218 
219  z->next_in = (unsigned char*)s;
220  z->avail_in = len;
221  while (z->avail_in) {
222  z->next_out = df;
223  z->avail_out = dfallocated;
224  ret=deflate (z, Z_NO_FLUSH);
225  if (ret != Z_OK) {
226  (job->common->errorfn) ("deflation problem %d\n", ret);
227  exit(1);
228  }
229 
230  if ((olen = z->next_out - df)) {
231  ret = gvwrite_no_z (job, (char*)df, olen);
232  if (ret != olen) {
233  (job->common->errorfn) ("gvwrite_no_z problem %d\n", ret);
234  exit(1);
235  }
236  }
237  }
238 
239 #else
240  (job->common->errorfn) ("No libz support.\n");
241  exit(1);
242 #endif
243  }
244  else { /* uncompressed write */
245  ret = gvwrite_no_z (job, s, len);
246  if (ret != len) {
247  (job->common->errorfn) ("gvwrite_no_z problem %d\n", len);
248  exit(1);
249  }
250  }
251  return len;
252 }
253 
254 int gvferror (FILE* stream)
255 {
256  GVJ_t *job = (GVJ_t*)stream;
257 
258  if (!job->gvc->write_fn && !job->output_data)
259  return ferror(job->output_file);
260 
261  return 0;
262 }
263 
264 size_t gvfwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream)
265 {
266  assert(size = sizeof(char));
267  return gvwrite((GVJ_t*)stream, ptr, nmemb);
268 }
269 
270 int gvputs(GVJ_t * job, const char *s)
271 {
272  size_t len = strlen(s);
273 
274  if (gvwrite (job, s, len) != len) {
275  return EOF;
276  }
277  return +1;
278 }
279 
280 int gvputc(GVJ_t * job, int c)
281 {
282  const char cc = c;
283 
284  if (gvwrite (job, &cc, 1) != 1) {
285  return EOF;
286  }
287  return c;
288 }
289 
290 int gvflush (GVJ_t * job)
291 {
292  if (job->output_file
293  && ! job->external_context
294  && ! job->gvc->write_fn) {
295  return fflush(job->output_file);
296  }
297  else
298  return 0;
299 }
300 
301 static void gvdevice_close(GVJ_t * job)
302 {
303  if (job->output_filename
304  && job->output_file != stdout
305  && ! job->external_context) {
306  if (job->output_file) {
307  fclose(job->output_file);
308  job->output_file = NULL;
309  }
310  job->output_filename = NULL;
311  }
312 }
313 
315 {
316  gvdevice_engine_t *gvde = job->device.engine;
317 
318  if (gvde && gvde->format)
319  gvde->format(job);
320  gvflush (job);
321 }
322 
324 {
325  gvdevice_engine_t *gvde = job->device.engine;
326  boolean finalized_p = FALSE;
327 
328  if (job->flags & GVDEVICE_COMPRESSED_FORMAT) {
329 #ifdef HAVE_LIBZ
330  z_streamp z = &z_strm;
331  unsigned char out[8] = "";
332  int ret;
333  int cnt = 0;
334 
335  z->next_in = out;
336  z->avail_in = 0;
337  z->next_out = df;
338  z->avail_out = dfallocated;
339  while ((ret = deflate (z, Z_FINISH)) == Z_OK && (cnt++ <= 100)) {
340  gvwrite_no_z(job, (char*)df, z->next_out - df);
341  z->next_out = df;
342  z->avail_out = dfallocated;
343  }
344  if (ret != Z_STREAM_END) {
345  (job->common->errorfn) ("deflation finish problem %d cnt=%d\n", ret, cnt);
346  exit(1);
347  }
348  gvwrite_no_z(job, (char*)df, z->next_out - df);
349 
350  ret = deflateEnd(z);
351  if (ret != Z_OK) {
352  (job->common->errorfn) ("deflation end problem %d\n", ret);
353  exit(1);
354  }
355  out[0] = crc;
356  out[1] = crc >> 8;
357  out[2] = crc >> 16;
358  out[3] = crc >> 24;
359  out[4] = z->total_in;
360  out[5] = z->total_in >> 8;
361  out[6] = z->total_in >> 16;
362  out[7] = z->total_in >> 24;
363  gvwrite_no_z(job, (char*)out, sizeof(out));
364 #else
365  (job->common->errorfn) ("No libz support\n");
366  exit(1);
367 #endif
368  }
369 
370  if (gvde) {
371  if (gvde->finalize) {
372  gvde->finalize(job);
373  finalized_p = TRUE;
374  }
375  }
376 
377  if (! finalized_p) {
378  /* if the device has no finalization then it uses file output */
379  gvflush (job);
380  gvdevice_close(job);
381  }
382 }
383 /* gvprintf:
384  * Unless vsnprintf is available, this function is unsafe due to the fixed buffer size.
385  * It should only be used when the caller is sure the input will not
386  * overflow the buffer. In particular, it should be avoided for
387  * input coming from users.
388  */
389 void gvprintf(GVJ_t * job, const char *format, ...)
390 {
391  char buf[BUFSIZ];
392  int len;
393  va_list argp;
394  char* bp = buf;
395 
396  va_start(argp, format);
397 #ifdef HAVE_VSNPRINTF
398  len = vsnprintf((char *)buf, BUFSIZ, format, argp);
399  if (len < 0) {
400  agerr (AGERR, "gvprintf: %s\n", strerror(errno));
401  return;
402  }
403  else if (len >= BUFSIZ) {
404  /* C99 vsnprintf returns the length that would be required
405  * to write the string without truncation.
406  */
407  bp = gmalloc(len + 1);
408  va_end(argp);
409  va_start(argp, format);
410  len = vsprintf(bp, format, argp);
411  }
412 #else
413  len = vsprintf((char *)buf, format, argp);
414 #endif
415  va_end(argp);
416 
417  gvwrite(job, bp, len);
418  if (bp != buf)
419  free (bp);
420 }
421 
422 
423 /* Test with:
424  * cc -DGVPRINTNUM_TEST gvprintnum.c -o gvprintnum
425  */
426 
427 #define DECPLACES 4
428 #define DECPLACES_SCALE 10000
429 
430 /* use macro so maxnegnum is stated just once for both double and string versions */
431 #define val_str(n, x) static double n = x; static char n##str[] = #x;
432 val_str(maxnegnum, -999999999999999.99)
433 
434 /* we use len and don't need the string to be terminated */
435 /* #define TERMINATED_NUMBER_STRING */
436 
437 /* Note. Returned string is only good until the next call to gvprintnum */
438 static char * gvprintnum (size_t *len, double number)
439 {
440  static char tmpbuf[sizeof(maxnegnumstr)]; /* buffer big enough for worst case */
441  char *result = tmpbuf+sizeof(maxnegnumstr); /* init result to end of tmpbuf */
442  long int N;
443  boolean showzeros, negative;
444  int digit, i;
445 
446  /*
447  number limited to a working range: maxnegnum >= n >= -maxnegnum
448  N = number * DECPLACES_SCALE rounded towards zero,
449  printing to buffer in reverse direction,
450  printing "." after DECPLACES
451  suppressing trailing "0" and "."
452  */
453 
454  if (number < maxnegnum) { /* -ve limit */
455  *len = sizeof(maxnegnumstr)-1; /* len doesn't include terminator */
456  return maxnegnumstr;;
457  }
458  if (number > -maxnegnum) { /* +ve limit */
459  *len = sizeof(maxnegnumstr)-2; /* len doesn't include terminator or sign */
460  return maxnegnumstr+1; /* +1 to skip the '-' sign */
461  }
462  number *= DECPLACES_SCALE; /* scale by DECPLACES_SCALE */
463  if (number < 0.0) /* round towards zero */
464  N = number - 0.5;
465  else
466  N = number + 0.5;
467  if (N == 0) { /* special case for exactly 0 */
468  *len = 1;
469  return "0";
470  }
471  if ((negative = (N < 0))) /* avoid "-0" by testing rounded int */
472  N = -N; /* make number +ve */
473 #ifdef TERMINATED_NUMBER_STRING
474  *--result = '\0'; /* terminate the result string */
475 #endif
476  showzeros = FALSE; /* don't print trailing zeros */
477  for (i = DECPLACES; N || i > 0; i--) { /* non zero remainder,
478  or still in fractional part */
479  digit = N % 10; /* next least-significant digit */
480  N /= 10;
481  if (digit || showzeros) { /* if digit is non-zero,
482  or if we are printing zeros */
483  *--result = digit | '0'; /* convert digit to ascii */
484  showzeros = TRUE; /* from now on we must print zeros */
485  }
486  if (i == 1) { /* if completed fractional part */
487  if (showzeros) /* if there was a non-zero fraction */
488  *--result = '.'; /* print decimal point */
489  showzeros = TRUE; /* print all digits in int part */
490  }
491  }
492  if (negative) /* print "-" if needed */
493  *--result = '-';
494 #ifdef TERMINATED_NUMBER_STRING
495  *len = tmpbuf+sizeof(maxnegnumstr)-1 - result;
496 #else
497  *len = tmpbuf+sizeof(maxnegnumstr) - result;
498 #endif
499  return result;
500 }
501 
502 
503 #ifdef GVPRINTNUM_TEST
504 int main (int argc, char *argv[])
505 {
506  char *buf;
507  size_t len;
508 
509  double test[] = {
510  -maxnegnum*1.1, -maxnegnum*.9,
511  1e8, 10.008, 10, 1, .1, .01,
512  .006, .005, .004, .001, 1e-8,
513  0, -0,
514  -1e-8, -.001, -.004, -.005, -.006,
515  -.01, -.1, -1, -10, -10.008, -1e8,
516  maxnegnum*.9, maxnegnum*1.1
517  };
518  int i = sizeof(test) / sizeof(test[0]);
519 
520  while (i--) {
521  buf = gvprintnum(&len, test[i]);
522  fprintf (stdout, "%g = %s %d\n", test[i], buf, len);
523  }
524 
525  return 0;
526 }
527 #endif
528 
529 /* gv_trim_zeros
530 * Trailing zeros are removed and decimal point, if possible.
531 * Add trailing space if addSpace is non-zero.
532 */
533 static void gv_trim_zeros(char* buf, int addSpace)
534 {
535  char* dotp;
536  char* p;
537 
538  if ((dotp = strchr(buf, '.'))) {
539  p = dotp + 1;
540  while (*p) p++; // find end of string
541  p--;
542  while (*p == '0') *p-- = '\0';
543  if (*p == '.') // If all decimals were zeros, remove ".".
544  *p = '\0';
545  else
546  p++;
547  }
548  else if (addSpace)
549  p = buf + strlen(buf);
550 
551  if (addSpace) { /* p points to null byte */
552  *p++ = ' ';
553  *p = '\0';
554  }
555 }
556 
557 void gvprintdouble(GVJ_t * job, double num)
558 {
559  // Prevents values like -0
560  if (num > -0.00000001 && num < 0.00000001)
561  {
562  num = 0;
563  }
564 
565  char buf[50];
566 
567  snprintf(buf, 50, "%.02f", num);
568  gv_trim_zeros(buf, 0);
569 
570  gvwrite(job, buf, strlen(buf));
571 }
572 
573 void gvprintpointf(GVJ_t * job, pointf p)
574 {
575  char *buf;
576  size_t len;
577 
578  buf = gvprintnum(&len, p.x);
579  gvwrite(job, buf, len);
580  gvwrite(job, " ", 1);
581  buf = gvprintnum(&len, p.y);
582  gvwrite(job, buf, len);
583 }
584 
585 void gvprintpointflist(GVJ_t * job, pointf *p, int n)
586 {
587  int i = 0;
588 
589  while (TRUE) {
590  gvprintpointf(job, p[i]);
591  if (++i >= n) break;
592  gvwrite(job, " ", 1);
593  }
594 }
595 
Definition: cgraph.h:388
int gvputc(GVJ_t *job, int c)
Definition: gvdevice.c:280
unsigned int output_data_allocated
Definition: gvcjob.h:288
#define GVDEVICE_BINARY_FORMAT
Definition: gvcjob.h:93
void gvprintpointflist(GVJ_t *job, pointf *p, int n)
Definition: gvdevice.c:585
#define assert(x)
Definition: cghdr.h:47
Definition: geom.h:28
char * input_filename
Definition: gvcjob.h:280
void * gmalloc(size_t nbytes)
Definition: memory.c:42
boolean auto_outfile_names
Definition: gvcommon.h:25
const char * output_filename
Definition: gvcjob.h:285
int agerr(agerrlevel_t level, const char *fmt,...)
Definition: agerror.c:141
int flags
Definition: gvcjob.h:308
#define DECPLACES
Definition: gvdevice.c:427
Definition: gvcjob.h:271
void(* finalize)(GVJ_t *firstjob)
gvplugin_active_device_t device
Definition: gvcjob.h:295
int gvputs(GVJ_t *job, const char *s)
Definition: gvdevice.c:270
char * output_data
Definition: gvcjob.h:287
#define DECPLACES_SCALE
Definition: gvdevice.c:428
unsigned int output_data_position
Definition: gvcjob.h:289
int graph_index
Definition: gvcjob.h:281
void gvdevice_finalize(GVJ_t *job)
Definition: gvdevice.c:323
double y
Definition: geom.h:28
Definition: gvcint.h:70
GVCOMMON_t * common
Definition: gvcjob.h:276
void gvprintpointf(GVJ_t *job, pointf p)
Definition: gvdevice.c:573
#define static
Definition: dtlist.c:171
GVC_t * gvc
Definition: gvcjob.h:272
#define val_str(n, x)
Definition: gvdevice.c:431
Definition: grammar.c:79
void(* errorfn)(const char *fmt,...)
Definition: gvcommon.h:26
void gvprintdouble(GVJ_t *job, double num)
Definition: gvdevice.c:557
int gvferror(FILE *stream)
Definition: gvdevice.c:254
#define NULL
Definition: logic.h:39
double x
Definition: geom.h:28
boolean external_context
Definition: gvcjob.h:305
void gvdevice_format(GVJ_t *job)
Definition: gvdevice.c:314
GVC_t * gvc
Definition: htmlparse.c:87
void(* initialize)(GVJ_t *firstjob)
size_t gvfwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
Definition: gvdevice.c:264
#define GVDEVICE_COMPRESSED_FORMAT
Definition: gvcjob.h:94
int gvflush(GVJ_t *job)
Definition: gvdevice.c:290
GVCOMMON_t common
Definition: gvcint.h:71
void(* format)(GVJ_t *firstjob)
int gvdevice_initialize(GVJ_t *job)
Definition: gvdevice.c:126
gvdevice_engine_t * engine
Definition: gvcjob.h:131
FILE * output_file
Definition: gvcjob.h:286
#define FALSE
Definition: cgraph.h:35
int main(int argc, char **argv)
Definition: dot.c:95
const char * output_langname
Definition: gvcjob.h:291
size_t gvwrite(GVJ_t *job, const char *s, size_t len)
Definition: gvdevice.c:190
size_t(* write_fn)(GVJ_t *job, const char *s, size_t len)
Definition: gvcint.h:92
void gvprintf(GVJ_t *job, const char *format,...)
Definition: gvdevice.c:389
#define TRUE
Definition: cgraph.h:38