• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 24927520513

25 Apr 2026 08:55AM UTC coverage: 92.199% (-0.007%) from 92.206%
24927520513

push

github

nickg
Avoid division by zero when simplifying Verilog expression

2 of 2 new or added lines in 1 file covered. (100.0%)

32 existing lines in 4 files now uncovered.

77119 of 83644 relevant lines covered (92.2%)

589801.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.33
/src/printf.c
1
//
2
//  Copyright (C) 2025-2026  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "ident.h"
20
#include "mir/mir-node.h"
21
#include "object.h"
22
#include "printf.h"
23
#include "thread.h"
24
#include "type.h"
25

26
#include <assert.h>
27
#include <stdio.h>
28
#include <string.h>
29
#include <stdlib.h>
30
#include <unistd.h>
31
#include <limits.h>
32

33
#define ANSI_RESET      0
34
#define ANSI_BOLD       1
35
#define ANSI_FG_BLACK   30
36
#define ANSI_FG_RED     31
37
#define ANSI_FG_GREEN   32
38
#define ANSI_FG_YELLOW  33
39
#define ANSI_FG_BLUE    34
40
#define ANSI_FG_MAGENTA 35
41
#define ANSI_FG_CYAN    36
42
#define ANSI_FG_WHITE   37
43

44
#define MAX_ARGS 25
45

46
typedef struct _printf_state printf_state_t;
47
typedef struct _printf_arg printf_arg_t;
48

49
typedef union {
50
   long long  ll;
51
   long       l;
52
   size_t     z;
53
   double     f;
54
   int        i;
55
   void      *p;
56
} printf_value_t;
57

58
typedef int (*fmt_fn_t)(ostream_t *, printf_state_t *, printf_arg_t *);
59

60
typedef struct _printf_arg {
61
   const char     *start;
62
   size_t          len;
63
   fmt_fn_t        fn;
64
   printf_value_t  value;
65
   int             precision;
66
} printf_arg_t;
67

68
typedef struct _printf_state {
69
   printf_arg_t args[MAX_ARGS];
70
   unsigned     nargs;
71
   unsigned     pos;
72
} printf_state_t;
73

74
typedef struct {
75
   const char *name;
76
   int         value;
77
} color_escape_t;
78

79
static const color_escape_t escapes[] = {
80
   { "",        ANSI_RESET },
81
   { "bold",    ANSI_BOLD },
82
   { "black",   ANSI_FG_BLACK },
83
   { "red",     ANSI_FG_RED },
84
   { "green",   ANSI_FG_GREEN },
85
   { "yellow",  ANSI_FG_YELLOW },
86
   { "blue",    ANSI_FG_BLUE },
87
   { "magenta", ANSI_FG_MAGENTA },
88
   { "cyan",    ANSI_FG_CYAN },
89
   { "white",   ANSI_FG_WHITE },
90
};
91

92
static int printf_interpret(ostream_t *os, printf_state_t *state,
93
                            const char *fmt, const char *end);
94

95
int ostream_write(ostream_t *os, const char *buf, size_t len)
2,326,914✔
96
{
97
   (*os->callback)(buf, len, os->context);
2,326,914✔
98
   return len;
2,326,914✔
99
}
100

101
int ostream_putc(ostream_t *os, char ch)
801,297✔
102
{
103
   char buf[1] = { ch };
801,297✔
104
   return ostream_write(os, buf, 1);
801,297✔
105
}
106

107
int ostream_puts(ostream_t *os, const char *str)
11,699✔
108
{
109
   if (str == NULL)
11,699✔
110
      return ostream_write(os, "(null)", 6);
×
111
   else
112
      return ostream_write(os, str, strlen(str));
11,699✔
113
}
114

115
static int format_ident(ostream_t *os, printf_state_t *state, printf_arg_t *arg)
180✔
116
{
117
   ident_t id = arg->value.p;
180✔
118
   return ostream_write(os, istr(id), ident_len(id));
180✔
119
}
120

121
static int format_ident_toupper(ostream_t *os, printf_state_t *state,
225✔
122
                                printf_arg_t *arg)
