1 /* $NetBSD: print.c,v 1.18 2026/06/10 20:54:16 christos Exp $ */ 2 3 /* 4 * Copyright (c) Ian F. Darwin 1986-1995. 5 * Software written by Ian F. Darwin and others; 6 * maintained 1995-present by Christos Zoulas and others. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice immediately at the beginning of the file, without modification, 13 * this list of conditions, and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 /* 31 * print.c - debugging printout routines 32 */ 33 34 #include "file.h" 35 36 #ifndef lint 37 #if 0 38 FILE_RCSID("@(#)$File: print.c,v 1.111 2026/04/19 19:56:49 christos Exp $") 39 #else 40 __RCSID("$NetBSD: print.c,v 1.18 2026/06/10 20:54:16 christos Exp $"); 41 #endif 42 #endif /* lint */ 43 44 #include <string.h> 45 #include <stdarg.h> 46 #include <stdlib.h> 47 #ifdef HAVE_UNISTD_H 48 #include <unistd.h> 49 #endif 50 #include <time.h> 51 52 #include "cdf.h" 53 54 #ifndef COMPILE_ONLY 55 file_protected void 56 file_mdump(struct magic *m) 57 { 58 static const char optyp[] = { FILE_OPS }; 59 char tbuf[256]; 60 61 (void) fprintf(stderr, "%s, %u: %.*s %d", 62 m->desc[0] == '\0' ? m->desc + 1 : "*unknown*", m->lineno, 63 (m->cont_level & 7) + 1, ">>>>>>>>", m->offset); 64 65 if (m->flag & INDIR) { 66 (void) fprintf(stderr, "(%s,", 67 /* Note: type is unsigned */ 68 (m->in_type < file_nnames) ? file_names[m->in_type] : 69 "*bad in_type*"); 70 if (m->in_op & FILE_OPINVERSE) 71 (void) fputc('~', stderr); 72 (void) fprintf(stderr, "%c%d),", 73 (CAST(size_t, m->in_op & FILE_OPS_MASK) < 74 __arraycount(optyp)) ? 75 optyp[m->in_op & FILE_OPS_MASK] : '?', m->in_offset); 76 } 77 (void) fprintf(stderr, " %s%s", (m->flag & UNSIGNED) ? "u" : "", 78 /* Note: type is unsigned */ 79 (m->type < file_nnames) ? file_names[m->type] : "*bad type"); 80 if (m->mask_op & FILE_OPINVERSE) 81 (void) fputc('~', stderr); 82 83 if (IS_STRING(m->type)) { 84 if (m->str_flags) { 85 (void) fputc('/', stderr); 86 if (m->str_flags & STRING_COMPACT_WHITESPACE) 87 (void) fputc(CHAR_COMPACT_WHITESPACE, stderr); 88 if (m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) 89 (void) fputc(CHAR_COMPACT_OPTIONAL_WHITESPACE, 90 stderr); 91 if (m->str_flags & STRING_IGNORE_LOWERCASE) 92 (void) fputc(CHAR_IGNORE_LOWERCASE, stderr); 93 if (m->str_flags & STRING_IGNORE_UPPERCASE) 94 (void) fputc(CHAR_IGNORE_UPPERCASE, stderr); 95 if (m->str_flags & REGEX_OFFSET_START) 96 (void) fputc(CHAR_REGEX_OFFSET_START, stderr); 97 if (m->str_flags & STRING_TEXTTEST) 98 (void) fputc(CHAR_TEXTTEST, stderr); 99 if (m->str_flags & STRING_BINTEST) 100 (void) fputc(CHAR_BINTEST, stderr); 101 if (m->str_flags & PSTRING_1_BE) 102 (void) fputc(CHAR_PSTRING_1_BE, stderr); 103 if (m->str_flags & PSTRING_2_BE) 104 (void) fputc(CHAR_PSTRING_2_BE, stderr); 105 if (m->str_flags & PSTRING_2_LE) 106 (void) fputc(CHAR_PSTRING_2_LE, stderr); 107 if (m->str_flags & PSTRING_4_BE) 108 (void) fputc(CHAR_PSTRING_4_BE, stderr); 109 if (m->str_flags & PSTRING_4_LE) 110 (void) fputc(CHAR_PSTRING_4_LE, stderr); 111 if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF) 112 (void) fputc( 113 CHAR_PSTRING_LENGTH_INCLUDES_ITSELF, 114 stderr); 115 } 116 if (m->str_range) 117 (void) fprintf(stderr, "/%u", m->str_range); 118 } 119 else { 120 if (CAST(size_t, m->mask_op & FILE_OPS_MASK) < 121 __arraycount(optyp)) 122 (void) fputc(optyp[m->mask_op & FILE_OPS_MASK], stderr); 123 else 124 (void) fputc('?', stderr); 125 126 if (m->num_mask) { 127 (void) fprintf(stderr, "%.8llx", 128 CAST(unsigned long long, m->num_mask)); 129 } 130 } 131 (void) fprintf(stderr, ",%c", m->reln); 132 133 if (m->reln != 'x') { 134 switch (m->type) { 135 case FILE_BYTE: 136 case FILE_SHORT: 137 case FILE_LONG: 138 case FILE_LESHORT: 139 case FILE_LELONG: 140 case FILE_MELONG: 141 case FILE_BESHORT: 142 case FILE_BELONG: 143 case FILE_INDIRECT: 144 (void) fprintf(stderr, "%d", CAST(int32_t, m->value.l)); 145 break; 146 case FILE_BEQUAD: 147 case FILE_LEQUAD: 148 case FILE_QUAD: 149 case FILE_OFFSET: 150 (void) fprintf(stderr, "%" INT64_T_FORMAT "d", 151 CAST(long long, m->value.q)); 152 break; 153 case FILE_PSTRING: 154 case FILE_STRING: 155 case FILE_REGEX: 156 case FILE_BESTRING16: 157 case FILE_LESTRING16: 158 case FILE_SEARCH: 159 file_showstr(stderr, m->value.s, 160 CAST(size_t, m->vallen)); 161 break; 162 case FILE_DATE: 163 case FILE_LEDATE: 164 case FILE_BEDATE: 165 case FILE_MEDATE: 166 (void)fprintf(stderr, "%s,", 167 file_fmtdatetime(tbuf, sizeof(tbuf), m->value.l, 0)); 168 break; 169 case FILE_LDATE: 170 case FILE_LELDATE: 171 case FILE_BELDATE: 172 case FILE_MELDATE: 173 (void)fprintf(stderr, "%s,", 174 file_fmtdatetime(tbuf, sizeof(tbuf), m->value.l, 175 FILE_T_LOCAL)); 176 break; 177 case FILE_QDATE: 178 case FILE_LEQDATE: 179 case FILE_BEQDATE: 180 (void)fprintf(stderr, "%s,", 181 file_fmtdatetime(tbuf, sizeof(tbuf), m->value.q, 0)); 182 break; 183 case FILE_QLDATE: 184 case FILE_LEQLDATE: 185 case FILE_BEQLDATE: 186 (void)fprintf(stderr, "%s,", 187 file_fmtdatetime(tbuf, sizeof(tbuf), m->value.q, 188 FILE_T_LOCAL)); 189 break; 190 case FILE_QWDATE: 191 case FILE_LEQWDATE: 192 case FILE_BEQWDATE: 193 (void)fprintf(stderr, "%s,", 194 file_fmtdatetime(tbuf, sizeof(tbuf), m->value.q, 195 FILE_T_WINDOWS)); 196 break; 197 case FILE_FLOAT: 198 case FILE_BEFLOAT: 199 case FILE_LEFLOAT: 200 (void) fprintf(stderr, "%G", m->value.f); 201 break; 202 case FILE_DOUBLE: 203 case FILE_BEDOUBLE: 204 case FILE_LEDOUBLE: 205 (void) fprintf(stderr, "%G", m->value.d); 206 break; 207 case FILE_LEVARINT: 208 case FILE_BEVARINT: 209 (void)fprintf(stderr, "%s", file_fmtvarint( 210 tbuf, sizeof(tbuf), m->value.us, m->type)); 211 break; 212 case FILE_MSDOSDATE: 213 case FILE_BEMSDOSDATE: 214 case FILE_LEMSDOSDATE: 215 (void)fprintf(stderr, "%s,", 216 file_fmtdate(tbuf, sizeof(tbuf), m->value.h)); 217 break; 218 case FILE_MSDOSTIME: 219 case FILE_BEMSDOSTIME: 220 case FILE_LEMSDOSTIME: 221 (void)fprintf(stderr, "%s,", 222 file_fmttime(tbuf, sizeof(tbuf), m->value.h)); 223 break; 224 case FILE_OCTAL: 225 (void)fprintf(stderr, "%s", 226 file_fmtnum(tbuf, sizeof(tbuf), m->value.s, 8)); 227 break; 228 case FILE_DEFAULT: 229 /* XXX - do anything here? */ 230 break; 231 case FILE_USE: 232 case FILE_NAME: 233 case FILE_DER: 234 (void) fprintf(stderr, "'%s'", m->value.s); 235 break; 236 case FILE_LEGUID: 237 case FILE_GUID: 238 (void) file_print_leguid(tbuf, sizeof(tbuf), 239 m->value.guid); 240 (void) fprintf(stderr, "%s", tbuf); 241 break; 242 case FILE_BEGUID: 243 (void) file_print_beguid(tbuf, sizeof(tbuf), 244 m->value.guid); 245 (void) fprintf(stderr, "%s", tbuf); 246 break; 247 default: 248 (void) fprintf(stderr, "*bad type %d*", m->type); 249 break; 250 } 251 } 252 (void) fprintf(stderr, ",\"%s\"]\n", m->desc); 253 } 254 #endif 255 256 static void __attribute__((__format__(__printf__, 1, 0))) 257 file_vmagwarn(const char *f, va_list va) 258 { 259 /* cuz we use stdout for most, stderr here */ 260 (void) fflush(stdout); 261 262 (void) fprintf(stderr, "Warning: "); 263 (void) vfprintf(stderr, f, va); 264 (void) fputc('\n', stderr); 265 } 266 267 /*VARARGS*/ 268 file_protected void 269 file_magwarn1(const char *f, ...) 270 { 271 va_list va; 272 273 va_start(va, f); 274 file_vmagwarn(f, va); 275 va_end(va); 276 } 277 278 279 /*VARARGS*/ 280 file_protected void 281 file_magwarn(struct magic_set *ms, const char *f, ...) 282 { 283 va_list va; 284 285 if (++ms->magwarn == ms->magwarn_max) { 286 (void) fprintf(stderr, 287 "%s, %lu: Maximum number of warnings (%u) exceeded.\n", 288 ms->file, CAST(unsigned long, ms->line), 289 ms->magwarn_max); 290 (void) fprintf(stderr, 291 "%s, %lu: Additional warnings are suppressed.\n", 292 ms->file, CAST(unsigned long, ms->line)); 293 } 294 if (ms->magwarn >= ms->magwarn_max) { 295 return; 296 } 297 298 if (ms->file) 299 (void) fprintf(stderr, "%s, %lu: ", ms->file, 300 CAST(unsigned long, ms->line)); 301 302 va_start(va, f); 303 file_vmagwarn(f, va); 304 va_end(va); 305 } 306 307 file_protected const char * 308 file_fmtvarint(char *buf, size_t blen, const unsigned char *us, int t) 309 { 310 snprintf(buf, blen, "%jd", CAST(intmax_t, 311 file_varint2uintmax_t(us, t, NULL))); 312 return buf; 313 } 314 315 file_protected const char * 316 file_fmtdatetime(char *buf, size_t bsize, uint64_t v, int flags) 317 { 318 char *pp; 319 time_t t; 320 struct tm *tm, tmz; 321 322 if (flags & FILE_T_WINDOWS) { 323 struct timespec ts; 324 cdf_timestamp_to_timespec(&ts, CAST(cdf_timestamp_t, v)); 325 t = ts.tv_sec; 326 } else { 327 // XXX: perhaps detect and print something if overflow 328 // on 32 bit time_t? 329 t = CAST(time_t, v); 330 } 331 332 if (t > MAX_CTIME) 333 goto out; 334 335 if (flags & FILE_T_LOCAL) { 336 tzset(); 337 tm = localtime_r(&t, &tmz); 338 } else { 339 tm = gmtime_r(&t, &tmz); 340 } 341 if (tm == NULL) 342 goto out; 343 pp = asctime_r(tm, buf); 344 345 if (pp == NULL) 346 goto out; 347 pp[strcspn(pp, "\n")] = '\0'; 348 return pp; 349 out: 350 strlcpy(buf, "*Invalid datetime*", bsize); 351 return buf; 352 } 353 354 /* 355 * https://docs.microsoft.com/en-us/windows/win32/api/winbase/\ 356 * nf-winbase-dosdatetimetofiletime?redirectedfrom=MSDN 357 */ 358 file_protected const char * 359 file_fmtdate(char *buf, size_t bsize, uint16_t v) 360 { 361 struct tm tm; 362 363 memset(&tm, 0, sizeof(tm)); 364 tm.tm_mday = v & 0x1f; 365 tm.tm_mon = ((v >> 5) & 0xf) - 1; 366 // Sanity check because some OS's coredump with invalid values. 367 // Yes, Cygwin I am looking at you! 368 if (tm.tm_mon < 0 || tm.tm_mon > 11) 369 tm.tm_mon = 0; 370 tm.tm_year = (v >> 9) + 80; 371 372 if (strftime(buf, bsize, "%b %d %Y", &tm) == 0) 373 goto out; 374 375 return buf; 376 out: 377 strlcpy(buf, "*Invalid date*", bsize); 378 return buf; 379 } 380 381 file_protected const char * 382 file_fmttime(char *buf, size_t bsize, uint16_t v) 383 { 384 struct tm tm; 385 386 memset(&tm, 0, sizeof(tm)); 387 tm.tm_sec = (v & 0x1f) * 2; 388 tm.tm_min = ((v >> 5) & 0x3f); 389 tm.tm_hour = (v >> 11); 390 391 if (strftime(buf, bsize, "%T", &tm) == 0) 392 goto out; 393 394 return buf; 395 out: 396 strlcpy(buf, "*Invalid time*", bsize); 397 return buf; 398 399 } 400 401 file_protected const char * 402 file_fmtnum(char *buf, size_t blen, const char *us, int base) 403 { 404 char *endptr; 405 unsigned long long val; 406 407 errno = 0; 408 val = strtoull(us, &endptr, base); 409 if (*endptr || errno) { 410 bad: strlcpy(buf, "*Invalid number*", blen); 411 return buf; 412 } 413 414 if (snprintf(buf, blen, "%llu", val) < 0) 415 goto bad; 416 return buf; 417 } 418