File: agerror.c

package info (click to toggle)
graphviz 14.0.5-2
  • links: PTS
  • area: main
  • in suites: forky
  • size: 139,388 kB
  • sloc: ansic: 141,938; cpp: 11,957; python: 7,766; makefile: 4,043; yacc: 3,030; xml: 2,972; tcl: 2,495; sh: 1,388; objc: 1,159; java: 560; lex: 423; perl: 243; awk: 156; pascal: 139; php: 58; ruby: 49; cs: 31; sed: 1
file content (187 lines) | stat: -rw-r--r-- 4,558 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
 * @file
 * @brief API: cgraph.h
 * @ingroup cgraph_utils
 */
/*************************************************************************
 * Copyright (c) 2011 AT&T Intellectual Property
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: Details at https://graphviz.org
 *************************************************************************/

#include <cgraph/cghdr.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <util/agxbuf.h>
#include <util/gv_ctype.h>
#include <util/gv_math.h>
#include <util/streq.h>

static agerrlevel_t agerrno;             /* Last error level */
static agerrlevel_t agerrlevel = AGWARN; /* Report errors >= agerrlevel */
static int agmaxerr;

static agxbuf last;         ///< last message
static agusererrf usererrf; /* User-set error function */

agusererrf agseterrf(agusererrf newf) {
  agusererrf oldf = usererrf;
  usererrf = newf;
  return oldf;
}

agerrlevel_t agseterr(agerrlevel_t lvl) {
  agerrlevel_t oldv = agerrlevel;
  agerrlevel = lvl;
  return oldv;
}

char *aglasterr(void) {
  // Extract a heap-allocated copy of the last message. Note that this resets
  // `last` to an empty buffer ready to be written to again.
  char *buf = agxbdisown(&last);

  // store the message back again so multiple calls to `aglasterr` can be made
  // without losing the last error
  agxbput(&last, buf);

  // was there no last message?
  if (streq(buf, "")) {
    free(buf);
    return NULL;
  }

  return buf;
}

/// default error reporting implementation
static int default_usererrf(char *message) {
  // `fputs`, escaping characters that may interfere with a terminal
  for (const char *p = message; *p != '\0'; ++p) {

    if (gv_iscntrl(*p) && !gv_isspace(*p)) {
      const int rc = fprintf(stderr, "\\%03o", (unsigned)*p);
      if (rc < 0) {
        return rc;
      }
      continue;
    }

    const int rc = putc(*p, stderr);
    if (rc < 0) {
      return rc;
    }
  }
  return 0;
}

/// Report messages using a user-supplied or default write function
static void out(agerrlevel_t level, const char *fmt, va_list args) {
  // find out how much space we need to construct this string
  size_t bufsz;
  {
    va_list args2;
    va_copy(args2, args);
    int rc = vsnprintf(NULL, 0, fmt, args2);
    va_end(args2);
    if (rc < 0) {
      fprintf(stderr, "%s: vsnprintf failure\n", __func__);
      return;
    }
    bufsz = (size_t)rc + 1; // account for NUL terminator
  }

  // allocate a buffer for the string
  char *buf = malloc(bufsz);
  if (buf == NULL) {
    fprintf(stderr, "%s: could not allocate memory\n", __func__);
    return;
  }

  // determine how errors are to be reported
  const agusererrf errf = usererrf ? usererrf : default_usererrf;

  if (level != AGPREV) {
    (void)errf(level == AGERR ? "Error" : "Warning");
    (void)errf(": ");
  }

  // construct the full error in our buffer
  int rc = vsnprintf(buf, bufsz, fmt, args);
  if (rc < 0) {
    free(buf);
    fprintf(stderr, "%s: vsnprintf failure\n", __func__);
    return;
  }

  // yield our constructed error
  (void)errf(buf);

  free(buf);
}

static int agerr_va(agerrlevel_t level, const char *fmt, va_list args) {
  agerrlevel_t lvl;

  /* Use previous error level if continuation message;
   * Convert AGMAX to AGERROR;
   * else use input level
   */
  lvl = (level == AGPREV ? agerrno : (level == AGMAX) ? AGERR : level);

  /* store this error level */
  agerrno = lvl;
  agmaxerr = imax(agmaxerr, (int)agerrno);

  /* We report all messages whose level is bigger than the user set agerrlevel
   * Setting agerrlevel to AGMAX turns off immediate error reporting.
   */
  if (lvl >= agerrlevel) {
    out(level, fmt, args);
    return 0;
  }

  if (level != AGPREV)
    agxbclear(&last);
  vagxbprint(&last, fmt, args);
  return 0;
}

int agerr(agerrlevel_t level, const char *fmt, ...) {
  va_list args;
  int ret;

  va_start(args, fmt);
  ret = agerr_va(level, fmt, args);
  va_end(args);
  return ret;
}

void agerrorf(const char *fmt, ...) {
  va_list args;

  va_start(args, fmt);
  agerr_va(AGERR, fmt, args);
  va_end(args);
}

void agwarningf(const char *fmt, ...) {
  va_list args;

  va_start(args, fmt);
  agerr_va(AGWARN, fmt, args);
  va_end(args);
}

int agerrors(void) { return agmaxerr; }

int agreseterrors(void) {
  int rc = agmaxerr;
  agmaxerr = 0;
  return rc;
}