123
{
124
   ident_t id = arg->value.p;
225✔
125
   size_t len = ident_len(id);
225✔
126
   const char *str = istr(id);
225✔
127
   int nchars = 0;
225✔
128
   char chunk[32];
225✔
129

130
   for (int i = 0; i < len; i += sizeof(chunk)) {
450✔
131
      const int tocopy = MIN(sizeof(chunk), len - i);
225✔
132
      for (int j = 0; j < tocopy; j++)
1,057✔
133
         chunk[j] = toupper_iso88591(str[i + j]);
832✔
134

135
      nchars += ostream_write(os, chunk, tocopy);
225✔
136
   }
137

138
   return nchars;
225✔
139
}
140

141
static int format_type(ostream_t *os, printf_state_t *state, printf_arg_t *arg)
228✔
142
{
143
   type_t type = arg->value.p;
228✔
144

145
   // Print a fully qualified name if there is another type in the
146
   // argument list with the same simple name
147

148
   for (int i = 0; i < state->nargs; i++) {
443✔
149
      const printf_arg_t *other = &(state->args[i]);
353✔
150
      if (other != arg && other->fn == format_type)
353✔
151
         return ostream_puts(os, type_pp2(type, other->value.p));
138✔
152
   }
153

154
   return ostream_puts(os, type_pp(type));
90✔
155
}
156

157
static int format_object_kind(ostream_t *os, printf_state_t *state,
×
158
                              printf_arg_t *arg)
159
{
160
   object_t *obj = arg->value.p;
×
161
   return ostream_puts(os, object_kind_str(obj));
×
162
}
163

164
static int format_mir(ostream_t *os, printf_state_t *state, printf_arg_t *arg)
12✔
165
{
166
   mir_value_t *value = arg->value.p;
12✔
167
   char buf[64];
12✔
168

169
   switch (value->tag) {
12✔
170
   case MIR_TAG_NODE:
×
171
      checked_sprintf(buf, sizeof(buf), "%%%d", value->id);
×
172
      break;
×
173
   case MIR_TAG_CONST:
12✔
174
      checked_sprintf(buf, sizeof(buf), "#%d",
12✔
175
                      value->id - (1 << (_MIR_ID_BITS - 1)));
12✔
176
      break;
12✔
177
   default:
×
178
      checked_sprintf(buf, sizeof(buf), "{tag:%d, id:%x}",
×
179
                      value->tag, value->id);
×
180
      break;
×
181
   }
182

183
   return ostream_puts(os, buf);
12✔
184
}
185

186
static int delegate(ostream_t *os, printf_state_t *s, printf_arg_t *arg, ...)
770,312✔
187
{
188
   char spec[32];
770,312✔
189
   assert(arg->len + 1 < sizeof(spec));
770,312✔
190
   memcpy(spec, arg->start, arg->len);
770,312✔
191
   spec[arg->len] = '\0';
770,312✔
192

193
   va_list ap, ap2;
770,312✔
194
   va_start(ap, arg);
770,312✔
195
   va_copy(ap2, ap);
770,312✔
196

197
   char small[64];
770,312✔
198
   int req = vsnprintf(small, sizeof(small), spec, ap);
770,312✔
199

200
   if (req + 1 > sizeof(small)) {
770,312✔
201
      char *large = xmalloc(req + 1);
880✔
202
      vsnprintf(large, req + 1, spec, ap2);
880✔
203
      ostream_write(os, large, req);
880✔
204
      free(large);
880✔
205
   }
206
   else
207
      ostream_write(os, small, req);
769,432✔
208

209
   va_end(ap);
770,312✔
210
   va_end(ap2);
770,312✔
211
   return req;
770,312✔
212
}
213

214
static fmt_fn_t get_pointer_formatter(char ch)
650✔
215
{
216
   switch (ch) {
650✔
217
   case 'i': return format_ident;
218
   case 'I': return format_ident_toupper;
225✔
219
   case 'T': return format_type;
228✔
220
   case 'K': return format_object_kind;
×
221
   case 'M': return format_mir;
12✔
222
   default: return NULL;
5✔
223
   }
224
}
225

