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