226
static int ansi_escape(ostream_t *os, printf_state_t *state, const char **fmt)
204,484✔
227
{
228
   bool has_format = false;
204,484✔
229
   const char *start = *fmt;
204,484✔
230
   do {
1,074,042✔
231
      has_format |= (**fmt == '%');
1,074,042✔
232
      (*fmt)++;
1,074,042✔
233
   } while (**fmt != '\0' && **fmt != '$');
1,074,042✔
234

235
   if (**fmt == '\0')
204,484✔
236
      return ostream_write(os, start, *fmt - start);
129✔
237

238
   const char *end = *fmt;
204,355✔
239
   (*fmt)++;   // Advance past final '$'
204,355✔
240

241
   size_t len = len = *fmt - start - 2;
204,355✔
242
   const char *e = e = start + 1;
204,355✔
243
   LOCAL_TEXT_BUF tb = NULL;
408,710✔
244

245
   if (has_format) {
204,355✔
246
      // Expand any embedded formatting inside the ANSI escape
247
      tb = tb_new();
22,038✔
248

249
      ostream_t aos = {
22,038✔
250
         tb_ostream_write,
251
         tb,
252
         os->charset,
22,038✔
253
      };
254

255
      printf_interpret(&aos, state, start + 1, end);
22,038✔
256

257
      e = tb_get(tb);
22,038✔
258
      len = tb_len(tb);
22,038✔
259
   }
260

261
   bool bold;
204,355✔
262
   if ((bold = (*e == '!')))
204,355✔
263
      ++e, --len;
2✔
264

265
   bool bright;
204,355✔
266
   if ((bright = (*e == '+')))
204,355✔
267
      ++e, --len;
39✔
268

269
   if (*e == '#') {
204,355✔
270
      char *eptr;
1✔
271
      int code = strtoul(e + 1, &eptr, 10);
1✔
272
      if (eptr == e + len) {
1✔
273
         char buf[16];
1✔
274
         if (bold)
1✔
275
            checked_sprintf(buf, sizeof(buf), "\033[1;38;5;%dm", code);
×
276
         else
277
            checked_sprintf(buf, sizeof(buf), "\033[38;5;%dm", code);
1✔
278

279
         if (os->flags & OS_COLOR)
1✔
280
            ostream_puts(os, buf);
1✔
281

282
         return 0;
1✔
283
      }
284
   }
285

286
   if (strncmp(e, "link:", 5) == 0) {
204,354✔
287
      const char *bel = strchr(e, '\07');
22,039✔
288

289
#ifndef __MINGW32__    // Winpty doesn't recognise these
290
      if (os->flags & OS_TERMINAL) {
22,039✔
291
         ostream_puts(os, "\033]8;;");
2✔
292
         ostream_write(os, e + 5, len - 5);
2✔
293
         ostream_puts(os, "\033]8;;\07");
2✔
294
         return e + len - bel - 1;
2✔
295
      }
296
#endif
297

298
      return ostream_write(os, bel + 1, e + len - bel - 1);
22,037✔
299
   }
300

301
   for (int i = 0; i < ARRAY_LEN(escapes); i++) {
689,552✔
302
      if (strncmp(e, escapes[i].name, len) == 0) {
689,550✔
303
         int code = escapes[i].value + (bright ? 60 : 0);
182,313✔
304
         char buf[16];
182,313✔
305
         if (bold)
182,313✔
306
            checked_sprintf(buf, sizeof(buf), "\033[1;%dm", code);
1✔
307
         else
308
            checked_sprintf(buf, sizeof(buf), "\033[%dm", code);
182,312✔
309

310
         if (os->flags & OS_COLOR)
182,313✔
311
            ostream_puts(os, buf);
1,839✔
312

313
         return 0;
182,313✔
314
      }
315
   }
316

317
   return ostream_write(os, start, *fmt - start);
2✔
318
}
319

320
static int format_z(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
147,559✔
321
{
322
   assert(arg->precision == INT_MIN);
147,559✔
323
   return delegate(os, s, arg, arg->value.z);
147,559✔
324
}
325

326
static int format_ll(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
×
327
{
328
   if (arg->precision != INT_MIN)
×
329
      return delegate(os, s, arg, arg->precision, arg->value.ll);
×
330
   else
331
      return delegate(os, s, arg, arg->value.ll);
×
332
}
333

334
static int format_l(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
44,242✔
335
{
336
   if (arg->precision != INT_MIN)
44,242✔
337
      return delegate(os, s, arg, arg->precision, arg->value.l);
10,569✔
338
   else
339
      return delegate(os, s, arg, arg->value.l);
33,673✔
340
}
341

342
static int format_i(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
95,663✔
343
{
344
   if (arg->precision != INT_MIN)
95,663✔
345
      return delegate(os, s, arg, arg->precision, arg->value.i);
13,652✔
346
   else
347
      return delegate(os, s, arg, arg->value.i);
82,011✔
348
}
349

350
static int format_f(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
880✔
351
{
352
   if (arg->precision != INT_MIN)
880✔
353
      return delegate(os, s, arg, arg->precision, arg->value.f);
9✔
354
   else
355
      return delegate(os, s, arg, arg->value.f);
871✔
356
}
357

358
static int format_s(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
481,963✔
359
{
360
   if (arg->precision != INT_MIN)
481,963✔
361
      return delegate(os, s, arg, arg->precision, arg->value.p);
55,291✔
362
   else
363
      return delegate(os, s, arg, arg->value.p);
426,672✔
364
}
365

366
static int format_p(ostream_t *os, printf_state_t *s, printf_arg_t *arg)
5✔
367
{
368
   return delegate(os, s, arg, arg->value.p);
5✔
369
}
370

371
static int printf_interpret(ostream_t *os, printf_state_t *state,
626,977✔
372
                            const char *fmt, const char *end)
373
{
374
   int nchars = 0;
626,977✔
375
   for (const char *p = fmt;;) {
626,977✔
376
      const char *start = p;
1,619,302✔
377
      while (p < end && *p != '%' && *p != '$')
3,860,120✔
378
         p++;
2,240,818✔
379

380
      if (start < p)
1,619,302✔
381
         nchars += ostream_write(os, start, p - start);
674,080✔
382

383
      if (p == end)
1,619,302✔
384
         return nchars;
626,977✔
385
      else if (*p == '$') {
992,325✔
386
         nchars += ansi_escape(os, state, &p);
204,484✔
387
         continue;
204,484✔
388
      }
389
      else if (*p == '%' && *(p + 1) == '%') {
787,841✔
390
         nchars += ostream_write(os, "%", 1);
16,884✔
391
         p += 2;
16,884✔
392
         continue;
16,884✔
393
      }
394

395
      assert(state->pos < state->nargs);
770,957✔
396

397
      printf_arg_t *arg = &(state->args[state->pos]);
770,957✔
398
      nchars += (*arg->fn)(os, state, arg);
770,957✔
399
      p += arg->len;
770,957✔
400

401
      state->pos++;
770,957✔
402
   }
403
}
404

405
int nvc_vfprintf(ostream_t *os, const char *fmt, va_list ap)
604,939✔
406
{
407
   printf_state_t state = {};
604,939✔
408

409
   const char *end = NULL;
604,939✔
410
   for (const char *p = fmt;;) {
604,939✔
411
      while (*p != '\0' && *p != '%')
4,405,134✔
412
         p++;
3,012,354✔
413

414
      if (*p == '\0') {
1,392,780✔
415
         end = p;
604,939✔
416
         break;
604,939✔
417
      }
418

419
      printf_arg_t arg = { .start = p, .precision = INT_MIN };
787,841✔
420
      bool z_mod = false;
787,841✔
421
      int l_mod = 0;
787,841✔
422
   again:
1,085,959✔
423
      switch (*++p) {
1,085,959✔
424
      case 'l':
44,244✔
425
         l_mod++;
44,244✔
426
         goto again;
44,244✔
427
      case 'z':
147,559✔
428
         z_mod = true;
147,559✔
429
         goto again;
147,559✔
430
      case '-':
26,794✔
431
      case '+':
432
      case '.':
433
      case '0'...'9':
434
         goto again;
26,794✔
435
      case '*':
79,521✔
436
         arg.precision = va_arg(ap, int);
79,521✔
437
         goto again;
79,521✔
438
      case 'd':
285,998✔
439
      case 'i':
440
      case 'x':
441
      case 'u':
442
      case 'o':
443
         if (z_mod) {
285,998✔
444
            arg.value.z = va_arg(ap, size_t);
147,559✔
445
            arg.fn = format_z;
147,559✔
446
         }
447
         else if (l_mod >= 2) {
138,439✔
448
            arg.value.ll = va_arg(ap, long long);
×
UNCOV
449
            arg.fn = format_ll;
×
450
         }
451
         else if (l_mod == 1) {
138,439✔
452
            arg.value.l = va_arg(ap, long);
44,242✔
453
            arg.fn = format_l;
44,242✔
454
         }
455
         else {
456
            arg.value.i = va_arg(ap, int);
94,197✔
457
            arg.fn = format_i;
94,197✔
458
         }
459
         break;
460
      case 'c':
1,466✔
461
         arg.value.i = va_arg(ap, int);
1,466✔
462
         arg.fn = format_i;
1,466✔
463
         break;
1,466✔
464
      case 'e':
880✔
465
      case 'f':
466
      case 'g':
467
         arg.value.f = va_arg(ap, double);
880✔
468
         arg.fn = format_f;
880✔
469
         break;
880✔
470
      case 's':
481,963✔
471
         arg.value.p = va_arg(ap, char *);
481,963✔
472
         arg.fn = format_s;
481,963✔
473
         break;
481,963✔
474
      case 'p':
650✔
475
         arg.value.p = va_arg(ap, void *);
650✔
476
         if ((arg.fn = get_pointer_formatter(p[1])))
650✔
477
            p++;
645✔
478
         else
479
            arg.fn = format_p;
480
         break;
481
      case '%':
16,884✔
482
         p++;
16,884✔
483
         continue;
16,884✔
UNCOV
484
      default:
×
485
         fatal_trace("unhandled character '%c' in format", *p);
486
      }
487

488
      arg.len = ++p - arg.start;
770,957✔
489

490
      if (state.nargs == MAX_ARGS)
770,957✔
491
         fatal_trace("maximum of %d printf arguments supported", MAX_ARGS);
492
      else
493
         state.args[state.nargs++] = arg;
770,957✔
494
   }
495

496
   return printf_interpret(os, &state, fmt, end);
1,209,878✔
497
}
498

499
int nvc_vprintf(const char *fmt, va_list ap)
8✔
500
{
501
   return nvc_vfprintf(nvc_stdout(), fmt, ap);
8✔
502
}
503

504
int nvc_printf(const char *fmt, ...)
8✔
505
{
506
   va_list ap;
8✔
507
   va_start(ap, fmt);
8✔
508
   const int nchars = nvc_vprintf(fmt, ap);
8✔
509
   va_end(ap);
8✔
510
   return nchars;
8✔
511
}
512

513
int nvc_fprintf(ostream_t *os, const char *fmt, ...)
171,852✔
514
{
515
   va_list ap;
171,852✔
516
   va_start(ap, fmt);
171,852✔
517
   const int nchars = nvc_vfprintf(os, fmt, ap);
171,852✔
518
   va_end(ap);
171,852✔
519
   return nchars;
171,852✔
520
}
521

522
void stdio_ostream_write(const char *buf, size_t len, void *ctx)
1,166,020✔
523
{
524
   FILE *f = ctx;
1,166,020✔
525
   fwrite(buf, len, 1, f);
1,166,020✔
526
}
1,166,020✔
527

528
static void init_terminal_ostream(ostream_t *os, FILE *f)
3,969✔
529
{
530
   os->callback = stdio_ostream_write;
3,969✔
531
   os->context = f;
3,969✔
532
   os->flags = 0;
3,969✔
533

534
   if (isatty(fileno(f))) {
3,969✔
535
      os->charset = utf8_terminal() ? CHARSET_UTF8 : CHARSET_ISO88591;
×
UNCOV
536
      os->flags |= OS_TERMINAL;
×
537
   }
538
   else
539
      os->charset = CHARSET_ISO88591;
3,969✔
540

541
   if (color_terminal())
3,969✔
542
      os->flags |= OS_COLOR;
4✔
543
}
3,969✔
544

545
ostream_t *nvc_stdout(void)
226✔
546
{
547
   static ostream_t os;
226✔
548
   INIT_ONCE(init_terminal_ostream(&os, stdout));
226✔
549
   return &os;
226✔
550
}
551

552
ostream_t *nvc_stderr(void)
62,241✔
553
{
554
   static ostream_t os;
62,241✔
555
   INIT_ONCE(init_terminal_ostream(&os, stderr));
62,241✔
556
   return &os;
62,241✔
557
}
558

559
char *color_asprintf(const char *fmt, ...)
31✔
560
{
561
   LOCAL_TEXT_BUF tb = tb_new();
62✔
562
   ostream_t os = { tb_ostream_write, tb, CHARSET_ISO88591, OS_COLOR };
31✔
563

564
   va_list ap;
31✔
565
   va_start(ap, fmt);
31✔
566
   nvc_vfprintf(&os, fmt, ap);
31✔
567
   va_end(ap);
31✔
568

569
   return tb_claim(tb);
31✔
570
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc