From 1bc48bc8e4c212c73c3f64b3ed6179c4171463a7 Mon Sep 17 00:00:00 2001 From: mataha Date: Sat, 23 Dec 2023 06:39:27 +0100 Subject: [PATCH 01/15] Update stb (#885) This commit and, by extension, PR attempts to update `stb` in the most straightforward way possible as well as include fixes from main repo's unmerged PRs for cases rearing their ugly heads during everyday usage: - stb#1299: stb_rect_pack: Make rect_height_compare a stable sort - stb#1402: stb_image: Fix "unused invalid_chunk" with STBI_FAILURE_USERMSG - stb#1404: stb_image: Fix gif two_back memory address - stb#1420: stb_image: Improve error reporting if file operations fail within *_from_file functions - stb#1445: stb_vorbis: Few static analyzers fixes - stb#1487: stb_vorbis: Fix residue classdata bounding for f->temp_memory_required - stb#1490: stb_vorbis: Fix broken clamp in codebook_decode_deinterleave_repeat - stb#1496: stb_image: Fix pnm only build - stb#1497: stb_image: Fix memory leaks if stbi__convert failed - stb#1498: stb_vorbis: Fix memory leaks in stb_vorbis - stb#1499: stb_vorbis: Minor change to prevent the undefined behavior - left shift of a negative value - stb#1500: stb_vorbis: Fix signed integer overflow Includes additional small fixes that I felt didn't warrant a separate PR. --- dsp/core/core.h | 5 +- dsp/core/dct.c | 79 +- third_party/stb/README.cosmo | 29 +- third_party/stb/README.txt | 143 ++-- third_party/stb/stb_image.c | 1014 +++++++++++++++---------- third_party/stb/stb_image.h | 15 +- third_party/stb/stb_image_resize.c | 30 +- third_party/stb/stb_image_write.c | 621 ++++++++++----- third_party/stb/stb_image_write.h | 1 - third_party/stb/stb_image_write_png.c | 379 --------- third_party/stb/stb_rect_pack.c | 45 +- third_party/stb/stb_truetype.c | 4 +- third_party/stb/stb_vorbis.c | 358 +++++---- third_party/stb/stb_vorbis.h | 15 + tool/viz/derasterize.c | 11 +- tool/viz/memzoom.c | 23 +- tool/viz/od16.c | 19 +- tool/viz/printansi.c | 15 +- tool/viz/printimage.c | 17 +- tool/viz/printvideo.c | 19 +- 20 files changed, 1560 insertions(+), 1282 deletions(-) delete mode 100644 third_party/stb/stb_image_write_png.c diff --git a/dsp/core/core.h b/dsp/core/core.h index 714f3a392..eadf040f9 100644 --- a/dsp/core/core.h +++ b/dsp/core/core.h @@ -9,8 +9,9 @@ int mulaw(int); int unmulaw(int); void *double2byte(long, const void *, double, double) vallocesque; void *byte2double(long, const void *, double, double) vallocesque; -void *dct(float[8][8], float, float, float, float, float); -void *dctjpeg(float[8][8]); +void *dct(float[restrict hasatleast 8][8], unsigned, + float, float, float, float, float); +void *dctjpeg(float[restrict hasatleast 8][8], unsigned); double det3(const double[3][3]) nosideeffect; void *inv3(double[restrict 3][3], const double[restrict 3][3], double); void *matmul3(double[restrict 3][3], const double[3][3], const double[3][3]); diff --git a/dsp/core/dct.c b/dsp/core/dct.c index 506c96f86..cae19d596 100644 --- a/dsp/core/dct.c +++ b/dsp/core/dct.c @@ -18,40 +18,40 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/core/core.h" -#define DCT(A, B, C, D, E, F, G, H, T, C0, C1, C2, C3, C4) \ - do { \ - T z1, z2, z3, z4, z5, z11, z13; \ - T t0, t1, t2, t3, t4, t5, t6, t7, t10, t11, t12, t13; \ - t0 = A + H; \ - t7 = A - H; \ - t1 = B + G; \ - t6 = B - G; \ - t2 = C + F; \ - t5 = C - F; \ - t3 = D + E; \ - t4 = D - E; \ - t10 = t0 + t3; \ - t13 = t0 - t3; \ - t11 = t1 + t2; \ - t12 = t1 - t2; \ - A = t10 + t11; \ - E = t10 - t11; \ - z1 = (t12 + t13) * C0; \ - C = t13 + z1; \ - G = t13 - z1; \ - t10 = t4 + t5; \ - t11 = t5 + t6; \ - t12 = t6 + t7; \ - z5 = (t10 - t12) * C1; \ - z2 = t10 * C2 + z5; \ - z4 = t12 * C3 + z5; \ - z3 = t11 * C4; \ - z11 = t7 + z3; \ - z13 = t7 - z3; \ - F = z13 + z2; \ - D = z13 - z2; \ - B = z11 + z4; \ - H = z11 - z4; \ +#define DCT(A, B, C, D, E, F, G, H, T, C0, C1, C2, C3, C4) \ + do { \ + T z1, z2, z3, z4, z5, z11, z13; \ + T t0, t1, t2, t3, t4, t5, t6, t7, t10, t11, t12, t13; \ + t0 = A + H; \ + t7 = A - H; \ + t1 = B + G; \ + t6 = B - G; \ + t2 = C + F; \ + t5 = C - F; \ + t3 = D + E; \ + t4 = D - E; \ + t10 = t0 + t3; \ + t13 = t0 - t3; \ + t11 = t1 + t2; \ + t12 = t1 - t2; \ + A = t10 + t11; \ + E = t10 - t11; \ + z1 = (t12 + t13) * C0; \ + C = t13 + z1; \ + G = t13 - z1; \ + t10 = t4 + t5; \ + t11 = t5 + t6; \ + t12 = t6 + t7; \ + z5 = (t10 - t12) * C1; \ + z2 = t10 * C2 + z5; \ + z4 = t12 * C3 + z5; \ + z3 = t11 * C4; \ + z11 = t7 + z3; \ + z13 = t7 - z3; \ + F = z13 + z2; \ + D = z13 - z2; \ + B = z11 + z4; \ + H = z11 - z4; \ } while (0) /** @@ -65,20 +65,21 @@ * * @cost ~100ns */ -void *dct(float M[8][8], float c0, float c1, float c2, float c3, float c4) { +void *dct(float M[restrict hasatleast 8][8], unsigned stride, + float c0, float c1, float c2, float c3, float c4) { unsigned y, x; - for (y = 0; y < 8; ++y) { + for (y = 0; y < stride * 8; y += stride) { DCT(M[y][0], M[y][1], M[y][2], M[y][3], M[y][4], M[y][5], M[y][6], M[y][7], float, c0, c1, c2, c3, c4); } - for (x = 0; x < 8; ++x) { + for (x = 0; x < stride * 8; x += stride) { DCT(M[0][x], M[1][x], M[2][x], M[3][x], M[4][x], M[5][x], M[6][x], M[7][x], float, c0, c1, c2, c3, c4); } return M; } -void *dctjpeg(float M[8][8]) { - return dct(M, .707106781f, .382683433f, .541196100f, 1.306562965f, +void *dctjpeg(float M[restrict hasatleast 8][8], unsigned stride) { + return dct(M, stride, .707106781f, .382683433f, .541196100f, 1.306562965f, .707106781f); } diff --git a/third_party/stb/README.cosmo b/third_party/stb/README.cosmo index ca3b785c9..b2c7717ad 100644 --- a/third_party/stb/README.cosmo +++ b/third_party/stb/README.cosmo @@ -5,8 +5,8 @@ LOCAL CHANGES - Removed undefined behavior - Removed BMP [endian code made it 100x slower than PNG/JPEG] - Removed PIC [never heard of it] - - Removed TGA [consider imaagemagick convert command] - - Removed PSD [consider imaagemagick convert command] + - Removed TGA [consider imagemagick convert command] + - Removed PSD [consider imagemagick convert command] - Removed HDR [mine eyes and wikipedia agree stb gamma math is off] - Patched PNG loading edge case - Fixed code C standard says is undefined @@ -14,10 +14,25 @@ LOCAL CHANGES - Removed unnecessary ifdefs - Removed MSVC torture code -SYNCHRONIZATION POINT +SYNCHRONIZATION POINT (`--date=format:"%a %b %d %H:%M:%S %Y %z"`) - commit f67165c2bb2af3060ecae7d20d6f731173485ad0 - Author: Sean Barrett - Date: Mon Oct 28 09:30:02 2019 -0700 + commit 5736b15f7ea0ffb08dd38af21067c314d6a3aae9 + Author: Sean Barrett + Date: Sun Jan 29 10:46:04 2023 -0800 - Update README.md + re-add perlin noise again + +ADDITIONAL CHANGES/FIXES: + + - https://github.com/nothings/stb/pull/1299 + - https://github.com/nothings/stb/pull/1402 + - https://github.com/nothings/stb/pull/1404 + - https://github.com/nothings/stb/pull/1420 + - https://github.com/nothings/stb/pull/1445 + - https://github.com/nothings/stb/pull/1487 + - https://github.com/nothings/stb/pull/1490 + - https://github.com/nothings/stb/pull/1496 + - https://github.com/nothings/stb/pull/1497 + - https://github.com/nothings/stb/pull/1498 + - https://github.com/nothings/stb/pull/1499 + - https://github.com/nothings/stb/pull/1500 diff --git a/third_party/stb/README.txt b/third_party/stb/README.txt index 4c6915271..5c2434237 100644 --- a/third_party/stb/README.txt +++ b/third_party/stb/README.txt @@ -1,13 +1,12 @@ -/* - * stb_image - v2.23 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb * no warranty implied; use at your own risk * * [heavily modified by justine tunney] * * JPEG baseline & progressive (12 bpc/arithmetic not supported, same - * as stock IJG lib) PNG 1/2/4/8/16-bit-per-channel + * as stock IJG lib) + * PNG 1/2/4/8/16-bit-per-channel * GIF (*comp always reports as 4-channel) - * HDR (radiance rgbE format) * PNM (PPM and PGM binary only) * * Animated GIF still needs a proper API, but here's one way to do it: @@ -18,45 +17,53 @@ * * ============================ Contributors ========================= * - * Image formats Extensions, features - * Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - * Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - * Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - * Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - * Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - * Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - * Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - * github:urraka (animated gif) Junggon Kim (PNM comments) - * Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - * socks-the-fox (16-bit PNG) - * Jeremy Sawicki (ImageNet JPGs) - * Mikhail Morozov (1-bit BMP) - * Optimizations & bugfixes Anael Seghezzi (is-16-bit query) - * Fabian "ryg" Giesen - * Arseny Kapoulkine - * John-Mark Allen + * Image formats Extensions, features + * Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + * Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + * Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + * Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + * Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + * Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + * Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + * github:urraka (animated gif) Junggon Kim (PNM comments) + * Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + * socks-the-fox (16-bit PNG) + * Optimizations & bugfixes Jeremy Sawicki (ImageNet JPGs) + * Fabian "ryg" Giesen Mikhail Morozov (1-bit BMP) + * Arseny Kapoulkine Anael Seghezzi (is-16-bit query) + * John-Mark Allen Simon Breuss (16-bit PNM) * Carmelo J Fdez-Aguera * * Bug & warning fixes - * Marc LeBlanc David Woo Guillaume George Martins Mozeiko - * Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - * Dave Moore Roy Eltham Hayaki Saito Nathan Reed - * Won Chun Luke Graham Johan Duparc Nick Verigakis - * the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - * Janez Zemva John Bartholomew Michal Cichon github:romigrou - * Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - * Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - * Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - * Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - * Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - * Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - * Julian Raschke Gregory Mullen Baldur Karlsson - * github:poppolopoppo Christian Floisand Kevin Schmidt JR Smith - * github:darealshinji Blazej Dariusz Roszkowski github:Michaelangel007 - */ - -/* - * DOCUMENTATION + * Marc LeBlanc Laurent Gomila JR Smith + * Christpher Lloyd Sergio Gonzalez Matvey Cherevko + * Phil Jordan Ryamond Barbiero Zack Middleton + * Hayaki Saito Engin Manap + * Luke Graham Dale Weiler Martins Mozeiko + * Thomas Ruf Neil Bickford Blazej Dariusz Roszkowski + * Janez Zemva Gregory Mullen Roy Eltham + * Jonathan Blow Kevin Schmidt + * Eugene Golushkov Brad Weinberger the Horde3D community + * Aruelien Pocheville Alexander Veselov github:rlyeh + * Cass Everitt [reserved] github:romigrou + * Paul Du Bois github:svdijk + * Philipp Wiesemann Guillaume George github:snagar + * Josh Tobin Joseph Thomson github:Zelex + * Julian Raschke Dave Moore github:grim210 + * Baldur Karlsson Won Chun github:sammyhw + * Nick Verigakis github:phprus + * Luca Sas github:poppolopoppo + * Ryan C. Gordon Michal Cichon github:darealshinji + * David Woo Tero Hanninen github:Michaelangel007 + * Jerry Jansson Cort Stratton github:mosra + * Thibault Reuille [reserved] + * Nathan Reed [reserved] + * Johan Duparc Aldo Culquicondor + * Ronny Chevalier Oriol Ferrer Jacko Dirks + * John Bartholomew Matthew Gregan + * Ken Hamada Christian Floisand + * + * ============================ Documentation ========================= * * Limitations: * - no 12-bit-per-channel JPEG @@ -70,14 +77,15 @@ * // ... x = width, y = height, n = # 8-bit components per pixel ... * // ... replace '0' with '1'..'4' to force that many components per pixel * // ... but 'n' will always be the number that it would have been if you - * said 0 stbi_image_free(data) + * // ... said 0 + * stbi_image_free(data); * * Standard parameters: * int *x -- outputs image width in pixels * int *y -- outputs image height in pixels * int *channels_in_file -- outputs # of image components in image file * int desired_channels -- if non-zero, # of image components requested in - * result + * result * * The return value from an image loader is an 'unsigned char *' which points * to the pixel data, or NULL on an allocation failure or if the image is @@ -110,6 +118,32 @@ * * Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. * + * To query the width, height and component count of an image without having to + * decode the full file, you can use the stbi_info family of functions: + * + * int x,y,n,ok; + * ok = stbi_info(filename, &x, &y, &n); + * // returns ok=1 and sets x, y, n if image is a supported format, + * // 0 otherwise. + * + * Note that stb_image pervasively uses ints in its public API for sizes, + * including sizes of memory buffers. This is now part of the API and thus + * hard to change without causing breakage. As a result, the various image + * loaders all have certain limits on image size; these differ somewhat + * by format but generally boil down to either just under 2GB or just under + * 1GB. When the decoded image would be larger than this, stb_image decoding + * will fail. + * + * Additionally, stb_image will reject image files that have any of their + * dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, + * which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, + * the only way to have an image with such dimensions load correctly + * is for it to have a rather extreme aspect ratio. Either way, the + * assumption here is that such larger images are likely to be malformed + * or malicious. If you do need to load an image with individual dimensions + * larger than that, and it still fits in the overall size limit, you can + * #define STBI_MAX_DIMENSIONS on your own to be something larger. + * * =========================================================================== * * I/O callbacks @@ -163,11 +197,10 @@ * * iPhone PNG support: * - * By default we convert iphone-formatted PNGs back to RGB, even though - * they are internally encoded differently. You can disable this conversion - * by calling stbi_convert_iphone_png_to_rgb(0), in which case - * you will always just get the native iphone "format" through (which - * is BGR stored in RGB). + * We optionally support converting iPhone-formatted PNGs (which store + * premultiplied BGRA) back to RGB, even though they're internally encoded + * differently. To enable this conversion, call + * stbi_convert_iphone_png_to_rgb(1). * * Call stbi_set_unpremultiply_on_load(1) as well to force a divide per * pixel to remove any premultiplied alpha *only* if the image file explicitly @@ -191,9 +224,18 @@ * - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still * want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB * + * - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater + * than that size (in either width or height) without further processing. + * This is to let programs in the wild set an upper bound to prevent + * denial-of-service attacks on untrusted data, as one could generate a + * valid image of gigantic dimensions and force stb_image to allocate a + * huge block of memory and spend disproportionate time decoding it. By + * default this is set to (1 << 24), which is 16777216, but that's still + * very big. + * */ -/* stb_image_resize - v0.96 - public domain image resizing +/* stb_image_resize - v0.97 - public domain image resizing * by Jorge L Rodriguez (@VinoBS) - 2014 * http://github.com/nothings/stb * @@ -214,9 +256,7 @@ * output_pixels, out_w, out_h, 0, * num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) * // WRAP/REFLECT/ZERO - */ - -/* + * * DOCUMENTATION * * SRGB & FLOATING POINT REPRESENTATION @@ -348,6 +388,7 @@ * Nathan Reed: warning fixes * * REVISIONS + * 0.97 (2020-02-02) fixed warning * 0.96 (2019-03-04) fixed warnings * 0.95 (2017-07-23) fixed warnings * 0.94 (2017-03-18) fixed warnings diff --git a/third_party/stb/stb_image.c b/third_party/stb/stb_image.c index 97b560a33..6852c3adc 100644 --- a/third_party/stb/stb_image.c +++ b/third_party/stb/stb_image.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -51,15 +51,12 @@ http://nothings.org/stb\""); #define idct_block_kernel stbi__idct_block #endif -#define ROL(w, k) ((w) << (k) | (w) >> (sizeof(w) * CHAR_BIT - (k))) +#define ROL(w, k) (((w) << (k)) | ((w) >> (-(k) & (sizeof(w) * CHAR_BIT - 1)))) -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p, oldsz, newsz) realloc(p, newsz) +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) #endif -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - // stbi__context structure is our basic context used by all images, so it // contains all the IO context, plus some basic image information typedef struct { @@ -70,6 +67,7 @@ typedef struct { int read_from_callbacks; int buflen; unsigned char buffer_start[128]; + int callback_already_read; unsigned char *img_buffer, *img_buffer_end; unsigned char *img_buffer_original, *img_buffer_original_end; } stbi__context; @@ -83,6 +81,7 @@ static void stbi__start_mem(stbi__context *s, unsigned char const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; + s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (unsigned char *)buffer; s->img_buffer_end = s->img_buffer_original_end = (unsigned char *)buffer + len; @@ -95,7 +94,8 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } @@ -105,11 +105,16 @@ static int stbi__stdio_read(void *user, char *data, int size) { } static void stbi__stdio_skip(void *user, int n) { + int ch; fseek(user, n, SEEK_CUR); + ch = fgetc(user); + if (ch != EOF) { + ungetc(ch, user); + } } static int stbi__stdio_eof(void *user) { - return feof(user); + return feof(user) || ferror(user); } static stbi_io_callbacks stbi__stdio_callbacks = { @@ -168,8 +173,8 @@ const char *stbi_failure_reason(void) { static int stbi__err(const char *specific_details, const char *general_details) { - /* DebugBreak(); */ - /* WARNF("%s: %s", general_details, specific_details); */ + // DebugBreak(); + // WARNF("%s: %s", general_details, specific_details); stbi__g_failure_reason = general_details; return 0; } @@ -204,17 +209,27 @@ static int stbi__mul2sizes_valid(int a, int b) { return a <= INT_MAX / b; } -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +// returns 1 if "a * b + add" has no negative terms/factors +// and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a * b, add); } -// returns 1 if "a*b*c + add" has no negaive terms/factors and doesn't overflow +// returns 1 if "a * b * c + add" has no negative terms/factors +// and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && stbi__addsizes_valid(a * b * c, add); } +// returns 1 if "a * b * c * d + add" has no negative terms/factors +// and doesn't overflow +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && + stbi__mul2sizes_valid(a * b * c, d) && + stbi__addsizes_valid(a * b * c * d, add); +} + // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; @@ -226,6 +241,44 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) { return xmalloc(a * b * c + add); } +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return xmalloc(a * b * c * d + add); +} + +// returns 1 if the sum of two signed ints is valid +// (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) { + if ((a >= 0) != (b >= 0)) { + // a and b have different signs, so no overflow + return 1; + } + if (a < 0 && b < 0) { + // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a >= INT_MIN - b; + } + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, +// 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) { + if (b == 0 || b == -1) { + // multiplication by 0 is always 0; + // check for -1 so SHRT_MIN / b doesn't overflow + return 1; + } + if ((a >= 0) == (b >= 0)) { + // product is positive, so similar to mul2sizes_valid + return a <= SHRT_MAX / b; + } + if (b < 0) { + // same as a * b >= SHRT_MIN + return a <= SHRT_MIN / b; + } + return a >= SHRT_MIN / b; +} + #define stbi__errpf(x, y) \ ({ \ stbi__err(x, y); \ @@ -247,22 +300,22 @@ void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { } static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, - int req_comp, stbi__result_info *ri, int bpc) { + int req_comp, stbi__result_info *ri) { bzero(ri, sizeof(*ri)); ri->bits_per_channel = 8; ri->num_channels = 0; -#ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s, x, y, comp, req_comp, ri); -#endif -#ifndef STBI_NO_PNG + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) if (stbi__png_test(s)) return stbi__png_load(s, x, y, comp, req_comp, ri); -#endif -#ifndef STBI_NO_GIF if (stbi__gif_test(s)) return stbi__gif_load(s, x, y, comp, req_comp, ri); -#endif -#ifndef STBI_NO_PNM + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s, x, y, comp, req_comp, ri); if (stbi__pnm_test(s)) return stbi__pnm_load(s, x, y, comp, req_comp, ri); -#endif + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } @@ -335,12 +388,18 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int req_comp) { void *result; stbi__result_info ri; - result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + result = stbi__load_main(s, x, y, comp, req_comp, &ri); if (result == NULL) return NULL; + assert(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 8) { - assert(ri.bits_per_channel == 16); - result = - stbi__convert_16_to_8(result, *x, *y, req_comp == 0 ? *comp : req_comp); + // https://github.com/nothings/stb/pull/1497 + unsigned char *converted = stbi__convert_16_to_8( + (uint16_t *)result, *x, *y, req_comp == 0 ? *comp : req_comp); + if (converted == NULL) { + free(result); + return NULL; + } + result = converted; ri.bits_per_channel = 8; } // @TODO: move stbi__convert_format to here @@ -354,13 +413,20 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, static uint16_t *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + void *result; stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + result = stbi__load_main(s, x, y, comp, req_comp, &ri); if (result == NULL) return NULL; + assert(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 16) { - assert(ri.bits_per_channel == 8); - result = stbi__convert_8_to_16((unsigned char *)result, *x, *y, - req_comp == 0 ? *comp : req_comp); + // https://github.com/nothings/stb/pull/1497 + uint16_t *converted = stbi__convert_8_to_16( + (unsigned char *)result, *x, *y, req_comp == 0 ? *comp : req_comp); + if (converted == NULL) { + free(result); + return NULL; + } + result = converted; ri.bits_per_channel = 16; } // @TODO: move stbi__convert_format16 to here @@ -396,7 +462,14 @@ unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, result = stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); if (result) { // need to 'unget' all the characters in the IO buffer - fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR); + // https://github.com/nothings/stb/pull/1420 + if (fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR)) { + // fseek() failed; we can no longer maintain the file cursor position + // guarantee of this function, so return null. + free(result); + return stbi__errpuc("bad file", + "fseek() failed; seek position unreliable"); + } } return result; } @@ -409,7 +482,14 @@ uint16_t *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, result = stbi__load_and_postprocess_16bit(&s, x, y, comp, req_comp); if (result) { // need to 'unget' all the characters in the IO buffer - fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR); + // https://github.com/nothings/stb/pull/1420 + if (fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR)) { + // fseek() failed; we can no longer maintain the file cursor position + // guarantee of this function, so return null. + free(result); + return (uint16_t *)stbi__errpuc( + "bad file", "fseek() failed; seek position unreliable"); + } } return result; } @@ -476,6 +556,7 @@ enum { STBI__SCAN_load = 0, STBI__SCAN_type, STBI__SCAN_header }; static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data, (char *)s->buffer_start, s->buflen); + s->callback_already_read += (int)(s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file @@ -510,6 +591,7 @@ forceinline int stbi__at_eof(stbi__context *s) { } static void stbi__skip(stbi__context *s, int n) { + if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; @@ -569,7 +651,7 @@ static uint32_t stbi__get32be(stbi__context *s) { // generic converter from built-in img_n to req_comp // individual types do this automatically as much as possible (e.g. jpeg // does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically +// and it never has alpha, so very few cases). png can automatically // interleave an alpha=255 channel, but falls back to this for other cases // // assume data buffer is malloced, so malloc a new one and free that one @@ -658,6 +740,9 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, break; default: assert(0); + free(data); + free(good); + return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -751,6 +836,10 @@ static uint16_t *stbi__convert_format16(uint16_t *data, int img_n, int req_comp, break; default: assert(0); + free(data); + free(good); + return (uint16_t *)stbi__errpuc("unsupported", + "Unsupported format conversion"); } #undef STBI__CASE } @@ -849,8 +938,14 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) { int i, j, k = 0; unsigned int code; // build size list for each symbol (from JPEG spec) - for (i = 0; i < 16; ++i) - for (j = 0; j < count[i]; ++j) h->size[k++] = (unsigned char)(i + 1); + for (i = 0; i < 16; ++i) { + for (j = 0; j < count[i]; ++j) { + h->size[k++] = (unsigned char)(i + 1); + if (k >= 257) { + return stbi__err("bad size list", "Corrupt JPEG"); + } + } + } h->size[k] = 0; // compute actual symbols (from jpeg spec) @@ -962,8 +1057,10 @@ forceinline int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { for (k = FAST_BITS + 1;; ++k) if (temp < h->maxcode[k]) break; if (k == 17) { + WARNF("j->code_bits: %d", j->code_bits); // error! code not found j->code_bits -= 16; + WARNF("Symbol: %d", k); return -1; } @@ -971,6 +1068,10 @@ forceinline int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if (c < 0 || c >= 256) { + // symbol id out of bounds! + return -1; + } assert((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); @@ -980,7 +1081,7 @@ forceinline int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { return h->values[c]; } -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - sgn = (int32_t)j->code_buffer >> 31; // sign bit is always in MSB + if (j->code_bits < n) { + // ran out of bits from stream, return 0s intead of continuing + return 0; + } + // sign bit is always in MSB; + // 0 if MSB clear (positive), 1 if MSB set (negative) + sgn = j->code_buffer >> 31; k = ROL(j->code_buffer, n); - assert(n >= 0 && n < (int)(sizeof(stbi__bmask) / sizeof(*stbi__bmask))); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits forceinline int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) { + // ran out of bits from stream, return 0s intead of continuing + return 0; + } k = ROL(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; @@ -1015,6 +1124,10 @@ forceinline int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { forceinline int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) { + // ran out of bits from stream, return 0s intead of continuing + return 0; + } k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; @@ -1039,12 +1152,18 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], int diff, dc, k, t, c, r, s, rs; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code", "Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("bad huffman code", "Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time bzero(data, 64 * sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) { + return stbi__err("bad delta", "Corrupt JPEG"); + } dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) { + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + } data[0] = (short)(dc * dequant[0]); // decode AC components, see JPEG spec k = 1; @@ -1055,6 +1174,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) { + return stbi__err("bad huffman code", + "Combined length longer than code bits available"); + } j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location @@ -1082,7 +1205,6 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { int t; - short s; int diff, dc; if (j->spec_end != 0) { return stbi__err("can't merge dc and ac", "Corrupt JPEG"); @@ -1092,15 +1214,22 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], // first scan for DC coefficient, must be first bzero(data, 64 * sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) { + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + } diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) { + return stbi__err("bad delta", "Corrupt JPEG"); + } dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; - s = dc; - s *= 1u << j->succ_low; - data[0] = s; /* (short)(dc << j->succ_low); */ + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) { + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + } + data[0] = (short)(dc * (1u << j->succ_low)); } else { // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) data[0] += (short)(1 << j->succ_low); + if (stbi__jpeg_get_bit(j)) data[0] += (short)(1u << j->succ_low); } return 1; } @@ -1129,10 +1258,14 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) { + return stbi__err("bad huffman code", + "Combined length longer than code bits available"); + } j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (r / 256) * (1u << shift); + data[zig] = (short)((r >> 8) * (1u << shift)); } else { rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG"); @@ -1149,15 +1282,13 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], } else { k += r; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = stbi__extend_receive(j, s) * (1u << shift); + data[zig] = (short)(stbi__extend_receive(j, s) * (1u << shift)); } } } while (k <= j->spec_end); } else { // refinement scan for these AC coefficients - - bit = (short)(1 << j->succ_low); - + bit = (short)(1u << j->succ_low); if (j->eob_run) { --j->eob_run; for (k = j->spec_start; k <= j->spec_end; ++k) { @@ -1273,9 +1404,10 @@ forceinline unsigned char stbi__clamp(int x) { t1 += p2 + p4; \ t0 += p1 + p3; -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { +static void stbi__idct_block(unsigned char *out, int out_stride, + short data[64]) { int i, val[64], *v = val; - stbi_uc *o; + unsigned char *o; short *d = data; // columns @@ -1338,7 +1470,8 @@ static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { +static void stbi__idct_simd(unsigned char *out, int out_stride, + short data[64]) { // This is constructed to match our regular (generic) integer IDCT exactly. __m128i row0, row1, row2, row3, row4, row5, row6, row7; __m128i tmp; @@ -1540,7 +1673,8 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { // NEON integer IDCT. should produce bit-identical // results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { +static void stbi__idct_simd(unsigned char *out, int out_stride, + short data[64]) { int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); @@ -2025,6 +2159,10 @@ static int stbi__process_marker(stbi__jpeg *z, int m) { sizes[i] = stbi__get8(z->s); n += sizes[i]; } + if (n > 256) { + // Loop over i < n would write past end of values! + return stbi__err("bad DHT header", "Corrupt JPEG"); + } L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc + th, sizes)) return 0; @@ -2058,7 +2196,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) { for (i = 0; i < 5; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 5; - if (ok) z->jfif = 1; + if (ok) z->jfif = m; } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment static const unsigned char tag[6] = {'A', 'd', 'o', 'b', 'e', '\0'}; int ok = 1; @@ -2150,24 +2288,36 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; int Lf, p, i, q, h_max = 1, v_max = 1, c; Lf = stbi__get16be(s); - if (Lf < 11) return stbi__err("bad SOF len", "Corrupt JPEG"); // JPEG + if (Lf < 11) { + // JPEG + return stbi__err("bad SOF len", "Corrupt JPEG"); + } p = stbi__get8(s); - if (p != 8) - return stbi__err("only 8-bit", - "JPEG format not supported: 8-bit only"); // JPEG baseline + if (p != 8) { + // JPEG baseline + return stbi__err("only 8-bit", "JPEG format not supported: 8-bit only"); + } s->img_y = stbi__get16be(s); - if (s->img_y == 0) - return stbi__err( - "no header height", - "JPEG format not supported: delayed height"); // Legal, but we don't - // handle it--but neither - // does IJG + if (s->img_y == 0) { + // Legal, but we don't handle it--but neither does IJG + return stbi__err("no header height", + "JPEG format not supported: delayed height"); + } s->img_x = stbi__get16be(s); - if (s->img_x == 0) - return stbi__err("0 width", "Corrupt JPEG"); // JPEG requires + if (s->img_x == 0) { + // JPEG requires + return stbi__err("0 width", "Corrupt JPEG"); + } + if (s->img_y > STBI_MAX_DIMENSIONS) { + return stbi__err("too large", "Very large image (corrupt?)"); + } + if (s->img_x > STBI_MAX_DIMENSIONS) { + return stbi__err("too large", "Very large image (corrupt?)"); + } c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) + if (c != 3 && c != 1 && c != 4) { return stbi__err("bad component count", "Corrupt JPEG"); + } s->img_n = c; for (i = 0; i < c; ++i) { z->img_comp[i].data = NULL; @@ -2202,6 +2352,18 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) { if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } + // check that plane subsampling factors are integer ratios; + // our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i = 0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) { + return stbi__err("bad H", "Corrupt JPEG"); + } + if (v_max % z->img_comp[i].v != 0) { + return stbi__err("bad V", "Corrupt JPEG"); + } + } + // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; @@ -2272,6 +2434,27 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { return 1; } +static unsigned char stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) { + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + unsigned char x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { int m; @@ -2287,26 +2470,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) { if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below // will fail and we'll eventually return 0 } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); uint32_t NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); } else { - if (!stbi__process_marker(j, m)) return 0; + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); } - m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); return 1; @@ -2471,9 +2650,10 @@ static unsigned char *stbi__resample_row_nearest(unsigned char *out, // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar #define stbi__float2fixed(x) (((int)((x)*4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, - const stbi_uc *pcb, const stbi_uc *pcr, - int count, int step) { +static void stbi__YCbCr_to_RGB_row(unsigned char *out, const unsigned char *y, + const unsigned char *pcb, + const unsigned char *pcr, int count, + int step) { int i; for (i = 0; i < count; ++i) { int y_fixed = (y[i] << 20) + (1 << 19); // rounding @@ -2505,18 +2685,19 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; + out[0] = (unsigned char)r; + out[1] = (unsigned char)g; + out[2] = (unsigned char)b; out[3] = 255; out += step; } } #if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, - stbi_uc const *pcb, stbi_uc const *pcr, - int count, int step) { +static void stbi__YCbCr_to_RGB_simd(unsigned char *out, unsigned char const *y, + unsigned char const *pcb, + unsigned char const *pcr, int count, + int step) { int i = 0; #ifdef STBI_SSE2 @@ -2656,9 +2837,9 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; + out[0] = (unsigned char)r; + out[1] = (unsigned char)g; + out[2] = (unsigned char)b; out[3] = 255; out += step; } @@ -2722,6 +2903,13 @@ static unsigned char *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, decode_n = z->s->img_n; } + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { + stbi__cleanup_jpeg(z); + return NULL; + } + // resample and color-convert { int k; @@ -2876,7 +3064,10 @@ static dontinline void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char *result; - stbi__jpeg *j = (stbi__jpeg *)malloc(sizeof(stbi__jpeg)); + stbi__jpeg *j; + j = malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + bzero(j, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); result = load_jpeg_image(j, x, y, comp, req_comp); @@ -2888,6 +3079,8 @@ static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg *j; j = malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + bzero(j, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -2910,6 +3103,8 @@ static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg *j = (stbi__jpeg *)(malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + bzero(j, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); free(j); @@ -2926,6 +3121,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) @@ -2934,8 +3130,8 @@ typedef struct { uint16_t firstcode[16]; int maxcode[17]; uint16_t firstsymbol[16]; - unsigned char size[288]; - uint16_t value[288]; + unsigned char size[STBI__ZNSYMS]; + uint16_t value[STBI__ZNSYMS]; } stbi__zhuffman; static uint32_t ReverseBits32(uint32_t x) { @@ -3010,6 +3206,7 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, typedef struct { unsigned char *zbuffer, *zbuffer_end; int num_bits; + int hit_zeof_once; uint32_t code_buffer; char *zout; char *zout_start; @@ -3018,14 +3215,20 @@ typedef struct { stbi__zhuffman z_length, z_distance; } stbi__zbuf; +forceinline int stbi__zeof(stbi__zbuf *z) { + return (z->zbuffer >= z->zbuffer_end); +} + forceinline unsigned char stbi__zget8(stbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; + return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { - assert(z->code_buffer < (1u << z->num_bits)); + if (z->code_buffer >= (1u << z->num_bits)) { + z->zbuffer = z->zbuffer_end; // treat this as EOF so we fail. + return; + } z->code_buffer |= (unsigned int)stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); @@ -3047,10 +3250,17 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { k = stbi__bit_reverse(a->code_buffer, 16); for (s = STBI__ZFAST_BITS + 1;; ++s) if (k < z->maxcode[s]) break; - if (s == 16) return -1; // invalid code! + if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; - assert(z->size[b] == s); + if (b >= STBI__ZNSYMS) { + // some data was corrupt somewhere! + return -1; + } + if (z->size[b] != s) { + // was originally an assert, but report failure instead. + return -1; + } a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; @@ -3058,7 +3268,23 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { forceinline int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b, s; - if (a->num_bits < 16) stbi__fill_bits(a); + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding bits + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; @@ -3071,13 +3297,19 @@ forceinline int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) { char *q; - int cur, limit, old_limit; + unsigned int cur, limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit", "Corrupt PNG"); - cur = (int)(z->zout - z->zout_start); - limit = old_limit = (int)(z->zout_end - z->zout_start); - while (cur + n > limit) limit *= 2; - q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + cur = (unsigned int)(z->zout - z->zout_start); + limit = (unsigned)(z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned)n) { + return stbi__err("outofmem", "Out of memory"); + } + while (cur + n > limit) { + if (limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *)realloc(z->zout_start, limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; z->zout = q + cur; @@ -3122,19 +3354,36 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) { int len, dist; if (z == 256) { a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our + // bit buffer so the decoder can just do its speculative decoding. But + // if we actually consumed any of those bits (which is the case when + // num_bits < 16), the stream actually read past the end so it is + // malformed. + return stbi__err("unexpected end", "Corrupt PNG"); + } return 1; } + if (z >= 286) { + // per DEFLATE, length codes 286 and 287 + // must not appear in compressed data + return stbi__err("bad huffman code", "Corrupt PNG"); + } z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code", "Corrupt PNG"); + if (z < 0 || z >= 30) { + // per DEFLATE, distance codes 30 and 31 + // must not appear in compressed data + return stbi__err("bad huffman code", "Corrupt PNG"); + } dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist", "Corrupt PNG"); - if (zout + len > a->zout_end) { + if (len > a->zout_end - zout) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } @@ -3184,11 +3433,12 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) { c = stbi__zreceive(a, 2) + 3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n - 1]; - } else if (c == 17) + } else if (c == 17) { c = stbi__zreceive(a, 3) + 3; - else { - assert(c == 18); + } else if (c == 18) { c = stbi__zreceive(a, 7) + 11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes + n, fill, c); @@ -3213,7 +3463,7 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) { a->code_buffer >>= 8; a->num_bits -= 8; } - assert(a->num_bits == 0); + if (a->num_bits < 0) return stbi__err("zlib corrupt", "Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); len = header[1] * 256 + header[0]; @@ -3234,6 +3484,8 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) { int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); + if (stbi__zeof(a)) + return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec if ((cmf * 256 + flg) % 31 != 0) return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec if (flg & 32) @@ -3246,7 +3498,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) { return 1; } -static const unsigned char stbi__zdefault_length[288] = { +static const unsigned char stbi__zdefault_length[STBI__ZNSYMS] = { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -3282,6 +3534,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; + a->hit_zeof_once = 0; do { final = stbi__zreceive(a, 1); type = stbi__zreceive(a, 2); @@ -3292,7 +3545,8 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { } else { if (type == 1) { // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, 288)) + if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, + STBI__ZNSYMS)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; @@ -3443,31 +3697,57 @@ enum { STBI__F_up = 2, STBI__F_avg = 3, STBI__F_paeth = 4, - // synthetic filters used for first scanline to avoid needing a dummy row of - // 0s - STBI__F_avg_first, - STBI__F_paeth_first + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first }; static int stbi__de_iphone_flag = 0; static int stbi__unpremultiply_on_load = 0; -static unsigned char first_row_filter[5] = {STBI__F_none, STBI__F_sub, - STBI__F_none, STBI__F_avg_first, - STBI__F_paeth_first}; +static unsigned char first_row_filter[5] = { + STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, + STBI__F_sub // Paeth with b = c = 0 turns out to be equivalent to sub +}; static int stbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; + // This formulation looks very different from the reference in the PNG spec, + // but is actually equivalent and has favorable data dependencies and admits + // straightforward generation of branch-free code, which helps performance + // significantly. + int thresh = c * 3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; } static const unsigned char stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01}; +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(unsigned char *dest, + unsigned char *src, uint32_t x, + int img_n) { + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i = x - 1; i >= 0; --i) { + dest[i * 2 + 1] = 255; + dest[i * 2 + 0] = src[i]; + } + } else { + assert(img_n == 3); + for (i = x - 1; i >= 0; --i) { + dest[i * 4 + 3] = 255; + dest[i * 4 + 2] = src[i * 3 + 2]; + dest[i * 4 + 1] = src[i * 3 + 1]; + dest[i * 4 + 0] = src[i * 3 + 0]; + } + } +} + // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, unsigned char *raw, uint32_t raw_len, int out_n, uint32_t x, @@ -3476,6 +3756,8 @@ static int stbi__create_png_image_raw(stbi__png *a, unsigned char *raw, stbi__context *s = a->s; uint32_t i, j, stride = x * out_n * bytes; uint32_t img_len, img_width_bytes; + unsigned char *filler_buf; + int all_ok = 1; int k; int img_n = s->img_n; // copy it into a local for later @@ -3487,9 +3769,13 @@ static int stbi__create_png_image_raw(stbi__png *a, unsigned char *raw, a->out = stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) + return stbi__err("too large", "Corrupt PNG"); img_len = (img_width_bytes + 1) * y; // we used to check for exact match between raw_len and img_len on @@ -3498,260 +3784,143 @@ static int stbi__create_png_image_raw(stbi__png *a, unsigned char *raw, // always. if (raw_len < img_len) return stbi__err("not enough pixels", "Corrupt PNG"); + // Allocate two scan lines worth of filter workspace buffer. + filler_buf = stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filler_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *prior; + // cur/prior filter buffers alternate + unsigned char *cur = filler_buf + (j & 1) * img_width_bytes; + unsigned char *prior = filler_buf + (~j & 1) * img_width_bytes; + unsigned char *dest = a->out + stride * j; + int nk = width * filter_bytes; int filter = *raw++; - if (filter > 4) return stbi__err("invalid filter", "Corrupt PNG"); - - if (depth < 8) { - assert(img_width_bytes <= x); - cur += - x * out_n - img_width_bytes; // store output to the rightmost img_len - // bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter", "Corrupt PNG"); + break; } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' - // computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; - // handle first byte explicitly - for (k = 0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none: - cur[k] = raw[k]; - break; - case STBI__F_sub: - cur[k] = raw[k]; - break; - case STBI__F_up: - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); - break; - case STBI__F_avg: + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); - break; - case STBI__F_paeth: - cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0, prior[k], 0)); - break; - case STBI__F_avg_first: - cur[k] = raw[k]; - break; - case STBI__F_paeth_first: - cur[k] = raw[k]; - break; - } - } - - if (depth == 8) { - if (img_n != out_n) cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes + 1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or - // per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1) * filter_bytes; -#define STBI__CASE(f) \ - case f: \ - for (k = 0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: - memcpy(cur, raw, nk); - break; - STBI__CASE(STBI__F_sub) { - cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); - } - break; - STBI__CASE(STBI__F_up) { - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); - } - break; - STBI__CASE(STBI__F_avg) { - cur[k] = STBI__BYTECAST(raw[k] + - ((prior[k] + cur[k - filter_bytes]) >> 1)); - } - break; - STBI__CASE(STBI__F_paeth) { - cur[k] = STBI__BYTECAST(raw[k] + - stbi__paeth(cur[k - filter_bytes], prior[k], - prior[k - filter_bytes])); - } - break; - STBI__CASE(STBI__F_avg_first) { - cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); - } - break; - STBI__CASE(STBI__F_paeth_first) { - cur[k] = STBI__BYTECAST(raw[k] + - stbi__paeth(cur[k - filter_bytes], 0, 0)); - } - break; - } -#undef STBI__CASE - raw += nk; - } else { - assert(img_n + 1 == out_n); -#define STBI__CASE(f) \ - case f: \ - for (i = x - 1; i >= 1; --i, cur[filter_bytes] = 255, raw += filter_bytes, \ - cur += output_bytes, prior += output_bytes) \ - for (k = 0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { - cur[k] = raw[k]; - } + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + + ((prior[k] + cur[k - filter_bytes]) >> 1)); break; - STBI__CASE(STBI__F_sub) { - cur[k] = STBI__BYTECAST(raw[k] + cur[k - output_bytes]); - } - break; - STBI__CASE(STBI__F_up) { + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + // prior[k] == stbi__paeth(0, prior[k], 0) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); - } - break; - STBI__CASE(STBI__F_avg) { + for (k = filter_bytes; k < nk; ++k) cur[k] = STBI__BYTECAST(raw[k] + - ((prior[k] + cur[k - output_bytes]) >> 1)); - } + stbi__paeth(cur[k - filter_bytes], prior[k], + prior[k - filter_bytes])); break; - STBI__CASE(STBI__F_paeth) { - cur[k] = STBI__BYTECAST(raw[k] + - stbi__paeth(cur[k - output_bytes], prior[k], - prior[k - output_bytes])); - } + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); break; - STBI__CASE(STBI__F_avg_first) { - cur[k] = STBI__BYTECAST(raw[k] + (cur[k - output_bytes] >> 1)); - } - break; - STBI__CASE(STBI__F_paeth_first) { - cur[k] = - STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - output_bytes], 0, 0)); - } - break; - } -#undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride * j; // start at the beginning of the row again - for (i = 0; i < x; ++i, cur += output_bytes) { - cur[filter_bytes + 1] = 255; - } - } } - } - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *in = a->out + stride * j + x * out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common - // 8-bit path optimal at minimal cost for 1/2/4-bit png guarante byte - // alignment, if width is not multiple of 8/4/2 we'll decode dummy - // trailing data that will be skipped in the later loop + raw += nk; + + // expand decoded bits in cur to dest, also adding an extra alpha channel + // if desired + if (depth < 8) { unsigned char scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + unsigned char *in = cur; + unsigned char *out = dest; + unsigned char inb = 0; + uint32_t nsmp = x * img_n; - // note that the final byte might overshoot and write more data than - // desired. we can allocate enough data that this never writes out of - // memory, but it could also overwrite the next scanline. can it - // overwrite non-empty data on the next scanline? yes, consider - // 1-pixel-wide scanlines with 1-bit-per-pixel. so we need to explicitly - // clamp the final ones - + // expand bits to bytes first if (depth == 4) { - for (k = x * img_n; k >= 2; k -= 2, ++in) { - *cur++ = scale * ((*in >> 4)); - *cur++ = scale * ((*in) & 0x0f); + for (i = 0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; } - if (k > 0) *cur++ = scale * ((*in >> 4)); } else if (depth == 2) { - for (k = x * img_n; k >= 4; k -= 4, ++in) { - *cur++ = scale * ((*in >> 6)); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in) & 0x03); + for (i = 0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; } - if (k > 0) *cur++ = scale * ((*in >> 6)); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k = x * img_n; k >= 8; k -= 8, ++in) { - *cur++ = scale * ((*in >> 7)); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in) & 0x01); + } else { + assert(depth == 1); + for (i = 0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; } - if (k > 0) *cur++ = scale * ((*in >> 7)); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride * j; + + // insert alpha=255 values if desired + if (img_n != out_n) stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x * img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + // TODO TYPES + uint16_t *dest16 = (uint16_t *)dest; + uint32_t nsmp = x * img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + assert(img_n + 1 == out_n); if (img_n == 1) { - for (q = x - 1; q >= 0; --q) { - cur[q * 2 + 1] = 255; - cur[q * 2 + 0] = cur[q]; + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; } } else { assert(img_n == 3); - for (q = x - 1; q >= 0; --q) { - cur[q * 4 + 3] = 255; - cur[q * 4 + 2] = cur[q * 3 + 2]; - cur[q * 4 + 1] = cur[q * 3 + 1]; - cur[q * 4 + 0] = cur[q * 3 + 0]; + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; } } } } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - unsigned char *cur = a->out; - uint16_t *cur16 = (uint16_t *)cur; - - for (i = 0; i < x * y * out_n; ++i, cur16++, cur += 2) { - *cur16 = (cur[0] << 8) | cur[1]; - } } + free(filler_buf); + if (!all_ok) return 0; + return 1; } @@ -3767,6 +3936,7 @@ static int stbi__create_png_image(stbi__png *a, unsigned char *image_data, a->s->img_x, a->s->img_y, depth, color); // de-interlacing final = stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); for (p = 0; p < 7; ++p) { int xorig[] = {0, 4, 0, 2, 0, 1, 0}; int yorig[] = {0, 0, 4, 0, 2, 0, 1}; @@ -3954,10 +4124,10 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { first = 0; if (c.length != 13) return stbi__err("bad IHDR len", "Corrupt PNG"); s->img_x = stbi__get32be(s); - if (s->img_x > (1 << 24)) - return stbi__err("too large", "Very large image (corrupt?)"); s->img_y = stbi__get32be(s); - if (s->img_y > (1 << 24)) + if (s->img_y > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large", "Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && @@ -3985,15 +4155,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large", "Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS } + // even with SCAN_header, have to scan to see if we have a tRNS break; } @@ -4031,6 +4200,12 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { if (c.length != (uint32_t)s->img_n * 2) return stbi__err("bad tRNS len", "Corrupt PNG"); has_trans = 1; + // non-paletted with tRNS = constant alpha. + // if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { + ++s->img_n; + return 1; + } if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (uint16_t)stbi__get16be(s); // copy the values as-is @@ -4048,17 +4223,19 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE", "Corrupt PNG"); if (scan == STBI__SCAN_header) { - s->img_n = pal_img_n; + // header scan definitely stops at first IDAT + if (pal_img_n) s->img_n = pal_img_n; return 1; } + if (c.length > (1u << 30)) + return stbi__err("IDAT size limit", + "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { - uint32_t idata_limit_old = idata_limit; unsigned char *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; - (void)idata_limit_old; - p = STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); + p = realloc(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); z->idata = p; } @@ -4114,7 +4291,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { } free(z->expanded); z->expanded = NULL; - stbi__get32be(s); /* nothings/stb#835 */ + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -4122,7 +4300,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { // if critical, fail if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if ((c.type & (1 << 29)) == 0) { -#ifndef STBI_NO_FAILURE_STRINGS +#if !defined(STBI_NO_FAILURE_STRINGS) && !defined(STBI_FAILURE_USERMSG) // not threadsafe static char invalid_chunk[] = "XXXX PNG chunk not known"; invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); @@ -4147,10 +4325,13 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) + if (p->depth <= 8) ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; else - ri->bits_per_channel = p->depth; + return stbi__errpuc("bad bits_per_channel", + "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { @@ -4297,6 +4478,10 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, g->bgindex = stbi__get8(s); g->ratio = stbi__get8(s); g->transparent = -1; + if (g->w > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); if (comp != 0) { *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the // comments @@ -4310,6 +4495,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif *g = (stbi__gif *)malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { free(g); stbi__rewind(s); @@ -4458,7 +4644,7 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, if (!g->out || !g->background || !g->history) return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "transparent" at the start - ie, nothing overwrites + // image is treated as "transparent" at the start - i.e. nothing overwrites // the current background; background colour is only used for pixels that // are not rendered first frame, after that "background" color refers to // the color that was there the previous frame. @@ -4469,7 +4655,7 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, pcount); // pixels that were affected previous frame first_frame = 1; } else { - // second frame - how do we dispoase of the previous one? + // second frame - how do we dispose of the previous one? dispose = (g->eflags & 0x1C) >> 2; pcount = g->w * g->h; @@ -4492,10 +4678,10 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case either way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose - // 0: not specified. + // 0: not specified. } // background is what out is after the undoing of the previou frame; @@ -4622,6 +4808,16 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, } } +static void *stbi__load_gif_main_outofmem(stbi__gif *g, unsigned char *out, + int **delays) { + free(g->out); + free(g->history); + free(g->background); + if (out) free(out); + if (delays && *delays) free(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { @@ -4644,21 +4840,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, ++layers; stride = g->w * g->h * 4; if (out) { - out = (unsigned char *)realloc(out, layers * stride); - if (!out) abort(); + void *tmp = (unsigned char *)realloc(out, layers * stride); + if (!tmp) + return stbi__load_gif_main_outofmem(g, out, delays); + else { + out = (unsigned char *)tmp; + } if (delays) { - *delays = (int *)realloc(*delays, sizeof(int) * layers); - if (!*delays) abort(); + int *new_delays = (int *)realloc(*delays, sizeof(int) * layers); + if (!new_delays) + return stbi__load_gif_main_outofmem(g, out, delays); + *delays = new_delays; } } else { out = malloc(layers * stride); + if (!out) return stbi__load_gif_main_outofmem(g, out, delays); if (delays) { *delays = malloc(layers * sizeof(int)); + if (!*delays) return stbi__load_gif_main_outofmem(g, out, delays); } } memcpy(out + ((layers - 1) * stride), u, stride); if (layers >= 2) { - two_back = out - 2 * stride; + two_back = out + ((layers - 2) * stride); } if (delays) { (*delays)[layers - 1U] = g->delay; @@ -4720,7 +4924,6 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel static int stbi__pnm_test(stbi__context *s) { char p, t; @@ -4737,20 +4940,37 @@ static dontinline void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char *out; - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, - (int *)&s->img_n)) { - return 0; + ri->bits_per_channel = + stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) return 0; + if (s->img_y > STBI_MAX_DIMENSIONS) { + return stbi__errpuc("too large", "Very large image (corrupt?)"); + } + if (s->img_x > STBI_MAX_DIMENSIONS) { + return stbi__errpuc("too large", "Very large image (corrupt?)"); } *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) { + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, + ri->bits_per_channel / 8, 0)) { return stbi__errpuc("too large", "PNM too large"); } - out = stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + out = stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, + ri->bits_per_channel / 8, 0); + if (!stbi__getn( + s, out, + s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + free(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (ri->bits_per_channel == 16) { + out = (unsigned char *)stbi__convert_format16( + (uint16_t *)out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; @@ -4779,6 +4999,12 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) { while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value * 10 + (*c - '0'); *c = (char)stbi__get8(s); + // TODO INT_MAX + if ((value > 214748364) || (value == 214748364 && *c > '7')) { + return stbi__err( + "integer parse overflow", + "Parsing an integer in the PPM header overflowed a 32-bit int"); + } } return value; } @@ -4802,15 +5028,30 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { c = (char)stbi__get8(s); stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width + if (*x == 0) { + return stbi__err("invalid_width", + "PPM image header had zero or overflowing width"); + } stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) { + return stbi__err("invalid height", + "PPM image header had zero or overflowing height"); + } stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else { - return 1; - } + if (maxv > 65535) + return stbi__err("max value > 65535", + "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) { + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) return 1; + return 0; } static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { @@ -4831,7 +5072,12 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { } static int stbi__is_16_main(stbi__context *s) { +#ifndef STBI_NO_PNG if (stbi__png_is16(s)) return 1; +#endif +#ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; +#endif return 0; } @@ -4848,9 +5094,10 @@ int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { int r; stbi__context s; long pos = ftell(f); + if (pos < 0) return stbi__err("bad file", "ftell() failed"); stbi__start_file(&s, f); r = stbi__info_main(&s, x, y, comp); - fseek(f, pos, SEEK_SET); + if (fseek(f, pos, SEEK_SET)) return stbi__err("bad file", "fseek() failed"); return r; } @@ -4867,9 +5114,10 @@ int stbi_is_16_bit_from_file(FILE *f) { int r; stbi__context s; long pos = ftell(f); + if (pos < 0) return stbi__err("bad file", "ftell() failed"); stbi__start_file(&s, f); r = stbi__is_16_main(&s); - fseek(f, pos, SEEK_SET); + if (fseek(f, pos, SEEK_SET)) return stbi__err("bad file", "fseek() failed"); return r; } diff --git a/third_party/stb/stb_image.h b/third_party/stb/stb_image.h index 9f3076887..558506908 100644 --- a/third_party/stb/stb_image.h +++ b/third_party/stb/stb_image.h @@ -13,12 +13,14 @@ enum { struct FILE; typedef struct { - int (*read)(void *user, char *data, - int size); // fill 'data' with 'size' bytes. return number of - // bytes actually read - void (*skip)(void *user, int n); // skip the next 'n' bytes, or 'unget' the - // last -n bytes if negative - int (*eof)(void *user); // returns nonzero if we are at end of file/data + // fill 'data' with 'size' bytes. return number of bytes actually read + int (*read)(void *user, char *data, int size); + + // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + void (*skip)(void *user, int n); + + // returns nonzero if we are at end of file/data + int (*eof)(void *user); } stbi_io_callbacks; // @@ -63,7 +65,6 @@ unsigned short *stbi_load_from_file_16(struct FILE *f, int *x, int *y, int desired_channels); // get a VERY brief reason for failure -// NOT THREADSAFE const char *stbi_failure_reason(void); // free the loaded image -- this is just free() diff --git a/third_party/stb/stb_image_resize.c b/third_party/stb/stb_image_resize.c index 55d1b8351..24cb373e5 100644 --- a/third_party/stb/stb_image_resize.c +++ b/third_party/stb/stb_image_resize.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -655,9 +655,14 @@ static void stbir__calculate_coefficients_upsample( total_filter += coefficient_group[i]; } - STBIR_ASSERT(stbir__filter_info_table[filter].kernel( - (float)(in_last_pixel + 1) + 0.5f - in_center_of_out, - 1 / scale) == 0); + // NOTE(fg): Not actually true in general, nor is there any reason to expect + // it should be. It would be true in exact math but is at best approximately + // true in floating-point math, and it would not make sense to try and put + // actual bounds on this here because it depends on the image aspect ratio + // which can get pretty extreme. + // STBIR_ASSERT(stbir__filter_info_table[filter].kernel( + // (float)(in_last_pixel + 1) + 0.5f - in_center_of_out, + // 1 / scale) == 0); STBIR_ASSERT(total_filter > 0.9); STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. @@ -701,9 +706,14 @@ static void stbir__calculate_coefficients_downsample( stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; } - STBIR_ASSERT(stbir__filter_info_table[filter].kernel( - (float)(out_last_pixel + 1) + 0.5f - out_center_of_in, - scale_ratio) == 0); + // NOTE(fg): Not actually true in general, nor is there any reason to expect + // it should be. It would be true in exact math but is at best approximately + // true in floating-point math, and it would not make sense to try and put + // actual bounds on this here because it depends on the image aspect ratio + // which can get pretty extreme. + // STBIR_ASSERT(stbir__filter_info_table[filter].kernel( + // (float)(out_last_pixel + 1) + 0.5f - out_center_of_in, + // scale_ratio) == 0); for (i = out_last_pixel - out_first_pixel; i >= 0; i--) { if (coefficient_group[i]) break; @@ -851,7 +861,7 @@ static float* stbir__get_decode_buffer(stbir__info* stbir_info) { } #define STBIR__DECODE(type, colorspace) \ - ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace)) static void stbir__decode_scanline(stbir__info* stbir_info, int n) { int c; @@ -1199,7 +1209,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 1; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; } @@ -1220,7 +1229,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 2; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += @@ -1243,7 +1251,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 3; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += @@ -1268,7 +1275,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 4; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += diff --git a/third_party/stb/stb_image_write.c b/third_party/stb/stb_image_write.c index 63187511d..1f984bc78 100644 --- a/third_party/stb/stb_image_write.c +++ b/third_party/stb/stb_image_write.c @@ -1,123 +1,21 @@ -/* stb_image_write - v1.13 - public domain - http://nothings.org/stb - * writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 - * no warranty implied; use at your own risk - * - * ABOUT: - * - * This file is a library for writing images to stdio or a callback. - * - * The PNG output is not optimal; it is 20-50% larger than the file - * written by a decent optimizing implementation; though providing a - * custom zlib compress function (see STBIW_ZLIB_COMPRESS) can - * mitigate that. This library is designed for source code - * compactness and simplicity, not optimal image file size or - * run-time performance. - * - * USAGE: - * - * There are five functions, one for each image file format: - * - * stbi_write_png - * stbi_write_bmp - * stbi_write_tga - * stbi_write_jpg - * stbi_write_hdr - * - * stbi_flip_vertically_on_write - * - * There are also five equivalent functions that use an arbitrary - * write function. You are expected to open/close your - * file-equivalent before and after calling these: - * - * stbi_write_png_to_func - * stbi_write_bmp_to_func - * stbi_write_tga_to_func - * stbi_write_hdr_to_func - * stbi_write_jpg_to_func - * - * where the callback is: - * void stbi_write_func(void *context, void *data, int size); - * - * You can configure it with these: - * stbi_write_tga_with_rle - * stbi_write_png_compression_level - * stbi_write_force_png_filter - * - * Each function returns 0 on failure and non-0 on success. - * - * The functions create an image file defined by the parameters. The - * image is a rectangle of pixels stored from left-to-right, - * top-to-bottom. Each pixel contains 'comp' channels of data stored - * interleaved with 8-bits per channel, in the following order: 1=Y, - * 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' - * pixels wide and 'h' pixels tall. The *data pointer points to the - * first byte of the top-left-most pixel. For PNG, "stride_in_bytes" - * is the distance in bytes from the first byte of a row of pixels to - * the first byte of the next row of pixels. - * - * PNG creates output files with the same number of components as the - * input. The BMP format expands Y to RGB in the file format and does - * not output alpha. - * - * PNG supports writing rectangles of data even when the bytes - * storing rows of data are not consecutive in memory (e.g. - * sub-rectangles of a larger image), by supplying the stride between - * the beginning of adjacent rows. The other formats do not. (Thus - * you cannot write a native-format BMP through the BMP writer, both - * because it is in BGR order and because it may have padding at the - * end of the line.) - * - * PNG allows you to set the deflate compression level by setting the - * global variable 'stbi_write_png_compression_level' (it defaults to - * 8). - * - * HDR expects linear float data. Since the format is always 32-bit - * rgb(e) data, alpha (if provided) is discarded, and for monochrome - * data it is replicated across all three channels. - * - * TGA supports RLE or non-RLE compressed data. To use - * non-RLE-compressed data, set the global variable - * 'stbi_write_tga_with_rle' to 0. - * - * JPEG does ignore alpha channels in input data; quality is between - * 1 and 100. Higher quality looks better but results in a bigger - * image. JPEG baseline (no JPEG progressive). - * - * CREDITS: - * - * - * Sean Barrett - PNG/BMP/TGA - * Baldur Karlsson - HDR - * Jean-Sebastien Guay - TGA monochrome - * Tim Kelsey - misc enhancements - * Alan Hickman - TGA RLE - * Emmanuel Julien - initial file IO callback implementation - * Jon Olick - original jo_jpeg.cpp code - * Daniel Gibson - integrate JPEG, allow external zlib - * Aarni Koskela - allow choosing PNG filter - * - * bugfixes: - * github:Chribba - * Guillaume Chereau - * github:jry2 - * github:romigrou - * Sergio Gonzalez - * Jonas Karlsson - * Filip Wasil - * Thatcher Ulrich - * github:poppolopoppo - * Patrick Boettcher - * github:xeekworx - * Cap Petschulat - * Simon Rodriguez - * Ivan Tikhonov - * github:ignotion - * Adam Schackart - * - * LICENSE - * - * Public Domain (www.unlicense.org) - */ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/stb/stb_image_write.h" #include "dsp/core/core.h" #include "libc/assert.h" @@ -131,16 +29,32 @@ #include "libc/str/str.h" #include "third_party/zlib/zlib.h" +asm(".ident\t\"\\n\\n\ +stb_image_write (Public Domain)\\n\ +Credit: Sean Barrett, et al.\\n\ +http://nothings.org/stb\""); + #define STBIW_UCHAR(x) (unsigned char)((x)&0xff) -#define STBIW_REALLOC_SIZED(p, oldsz, newsz) realloc(p, newsz) + +#define stbiw__wpng4(o, a, b, c, d) \ + ((o)[0] = STBIW_UCHAR(a), (o)[1] = STBIW_UCHAR(b), (o)[2] = STBIW_UCHAR(c), \ + (o)[3] = STBIW_UCHAR(d), (o) += 4) +#define stbiw__wp32(data, v) \ + stbiw__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v)); +#define stbiw__wptag(data, s) stbiw__wpng4(data, s[0], s[1], s[2], s[3]) typedef struct { stbi_write_func *func; void *context; + unsigned char buffer[64]; + int buf_used; } stbi__write_context; -int stbi__flip_vertically_on_write = 0; +int stbi_write_png_compression_level = 4; int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; + +static int stbi__flip_vertically_on_write = 0; void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; @@ -168,9 +82,6 @@ static void stbi__end_write_file(stbi__write_context *s) { fclose((FILE *)s->context); } -typedef unsigned int stbiw_uint32; -typedef int stb_image_write_test[sizeof(stbiw_uint32) == 4 ? 1 : -1]; - static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { @@ -190,7 +101,7 @@ static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { break; } case '4': { - stbiw_uint32 x = va_arg(v, int); + unsigned int x = va_arg(v, int); unsigned char b[4]; b[0] = STBIW_UCHAR(x); b[1] = STBIW_UCHAR(x >> 8); @@ -212,17 +123,31 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { va_end(v); } +static void stbiw__write_flush(stbi__write_context *s) { + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } +static void stbiw__write1(stbi__write_context *s, unsigned char a) { + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { - unsigned char arr[3]; - arr[0] = a; - arr[1] = b; - arr[2] = c; - s->func(s->context, arr, 3); + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n + 3; + s->buffer[n + 0] = a; + s->buffer[n + 1] = b; + s->buffer[n + 2] = c; } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, @@ -231,7 +156,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, unsigned char bg[3] = {255, 0, 255}, px[3]; int k; - if (write_alpha < 0) s->func(s->context, &d[comp - 1], 1); + if (write_alpha < 0) stbiw__write1(s, d[comp - 1]); switch (comp) { case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as @@ -240,7 +165,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else - s->func(s->context, d, 1); // monochrome TGA + stbiw__write1(s, d[0]); // monochrome TGA break; case 4: if (!write_alpha) { @@ -254,14 +179,14 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); break; } - if (write_alpha > 0) s->func(s->context, &d[comp - 1], 1); + if (write_alpha > 0) stbiw__write1(s, d[comp - 1]); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) { - stbiw_uint32 zero = 0; + unsigned int zero = 0; int i, j, j_end; if (y <= 0) return; if (stbi__flip_vertically_on_write) vdir *= -1; @@ -277,6 +202,7 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, unsigned char *d = (unsigned char *)data + (j * x + i) * comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } + stbiw__write_flush(s); s->func(s->context, &zero, scanline_pad); } } @@ -299,25 +225,41 @@ static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { - int pad = (-x * 3) & 3; - return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad, - "11 4 22 4" - "4 44 22 444444", - 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, - 14 + 40, // file header - 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header + if (comp != 4) { + // write RGB bitmap + int pad; + pad = (-x * 3) & 3; + return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad, + "11 4 22 4" + "4 44 22 444444", + 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, + 14 + 40, // file header + 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 1, 0, + "11 4 22 4" + "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14 + 108 + x * y * 4, 0, 0, + 14 + 108, // file header + 108, x, y, 1, 32, 3, 0, 0, 0, 0, 0, 0xff0000, 0xff00, + 0xff, 0xff000000u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0); // bitmap V4 header + } } int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); @@ -393,31 +335,32 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, if (diff) { unsigned char header = STBIW_UCHAR(len - 1); - s->func(s->context, &header, 1); + stbiw__write1(s, header); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); - s->func(s->context, &header, 1); + stbiw__write1(s, header); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } + stbiw__write_flush(s); } return 1; } int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *)data); } int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *)data); stbi__end_write_file(&s); @@ -426,6 +369,250 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, return 0; } +/* + * PNG writer + */ + +static unsigned char *stbi_zlib_compress(unsigned char *data, int size, + int *out_len, int quality) { + unsigned long newsize; + unsigned char *newdata, *trimdata; + assert(0 <= size && size <= INT_MAX); + if ((newdata = malloc((newsize = compressBound(size)))) && + compress2(newdata, &newsize, data, size, + stbi_write_png_compression_level) == Z_OK) { + *out_len = newsize; + if ((trimdata = realloc(newdata, newsize))) { + return trimdata; + } else { + return newdata; + } + } + free(newdata); + return NULL; +} + +static void stbiw__wpcrc(unsigned char **data, int len) { + unsigned int crc = crc32(0, *data - len - 4, len + 4); + stbiw__wp32(*data, crc); +} + +forceinline unsigned char stbiw__paeth(int a, int b, int c) { + int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(const unsigned char *pixels, + int stride_bytes, int width, int height, + int y, int n, int filter_type, + signed char *line_buffer) { + int mapping[] = {0, 1, 2, 3, 4}; + int firstmap[] = {0, 1, 0, 5, 6}; + const unsigned char *z; + int *mymap, i, type, signed_stride; + + mymap = (y != 0) ? mapping : firstmap; + type = mymap[filter_type]; + z = pixels + + stride_bytes * (stbi__flip_vertically_on_write ? height - 1 - y : y); + signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type == 0) { + memcpy(line_buffer, z, width * n); + return; + } + + for (i = 0; i < n; ++i) { + switch (type) { + case 1: + line_buffer[i] = z[i]; + break; + case 2: + line_buffer[i] = z[i] - z[i - signed_stride]; + break; + case 3: + line_buffer[i] = z[i] - (z[i - signed_stride] >> 1); + break; + case 4: + line_buffer[i] = + (signed char)(z[i] - stbiw__paeth(0, z[i - signed_stride], 0)); + break; + case 5: + line_buffer[i] = z[i]; + break; + case 6: + line_buffer[i] = z[i]; + break; + } + } + + switch (type) { + case 1: + for (i = n; i < width * n; ++i) { + line_buffer[i] = z[i] - z[i - n]; + } + break; + case 2: + for (i = n; i < width * n; ++i) { + line_buffer[i] = z[i] - z[i - signed_stride]; + } + break; + case 3: + for (i = n; i < width * n; ++i) { + line_buffer[i] = z[i] - ((z[i - n] + z[i - signed_stride]) >> 1); + } + break; + case 4: + for (i = n; i < width * n; ++i) { + line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - signed_stride], + z[i - signed_stride - n]); + } + break; + case 5: + for (i = n; i < width * n; ++i) { + line_buffer[i] = z[i] - (z[i - n] >> 1); + } + break; + case 6: + for (i = n; i < width * n; ++i) { + line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0); + } + break; + } +} + +unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, + int stride_bytes, int x, int y, int n, + int *out_len) { + int force_filter = stbi_write_force_png_filter; + int ctype[5] = {-1, 0, 4, 2, 6}; + unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + unsigned char *out, *o, *filt, *zlib; + signed char *line_buffer; + int j, zlen; + + if (stride_bytes == 0) stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = malloc((x * n + 1) * y); + if (!filt) return 0; + line_buffer = malloc(x * n); + if (!line_buffer) { + free(filt); + return 0; + } + for (j = 0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, + line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, + line_buffer); + + // Estimate the entropy of the line using this filter; the less, the + // better. + est = 0; + for (i = 0; i < x * n; ++i) { + est += abs((signed char)line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us + // the best filter, don't redo it + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, + line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer + // contains the data + filt[j * (x * n + 1)] = (unsigned char)filter_type; + memmove(filt + j * (x * n + 1) + 1, line_buffer, x * n); + } + free(line_buffer); + zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen, + stbi_write_png_compression_level); + free(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = malloc(8 + 12 + 13 + 12 + zlen + 12); + if (!out) return 0; + *out_len = 8 + 12 + 13 + 12 + zlen + 12; + + o = out; + memmove(o, sig, 8); + o += 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o, 13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + memmove(o, zlib, zlen); + o += zlen; + free(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o, 0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o, 0); + + assert(o == out + *out_len); + + return out; +} + +int stbi_write_png(const char *filename, int x, int y, int comp, + const void *data, int stride_bytes) { + int len; + FILE *f; + unsigned char *png; + png = stbi_write_png_to_mem(data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { + free(png); + return 0; + } + fwrite(png, 1, len, f); + fclose(f); + free(png); + return 1; +} + +int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, + int comp, const void *data, int stride_bytes) { + int len; + unsigned char *png; + png = stbi_write_png_to_mem((const unsigned char *)data, stride_bytes, x, y, + comp, &len); + if (png == NULL) return 0; + func(context, png, len); + free(png); + return 1; +} + /* JPEG writer * * This is based on Jon Olick's jo_jpeg.cpp: @@ -472,24 +659,25 @@ static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { } static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, - int *bitCnt, float *CDU, float *fdtbl, int DC, + int *bitCnt, float *CDU, unsigned du_stride, + float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) { const unsigned short EOB[2] = {HTAC[0x00][0], HTAC[0x00][1]}; const unsigned short M16zeroes[2] = {HTAC[0xF0][0], HTAC[0xF0][1]}; - unsigned i, diff, end0pos; + unsigned i, j, diff, end0pos, x, y; int DU[64]; - dctjpeg((void *)CDU); + dctjpeg((void *)CDU, du_stride / 8); // Quantize/descale/zigzag the coefficients - for (i = 0; i < 64; ++i) { - float v = CDU[i] * fdtbl[i]; - DU[stbiw__jpg_ZigZag[i]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f); - // DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + - // 0.5f)); ceilf() and floorf() are C99, not C89, but I /think/ they're not - // needed here anyway? - /* DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); */ + for (j = 0, y = 0; y < 8; ++y) { + for (x = 0; x < 8; ++x, ++j) { + float v; + i = y * du_stride + x; + v = CDU[i] * fdtbl[j]; + DU[stbiw__jpg_ZigZag[j]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f); + } } // Encode DC @@ -709,7 +897,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f}; - int row, col, i, k; + int row, col, i, k, subsample; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; @@ -718,6 +906,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, } quality = quality ? quality : 97; + subsample = quality <= 97 ? 1 : 0; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; @@ -758,7 +947,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, STBIW_UCHAR(width), 3, 1, - 0x11, + (unsigned char)(subsample ? 0x22 : 0x11), 0, 2, 0x11, @@ -802,42 +991,92 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; - const unsigned char *imageData = (const unsigned char *)data; int DCY = 0, DCU = 0, DCV = 0; int bitBuf = 0, bitCnt = 0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; int x, y, pos; - for (y = 0; y < height; y += 8) { - for (x = 0; x < width; x += 8) { - float YDU[64], UDU[64], VDU[64]; - for (row = y, pos = 0; row < y + 8; ++row) { - // row >= height => use last input row - int clamped_row = (row < height) ? row : height - 1; - int base_p = - (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) - : clamped_row) * - width * comp; - for (col = x; col < x + 8; ++col, ++pos) { - float r, g, b; - // if col >= width => use pixel from last input column - int p = base_p + ((col < width) ? col : (width - 1)) * comp; + if (subsample) { + for (y = 0; y < height; y += 16) { + for (x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for (row = y, pos = 0; row < y + 16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = + (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) + : clamped_row) * + width * comp; + for (col = x; col < x + 16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width - 1)) * comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; + U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; + V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + } + } - r = imageData[p + 0]; - g = imageData[p + ofsG]; - b = imageData[p + ofsB]; - YDU[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; - UDU[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; - VDU[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 136, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for (yy = 0, pos = 0; yy < 8; ++yy) { + for (xx = 0; xx < 8; ++xx, ++pos) { + int j = yy * 32 + xx * 2; + subU[pos] = + (U[j + 0] + U[j + 1] + U[j + 16] + U[j + 17]) * 0.25f; + subV[pos] = + (V[j + 0] + V[j + 1] + V[j + 16] + V[j + 17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, + DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, + DCV, UVDC_HT, UVAC_HT); } } + } + } else { + for (y = 0; y < height; y += 8) { + for (x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for (row = y, pos = 0; row < y + 8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = + (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) + : clamped_row) * + width * comp; + for (col = x; col < x + 8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width - 1)) * comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; + U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; + V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + } + } - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, - YDC_HT, YAC_HT); - DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, - UVDC_HT, UVAC_HT); - DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, - UVDC_HT, UVAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, + YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, + UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, + UVDC_HT, UVAC_HT); + } } } @@ -854,14 +1093,14 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *)data, quality); } int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); @@ -1026,14 +1265,14 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *)data); } int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *)data); stbi__end_write_file(&s); diff --git a/third_party/stb/stb_image_write.h b/third_party/stb/stb_image_write.h index 2b21f4f15..f1ad8e167 100644 --- a/third_party/stb/stb_image_write.h +++ b/third_party/stb/stb_image_write.h @@ -3,7 +3,6 @@ COSMOPOLITAN_C_START_ extern int stbi_write_png_compression_level; -extern int stbi__flip_vertically_on_write; extern int stbi_write_tga_with_rle; extern int stbi_write_force_png_filter; diff --git a/third_party/stb/stb_image_write_png.c b/third_party/stb/stb_image_write_png.c deleted file mode 100644 index 9ee926f58..000000000 --- a/third_party/stb/stb_image_write_png.c +++ /dev/null @@ -1,379 +0,0 @@ -/* stb_image_write - v1.13 - public domain - http://nothings.org/stb - * writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 - * no warranty implied; use at your own risk - * - * ABOUT: - * - * This file is a library for writing images to stdio or a callback. - * - * The PNG output is not optimal; it is 20-50% larger than the file - * written by a decent optimizing implementation; though providing a - * custom zlib compress function (see STBIW_ZLIB_COMPRESS) can - * mitigate that. This library is designed for source code - * compactness and simplicity, not optimal image file size or - * run-time performance. - * - * USAGE: - * - * There are five functions, one for each image file format: - * - * stbi_write_png - * stbi_write_bmp - * stbi_write_tga - * stbi_write_jpg - * stbi_write_hdr - * - * stbi_flip_vertically_on_write - * - * There are also five equivalent functions that use an arbitrary - * write function. You are expected to open/close your - * file-equivalent before and after calling these: - * - * stbi_write_png_to_func - * stbi_write_bmp_to_func - * stbi_write_tga_to_func - * stbi_write_hdr_to_func - * stbi_write_jpg_to_func - * - * where the callback is: - * void stbi_write_func(void *context, void *data, int size); - * - * You can configure it with these: - * stbi_write_tga_with_rle - * stbi_write_png_compression_level - * stbi_write_force_png_filter - * - * Each function returns 0 on failure and non-0 on success. - * - * The functions create an image file defined by the parameters. The - * image is a rectangle of pixels stored from left-to-right, - * top-to-bottom. Each pixel contains 'comp' channels of data stored - * interleaved with 8-bits per channel, in the following order: 1=Y, - * 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' - * pixels wide and 'h' pixels tall. The *data pointer points to the - * first byte of the top-left-most pixel. For PNG, "stride_in_bytes" - * is the distance in bytes from the first byte of a row of pixels to - * the first byte of the next row of pixels. - * - * PNG creates output files with the same number of components as the - * input. The BMP format expands Y to RGB in the file format and does - * not output alpha. - * - * PNG supports writing rectangles of data even when the bytes - * storing rows of data are not consecutive in memory (e.g. - * sub-rectangles of a larger image), by supplying the stride between - * the beginning of adjacent rows. The other formats do not. (Thus - * you cannot write a native-format BMP through the BMP writer, both - * because it is in BGR order and because it may have padding at the - * end of the line.) - * - * PNG allows you to set the deflate compression level by setting the - * global variable 'stbi_write_png_compression_level' (it defaults to - * 8). - * - * HDR expects linear float data. Since the format is always 32-bit - * rgb(e) data, alpha (if provided) is discarded, and for monochrome - * data it is replicated across all three channels. - * - * TGA supports RLE or non-RLE compressed data. To use - * non-RLE-compressed data, set the global variable - * 'stbi_write_tga_with_rle' to 0. - * - * JPEG does ignore alpha channels in input data; quality is between - * 1 and 100. Higher quality looks better but results in a bigger - * image. JPEG baseline (no JPEG progressive). - * - * CREDITS: - * - * - * Sean Barrett - PNG/BMP/TGA - * Baldur Karlsson - HDR - * Jean-Sebastien Guay - TGA monochrome - * Tim Kelsey - misc enhancements - * Alan Hickman - TGA RLE - * Emmanuel Julien - initial file IO callback implementation - * Jon Olick - original jo_jpeg.cpp code - * Daniel Gibson - integrate JPEG, allow external zlib - * Aarni Koskela - allow choosing PNG filter - * - * bugfixes: - * github:Chribba - * Guillaume Chereau - * github:jry2 - * github:romigrou - * Sergio Gonzalez - * Jonas Karlsson - * Filip Wasil - * Thatcher Ulrich - * github:poppolopoppo - * Patrick Boettcher - * github:xeekworx - * Cap Petschulat - * Simon Rodriguez - * Ivan Tikhonov - * github:ignotion - * Adam Schackart - * - * LICENSE - * - * Public Domain (www.unlicense.org) - */ -#include "libc/assert.h" -#include "libc/fmt/conv.h" -#include "libc/limits.h" -#include "libc/mem/mem.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "third_party/stb/stb_image_write.h" -#include "third_party/zlib/zlib.h" - -#define STBIW_UCHAR(x) (unsigned char)((x)&0xff) -#define stbiw__wpng4(o, a, b, c, d) \ - ((o)[0] = STBIW_UCHAR(a), (o)[1] = STBIW_UCHAR(b), (o)[2] = STBIW_UCHAR(c), \ - (o)[3] = STBIW_UCHAR(d), (o) += 4) -#define stbiw__wp32(data, v) \ - stbiw__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v)); -#define stbiw__wptag(data, s) stbiw__wpng4(data, s[0], s[1], s[2], s[3]) - -int stbi_write_png_compression_level = 4; -int stbi_write_force_png_filter = -1; - -static unsigned char *stbi_zlib_compress(unsigned char *data, int size, - int *out_len, int quality) { - unsigned long newsize; - unsigned char *newdata, *trimdata; - assert(0 <= size && size <= INT_MAX); - if ((newdata = malloc((newsize = compressBound(size)))) && - compress2(newdata, &newsize, data, size, - stbi_write_png_compression_level) == Z_OK) { - *out_len = newsize; - if ((trimdata = realloc(newdata, newsize))) { - return trimdata; - } else { - return newdata; - } - } - free(newdata); - return NULL; -} - -static void stbiw__wpcrc(unsigned char **data, int len) { - unsigned int crc = crc32(0, *data - len - 4, len + 4); - stbiw__wp32(*data, crc); -} - -forceinline unsigned char stbiw__paeth(int a, int b, int c) { - int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c); - if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); - if (pb <= pc) return STBIW_UCHAR(b); - return STBIW_UCHAR(c); -} - -// @OPTIMIZE: provide an option that always forces left-predict or paeth predict -static void stbiw__encode_png_line(const unsigned char *pixels, - int stride_bytes, int width, int height, - int y, int n, int filter_type, - signed char *line_buffer) { - int mapping[] = {0, 1, 2, 3, 4}; - int firstmap[] = {0, 1, 0, 5, 6}; - const unsigned char *z; - int *mymap, i, type, signed_stride; - - mymap = (y != 0) ? mapping : firstmap; - type = mymap[filter_type]; - z = pixels + - stride_bytes * (stbi__flip_vertically_on_write ? height - 1 - y : y); - signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; - - if (type == 0) { - memcpy(line_buffer, z, width * n); - return; - } - - for (i = 0; i < n; ++i) { - switch (type) { - case 1: - line_buffer[i] = z[i]; - break; - case 2: - line_buffer[i] = z[i] - z[i - signed_stride]; - break; - case 3: - line_buffer[i] = z[i] - (z[i - signed_stride] >> 1); - break; - case 4: - line_buffer[i] = - (signed char)(z[i] - stbiw__paeth(0, z[i - signed_stride], 0)); - break; - case 5: - line_buffer[i] = z[i]; - break; - case 6: - line_buffer[i] = z[i]; - break; - } - } - - switch (type) { - case 1: - for (i = n; i < width * n; ++i) { - line_buffer[i] = z[i] - z[i - n]; - } - break; - case 2: - for (i = n; i < width * n; ++i) { - line_buffer[i] = z[i] - z[i - signed_stride]; - } - break; - case 3: - for (i = n; i < width * n; ++i) { - line_buffer[i] = z[i] - ((z[i - n] + z[i - signed_stride]) >> 1); - } - break; - case 4: - for (i = n; i < width * n; ++i) { - line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - signed_stride], - z[i - signed_stride - n]); - } - break; - case 5: - for (i = n; i < width * n; ++i) { - line_buffer[i] = z[i] - (z[i - n] >> 1); - } - break; - case 6: - for (i = n; i < width * n; ++i) { - line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0); - } - break; - } -} - -unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, - int stride_bytes, int x, int y, int n, - int *out_len) { - int force_filter = stbi_write_force_png_filter; - int ctype[5] = {-1, 0, 4, 2, 6}; - unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - unsigned char *out, *o, *filt, *zlib; - signed char *line_buffer; - int j, zlen; - - if (stride_bytes == 0) stride_bytes = x * n; - - if (force_filter >= 5) { - force_filter = -1; - } - - filt = malloc((x * n + 1) * y); - if (!filt) return 0; - line_buffer = malloc(x * n); - if (!line_buffer) { - free(filt); - return 0; - } - for (j = 0; j < y; ++j) { - int filter_type; - if (force_filter > -1) { - filter_type = force_filter; - stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, - line_buffer); - } else { // Estimate the best filter by running through all of them: - int best_filter = 0, best_filter_val = 0x7fffffff, est, i; - for (filter_type = 0; filter_type < 5; filter_type++) { - stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, - line_buffer); - - // Estimate the entropy of the line using this filter; the less, the - // better. - est = 0; - for (i = 0; i < x * n; ++i) { - est += abs((signed char)line_buffer[i]); - } - if (est < best_filter_val) { - best_filter_val = est; - best_filter = filter_type; - } - } - if (filter_type != best_filter) { // If the last iteration already got us - // the best filter, don't redo it - stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, - line_buffer); - filter_type = best_filter; - } - } - // when we get here, filter_type contains the filter type, and line_buffer - // contains the data - filt[j * (x * n + 1)] = (unsigned char)filter_type; - memmove(filt + j * (x * n + 1) + 1, line_buffer, x * n); - } - free(line_buffer); - zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen, - stbi_write_png_compression_level); - free(filt); - if (!zlib) return 0; - - // each tag requires 12 bytes of overhead - out = malloc(8 + 12 + 13 + 12 + zlen + 12); - if (!out) return 0; - *out_len = 8 + 12 + 13 + 12 + zlen + 12; - - o = out; - memmove(o, sig, 8); - o += 8; - stbiw__wp32(o, 13); // header length - stbiw__wptag(o, "IHDR"); - stbiw__wp32(o, x); - stbiw__wp32(o, y); - *o++ = 8; - *o++ = STBIW_UCHAR(ctype[n]); - *o++ = 0; - *o++ = 0; - *o++ = 0; - stbiw__wpcrc(&o, 13); - - stbiw__wp32(o, zlen); - stbiw__wptag(o, "IDAT"); - memmove(o, zlib, zlen); - o += zlen; - free(zlib); - stbiw__wpcrc(&o, zlen); - - stbiw__wp32(o, 0); - stbiw__wptag(o, "IEND"); - stbiw__wpcrc(&o, 0); - - assert(o == out + *out_len); - - return out; -} - -int stbi_write_png(const char *filename, int x, int y, int comp, - const void *data, int stride_bytes) { - int len; - FILE *f; - unsigned char *png; - png = stbi_write_png_to_mem(data, stride_bytes, x, y, comp, &len); - if (png == NULL) return 0; - f = fopen(filename, "wb"); - if (!f) { - free(png); - return 0; - } - fwrite(png, 1, len, f); - fclose(f); - free(png); - return 1; -} - -int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, - int comp, const void *data, int stride_bytes) { - int len; - unsigned char *png; - png = stbi_write_png_to_mem((const unsigned char *)data, stride_bytes, x, y, - comp, &len); - if (png == NULL) return 0; - func(context, png, len); - free(png); - return 1; -} diff --git a/third_party/stb/stb_rect_pack.c b/third_party/stb/stb_rect_pack.c index 052a2c3f1..65fab7afa 100644 --- a/third_party/stb/stb_rect_pack.c +++ b/third_party/stb/stb_rect_pack.c @@ -1,29 +1,20 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:3;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=3 sts=3 sw=3 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ -│ stb_truetype │ -│ Copyright 2017 Sean Barrett │ -│ │ -│ Permission is hereby granted, free of charge, to any person obtaining │ -│ a copy of this software and associated documentation files (the │ -│ "Software"), to deal in the Software without restriction, including │ -│ without limitation the rights to use, copy, modify, merge, publish, │ -│ distribute, sublicense, and/or sell copies of the Software, and to │ -│ permit persons to whom the Software is furnished to do so, subject to │ -│ the following conditions: │ -│ │ -│ The above copyright notice and this permission notice shall be │ -│ included in all copies or substantial portions of the Software. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ -│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ -│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ -│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ │ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/stb/stb_rect_pack.h" #include "libc/assert.h" @@ -41,8 +32,6 @@ asm(".include \"libc/disclaimer.inc\""); // Useful for e.g. packing rectangular textures into an atlas. // Does not do rotation. // -// in the file that you want to have the implementation. -// // Not necessarily the awesomest packing method, but better than // the totally naive one in stb_truetype (which is primarily what // this is meant to replace). @@ -390,7 +379,11 @@ static int rect_height_compare(const void *a, const void *b) return -1; if (p->h < q->h) return 1; - return (p->w > q->w) ? -1 : (p->w < q->w); + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); } static int rect_original_order(const void *a, const void *b) diff --git a/third_party/stb/stb_truetype.c b/third_party/stb/stb_truetype.c index 742b03469..a59346df3 100644 --- a/third_party/stb/stb_truetype.c +++ b/third_party/stb/stb_truetype.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:3;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=3 sts=3 sw=3 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ │ │ │ stb_truetype │ diff --git a/third_party/stb/stb_vorbis.c b/third_party/stb/stb_vorbis.c index 88124b77d..3b3da48f3 100644 --- a/third_party/stb/stb_vorbis.c +++ b/third_party/stb/stb_vorbis.c @@ -32,6 +32,7 @@ // manxorist@github saga musix github:infatum // Timur Gagiev Maxwell Koo // + #include "third_party/stb/stb_vorbis.h" #include "libc/assert.h" #include "libc/calls/calls.h" @@ -45,6 +46,11 @@ #include "libc/mem/mem.h" #include "libc/str/str.h" +asm(".ident\t\"\\n\\n\ +stb_vorbis (Public Domain)\\n\ +Credit: Sean Barrett, et al.\\n\ +http://nothings.org/stb\""); + // STB_VORBIS_NO_PUSHDATA_API // does not compile the code for the various stb_vorbis_*_pushdata() // functions @@ -343,6 +349,10 @@ struct stb_vorbis { unsigned int temp_memory_required; unsigned int setup_temp_memory_required; + char *vendor; + int comment_list_length; + char **comment_list; + // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; @@ -358,8 +368,11 @@ struct stb_vorbis { uint8 push_mode; + // the page to seek to when seeking to start, may be zero uint32 first_audio_page_offset; + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) ProbedPage p_first, p_last; // memory management @@ -493,7 +506,7 @@ static dontinline void *make_block_array(void *mem, int count, int size) { } static dontinline void *setup_malloc(vorb *f, int sz) { - sz = (sz + 3) & ~3; + sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *)f->alloc.alloc_buffer + f->setup_offset; @@ -510,7 +523,7 @@ static dontinline void setup_free(vorb *f, void *p) { } static dontinline void *setup_temp_malloc(vorb *f, int sz) { - sz = (sz + 3) & ~3; + sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; @@ -521,7 +534,7 @@ static dontinline void *setup_temp_malloc(vorb *f, int sz) { static dontinline void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { - f->temp_offset += (sz + 3) & ~3; + f->temp_offset += (sz + 7) & ~7; return; } free(p); @@ -593,7 +606,7 @@ static float float32_unpack(uint32 x) { uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; - return (float)ldexp((float)res, exp - 788); + return (float)ldexp((float)res, (int)exp - 788); } // zlib & jpeg huffman tables assume that the output symbols @@ -636,6 +649,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { assert(c->sorted_entries == 0); return TRUE; } + // no error return required, code reading lens checks this + assert(len[k] < 32); // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves @@ -648,6 +663,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { uint32 res; int z = len[i], y; if (z == NO_CODE) continue; + // no error return required, code reading lens checks this + assert(z < 32); // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have @@ -659,12 +676,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { return FALSE; } res = available[z]; - assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, ReverseBits32(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); for (y = len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32 - y)); @@ -991,6 +1006,9 @@ static int capture_pattern(vorb *f) { static int start_page_no_capturepattern(vorb *f) { uint32 loc0, loc1, n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } // stream structure version if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag @@ -1027,14 +1045,12 @@ static int start_page_no_capturepattern(vorb *f) { } if (f->first_decode) { int i, len; - ProbedPage p; len = 0; for (i = 0; i < f->segment_count; ++i) len += f->segments[i]; len += 27 + f->segment_count; - p.page_start = f->first_audio_page_offset; - p.page_end = p.page_start + len; - p.last_decoded_sample = loc0; - f->p_first = p; + + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; @@ -1124,6 +1140,15 @@ static int get8_packet(vorb *f) { return x; } +static int get32_packet(vorb *f) { + uint32 x; + x = get8_packet(f); + x += (uint32)get8_packet(f) << 8; + x += (uint32)get8_packet(f) << 16; + x += (uint32)get8_packet(f) << 24; + return x; +} + static void flush_packet(vorb *f) { while (get8_packet_raw(f) != EOP) ; @@ -1153,7 +1178,7 @@ static uint32 get_bits(vorb *f, int n) { f->valid_bits += 8; } } - if (f->valid_bits < 0) return 0; + assert(f->valid_bits >= n); z = f->acc & ((1 << n) - 1); f->acc >>= n; f->valid_bits -= n; @@ -1225,7 +1250,7 @@ static int codebook_decode_scalar_raw(vorb *f, Codebook *c) { assert(!c->sparse); for (i = 0; i < c->entries; ++i) { if (c->codeword_lengths[i] == NO_CODE) continue; - if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i]) - 1))) { + if (c->codewords[i] == (f->acc & ((1u << c->codeword_lengths[i]) - 1))) { if (f->valid_bits >= c->codeword_lengths[i]) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; @@ -1414,7 +1439,8 @@ static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), // and the length we'll be using (effective) if (c_inter + p_inter * ch + effective > len * ch) { - effective = len * ch - (p_inter * ch - c_inter); + // https://github.com/nothings/stb/pull/1490 + effective = len * ch - (p_inter * ch + c_inter); } #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK @@ -1717,49 +1743,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, ++class_set; #endif } - } else if (ch == 1) { - while (pcount < part_read) { - int z = r->begin + pcount * r->part_size; - int c_inter = 0, p_inter = z; - if (pass_ == 0) { - Codebook *c = f->codebooks + r->classbook; - int q; - DECODE(q, f, c); - if (q == EOP) goto done; -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; -#else - for (i = classwords - 1; i >= 0; --i) { - classifications[0][i + pcount] = q % r->classifications; - q /= r->classifications; - } -#endif - } - for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount * r->part_size; -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; -#else - int c = classifications[0][pcount]; -#endif - int b = r->residue_books[c][pass_]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, - ch, &c_inter, &p_inter, - n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = 0; - p_inter = z; - } - } -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; -#endif - } - } else { + } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount * r->part_size; int c_inter = z % ch, p_inter = z / ch; @@ -2165,34 +2149,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, while (z > base) { float k00, k11; + float l00, l11; k00 = z[-0] - z[-8]; k11 = z[-1] - z[-9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; z[-0] = z[-0] + z[-8]; z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11; - - k00 = z[-2] - z[-10]; - k11 = z[-3] - z[-11]; z[-2] = z[-2] + z[-10]; z[-3] = z[-3] + z[-11]; - z[-10] = (k00 + k11) * A2; - z[-11] = (k11 - k00) * A2; + z[-8] = k00; + z[-9] = k11; + z[-10] = (l00 + l11) * A2; + z[-11] = (l11 - l00) * A2; - k00 = z[-12] - z[-4]; // reverse to avoid a unary negation + k00 = z[-4] - z[-12]; k11 = z[-5] - z[-13]; + l00 = z[-6] - z[-14]; + l11 = z[-7] - z[-15]; z[-4] = z[-4] + z[-12]; z[-5] = z[-5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[-6]; // reverse to avoid a unary negation - k11 = z[-7] - z[-15]; z[-6] = z[-6] + z[-14]; z[-7] = z[-7] + z[-15]; - z[-14] = (k00 + k11) * A2; - z[-15] = (k00 - k11) * A2; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11 - l00) * A2; + z[-15] = (l00 + l11) * -A2; iter_54(z); iter_54(z - 8); @@ -2630,7 +2613,8 @@ void inverse_mdct_naive(float *buffer, int n) #endif static float *get_window(vorb *f, int len) { - len <<= 1; + // https://github.com/nothings/stb/pull/1499 + len = (unsigned int)len << 1; if (len == f->blocksize_0) return f->window[0]; if (len == f->blocksize_1) return f->window[1]; return NULL; @@ -2755,8 +2739,8 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int right_end, int *p_left) { Mapping *map; int i, j, k, n, n2; - int zero_channel[256]; - int really_zero_channel[256]; + int zero_channel[256] = {0}; + int really_zero_channel[256] = {0}; // WINDOWING @@ -2959,7 +2943,9 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard + f->current_loc = 0u - n2; // start of first frame is positioned for discard + // (NB this is an intentional unsigned + // overflow wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; @@ -3089,7 +3075,7 @@ static int vorbis_pump_first_frame(stb_vorbis *f) { } #ifndef STB_VORBIS_NO_PUSHDATA_API -static int is_whole_packet_present(stb_vorbis *f, int end_page) { +static int is_whole_packet_present(stb_vorbis *f) { // make sure that we have the packet available before continuing... // this requires a full ogg parse, but we know we can fetch from f->stream @@ -3109,8 +3095,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) { break; } // either this continues, or it ends it... - if (end_page) - if (s < f->segment_count - 1) return error(f, VORBIS_invalid_stream); if (s == f->segment_count) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; @@ -3144,8 +3128,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) { p += q[s]; if (q[s] < 255) break; } - if (end_page) - if (s < n - 1) return error(f, VORBIS_invalid_stream); if (s == n) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; @@ -3160,6 +3142,7 @@ static int start_decoder(vorb *f) { int longest_floorlist = 0; // first page, first packet + f->first_decode = TRUE; if (!start_page(f)) return FALSE; // validate page flag @@ -3218,6 +3201,50 @@ static int start_decoder(vorb *f) { if (!start_page(f)) return FALSE; if (!start_packet(f)) return FALSE; + + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) + return error(f, VORBIS_invalid_setup); + + for (i = 0; i < 6; ++i) header[i] = get8_packet(f); + + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + // file vendor + len = get32_packet(f); + f->vendor = (char *)setup_malloc(f, sizeof(char) * (len + 1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for (i = 0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + // user comments + f->comment_list_length = get32_packet(f); + f->comment_list = NULL; + if (f->comment_list_length > 0) { + f->comment_list = + (char **)setup_malloc(f, sizeof(char *) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } + + for (i = 0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char *)setup_malloc(f, sizeof(char) * (len + 1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for (j = 0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; + do { len = next_segment(f); skip(f, len); @@ -3229,7 +3256,7 @@ static int start_decoder(vorb *f) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (IS_PUSH_MODE(f)) { - if (!is_whole_packet_present(f, TRUE)) { + if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; return FALSE; @@ -3302,7 +3329,10 @@ static int start_decoder(vorb *f) { if (present) { lengths[j] = get_bits(f, 5) + 1; ++total; - if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); + if (lengths[j] == 32) { + if (c->sparse) setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_invalid_setup); + } } else { lengths[j] = NO_CODE; } @@ -3315,7 +3345,10 @@ static int start_decoder(vorb *f) { f->setup_temp_memory_required = c->entries; c->codeword_lengths = (uint8 *)setup_malloc(f, c->entries); - if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + if (c->codeword_lengths == NULL) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } memcpy(c->codeword_lengths, lengths, c->entries); setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been @@ -3349,13 +3382,22 @@ static int start_decoder(vorb *f) { unsigned int size; if (c->sorted_entries) { c->codeword_lengths = (uint8 *)setup_malloc(f, c->sorted_entries); - if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + if (!c->codeword_lengths) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } c->codewords = (uint32 *)setup_temp_malloc( f, sizeof(*c->codewords) * c->sorted_entries); - if (!c->codewords) return error(f, VORBIS_outofmem); + if (!c->codewords) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } values = (uint32 *)setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); - if (!values) return error(f, VORBIS_outofmem); + if (!values) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } } size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; @@ -3364,7 +3406,10 @@ static int start_decoder(vorb *f) { } if (!compute_codewords(c, lengths, c->entries, values)) { - if (c->sparse) setup_temp_free(f, values, 0); + if (c->sparse) { + setup_temp_free(f, values, 0); + setup_temp_free(f, lengths, c->entries); + } return error(f, VORBIS_invalid_setup); } @@ -3372,12 +3417,18 @@ static int start_decoder(vorb *f) { // allocate an extra slot for sentinels c->sorted_codewords = (uint32 *)setup_malloc( f, sizeof(*c->sorted_codewords) * (c->sorted_entries + 1)); - if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + if (c->sorted_codewords == NULL) { + if (c->sparse) setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } // allocate an extra slot at the front so that c->sorted_values[-1] is // defined so that we can catch that case without an extra if c->sorted_values = (int *)setup_malloc( f, sizeof(*c->sorted_values) * (c->sorted_entries + 1)); - if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + if (c->sorted_values == NULL) { + if (c->sparse) setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } ++c->sorted_values; c->sorted_values[-1] = -1; compute_sorted_huffman(c, lengths, values); @@ -3446,8 +3497,7 @@ static int start_decoder(vorb *f) { unsigned int div = 1; for (k = 0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off] * c->delta_value + c->minimum_value + last; + float val = mults[off] * c->delta_value + c->minimum_value + last; c->multiplicands[j * c->dimensions + k] = val; if (c->sequence_p) last = val; if (k + 1 < c->dimensions) { @@ -3532,7 +3582,7 @@ static int start_decoder(vorb *f) { return error(f, VORBIS_invalid_setup); } for (k = 0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f, 8) - 1; + g->subclass_books[j][k] = (int16)get_bits(f, 8) - 1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } @@ -3560,7 +3610,7 @@ static int start_decoder(vorb *f) { for (j = 0; j < g->values; ++j) g->sorted_order[j] = (uint8)p[j].id; // precompute the neighbors for (j = 2; j < g->values; ++j) { - int low, hi; + int low = 0, hi = 0; neighbors(g->Xlist, j, &low, &hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; @@ -3738,7 +3788,9 @@ static int start_decoder(vorb *f) { int i, max_part_read = 0; for (i = 0; i < f->residue_count; ++i) { Residue *r = f->residue_config + i; - unsigned int actual_size = f->blocksize_1 / 2; + unsigned int rtype = f->residue_types[i]; + unsigned int actual_size = + rtype == 2 ? f->blocksize_1 : f->blocksize_1 / 2; unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; @@ -3761,8 +3813,6 @@ static int start_decoder(vorb *f) { f->temp_memory_required = imdct_mem; } - f->first_decode = TRUE; - if (f->alloc.alloc_buffer) { assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); // check if there's enough temp memory so we don't error later @@ -3771,13 +3821,30 @@ static int start_decoder(vorb *f) { return error(f, VORBIS_outofmem); } - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point + // to a page without PAGEFLAG_continued_packet, so this either points + // to the first page, or the page after the end of the headers. It might + // be cleaner to point to a page in the middle of the headers, when that's + // the page where the first audio packet starts, but we'd have to also + // correctly skip the end of any continued packet in stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } return TRUE; } static void vorbis_deinit(stb_vorbis *p) { int i, j; + + setup_free(p, p->vendor); + for (i = 0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); + if (p->residue_config) { for (i = 0; i < p->residue_count; ++i) { Residue *r = p->residue_config + i; @@ -3840,8 +3907,7 @@ static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) { memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes = - (p->alloc.alloc_buffer_length_in_bytes + 3) & ~3; + p->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; @@ -3873,6 +3939,14 @@ stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) { return d; } +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) { + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} + int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; f->error = VORBIS__no_error; @@ -4007,7 +4081,7 @@ int stb_vorbis_decode_frame_pushdata( f->error = VORBIS__no_error; // check that we have the entire packet in memory - if (!is_whole_packet_present(f, FALSE)) { + if (!is_whole_packet_present(f)) { *samples = 0; return 0; } @@ -4069,6 +4143,7 @@ stb_vorbis *stb_vorbis_open_pushdata( *error = VORBIS_need_more_data; else *error = p.error; + vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); @@ -4121,7 +4196,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) { if (f->eof) return 0; if (header[4] != 0) goto invalid; goal = header[22] + (header[23] << 8) + (header[24] << 16) + - (header[25] << 24); + ((uint32)header[25] << 24); for (i = 22; i < 26; ++i) header[i] = 0; crc = 0; for (i = 0; i < 27; ++i) crc = crc32_update(crc, header[i]); @@ -4232,8 +4307,8 @@ static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { ProbedPage left, right, mid; int i, start_seg_with_known_loc, end_pos, page_start; - uint32 delta, stream_length, padding; - double offset, bytes_per_sample; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; bytes_per_sample = 2; /* TODO(jart): ???? */ @@ -4249,9 +4324,9 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { // indicates should be the granule position (give or take one)). padding = ((f->blocksize_1 - f->blocksize_0) >> 2); if (sample_number < padding) - sample_number = 0; + last_sample_limit = 0; else - sample_number -= padding; + last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { @@ -4264,8 +4339,11 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { assert(right.last_decoded_sample != ~0U); // starting from the start is handled differently - if (sample_number <= left.last_decoded_sample) { - if (stb_vorbis_seek_start(f)) return 1; + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); + return 1; + } return 0; } @@ -4284,10 +4362,10 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { bytes_per_sample = data_bytes / right.last_decoded_sample; offset = left.page_start + - bytes_per_sample * (sample_number - left.last_decoded_sample); + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); } else { // second probe (try to bound the other side) - double error = ((double)sample_number - mid.last_decoded_sample) * + double error = ((double)last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; @@ -4318,13 +4396,15 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { } // if we've just found the last page again then we're in a tricky file, - // and we're close enough. - if (mid.page_start == right.page_start) break; - - if (sample_number < mid.last_decoded_sample) - right = mid; - else - left = mid; + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } ++probe; } @@ -4437,8 +4517,8 @@ int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) { flush_packet(f); } } - // the next frame will start with the sample - assert(f->current_loc == sample_number); + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); return 1; } @@ -4514,7 +4594,8 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { // set. whoops! break; } - previous_safe = last_page_loc + 1; + // NOTE: not used after this point, but note for debugging + // previous_safe = last_page_loc + 1; last_page_loc = stb_vorbis_get_file_offset(f); } @@ -4618,7 +4699,10 @@ stb_vorbis *stb_vorbis_open_filename(const char *filename, int *error, stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - if (data == NULL) return NULL; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } vorbis_init(&p, alloc); p.stream = (uint8 *)data; p.stream_end = (uint8 *)data + len; @@ -4684,18 +4768,18 @@ static void copy_samples(short *dest, float *src, int len) { for (i = 0; i < len; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i], 15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; dest[i] = v; } } static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { -#define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i, j, o, n = BUFFER_SIZE; +#define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i, j, o, n = STB_BUFFER_SIZE; check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { + for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j = 0; j < num_c; ++j) { @@ -4706,20 +4790,21 @@ static void compute_samples(int mask, short *output, int num_c, float **data, for (i = 0; i < n; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o + i] = v; } } +#undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { -#define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i, j, o, n = BUFFER_SIZE >> 1; +#define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i, j, o, n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); - for (o = 0; o < len; o += (BUFFER_SIZE >> 1)) { + for (o = 0; o < len; o += (STB_BUFFER_SIZE >> 1)) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); @@ -4744,10 +4829,11 @@ static void compute_stereo_samples(short *output, int num_c, float **data, for (i = 0; i < (n << 1); ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o2 + i] = v; } } +#undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, @@ -4771,7 +4857,7 @@ static void convert_samples_short(int buf_c, short **buffer, int b_offset, int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { - float **output; + float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); @@ -4796,7 +4882,7 @@ static void convert_channels_short_interleaved(int buf_c, short *buffer, float f = data[i][d_offset + j]; int v = FAST_SCALED_FLOAT_TO_INT(temp, f, 15); // data[i][d_offset+j],15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; *buffer++ = v; } for (; i < buf_c; ++i) *buffer++ = 0; @@ -4824,8 +4910,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, float **outputs; int len = num_shorts / channels; int n = 0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n + k >= len) k = len - n; @@ -4846,8 +4930,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) { float **outputs; int n = 0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n + k >= len) k = len - n; diff --git a/third_party/stb/stb_vorbis.h b/third_party/stb/stb_vorbis.h index 758e251b4..4510b0188 100644 --- a/third_party/stb/stb_vorbis.h +++ b/third_party/stb/stb_vorbis.h @@ -43,9 +43,18 @@ typedef struct { int max_frame_size; } stb_vorbis_info; +typedef struct { + char *vendor; + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + // get general information about the file stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); +// get ogg comments +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); + // get the last error detected (clears it, too) int stb_vorbis_get_error(stb_vorbis *f); @@ -119,6 +128,12 @@ int stb_vorbis_decode_frame_pushdata( // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with diff --git a/tool/viz/derasterize.c b/tool/viz/derasterize.c index 27c44c6ed..9f43b398d 100644 --- a/tool/viz/derasterize.c +++ b/tool/viz/derasterize.c @@ -551,8 +551,8 @@ static int ParseNumberOption(const char *arg) { return x; } -static void PrintUsage(int rc, FILE *f) { - fputs(HELPTEXT, f); +static void PrintUsage(int rc, int fd) { + tinyprint(fd, HELPTEXT, NULL); exit(rc); } @@ -573,9 +573,12 @@ static void GetOpts(int argc, char *argv[]) { break; case '?': case 'H': - PrintUsage(EXIT_SUCCESS, stdout); default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } } diff --git a/tool/viz/memzoom.c b/tool/viz/memzoom.c index 63c98dced..a0bcf9297 100644 --- a/tool/viz/memzoom.c +++ b/tool/viz/memzoom.c @@ -45,6 +45,7 @@ #include "libc/str/unicode.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" @@ -63,7 +64,7 @@ DESCRIPTION\n\ \n\ FLAGS\n\ \n\ - -h help\n\ + -h or -? help\n\ -z zoom\n\ -m morton ordering\n\ -H hilbert ordering\n\ @@ -887,10 +888,8 @@ static void MemZoom(void) { } while (!(action & INTERRUPTED)); } -static wontreturn void PrintUsage(int rc) { - Write("SYNOPSIS\n\n "); - Write(program_invocation_name); - Write(USAGE); +static wontreturn void PrintUsage(int rc, int fd) { + tinyprint(fd, "SYNOPSIS\n\n ", program_invocation_name, USAGE, NULL); exit(rc); } @@ -898,7 +897,7 @@ static void GetOpts(int argc, char *argv[]) { int opt; char *p; fps = 10; - while ((opt = getopt(argc, argv, "hzHNWf:p:")) != -1) { + while ((opt = getopt(argc, argv, "?hmzHNWf:p:")) != -1) { switch (opt) { case 'z': ++zoom; @@ -927,9 +926,13 @@ static void GetOpts(int argc, char *argv[]) { } break; case 'h': - PrintUsage(EXIT_SUCCESS); + case '?': default: - PrintUsage(EX_USAGE); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } if (pid) { @@ -941,10 +944,10 @@ static void GetOpts(int argc, char *argv[]) { stpcpy(p, "/maps"); } else { if (optind == argc) { - PrintUsage(EX_USAGE); + PrintUsage(EX_USAGE, STDERR_FILENO); } if (!memccpy(path, argv[optind], '\0', sizeof(path))) { - PrintUsage(EX_USAGE); + PrintUsage(EX_USAGE, STDERR_FILENO); } } } diff --git a/tool/viz/od16.c b/tool/viz/od16.c index 16e221fc6..9fabccc9d 100644 --- a/tool/viz/od16.c +++ b/tool/viz/od16.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/log/check.h" @@ -25,6 +26,7 @@ #include "libc/str/str.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/fileno.h" #include "third_party/getopt/getopt.internal.h" #define USAGE \ @@ -36,17 +38,15 @@ Flags:\n\ -c INT\n\ -w INT width (aka cols) [default 8]\n\ -o PATH output path [default -]\n\ - -h shows this information\n\ + -h or -? shows this information\n\ \n" static long width_; static FILE *in_, *out_; static char *inpath_, *outpath_; -void PrintUsage(int rc, FILE *f) { - fputs("Usage: ", f); - fputs(program_invocation_name, f); - fputs(USAGE, f); +void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL); exit(rc); } @@ -63,11 +63,14 @@ void GetOpts(int *argc, char *argv[]) { case 'w': width_ = strtol(optarg, NULL, 0); break; - case '?': case 'h': - PrintUsage(EXIT_SUCCESS, stdout); + case '?': default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } if (optind == *argc) { diff --git a/tool/viz/printansi.c b/tool/viz/printansi.c index 6e98e6014..810d8bb79 100644 --- a/tool/viz/printansi.c +++ b/tool/viz/printansi.c @@ -71,8 +71,8 @@ static struct Flags { enum TtyQuantizationAlgorithm quant; } g_flags; -static wontreturn void PrintUsage(int rc, FILE *f) { - fprintf(f, "Usage: %s%s", program_invocation_name, "\ +static wontreturn void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, "\ [FLAGS] [PATH]\n\ \n\ FLAGS\n\ @@ -86,7 +86,7 @@ EXAMPLES\n\ \n\ printansi.com -w80 -h40 logo.png\n\ \n\ -\n"); +\n", NULL); exit(rc); } @@ -107,7 +107,7 @@ static void GetOpts(int *argc, char *argv[]) { g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode; if (*argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { - PrintUsage(EXIT_SUCCESS, stdout); + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); } while ((opt = getopt(*argc, argv, "?ivpfrtxads234o:w:h:")) != -1) { switch (opt) { @@ -162,9 +162,12 @@ static void GetOpts(int *argc, char *argv[]) { ++__log_level; break; case '?': - PrintUsage(EXIT_SUCCESS, stdout); default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } if (optind == *argc) { diff --git a/tool/viz/printimage.c b/tool/viz/printimage.c index e82c39b57..7c2d35c67 100644 --- a/tool/viz/printimage.c +++ b/tool/viz/printimage.c @@ -66,8 +66,8 @@ static struct Flags { struct winsize g_winsize; -static wontreturn void PrintUsage(int rc, FILE *f) { - fprintf(f, "Usage: %s%s", program_invocation_name, "\ +static wontreturn void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, "\ [FLAGS] [PATH]\n\ \n\ FLAGS\n\ @@ -94,7 +94,7 @@ FLAGS\n\ EXAMPLES\n\ \n\ printimage.com -sxd lemurs.jpg # 256-color dither unsharp\n\ -\n"); +\n", NULL); exit(rc); } @@ -114,7 +114,7 @@ static void GetOpts(int *argc, char *argv[]) { g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode; if (*argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { - PrintUsage(EXIT_SUCCESS, stdout); + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); } while ((opt = getopt(*argc, argv, "?vpmfirtxads234o:w:h:")) != -1) { switch (opt) { @@ -170,9 +170,12 @@ static void GetOpts(int *argc, char *argv[]) { ++__log_level; break; case '?': - PrintUsage(EXIT_SUCCESS, stdout); default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } g_winsize.ws_col = 80; @@ -435,7 +438,7 @@ int main(int argc, char *argv[]) { int i; ShowCrashReports(); GetOpts(&argc, argv); - if (optind == argc) PrintUsage(0, stdout); + if (optind == argc) PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); stbi_set_unpremultiply_on_load(true); for (i = optind; i < argc; ++i) { WithImageFile(argv[i], ProcessImage); diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index ccff3df9f..727afad59 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -123,7 +123,7 @@ Flags & Keyboard Shortcuts:\n\ -v increases verbosity [flag]\n\ -L PATH redirects stderr to path [flag]\n\ -y yes to interactive prompts [flag]\n\ - -h shows this information [flag]\n\ + -h or -? shows this information [flag]\n\ UP/DOWN adjust volume [keyboard]\n\ CTRL+L redraw [keyboard]\n\ CTRL+Z suspend [keyboard]\n\ @@ -1374,10 +1374,8 @@ static bool CanPlayAudio(void) { } } -static void PrintUsage(int rc, FILE *f) { - fputs("Usage: ", f); - fputs(program_invocation_name, f); - fputs(USAGE, f); +static void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL); exit(rc); } @@ -1399,12 +1397,15 @@ static void GetOpts(int argc, char *argv[]) { case 'Y': yonly_ = true; break; - case '?': case 'h': - PrintUsage(EXIT_SUCCESS, stdout); + case '?': default: if (!ProcessOptKey(opt)) { - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } } @@ -1562,7 +1563,7 @@ int main(int argc, char *argv[]) { fullclear_ = true; GetOpts(argc, argv); if (!tuned_) PickDefaults(); - if (optind == argc) PrintUsage(EX_USAGE, stderr); + if (optind == argc) PrintUsage(EX_USAGE, STDERR_FILENO); patharg_ = argv[optind]; s = commandvenv("SOX", "sox"); sox_ = s ? strdup(s) : 0; From bb2602a5247736a7d421cb05078623d8a255e61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Sat, 23 Dec 2023 11:09:50 -0500 Subject: [PATCH 02/15] Minor mmake cleanup --- tool/zsh/mmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/zsh/mmake b/tool/zsh/mmake index 174155a5b..b7bb02d6a 100644 --- a/tool/zsh/mmake +++ b/tool/zsh/mmake @@ -6,11 +6,11 @@ # mmake tiny ape/ape.elf local mode case $1 in - (m|MODE)=*) mode=${1#*=}; shift ;; aarch64|x86_64|aarch64-(zero|tiny)|x86_64-tiny) ;& zero|fastbuild|opt|optlinux|rel|asan|dbg|sysv) ;& tiny(|linux(|bsd)|sysv|nowin)|nox87|llvm|ansi) ## mode=$1; shift ;; + (m|MODE)=*) mode=${1#*=}; shift ;; *) mode=${MODE:-${m}} ;; esac [[ -z $mode ]] && { From 25266b037bbe9d7f8909a67e387bb9d218249c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Sun, 24 Dec 2023 19:40:18 -0500 Subject: [PATCH 03/15] Save a redundant load in zipos read/seek (#1037) When h->pos has changed to something other than SIZE_MAX, we don't need the extra atomic load. --- libc/runtime/zipos-read.c | 16 +++++++--------- libc/runtime/zipos-seek.c | 16 +++++++--------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/libc/runtime/zipos-read.c b/libc/runtime/zipos-read.c index edc6ceab1..a7ca7c8be 100644 --- a/libc/runtime/zipos-read.c +++ b/libc/runtime/zipos-read.c @@ -38,17 +38,15 @@ static ssize_t __zipos_read_impl(struct ZiposHandle *h, const struct iovec *iov, return eisdir(); } if (opt_offset == -1) { - while (true) { - start_pos = atomic_load_explicit(&h->pos, memory_order_relaxed); + Restart: + start_pos = atomic_load_explicit(&h->pos, memory_order_relaxed); + do { if (UNLIKELY(start_pos == SIZE_MAX)) { - continue; + goto Restart; } - if (LIKELY(atomic_compare_exchange_weak_explicit( - &h->pos, &start_pos, SIZE_MAX, memory_order_acquire, - memory_order_relaxed))) { - break; - } - } + } while (!LIKELY(atomic_compare_exchange_weak_explicit( + &h->pos, &start_pos, SIZE_MAX, memory_order_acquire, + memory_order_relaxed))); x = y = start_pos; } else { x = y = opt_offset; diff --git a/libc/runtime/zipos-seek.c b/libc/runtime/zipos-seek.c index e76566a02..d12efc694 100644 --- a/libc/runtime/zipos-seek.c +++ b/libc/runtime/zipos-seek.c @@ -47,10 +47,11 @@ static int64_t Seek(int64_t pos, int64_t offset) { */ int64_t __zipos_seek(struct ZiposHandle *h, int64_t offset, unsigned whence) { int64_t pos, new_pos; - while (true) { - pos = atomic_load_explicit(&h->pos, memory_order_relaxed); +Restart: + pos = atomic_load_explicit(&h->pos, memory_order_relaxed); + do { if (UNLIKELY(pos == SIZE_MAX)) { - continue; + goto Restart; } switch (whence) { case SEEK_SET: @@ -65,11 +66,8 @@ int64_t __zipos_seek(struct ZiposHandle *h, int64_t offset, unsigned whence) { default: new_pos = einval(); } - if (LIKELY(atomic_compare_exchange_weak_explicit( - &h->pos, &pos, new_pos < 0 ? pos : new_pos, memory_order_release, - memory_order_relaxed))) { - break; - } - } + } while (!LIKELY(atomic_compare_exchange_weak_explicit( + &h->pos, &pos, new_pos < 0 ? pos : new_pos, memory_order_release, + memory_order_relaxed))); return new_pos; } From 4c6f928fbf274dd0f8cdd81790f41a2799a4c25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Sun, 24 Dec 2023 19:43:32 -0500 Subject: [PATCH 04/15] Disable argv[0] tests on non-XnuSilicon (#1015) Adds a TODO explaining the correct condition. --- test/libc/calls/getprogramexecutablename_test.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/libc/calls/getprogramexecutablename_test.c b/test/libc/calls/getprogramexecutablename_test.c index 8e480f960..0de855239 100644 --- a/test/libc/calls/getprogramexecutablename_test.c +++ b/test/libc/calls/getprogramexecutablename_test.c @@ -36,10 +36,15 @@ void SetUpOnce(void) { __attribute__((__constructor__)) static void Child(int argc, char *argv[]) { static bool skiparg0tests; - if (!__program_executable_name && !IsFreebsd() && !IsNetbsd()) { + if (!IsXnuSilicon()) { + /* TODO(mrdomino): these tests only pass on XnuSilicon right now because + __sys_execve fails there, so the ape loader is used. + the correct check here is "we have been invoked either + as an assimilated binary or via the ape loader, and not + via a raw __sys_execve." */ skiparg0tests = true; if (argc < 2) { - fprintf(stderr, "warning: old/no loader; skipping argv[0] tests\n"); + fprintf(stderr, "warning: skipping argv[0] tests\n"); } } if (argc >= 2 && !strcmp(argv[1], "Child")) { From fd772b9b2acdd37a3030c65a756ee1c7a1ea048f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Tue, 26 Dec 2023 10:27:04 -0500 Subject: [PATCH 05/15] mmake uses $MAKE if available --- tool/zsh/mmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tool/zsh/mmake b/tool/zsh/mmake index b7bb02d6a..5fdf97382 100644 --- a/tool/zsh/mmake +++ b/tool/zsh/mmake @@ -38,8 +38,7 @@ done whence nproc >/dev/null || autoload -Uz nproc j=-j$(nproc) } -local make -[[ -x /opt/cosmocc/bin/make ]] && make=/opt/cosmocc/bin/make +local make=${MAKE:-$(echo /opt/cosmocc/bin/make(X) 2>/dev/null)} [[ -z $make ]] && make=build/bootstrap/make.com ( set -x exec $make $j $flags MODE=$mode $targs ) From 5c358635249616157b1e02c603ceb852dfc33ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Tue, 26 Dec 2023 12:08:57 -0500 Subject: [PATCH 06/15] Rename __zipos_free -> __zipos_drop (#1043) Removes the separate decref function, uses keep/drop in the internal API. --- libc/runtime/zipos-close.c | 2 +- libc/runtime/zipos-open.c | 17 +++++------------ libc/runtime/zipos.S | 2 +- libc/runtime/zipos.internal.h | 2 +- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/libc/runtime/zipos-close.c b/libc/runtime/zipos-close.c index c6559016a..3c0ba3982 100644 --- a/libc/runtime/zipos-close.c +++ b/libc/runtime/zipos-close.c @@ -39,7 +39,7 @@ int __zipos_close(int fd) { if (!__vforked) { struct ZiposHandle *h; h = (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle; - __zipos_free(h); + __zipos_drop(h); } return rc; } diff --git a/libc/runtime/zipos-open.c b/libc/runtime/zipos-open.c index 9a9ebcb67..da92c9d37 100644 --- a/libc/runtime/zipos-open.c +++ b/libc/runtime/zipos-open.c @@ -88,18 +88,11 @@ struct ZiposHandle *__zipos_keep(struct ZiposHandle *h) { return h; } -static bool __zipos_drop(struct ZiposHandle *h) { - if (!atomic_fetch_sub_explicit(&h->refs, 1, memory_order_release)) { - atomic_thread_fence(memory_order_acquire); - return true; - } - return false; -} - -void __zipos_free(struct ZiposHandle *h) { - if (!__zipos_drop(h)) { +void __zipos_drop(struct ZiposHandle *h) { + if (atomic_fetch_sub_explicit(&h->refs, 1, memory_order_release)) { return; } + atomic_thread_fence(memory_order_acquire); if (IsAsan()) { __asan_poison((char *)h + sizeof(struct ZiposHandle), h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree); @@ -227,7 +220,7 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, int flags, } __fds_unlock(); } - __zipos_free(h); + __zipos_drop(h); return -1; } @@ -238,7 +231,7 @@ void __zipos_postdup(int oldfd, int newfd) { BLOCK_SIGNALS; __fds_lock(); if (__isfdkind(newfd, kFdZip)) { - __zipos_free((struct ZiposHandle *)(intptr_t)g_fds.p[newfd].handle); + __zipos_drop((struct ZiposHandle *)(intptr_t)g_fds.p[newfd].handle); if (!__isfdkind(oldfd, kFdZip)) { bzero(g_fds.p + newfd, sizeof(*g_fds.p)); } diff --git a/libc/runtime/zipos.S b/libc/runtime/zipos.S index 0129793dd..507db9efe 100644 --- a/libc/runtime/zipos.S +++ b/libc/runtime/zipos.S @@ -35,7 +35,7 @@ .yoink __zipos_mmap .yoink __zipos_postdup .yoink __zipos_keep - .yoink __zipos_free + .yoink __zipos_drop // TODO(jart): why does corruption happen when zip has no assets? .yoink .cosmo diff --git a/libc/runtime/zipos.internal.h b/libc/runtime/zipos.internal.h index 30fbf9adc..b7549fc9f 100644 --- a/libc/runtime/zipos.internal.h +++ b/libc/runtime/zipos.internal.h @@ -38,7 +38,7 @@ struct Zipos { }; int __zipos_close(int); -void __zipos_free(struct ZiposHandle *); +void __zipos_drop(struct ZiposHandle *); struct ZiposHandle *__zipos_keep(struct ZiposHandle *); struct Zipos *__zipos_get(void) pureconst; size_t __zipos_normpath(char *, const char *, size_t); From f51fd9764416e05ef643b375d1f46985d5d35d49 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 27 Dec 2023 22:24:22 -0800 Subject: [PATCH 07/15] Fix MODE=dbg build break --- libc/nexgen32e/BUILD.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/nexgen32e/BUILD.mk b/libc/nexgen32e/BUILD.mk index f2ff5b2ad..cf50a81e7 100644 --- a/libc/nexgen32e/BUILD.mk +++ b/libc/nexgen32e/BUILD.mk @@ -52,6 +52,7 @@ o/$(MODE)/libc/nexgen32e/ktolower.o \ o/$(MODE)/libc/nexgen32e/ktoupper.o \ o/$(MODE)/libc/nexgen32e/runlevel.o \ o/$(MODE)/libc/nexgen32e/pid.o \ +o/$(MODE)/libc/nexgen32e/program_executable_name.o \ o/$(MODE)/libc/nexgen32e/program_invocation_name2.o \ o/$(MODE)/libc/nexgen32e/threaded.o: private \ CFLAGS += \ From ce0143e2a164c1f33712025a018d72b6d66798ee Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 27 Dec 2023 22:39:41 -0800 Subject: [PATCH 08/15] Fix madvise() on Windows --- libc/calls/madvise-nt.c | 37 +++++++------- libc/calls/madvise.c | 18 +++---- libc/calls/posix_madvise.c | 4 ++ libc/nt/memory.h | 6 --- test/libc/calls/madvise_test.c | 91 ++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 35 deletions(-) create mode 100644 test/libc/calls/madvise_test.c diff --git a/libc/calls/madvise-nt.c b/libc/calls/madvise-nt.c index fe29ac94a..77ad7351c 100644 --- a/libc/calls/madvise-nt.c +++ b/libc/calls/madvise-nt.c @@ -17,46 +17,43 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/macros.internal.h" #include "libc/nt/enum/offerpriority.h" -#include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/memoryrangeentry.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/errfuns.h" -forceinline typeof(PrefetchVirtualMemory) *GetPrefetchVirtualMemory(void) { - static bool once; - static typeof(PrefetchVirtualMemory) *PrefetchVirtualMemory_; - if (!once) { +typedef bool32 (*__msabi PrefetchVirtualMemoryPtr)( + int64_t hProcess, uintptr_t NumberOfEntries, + struct NtMemoryRangeEntry *VirtualAddresses, uint32_t reserved_Flags); + +textwindows static PrefetchVirtualMemoryPtr GetPrefetchVirtualMemory(void) { + static PrefetchVirtualMemoryPtr PrefetchVirtualMemory_; + if (!PrefetchVirtualMemory_) { PrefetchVirtualMemory_ = /* win8.1+ */ GetProcAddressModule("Kernel32.dll", "PrefetchVirtualMemory"); - once = true; } return PrefetchVirtualMemory_; } -forceinline typeof(OfferVirtualMemory) *GetOfferVirtualMemory(void) { - static bool once; - static typeof(OfferVirtualMemory) *OfferVirtualMemory_; - if (!once) { +typedef bool32 (*__msabi OfferVirtualMemoryPtr)(void *inout_VirtualAddress, + size_t Size, int Priority); + +textwindows static OfferVirtualMemoryPtr GetOfferVirtualMemory(void) { + static OfferVirtualMemoryPtr OfferVirtualMemory_; + if (!OfferVirtualMemory_) { OfferVirtualMemory_ = /* win8.1+ */ GetProcAddressModule("Kernel32.dll", "OfferVirtualMemory"); - once = true; } return OfferVirtualMemory_; } textwindows int sys_madvise_nt(void *addr, size_t length, int advice) { - uint32_t rangecount; - struct NtMemoryRangeEntry ranges[1]; if (advice == MADV_WILLNEED || advice == MADV_SEQUENTIAL) { - typeof(PrefetchVirtualMemory) *fn = GetPrefetchVirtualMemory(); + PrefetchVirtualMemoryPtr fn = GetPrefetchVirtualMemory(); if (fn) { - ranges[0].VirtualAddress = addr; - ranges[0].NumberOfBytes = length; - rangecount = ARRAYLEN(ranges); - if (fn(GetCurrentProcess(), &rangecount, ranges, 0)) { + if (fn(GetCurrentProcess(), 1, &(struct NtMemoryRangeEntry){addr, length}, + 0)) { return 0; } else { return __winerr(); @@ -65,7 +62,7 @@ textwindows int sys_madvise_nt(void *addr, size_t length, int advice) { return enosys(); } } else if (advice == MADV_FREE) { - typeof(OfferVirtualMemory) *fn = GetOfferVirtualMemory(); + OfferVirtualMemoryPtr fn = GetOfferVirtualMemory(); if (fn) { if (fn(addr, length, kNtVmOfferPriorityNormal)) { return 0; diff --git a/libc/calls/madvise.c b/libc/calls/madvise.c index ebe6e6088..dcd81d02c 100644 --- a/libc/calls/madvise.c +++ b/libc/calls/madvise.c @@ -29,21 +29,21 @@ * * @param advice can be MADV_WILLNEED, MADV_SEQUENTIAL, MADV_FREE, etc. * @return 0 on success, or -1 w/ errno + * @raise EINVAL if `advice` isn't valid or supported by system + * @raise EINVAL on Linux if addr/length isn't page size aligned with + * respect to `getauxval(AT_PAGESZ)` + * @raise ENOMEM on Linux if addr/length overlaps unmapped regions * @see libc/sysv/consts.sh * @see fadvise() */ int madvise(void *addr, size_t length, int advice) { int rc; - if (advice != 127 /* see consts.sh */) { - if (IsAsan() && !__asan_is_valid(addr, length)) { - rc = efault(); - } else if (!IsWindows()) { - rc = sys_madvise(addr, length, advice); - } else { - rc = sys_madvise_nt(addr, length, advice); - } + if (IsAsan() && !__asan_is_valid(addr, length)) { + rc = efault(); + } else if (!IsWindows()) { + rc = sys_madvise(addr, length, advice); } else { - rc = einval(); + rc = sys_madvise_nt(addr, length, advice); } STRACE("madvise(%p, %'zu, %d) → %d% m", addr, length, advice, rc); return rc; diff --git a/libc/calls/posix_madvise.c b/libc/calls/posix_madvise.c index 4bf5b71eb..9503cd7a4 100644 --- a/libc/calls/posix_madvise.c +++ b/libc/calls/posix_madvise.c @@ -23,6 +23,10 @@ * Advises kernel about memory intentions, the POSIX way. * * @return 0 on success, or errno on error + * @raise EINVAL if `advice` isn't valid or supported by system + * @raise EINVAL on Linux if addr/length isn't page size aligned with + * respect to `getauxval(AT_PAGESZ)` + * @raise ENOMEM on Linux if addr/length overlaps unmapped regions * @returnserrno */ errno_t posix_madvise(void *addr, uint64_t len, int advice) { diff --git a/libc/nt/memory.h b/libc/nt/memory.h index b9c716f71..68ac1ef83 100644 --- a/libc/nt/memory.h +++ b/libc/nt/memory.h @@ -73,12 +73,6 @@ uint64_t VirtualQuery(const void *lpAddress, void *VirtualAllocEx(int64_t hProcess, void *lpAddress, uint64_t dwSize, uint32_t flAllocationType, uint32_t flProtect); -bool32 PrefetchVirtualMemory(int64_t hProcess, const uint32_t *NumberOfEntries, - struct NtMemoryRangeEntry *VirtualAddresses, - uint32_t reserved_Flags); -bool32 OfferVirtualMemory(void *inout_VirtualAddress, size_t Size, - int Priority); - int64_t GetProcessHeap(void); void *HeapAlloc(int64_t hHeap, uint32_t dwFlags, size_t dwBytes) __wur; bool32 HeapFree(int64_t hHeap, uint32_t dwFlags, void *opt_lpMem); diff --git a/test/libc/calls/madvise_test.c b/test/libc/calls/madvise_test.c new file mode 100644 index 000000000..ed6701307 --- /dev/null +++ b/test/libc/calls/madvise_test.c @@ -0,0 +1,91 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/madv.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/prot.h" +#include "libc/testlib/testlib.h" + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + +TEST(madvise, anon) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); + ASSERT_SYS(0, 0, madvise(p, FRAMESIZE, MADV_WILLNEED)); + ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); +} + +TEST(madvise, file) { + char *p; + ASSERT_SYS(0, 3, creat("foo.dat", 0644)); + ASSERT_SYS(0, 0, ftruncate(3, FRAMESIZE)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, open("foo.dat", O_RDWR)); + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE, 3, 0))); + ASSERT_SYS(0, 0, madvise(p, FRAMESIZE, MADV_WILLNEED)); + ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(madvise, short) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); + ASSERT_SYS(0, 0, madvise(p, FRAMESIZE - 1, MADV_WILLNEED)); + ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); +} + +TEST(madvise, subPages) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); + ASSERT_SYS(0, 0, madvise(p + 4096, FRAMESIZE - 4096, MADV_WILLNEED)); + ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); +} + +TEST(madvise, misalign) { + char *p; + if (!IsLinux()) return; // most platforms don't care + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); + ASSERT_SYS(EINVAL, -1, madvise(p + 1, FRAMESIZE - 1, MADV_WILLNEED)); + ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); +} + +TEST(madvise, badAdvice) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); + ASSERT_SYS(EINVAL, -1, madvise(p, FRAMESIZE, 127)); + ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); +} + +TEST(madvise, missingMemory) { + if (!IsLinux()) return; + ASSERT_SYS(ENOMEM, -1, + madvise((char *)0x83483838000, FRAMESIZE, MADV_WILLNEED)); +} From 6be9477b9e3933a453a3ce9589fdc5c374b1785c Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Thu, 28 Dec 2023 01:08:19 -0800 Subject: [PATCH 09/15] Fix redbean to only check additional content types when Lua is present (#1033) Closes #971 --- tool/net/redbean.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 0bd01c6f8..7111e9ab5 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -4956,10 +4956,11 @@ static int LuaProgramTokenBucket(lua_State *L) { } static const char *GetContentTypeExt(const char *path, size_t n) { - const char *r, *e; + const char *r = NULL, *e; + if ((r = FindContentType(path, n))) return r; +#ifndef STATIC int top; lua_State *L = GL; - if ((r = FindContentType(path, n))) return r; // extract the last .; use the entire path if none is present if ((e = memrchr(path, '.', n))) { @@ -4974,6 +4975,7 @@ static const char *GetContentTypeExt(const char *path, size_t n) { if (lua_gettable(L, -2) == LUA_TSTRING) r = FreeLater(strdup(lua_tostring(L, -1))); lua_settop(L, top); +#endif return r; } From 8b0e42a31bd9a90733a1cf7eab743d9031634b79 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 28 Dec 2023 04:22:15 -0800 Subject: [PATCH 10/15] Make zipos_test hermetic --- test/libc/runtime/zipos_test.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/libc/runtime/zipos_test.c b/test/libc/runtime/zipos_test.c index 83c036b31..b87e54bd7 100644 --- a/test/libc/runtime/zipos_test.c +++ b/test/libc/runtime/zipos_test.c @@ -117,7 +117,7 @@ TEST(zipos, closeAfterVfork) { ASSERT_SYS(0, 3, open("/zip/libc/testlib/hyperion.txt", O_RDONLY)); SPAWN(vfork); ASSERT_SYS(0, 0, close(3)); - ASSERT_SYS(0, 3, open("/etc/hosts", O_RDONLY)); + ASSERT_SYS(0, 3, open("/dev/null", O_RDONLY)); ASSERT_SYS(0, 0, close(3)); ASSERT_SYS(EBADF, -1, close(3)); EXITS(0); @@ -136,10 +136,10 @@ struct State { char buf[8]; \ ASSERT_SYS(0, 8, read(fd, buf, 8)); \ } -#define SEEKS() \ - for (int i = 0; i < 4; ++i) { \ - rc = lseek(fd, 8, SEEK_CUR); \ - ASSERT_NE(rc, -1); \ +#define SEEKS() \ + for (int i = 0; i < 4; ++i) { \ + rc = lseek(fd, 8, SEEK_CUR); \ + ASSERT_NE(rc, -1); \ } static void *pthread_main(void *ptr) { @@ -154,8 +154,9 @@ static void *pthread_main(void *ptr) { ASSERT_NE(-1, rc); children[i].fd = rc; children[i].id = 2 * s->id + i; - ASSERT_SYS(0, 0, pthread_create(&children[i].thread, NULL, pthread_main, - children + i)); + ASSERT_SYS(0, 0, + pthread_create(&children[i].thread, NULL, pthread_main, + children + i)); } } if (s->id & 1) { @@ -175,7 +176,7 @@ static void *pthread_main(void *ptr) { } TEST(zipos, ultraPosixAtomicSeekRead) { - struct State s = { 1, 4 }; + struct State s = {1, 4}; ASSERT_SYS(0, 3, open("/zip/libc/testlib/hyperion.txt", O_RDONLY)); ASSERT_SYS(0, 4, dup(3)); pthread_main((void *)&s); From 80fca1f7c3aa236ffbf0528cee6e7712c8d5cd41 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 28 Dec 2023 04:22:42 -0800 Subject: [PATCH 11/15] Make getprogramexecutablename_test pass fleet --- build/definitions.mk | 2 +- .../calls/getprogramexecutablename_test.c | 37 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/build/definitions.mk b/build/definitions.mk index e37ba4fae..15dc061e0 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -86,7 +86,7 @@ IGNORE := $(shell $(MKDIR) $(TMPDIR)) ifneq ($(findstring aarch64,$(MODE)),) ARCH = aarch64 -HOSTS ?= pi silicon +HOSTS ?= pi studio else ARCH = x86_64 HOSTS ?= freebsd rhel7 xnu win10 openbsd netbsd diff --git a/test/libc/calls/getprogramexecutablename_test.c b/test/libc/calls/getprogramexecutablename_test.c index 0de855239..13f475163 100644 --- a/test/libc/calls/getprogramexecutablename_test.c +++ b/test/libc/calls/getprogramexecutablename_test.c @@ -35,35 +35,30 @@ void SetUpOnce(void) { } __attribute__((__constructor__)) static void Child(int argc, char *argv[]) { - static bool skiparg0tests; - if (!IsXnuSilicon()) { - /* TODO(mrdomino): these tests only pass on XnuSilicon right now because - __sys_execve fails there, so the ape loader is used. - the correct check here is "we have been invoked either - as an assimilated binary or via the ape loader, and not - via a raw __sys_execve." */ - skiparg0tests = true; - if (argc < 2) { - fprintf(stderr, "warning: skipping argv[0] tests\n"); - } - } if (argc >= 2 && !strcmp(argv[1], "Child")) { - ASSERT_EQ(argc, 4); - EXPECT_STREQ(argv[2], GetProgramExecutableName()); - if (!skiparg0tests) { - EXPECT_STREQ(argv[3], argv[0]); + if (strcmp(argv[2], GetProgramExecutableName())) { + exit(123); } - exit(g_testlib_failed); + if (strcmp(argv[3], argv[0])) { + exit(124); + } + exit(0); } } TEST(GetProgramExecutableName, ofThisFile) { EXPECT_EQ('/', *self); - EXPECT_TRUE( - endswith(self, "test/libc/calls/getprogramexecutablename_test.com")); + EXPECT_TRUE(!!strstr(self, "getprogramexecutablename_test")); } +/* TODO(mrdomino): these tests only pass on XnuSilicon right now because + __sys_execve fails there, so the ape loader is used. + the correct check here is "we have been invoked either + as an assimilated binary or via the ape loader, and not + via a raw __sys_execve." */ + TEST(GetProgramExecutableName, nullEnv) { + if (!IsXnuSilicon()) return; SPAWN(fork); execve(self, (char *[]){self, "Child", self, self, 0}, (char *[]){0}); abort(); @@ -71,6 +66,7 @@ TEST(GetProgramExecutableName, nullEnv) { } TEST(GetProramExecutableName, weirdArgv0NullEnv) { + if (!IsXnuSilicon()) return; SPAWN(fork); execve(self, (char *[]){"hello", "Child", self, "hello", 0}, (char *[]){0}); abort(); @@ -78,6 +74,7 @@ TEST(GetProramExecutableName, weirdArgv0NullEnv) { } TEST(GetProgramExecutableName, weirdArgv0CosmoVar) { + if (!IsXnuSilicon()) return; char buf[32 + PATH_MAX]; stpcpy(stpcpy(buf, "COSMOPOLITAN_PROGRAM_EXECUTABLE="), self); SPAWN(fork); @@ -88,6 +85,7 @@ TEST(GetProgramExecutableName, weirdArgv0CosmoVar) { } TEST(GetProgramExecutableName, weirdArgv0WrongCosmoVar) { + if (!IsXnuSilicon()) return; char *bad = "COSMOPOLITAN_PROGRAM_EXECUTABLE=hi"; SPAWN(fork); execve(self, (char *[]){"hello", "Child", self, "hello", 0}, @@ -97,6 +95,7 @@ TEST(GetProgramExecutableName, weirdArgv0WrongCosmoVar) { } TEST(GetProgramExecutableName, movedSelf) { + if (!IsXnuSilicon()) return; char buf[BUFSIZ]; ASSERT_SYS(0, 3, open(GetProgramExecutableName(), O_RDONLY)); ASSERT_SYS(0, 4, creat("test", 0755)); From 1a28e35c62d8f85a73adf6b669f4b61a276a31a9 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 28 Dec 2023 04:55:50 -0800 Subject: [PATCH 12/15] Use good locks in dlmalloc Using mere spin locks causes runitd.com to go painstakingly slow on NetBSD for reasons that aren't clear yet. --- third_party/dlmalloc/dlmalloc.c | 1 + third_party/dlmalloc/locks.inc | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/third_party/dlmalloc/dlmalloc.c b/third_party/dlmalloc/dlmalloc.c index 356f72b04..5b2ae8891 100644 --- a/third_party/dlmalloc/dlmalloc.c +++ b/third_party/dlmalloc/dlmalloc.c @@ -32,6 +32,7 @@ #define HAVE_MREMAP 0 #define HAVE_MORECORE 0 #define USE_LOCKS 2 +#define USE_SPIN_LOCKS 0 #define MORECORE_CONTIGUOUS 0 #define MALLOC_INSPECT_ALL 1 #define ABORT_ON_ASSERT_FAILURE 0 diff --git a/third_party/dlmalloc/locks.inc b/third_party/dlmalloc/locks.inc index 8eab25ece..b74887916 100644 --- a/third_party/dlmalloc/locks.inc +++ b/third_party/dlmalloc/locks.inc @@ -29,7 +29,7 @@ */ -#ifdef USE_SPIN_LOCKS +#if USE_SPIN_LOCKS #define MLOCK_T atomic_uint @@ -62,11 +62,13 @@ static int malloc_wipe(MLOCK_T *lk) { } static int malloc_lock(MLOCK_T *lk) { + if (!__threaded) return 0; nsync_mu_lock(lk); return 0; } static int malloc_unlock(MLOCK_T *lk) { + if (!__threaded) return 0; nsync_mu_unlock(lk); return 0; } From 43fe5956adfe803249877309facd693306018c85 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 28 Dec 2023 22:58:17 -0800 Subject: [PATCH 13/15] Use DNS implementation from Musl Libc Now that our socket system call polyfills are good enough to support Musl's DNS library we should be using that rather than the barebones domain name system implementation we rolled on our own. There's many benefits to making this change. So many, that I myself wouldn't feel qualified to enumerate them all. The Musl DNS code had to be changed in order to support Windows of course, which looks very solid so far --- Makefile | 10 +- dsp/mpeg/buffer.h | 4 +- dsp/mpeg/mpeg.h | 7 +- dsp/tty/tty.h | 1 - examples/BUILD.mk | 1 - examples/nc.c | 24 +- examples/whois.c | 4 +- libc/BUILD.mk | 1 - libc/calls/{h_errno.c => gethoststxtpath.c} | 16 +- .../getprotocolstxtpath.c} | 21 +- .../getservicestxtpath.c} | 25 +- libc/{dns => calls}/getsystemdirectorypath.c | 11 +- libc/calls/sysdir.internal.h | 11 + libc/dns/BUILD.mk | 59 - libc/dns/comparednsnames.c | 77 - libc/dns/consts.h | 19 - libc/dns/dns.h | 79 - libc/dns/dnsheader.c | 57 - libc/dns/dnsheader.h | 19 - libc/dns/dnsquestion.h | 14 - libc/dns/ent.h | 73 - libc/dns/gai_strerror.c | 67 - libc/dns/getaddrinfo.c | 116 - libc/dns/gethostbyaddr.c | 58 - libc/dns/gethostbyname.c | 70 - libc/dns/gethoststxt.c | 94 - libc/dns/getnameinfo.c | 115 - libc/dns/getntnameservers.c | 93 - libc/dns/getntprotocolstxtpath.c | 37 - libc/dns/getntservicestxtpath.c | 36 - libc/dns/getprotobyname.c | 46 - libc/dns/getprotobynumber.c | 47 - libc/dns/getresolvconf.c | 83 - libc/dns/getservbyname.c | 55 - libc/dns/getservbyport.c | 56 - libc/dns/herror.c | 36 - libc/dns/hostent.c | 37 - libc/dns/hoststxt.h | 38 - libc/dns/lookupprotobyname.c | 101 - libc/dns/lookupprotobynumber.c | 90 - libc/dns/lookupservicesbyname.c | 107 - libc/dns/lookupservicesbyport.c | 107 - libc/dns/netent.c | 45 - libc/dns/newaddrinfo.c | 39 - libc/dns/parsehoststxt.c | 72 - libc/dns/parseresolvconf.c | 68 - libc/dns/pascalifydnsname.c | 58 - libc/dns/protoent.c | 37 - libc/dns/prototxt.h | 17 - libc/dns/resolvconf.h | 22 - libc/dns/resolvedns.c | 113 - libc/dns/resolvednsreverse.c | 128 - libc/dns/resolvehostsreverse.c | 52 - libc/dns/resolvehoststxt.c | 62 - libc/dns/rfc0226.txt | 59 - libc/dns/rfc0247.txt | 222 -- libc/dns/rfc1035.txt | 3077 ----------------- libc/dns/serializednsquestion.c | 39 - libc/dns/servent.c | 37 - libc/dns/servicestxt.h | 17 - libc/isystem/netdb.h | 3 +- libc/log/log.h | 2 +- libc/stdio/printargs.c | 1 - libc/sysv/consts/inaddr.h | 5 + libc/x/xgetline.h | 2 +- net/http/isacceptablehost.c | 3 +- test/libc/BUILD.mk | 1 - test/libc/dns/BUILD.mk | 60 - test/libc/dns/comparednsnames_test.c | 110 - test/libc/dns/dnsheader_test.c | 47 - test/libc/dns/dnsquestion_test.c | 46 - test/libc/dns/parsehoststxt_test.c | 80 - test/libc/dns/parseresolvconf_test.c | 70 - test/libc/dns/pascalifydnsname_test.c | 79 - test/libc/dns/prototxt_test.c | 78 - test/libc/dns/resolvehostsreverse_test.c | 79 - test/libc/dns/resolvehoststxt_test.c | 78 - test/libc/dns/servicestxt_test.c | 184 - third_party/bash/BUILD.mk | 1 - third_party/finger/BUILD.mk | 1 - third_party/finger/net.c | 3 +- third_party/hiredis/BUILD.mk | 4 +- third_party/hiredis/net.c | 3 +- third_party/lua/lunix.c | 3 +- third_party/mbedtls/BUILD.mk | 2 +- third_party/mbedtls/net_sockets.c | 3 +- third_party/mbedtls/test/BUILD.mk | 4 +- third_party/musl/BUILD.mk | 6 + third_party/musl/dn_comp.c | 23 +- third_party/musl/dn_expand.c | 11 +- third_party/musl/dn_skipname.c | 5 - {libc/dns => third_party/musl}/dns.png | Bin third_party/musl/dns_parse.c | 61 + third_party/musl/ent.c | 49 + third_party/musl/fgetspent.c | 29 +- third_party/musl/freeaddrinfo.c | 47 + third_party/musl/gai_strerror.c | 56 + third_party/musl/getaddrinfo.c | 171 + third_party/musl/gethostbyaddr.c | 50 + third_party/musl/gethostbyaddr_r.c | 103 + third_party/musl/gethostbyname.c | 34 + third_party/musl/gethostbyname2.c | 49 + third_party/musl/gethostbyname2_r.c | 110 + third_party/musl/gethostbyname_r.c | 36 + third_party/musl/getnameinfo.c | 254 ++ third_party/musl/getservbyname.c | 38 + third_party/musl/getservbyname_r.c | 85 + third_party/musl/getservbyport.c | 38 + third_party/musl/getservbyport_r.c | 92 + third_party/musl/getspnam_r.c | 32 + third_party/musl/h_errno.c | 36 + third_party/musl/herror.c | 34 + third_party/musl/hstrerror.c | 43 + third_party/musl/insque.c | 1 - third_party/musl/lookup.internal.h | 6 +- third_party/musl/lookup_ipliteral.c | 1 + third_party/musl/lookup_name.c | 486 +++ third_party/musl/lookup_serv.c | 154 + third_party/musl/netdb.h | 138 + third_party/musl/netname.c | 38 + third_party/musl/passwd.h | 11 +- third_party/musl/proto.c | 116 + third_party/musl/pwd.c | 2 +- third_party/musl/res_mkquery.c | 8 +- third_party/musl/res_msend.c | 30 +- third_party/musl/res_query.c | 2 +- third_party/musl/resolvconf.c | 3 +- third_party/musl/serv.c | 41 + third_party/python/BUILD.mk | 1 - third_party/python/Modules/socketmodule.c | 3 +- third_party/stb/stb_image.h | 11 +- tool/build/BUILD.mk | 1 - tool/build/runit.c | 14 +- tool/curl/BUILD.mk | 4 +- tool/curl/curl.c | 4 +- tool/decode/lib/bitabuilder.h | 4 +- tool/decode/lib/socknames.c | 4 +- tool/hello/BUILD.mk | 2 +- tool/net/BUILD.mk | 1 - tool/net/dig.c | 4 +- tool/net/fetch.inc | 2 +- tool/net/lfuncs.c | 9 +- tool/net/redbean.c | 102 +- tool/net/winbench.c | 1 - tool/viz/BUILD.mk | 1 - tool/viz/printvideo.c | 1 - 146 files changed, 2646 insertions(+), 7190 deletions(-) rename libc/calls/{h_errno.c => gethoststxtpath.c} (85%) rename libc/{dns/freeaddrinfo.c => calls/getprotocolstxtpath.c} (83%) rename libc/{dns/hstrerror.c => calls/getservicestxtpath.c} (81%) rename libc/{dns => calls}/getsystemdirectorypath.c (88%) create mode 100644 libc/calls/sysdir.internal.h delete mode 100644 libc/dns/BUILD.mk delete mode 100644 libc/dns/comparednsnames.c delete mode 100644 libc/dns/consts.h delete mode 100644 libc/dns/dns.h delete mode 100644 libc/dns/dnsheader.c delete mode 100644 libc/dns/dnsheader.h delete mode 100644 libc/dns/dnsquestion.h delete mode 100644 libc/dns/ent.h delete mode 100644 libc/dns/gai_strerror.c delete mode 100644 libc/dns/getaddrinfo.c delete mode 100644 libc/dns/gethostbyaddr.c delete mode 100644 libc/dns/gethostbyname.c delete mode 100644 libc/dns/gethoststxt.c delete mode 100644 libc/dns/getnameinfo.c delete mode 100644 libc/dns/getntnameservers.c delete mode 100644 libc/dns/getntprotocolstxtpath.c delete mode 100644 libc/dns/getntservicestxtpath.c delete mode 100644 libc/dns/getprotobyname.c delete mode 100644 libc/dns/getprotobynumber.c delete mode 100644 libc/dns/getresolvconf.c delete mode 100644 libc/dns/getservbyname.c delete mode 100644 libc/dns/getservbyport.c delete mode 100644 libc/dns/herror.c delete mode 100644 libc/dns/hostent.c delete mode 100644 libc/dns/hoststxt.h delete mode 100644 libc/dns/lookupprotobyname.c delete mode 100644 libc/dns/lookupprotobynumber.c delete mode 100644 libc/dns/lookupservicesbyname.c delete mode 100644 libc/dns/lookupservicesbyport.c delete mode 100644 libc/dns/netent.c delete mode 100644 libc/dns/newaddrinfo.c delete mode 100644 libc/dns/parsehoststxt.c delete mode 100644 libc/dns/parseresolvconf.c delete mode 100644 libc/dns/pascalifydnsname.c delete mode 100644 libc/dns/protoent.c delete mode 100644 libc/dns/prototxt.h delete mode 100644 libc/dns/resolvconf.h delete mode 100644 libc/dns/resolvedns.c delete mode 100644 libc/dns/resolvednsreverse.c delete mode 100644 libc/dns/resolvehostsreverse.c delete mode 100644 libc/dns/resolvehoststxt.c delete mode 100644 libc/dns/rfc0226.txt delete mode 100644 libc/dns/rfc0247.txt delete mode 100644 libc/dns/rfc1035.txt delete mode 100644 libc/dns/serializednsquestion.c delete mode 100644 libc/dns/servent.c delete mode 100644 libc/dns/servicestxt.h delete mode 100644 test/libc/dns/BUILD.mk delete mode 100644 test/libc/dns/comparednsnames_test.c delete mode 100644 test/libc/dns/dnsheader_test.c delete mode 100644 test/libc/dns/dnsquestion_test.c delete mode 100644 test/libc/dns/parsehoststxt_test.c delete mode 100644 test/libc/dns/parseresolvconf_test.c delete mode 100644 test/libc/dns/pascalifydnsname_test.c delete mode 100644 test/libc/dns/prototxt_test.c delete mode 100644 test/libc/dns/resolvehostsreverse_test.c delete mode 100644 test/libc/dns/resolvehoststxt_test.c delete mode 100644 test/libc/dns/servicestxt_test.c rename {libc/dns => third_party/musl}/dns.png (100%) create mode 100644 third_party/musl/dns_parse.c create mode 100644 third_party/musl/ent.c create mode 100644 third_party/musl/freeaddrinfo.c create mode 100644 third_party/musl/gai_strerror.c create mode 100644 third_party/musl/getaddrinfo.c create mode 100644 third_party/musl/gethostbyaddr.c create mode 100644 third_party/musl/gethostbyaddr_r.c create mode 100644 third_party/musl/gethostbyname.c create mode 100644 third_party/musl/gethostbyname2.c create mode 100644 third_party/musl/gethostbyname2_r.c create mode 100644 third_party/musl/gethostbyname_r.c create mode 100644 third_party/musl/getnameinfo.c create mode 100644 third_party/musl/getservbyname.c create mode 100644 third_party/musl/getservbyname_r.c create mode 100644 third_party/musl/getservbyport.c create mode 100644 third_party/musl/getservbyport_r.c create mode 100644 third_party/musl/h_errno.c create mode 100644 third_party/musl/herror.c create mode 100644 third_party/musl/hstrerror.c create mode 100644 third_party/musl/lookup_name.c create mode 100644 third_party/musl/lookup_serv.c create mode 100644 third_party/musl/netdb.h create mode 100644 third_party/musl/netname.c create mode 100644 third_party/musl/proto.c create mode 100644 third_party/musl/serv.c diff --git a/Makefile b/Makefile index cf679ec7e..2ac81ac7e 100644 --- a/Makefile +++ b/Makefile @@ -198,9 +198,9 @@ include third_party/nsync/mem/BUILD.mk # │ You can now use stdio include libc/proc/BUILD.mk # │ You can now use threads include libc/dlopen/BUILD.mk # │ You can now use processes include libc/thread/BUILD.mk # │ You can finally call malloc() -include tool/hello/BUILD.mk # │ include third_party/zlib/BUILD.mk # │ include libc/stdio/BUILD.mk # │ +include tool/hello/BUILD.mk # │ include libc/time/BUILD.mk # │ include net/BUILD.mk # │ include third_party/vqsort/BUILD.mk # │ @@ -213,9 +213,8 @@ include third_party/intel/BUILD.mk # │ include third_party/aarch64/BUILD.mk # │ include libc/BUILD.mk #─┘ include libc/sock/BUILD.mk #─┐ -include libc/dns/BUILD.mk # ├──ONLINE RUNTIME -include net/http/BUILD.mk # │ You can communicate with the network -include third_party/musl/BUILD.mk # │ +include net/http/BUILD.mk # ├──ONLINE RUNTIME +include third_party/musl/BUILD.mk # │ You can communicate with the network include libc/x/BUILD.mk # │ include dsp/scale/BUILD.mk # │ include dsp/mpeg/BUILD.mk # │ @@ -297,7 +296,6 @@ include test/libc/calls/BUILD.mk include test/libc/x/BUILD.mk include test/libc/xed/BUILD.mk include test/libc/fmt/BUILD.mk -include test/libc/dns/BUILD.mk include test/libc/time/BUILD.mk include test/libc/proc/BUILD.mk include test/libc/stdio/BUILD.mk @@ -373,7 +371,6 @@ loc: o/$(MODE)/tool/build/summy.com COSMOPOLITAN_OBJECTS = \ TOOL_ARGS \ NET_HTTP \ - LIBC_DNS \ LIBC_SOCK \ LIBC_NT_WS2_32 \ LIBC_NT_IPHLPAPI \ @@ -424,7 +421,6 @@ COSMOPOLITAN_H_PKGS = \ APE \ LIBC \ LIBC_CALLS \ - LIBC_DNS \ LIBC_ELF \ LIBC_FMT \ LIBC_DLOPEN \ diff --git a/dsp/mpeg/buffer.h b/dsp/mpeg/buffer.h index 5fefc72b5..5ab5faaa6 100644 --- a/dsp/mpeg/buffer.h +++ b/dsp/mpeg/buffer.h @@ -3,8 +3,6 @@ #include "dsp/mpeg/mpeg.h" COSMOPOLITAN_C_START_ -struct FILE; - enum plm_buffer_mode { PLM_BUFFER_MODE_FILE, PLM_BUFFER_MODE_FIXED_MEM, @@ -17,7 +15,7 @@ typedef struct plm_buffer_t { unsigned length; int free_when_done; int close_when_done; - struct FILE *fh; + FILE *fh; plm_buffer_load_callback load_callback; void *load_callback_user_data; unsigned char *bytes; diff --git a/dsp/mpeg/mpeg.h b/dsp/mpeg/mpeg.h index b96c622b9..f49ed953b 100644 --- a/dsp/mpeg/mpeg.h +++ b/dsp/mpeg/mpeg.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_DSP_MPEG_MPEG_H_ #define COSMOPOLITAN_DSP_MPEG_MPEG_H_ +#include "libc/stdio/stdio.h" COSMOPOLITAN_C_START_ -struct FILE; - typedef struct plm_t plm_t; typedef struct plm_buffer_t plm_buffer_t; typedef struct plm_demux_t plm_demux_t; @@ -112,7 +111,7 @@ plm_t *plm_create_with_filename(const char *filename); * to let plmpeg call fclose() on the handle when plm_destroy() is * called. */ -plm_t *plm_create_with_file(struct FILE *fh, int close_when_done); +plm_t *plm_create_with_file(FILE *fh, int close_when_done); /** * Create a plmpeg instance with pointer to memory as source. This assumes the @@ -257,7 +256,7 @@ plm_buffer_t *plm_buffer_create_with_filename(const char *filename); * to let plmpeg call fclose() on the handle when plm_destroy() is * called. */ -plm_buffer_t *plm_buffer_create_with_file(struct FILE *fh, int close_when_done); +plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done); /** * Create a buffer instance with a pointer to memory as source. This assumes diff --git a/dsp/tty/tty.h b/dsp/tty/tty.h index 99ca8f00e..2b4e8f46e 100644 --- a/dsp/tty/tty.h +++ b/dsp/tty/tty.h @@ -5,7 +5,6 @@ COSMOPOLITAN_C_START_ -struct FILE; struct termios; struct TtyIdent { diff --git a/examples/BUILD.mk b/examples/BUILD.mk index 8f372d849..4b839fdaf 100644 --- a/examples/BUILD.mk +++ b/examples/BUILD.mk @@ -44,7 +44,6 @@ EXAMPLES_DIRECTDEPS = \ DSP_TTY \ LIBC_CALLS \ LIBC_DLOPEN \ - LIBC_DNS \ LIBC_FMT \ LIBC_INTRIN \ LIBC_IRQ \ diff --git a/examples/nc.c b/examples/nc.c index f484013bb..3e29a51d2 100644 --- a/examples/nc.c +++ b/examples/nc.c @@ -8,7 +8,6 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/calls/calls.h" -#include "libc/dns/dns.h" #include "libc/fmt/conv.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -26,6 +25,7 @@ #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" #include "third_party/getopt/getopt.internal.h" +#include "third_party/musl/netdb.h" /** * @fileoverview netcat clone @@ -60,9 +60,7 @@ int main(int argc, char *argv[]) { halfclose = false; break; case 'h': - fputs("Usage: ", stdout); - fputs(argv[0], stdout); - fputs(" [-hH] IP PORT\n", stdout); + tinyprint(1, "Usage: ", argv[0], " [-hH] IP PORT\n", NULL); exit(0); default: fprintf(stderr, "bad option %d\n", opt); @@ -76,17 +74,9 @@ int main(int argc, char *argv[]) { host = argv[optind + 0]; port = argv[optind + 1]; - switch ((rc = getaddrinfo(host, port, &hint, &ai))) { - case EAI_SUCCESS: - break; - case EAI_SYSTEM: - perror("getaddrinfo"); - exit(1); - default: - fputs("EAI_", stderr); - fputs(gai_strerror(rc), stderr); - fputs("\n", stderr); - exit(1); + if ((rc = getaddrinfo(host, port, &hint, &ai))) { + tinyprint(2, host, ": ", gai_strerror(rc), "\n", NULL); + exit(1); } if ((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { @@ -95,12 +85,12 @@ int main(int argc, char *argv[]) { } if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) { - perror("setsockopt(SO_LINGER)"); + perror("SO_LINGER"); exit(1); } if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { - perror("connect"); + perror(host); exit(1); } diff --git a/examples/whois.c b/examples/whois.c index 9483cb960..bae2bc359 100644 --- a/examples/whois.c +++ b/examples/whois.c @@ -30,12 +30,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" -#include "libc/dns/dns.h" #include "libc/errno.h" #include "libc/log/bsd.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" #include "libc/sock/struct/pollfd.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/ex.h" @@ -44,6 +45,7 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sock.h" #include "third_party/getopt/getopt.internal.h" +#include "third_party/musl/netdb.h" // clang-format off asm(".ident\t\"\\n\\n\ diff --git a/libc/BUILD.mk b/libc/BUILD.mk index 2a60ab0bd..11e8eb3c2 100644 --- a/libc/BUILD.mk +++ b/libc/BUILD.mk @@ -276,7 +276,6 @@ LIBC_FILES := $(wildcard libc/*) o/$(MODE)/libc: o/$(MODE)/libc/calls \ o/$(MODE)/libc/crt \ o/$(MODE)/libc/dlopen \ - o/$(MODE)/libc/dns \ o/$(MODE)/libc/elf \ o/$(MODE)/libc/fmt \ o/$(MODE)/libc/intrin \ diff --git a/libc/calls/h_errno.c b/libc/calls/gethoststxtpath.c similarity index 85% rename from libc/calls/h_errno.c rename to libc/calls/gethoststxtpath.c index 401326c36..b80f28a2d 100644 --- a/libc/calls/h_errno.c +++ b/libc/calls/gethoststxtpath.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,11 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" +#include "libc/calls/sysdir.internal.h" +#include "libc/dce.h" -// error number global for gethostbyname*(), gethostbyaddr*(), etc. -static _Thread_local int __h_errno; - -errno_t *__h_errno_location(void) { - return &__h_errno; +const char *GetHostsTxtPath(char *path, size_t size) { + if (!IsWindows()) { + return "/etc/hosts"; + } else { + return GetSystemDirectoryPath(path, "drivers\\etc\\hosts", size); + } } diff --git a/libc/dns/freeaddrinfo.c b/libc/calls/getprotocolstxtpath.c similarity index 83% rename from libc/dns/freeaddrinfo.c rename to libc/calls/getprotocolstxtpath.c index f9fe85e10..782ba5633 100644 --- a/libc/dns/freeaddrinfo.c +++ b/libc/calls/getprotocolstxtpath.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,18 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" -#include "libc/mem/mem.h" +#include "libc/calls/sysdir.internal.h" +#include "libc/dce.h" -/** - * Frees addresses returned by getaddrinfo(). - */ -void freeaddrinfo(struct addrinfo *ai) { - struct addrinfo *next; - while (ai) { - /* we assume ai_addr and ai_canonname are shoehorned */ - next = ai->ai_next; - free(ai); - ai = next; +const char *GetProtocolsTxtPath(char *buf, size_t size) { + if (!IsWindows()) { + return "/etc/protocols"; + } else { + return GetSystemDirectoryPath(buf, "drivers\\etc\\protocol", size); } } diff --git a/libc/dns/hstrerror.c b/libc/calls/getservicestxtpath.c similarity index 81% rename from libc/dns/hstrerror.c rename to libc/calls/getservicestxtpath.c index 4344ed6b7..dae496cc2 100644 --- a/libc/dns/hstrerror.c +++ b/libc/calls/getservicestxtpath.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,22 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" +#include "libc/calls/sysdir.internal.h" +#include "libc/dce.h" -/** - * Turns `h_errno` value into string. - */ -const char *hstrerror(int err) { - switch (err) { - case HOST_NOT_FOUND: - return "HOST_NOT_FOUND"; - case TRY_AGAIN: - return "TRY_AGAIN"; - case NO_RECOVERY: - return "NO_RECOVERY"; - case NO_DATA: - return "NO_DATA"; - default: - return "UNKNOWN"; +const char *GetServicesTxtPath(char *path, size_t size) { + if (!IsWindows()) { + return "/etc/services"; + } else { + return GetSystemDirectoryPath(path, "drivers\\etc\\services", size); } } diff --git a/libc/dns/getsystemdirectorypath.c b/libc/calls/getsystemdirectorypath.c similarity index 88% rename from libc/dns/getsystemdirectorypath.c rename to libc/calls/getsystemdirectorypath.c index f382e2686..4030e3913 100644 --- a/libc/dns/getsystemdirectorypath.c +++ b/libc/calls/getsystemdirectorypath.c @@ -16,19 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" #include "libc/nt/systeminfo.h" #include "libc/str/str.h" -// e.g. GetSystemDirectoryPath(buf, size, "FOO") → "C:\WINDOWS\SYSTEM32\FOO" -textwindows char *GetSystemDirectoryPath(char *buf, size_t size, - const char *path) { +// e.g. GetSystemDirectoryPath(buf, "FOO", size) → "C:\WINDOWS\SYSTEM32\FOO" +textwindows char *GetSystemDirectoryPath(char *buf, const char *path, + size_t size) { uint32_t syslen = GetSystemDirectoryA(buf, size); size_t pathlen = strlen(path); if (syslen && syslen + pathlen + 1 < size) { - if (buf[syslen] == '\\') { - --syslen; - } + if (buf[syslen] == '\\') --syslen; memcpy(buf + syslen, path, pathlen + 1); return buf; } else { diff --git a/libc/calls/sysdir.internal.h b/libc/calls/sysdir.internal.h new file mode 100644 index 000000000..be2ab710c --- /dev/null +++ b/libc/calls/sysdir.internal.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_SYSDIR_H_ +#define COSMOPOLITAN_LIBC_CALLS_SYSDIR_H_ +COSMOPOLITAN_C_START_ + +const char *GetHostsTxtPath(char *, size_t); +const char *GetServicesTxtPath(char *, size_t); +const char *GetProtocolsTxtPath(char *, size_t); +char *GetSystemDirectoryPath(char *, const char *, size_t); + +COSMOPOLITAN_C_END_ +#endif /* COSMOPOLITAN_LIBC_CALLS_SYSDIR_H_ */ diff --git a/libc/dns/BUILD.mk b/libc/dns/BUILD.mk deleted file mode 100644 index 57fcdf6ba..000000000 --- a/libc/dns/BUILD.mk +++ /dev/null @@ -1,59 +0,0 @@ -#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ -#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘ - -PKGS += LIBC_DNS - -LIBC_DNS_ARTIFACTS += LIBC_DNS_A -LIBC_DNS = $(LIBC_DNS_A_DEPS) $(LIBC_DNS_A) -LIBC_DNS_A = o/$(MODE)/libc/dns/dns.a -LIBC_DNS_A_FILES := $(wildcard libc/dns/*) -LIBC_DNS_A_HDRS = $(filter %.h,$(LIBC_DNS_A_FILES)) -LIBC_DNS_A_SRCS_C = $(filter %.c,$(LIBC_DNS_A_FILES)) -LIBC_DNS_A_SRCS_S = $(filter %.S,$(LIBC_DNS_A_FILES)) - -LIBC_DNS_A_SRCS = \ - $(LIBC_DNS_A_SRCS_S) \ - $(LIBC_DNS_A_SRCS_C) - -LIBC_DNS_A_OBJS = \ - $(LIBC_DNS_A_SRCS_S:%.S=o/$(MODE)/%.o) \ - $(LIBC_DNS_A_SRCS_C:%.c=o/$(MODE)/%.o) - -LIBC_DNS_A_CHECKS = \ - $(LIBC_DNS_A).pkg \ - $(LIBC_DNS_A_HDRS:%=o/$(MODE)/%.ok) - -LIBC_DNS_A_DIRECTDEPS = \ - LIBC_CALLS \ - LIBC_FMT \ - LIBC_INTRIN \ - LIBC_MEM \ - LIBC_NEXGEN32E \ - LIBC_NT_ADVAPI32 \ - LIBC_NT_KERNEL32 \ - LIBC_RUNTIME \ - LIBC_SOCK \ - LIBC_STDIO \ - LIBC_STR \ - LIBC_SYSV - -LIBC_DNS_A_DEPS := \ - $(call uniq,$(foreach x,$(LIBC_DNS_A_DIRECTDEPS),$($(x)))) - -$(LIBC_DNS_A): libc/dns/ \ - $(LIBC_DNS_A).pkg \ - $(LIBC_DNS_A_OBJS) - -$(LIBC_DNS_A).pkg: \ - $(LIBC_DNS_A_OBJS) \ - $(foreach x,$(LIBC_DNS_A_DIRECTDEPS),$($(x)_A).pkg) - -LIBC_DNS_LIBS = $(foreach x,$(LIBC_DNS_ARTIFACTS),$($(x))) -LIBC_DNS_SRCS = $(foreach x,$(LIBC_DNS_ARTIFACTS),$($(x)_SRCS)) -LIBC_DNS_HDRS = $(foreach x,$(LIBC_DNS_ARTIFACTS),$($(x)_HDRS)) -LIBC_DNS_CHECKS = $(foreach x,$(LIBC_DNS_ARTIFACTS),$($(x)_CHECKS)) -LIBC_DNS_OBJS = $(foreach x,$(LIBC_DNS_ARTIFACTS),$($(x)_OBJS)) -$(LIBC_DNS_OBJS): $(BUILD_FILES) libc/dns/BUILD.mk - -.PHONY: o/$(MODE)/libc/dns -o/$(MODE)/libc/dns: $(LIBC_DNS_CHECKS) diff --git a/libc/dns/comparednsnames.c b/libc/dns/comparednsnames.c deleted file mode 100644 index bc8a56a1c..000000000 --- a/libc/dns/comparednsnames.c +++ /dev/null @@ -1,77 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" -#include "libc/macros.internal.h" -#include "libc/str/str.h" - -forceinline void FindDnsLabel(const char *A, size_t *i, size_t *n) { - while (*i) { - if (A[*i - 1] == '.') { - if (*i == *n) { - --(*n); - } else { - break; - } - } - --(*i); - } -} - -/** - * Compares DNS hostnames in reverse lexicographical asciibetical order. - * @return <0, 0, or >0 - * @see test/libc/dns/comparednsnames_test.c (the code that matters) - */ -int CompareDnsNames(const char *A, const char *B) { - int res; - bool first; - size_t n, m, i, j; - if (A == B) return 0; - n = strlen(A); - m = strlen(B); - if (!n || !m || ((A[n - 1] == '.') ^ (B[m - 1] == '.'))) { - if (n && m && A[n - 1] == '.' && strchr(B, '.')) { - --m; - } else if (n && m && B[m - 1] == '.' && strchr(A, '.')) { - --n; - } else { - return A[n ? n - 1 : 0] - B[m ? m - 1 : 0]; - } - } - i = n; - j = m; - first = true; - for (;;) { - FindDnsLabel(A, &i, &n); - FindDnsLabel(B, &j, &m); - if (first) { - first = false; - if (!i && j) return 1; - if (!j && i) return -1; - } - if ((res = strncasecmp(&A[i], &B[j], MIN(n - i + 1, m - j + 1)))) { - return res; - } - if (!i || !j) { - return i - j; - } - n = i; - m = j; - } -} diff --git a/libc/dns/consts.h b/libc/dns/consts.h deleted file mode 100644 index 03136ac79..000000000 --- a/libc/dns/consts.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_CONSTS_H_ -#define COSMOPOLITAN_LIBC_DNS_CONSTS_H_ -#include "libc/sock/sock.h" - -#define DNS_TYPE_A 1 -#define DNS_TYPE_NS 2 -#define DNS_TYPE_CNAME 5 -#define DNS_TYPE_SOA 6 -#define DNS_TYPE_PTR 12 -#define DNS_TYPE_MX 15 -#define DNS_TYPE_TXT 16 - -#define DNS_CLASS_IN 1 - - -#define kMinSockaddr4Size \ - (offsetof(struct sockaddr_in, sin_addr) + sizeof(struct in_addr)) - -#endif /* COSMOPOLITAN_LIBC_DNS_CONSTS_H_ */ diff --git a/libc/dns/dns.h b/libc/dns/dns.h deleted file mode 100644 index 0332b3077..000000000 --- a/libc/dns/dns.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_DNS_H_ -#define COSMOPOLITAN_LIBC_DNS_DNS_H_ -#include "libc/dns/resolvconf.h" -#include "libc/sock/struct/sockaddr.h" - -#define DNS_PORT 53 -#define DNS_NAME_MAX 253 -#define DNS_LABEL_MAX 63 - -#define EAI_SUCCESS 0 -#define EAI_BADFLAGS -1 -#define EAI_NONAME -2 -#define EAI_AGAIN -3 -#define EAI_FAIL -4 -#define EAI_NODATA -5 -#define EAI_FAMILY -6 -#define EAI_SOCKTYPE -7 -#define EAI_SERVICE -8 -#define EAI_ADDRFAMILY -9 -#define EAI_MEMORY -10 -#define EAI_OVERFLOW -12 -#define EAI_SYSTEM -11 -#define EAI_ALLDONE -103 -#define EAI_CANCELED -101 -#define EAI_IDN_ENCODE -105 -#define EAI_INPROGRESS -100 -#define EAI_INTR -104 -#define EAI_NOTCANCELED -102 - -/* AI_* conforms to NT ABI */ -#define AI_PASSIVE 1 -#define AI_CANONNAME 2 -#define AI_NUMERICHOST 4 -#define AI_NUMERICSERV 8 -#define AI_ALL 0x0100 -#define AI_ADDRCONFIG 0x0400 -#define AI_V4MAPPED 0x0800 - -#define NI_NUMERICSCOPE 0 -#define NI_NUMERICHOST 1 -#define NI_NUMERICSERV 2 -#define NI_NOFQDN 4 -#define NI_NAMEREQD 8 -#define NI_DGRAM 16 -#define NI_MAXSERV 32 -#define NI_MAXHOST 1025 - -COSMOPOLITAN_C_START_ - -struct addrinfo { - int32_t ai_flags; /* AI_XXX */ - int32_t ai_family; /* AF_INET */ - int32_t ai_socktype; /* SOCK_XXX */ - int32_t ai_protocol; /* IPPROTO_XXX */ - uint32_t ai_addrlen; - union { - struct sockaddr *ai_addr; - struct sockaddr_in *ai_addr4; - }; - char *ai_canonname /*[DNS_NAME_MAX + 1]*/; - struct addrinfo *ai_next; -}; - -int getaddrinfo(const char *, const char *, const struct addrinfo *, - struct addrinfo **); -void freeaddrinfo(struct addrinfo *); -int getnameinfo(const struct sockaddr *, uint32_t, char *, uint32_t, char *, - uint32_t, int); -const char *gai_strerror(int); -int CompareDnsNames(const char *, const char *) paramsnonnull(); -int PascalifyDnsName(uint8_t *, size_t, const char *) paramsnonnull(); -int ResolveDns(const struct ResolvConf *, int, const char *, struct sockaddr *, - uint32_t) paramsnonnull(); -int ResolveDnsReverse(const struct ResolvConf *, int, const char *, char *, - size_t) paramsnonnull(); -struct addrinfo *newaddrinfo(uint16_t); - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_DNS_DNS_H_ */ diff --git a/libc/dns/dnsheader.c b/libc/dns/dnsheader.c deleted file mode 100644 index 906517504..000000000 --- a/libc/dns/dnsheader.c +++ /dev/null @@ -1,57 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dnsheader.h" -#include "libc/serialize.h" -#include "libc/sysv/errfuns.h" - -/** - * Serializes DNS message h to wire. - * - * @return number of bytes written (always 12) or -1 w/ errno - * @see pascalifydnsname() - */ -void SerializeDnsHeader(uint8_t p[restrict 12], const struct DnsHeader *h) { - p[0x0] = h->id >> 8; - p[0x1] = h->id; - p[0x2] = h->bf1; - p[0x3] = h->bf2; - p[0x4] = h->qdcount >> 8; - p[0x5] = h->qdcount; - p[0x6] = h->ancount >> 8; - p[0x7] = h->ancount; - p[0x8] = h->nscount >> 8; - p[0x9] = h->nscount; - p[0xa] = h->arcount >> 8; - p[0xb] = h->arcount; -} - -/** - * Serializes DNS message h to wire. - * - * @return number of bytes read (always 12) or -1 w/ errno - */ -void DeserializeDnsHeader(struct DnsHeader *h, const uint8_t p[restrict 12]) { - h->id = READ16BE(p); - h->bf1 = p[2]; - h->bf2 = p[3]; - h->qdcount = READ16BE(p + 4); - h->ancount = READ16BE(p + 6); - h->nscount = READ16BE(p + 8); - h->arcount = READ16BE(p + 10); -} diff --git a/libc/dns/dnsheader.h b/libc/dns/dnsheader.h deleted file mode 100644 index 15318b9b9..000000000 --- a/libc/dns/dnsheader.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_DNSHEADER_H_ -#define COSMOPOLITAN_LIBC_DNS_DNSHEADER_H_ -COSMOPOLITAN_C_START_ - -struct DnsHeader { - uint16_t id; /* transaction id */ - uint8_t bf1; /* bit field 1 */ - uint8_t bf2; /* bit field 2 */ - uint16_t qdcount; /* question count */ - uint16_t ancount; /* answer count */ - uint16_t nscount; /* nameserver count */ - uint16_t arcount; /* additional record count */ -}; - -void SerializeDnsHeader(uint8_t[restrict 12], const struct DnsHeader *); -void DeserializeDnsHeader(struct DnsHeader *, const uint8_t[restrict 12]); - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_DNS_DNSHEADER_H_ */ diff --git a/libc/dns/dnsquestion.h b/libc/dns/dnsquestion.h deleted file mode 100644 index 0047747cb..000000000 --- a/libc/dns/dnsquestion.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_DNSQUESTION_H_ -#define COSMOPOLITAN_LIBC_DNS_DNSQUESTION_H_ -COSMOPOLITAN_C_START_ - -struct DnsQuestion { - const char *qname; - uint16_t qtype; - uint16_t qclass; -}; - -int SerializeDnsQuestion(uint8_t *, size_t, const struct DnsQuestion *); - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_DNS_DNSQUESTION_H_ */ diff --git a/libc/dns/ent.h b/libc/dns/ent.h deleted file mode 100644 index 4902038ce..000000000 --- a/libc/dns/ent.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_ENT_H_ -#define COSMOPOLITAN_LIBC_DNS_ENT_H_ -#include "libc/dns/dns.h" - -#define HOST_NOT_FOUND 1 -#define TRY_AGAIN 2 -#define NO_RECOVERY 3 -#define NO_DATA 4 -#define NO_ADDRESS NO_DATA - -COSMOPOLITAN_C_START_ - -struct netent { - char *n_name; /* official network name */ - char **n_aliases; /* alias list */ - int n_addrtype; /* net address type */ - uint32_t n_net; /* network number */ -}; - -struct protoent { - char *p_name; /* official protocol name */ - char **p_aliases; /* alias list */ - int p_proto; /* protocol number */ -}; - -struct hostent { - char *h_name; /* official name of host */ - char **h_aliases; /* alias list */ - int h_addrtype; /* host address type */ - int h_length; /* length of address */ - char **h_addr_list; /* list of addresses */ -}; -#define h_addr h_addr_list[0] - -struct servent { - char *s_name; /* official service name */ - char **s_aliases; /* alias list */ - int s_port; /* port number (in network byte order) */ - char *s_proto; /* protocol to use */ -}; - -#define h_errno (*__h_errno_location()) -errno_t *__h_errno_location(void) dontthrow pureconst; - -void herror(const char *); -const char *hstrerror(int); - -struct netent *getnetent(void); -struct netent *getnetbyname(const char *); -struct netent *getnetbyaddr(uint32_t, int); -void setnetent(int); -void endnetent(void); - -struct protoent *getprotoent(void); -struct protoent *getprotobyname(const char *); -struct protoent *getprotobynumber(int); -void setprotoent(int); -void endprotoent(void); - -struct hostent *gethostent(void); -struct hostent *gethostbyname(const char *); -struct hostent *gethostbyaddr(const void *, uint32_t, int); -void sethostent(int); -void endhostent(void); - -struct servent *getservent(void); -struct servent *getservbyname(const char *, const char *); -struct servent *getservbyport(int, const char *); -void setservent(int); -void endservent(void); - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_DNS_ENT_H_ */ diff --git a/libc/dns/gai_strerror.c b/libc/dns/gai_strerror.c deleted file mode 100644 index ef1ced002..000000000 --- a/libc/dns/gai_strerror.c +++ /dev/null @@ -1,67 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" - -/** - * Turns getaddrinfo() return code into string. - */ -const char *gai_strerror(int code) { - switch (code) { - case EAI_ADDRFAMILY: - return "ADDRFAMILY"; - case EAI_AGAIN: - return "AGAIN"; - case EAI_ALLDONE: - return "ALLDONE"; - case EAI_BADFLAGS: - return "BADFLAGS"; - case EAI_CANCELED: - return "CANCELED"; - case EAI_FAIL: - return "FAIL"; - case EAI_FAMILY: - return "FAMILY"; - case EAI_IDN_ENCODE: - return "ENCODE"; - case EAI_INPROGRESS: - return "INPROGRESS"; - case EAI_INTR: - return "INTR"; - case EAI_MEMORY: - return "MEMORY"; - case EAI_NODATA: - return "NODATA"; - case EAI_NONAME: - return "NONAME"; - case EAI_NOTCANCELED: - return "NOTCANCELED"; - case EAI_OVERFLOW: - return "OVERFLOW"; - case EAI_SERVICE: - return "SERVICE"; - case EAI_SOCKTYPE: - return "SOCKTYPE"; - case EAI_SUCCESS: - return "SUCCESS"; - case EAI_SYSTEM: - return "SYSTEM"; - default: - return "???"; - } -} diff --git a/libc/dns/getaddrinfo.c b/libc/dns/getaddrinfo.c deleted file mode 100644 index c59c1ce51..000000000 --- a/libc/dns/getaddrinfo.c +++ /dev/null @@ -1,116 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/dns/dns.h" -#include "libc/dns/hoststxt.h" -#include "libc/dns/resolvconf.h" -#include "libc/dns/servicestxt.h" -#include "libc/fmt/conv.h" -#include "libc/macros.internal.h" -#include "libc/mem/gc.h" -#include "libc/mem/mem.h" -#include "libc/sock/sock.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/inaddr.h" -#include "libc/sysv/consts/sock.h" -#include "libc/sysv/errfuns.h" - -/** - * Resolves address for internet name. - * - * @param name is either an ip string or a utf-8 hostname - * @param service is the port number as a string - * @param hints may be passed to specialize behavior (optional) - * @param res receives a pointer that must be freed with freeaddrinfo(), - * and won't be modified if non-zero is returned - * @return 0 on success or EAI_xxx value - */ -int getaddrinfo(const char *name, const char *service, - const struct addrinfo *hints, struct addrinfo **res) { - char *eptr; - int rc, port; - char proto[32]; - const char *canon; - struct addrinfo *ai; - port = 0; - if (!name && !service) { - return EAI_NONAME; - } - if (!name && hints && (hints->ai_flags & AI_CANONNAME)) { - return EAI_BADFLAGS; - } - if (service && ((port = strtol(service, &eptr, 10)), *eptr)) { - if (hints && hints->ai_socktype == SOCK_STREAM) { - strcpy(proto, "tcp"); - } else if (hints && hints->ai_socktype == SOCK_DGRAM) { - strcpy(proto, "udp"); - } else { // ai_socktype == 0 - strcpy(proto, ""); - } - if ((port = LookupServicesByName(service, proto, sizeof(proto), NULL, 0, - NULL)) == -1) { - return EAI_NONAME; - } - } - if (!(ai = newaddrinfo(port))) { - return EAI_MEMORY; - } - if (service) { - // if service isn't specified, port is left uninitialized - ai->ai_addr4->sin_port = htons(port); - } - if (hints) { - ai->ai_socktype = hints->ai_socktype; - ai->ai_protocol = hints->ai_protocol; - } - if (!name) { - ai->ai_addr4->sin_addr.s_addr = - (hints && (hints->ai_flags & AI_PASSIVE) == AI_PASSIVE) - ? htonl(INADDR_ANY) - : htonl(INADDR_LOOPBACK); - *res = ai; - return 0; - } - if (inet_pton(AF_INET, name, &ai->ai_addr4->sin_addr.s_addr) == 1) { - *res = ai; - return 0; - } else if (hints && (hints->ai_flags & AI_NUMERICHOST) == AI_NUMERICHOST) { - freeaddrinfo(ai); - return EAI_NONAME; - } else if (ResolveHostsTxt(GetHostsTxt(), AF_INET, name, ai->ai_addr, - sizeof(ai->ai_addr4), &canon) > 0) { - strlcpy(ai->ai_canonname, canon, DNS_NAME_MAX + 1); - *res = ai; - return 0; - } else { - rc = ResolveDns(GetResolvConf(), AF_INET, name, ai->ai_addr, - sizeof(ai->ai_addr4)); - if (rc > 0) { - *res = ai; - return 0; - } - freeaddrinfo(ai); - if (rc == 0) { - return EAI_NONAME; - } else { - return EAI_SYSTEM; - } - } -} diff --git a/libc/dns/gethostbyaddr.c b/libc/dns/gethostbyaddr.c deleted file mode 100644 index f250de7ba..000000000 --- a/libc/dns/gethostbyaddr.c +++ /dev/null @@ -1,58 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" -#include "libc/mem/mem.h" -#include "libc/sysv/consts/af.h" - -struct hostent *gethostbyaddr(const void *s_addr, uint32_t len, int type) { - static struct hostent *ptr1, he1; - static char h_name[DNS_NAME_MAX + 1]; - static char *h_aliases[1]; - static char *h_addr_list[2]; - static char h_addr_list0[4]; - struct sockaddr_in addr; - if (!ptr1) { - he1.h_name = h_name; - he1.h_aliases = h_aliases; - he1.h_aliases[0] = NULL; - he1.h_addrtype = AF_INET; - he1.h_length = 4; - he1.h_addr_list = h_addr_list; - he1.h_addr_list[0] = h_addr_list0; - he1.h_addr_list[1] = NULL; - ptr1 = &he1; - } - if (type != AF_INET || len != sizeof(uint32_t)) return NULL; - addr.sin_family = AF_INET; - addr.sin_port = 0; - addr.sin_addr.s_addr = *(uint32_t *)(s_addr); - if (getnameinfo((struct sockaddr *)&addr, sizeof(addr), ptr1->h_name, - DNS_NAME_MAX, NULL, 0, 0)) - return NULL; - *((uint32_t *)ptr1->h_addr_list[0]) = (addr.sin_addr.s_addr); - return ptr1; -} diff --git a/libc/dns/gethostbyname.c b/libc/dns/gethostbyname.c deleted file mode 100644 index f30d87af9..000000000 --- a/libc/dns/gethostbyname.c +++ /dev/null @@ -1,70 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" - -struct hostent *gethostbyname(const char *name) { - static struct hostent *ptr0, he0; - static char h_name[DNS_NAME_MAX + 1]; - static char *h_aliases[1]; - static char *h_addr_list[2]; - static char h_addr_list0[4]; - struct addrinfo *result = NULL; - - if (!ptr0) { - he0.h_name = h_name; - - he0.h_aliases = h_aliases; - he0.h_aliases[0] = NULL; - - he0.h_addrtype = AF_INET; - he0.h_length = 4; - he0.h_addr_list = h_addr_list; - - he0.h_addr_list[0] = h_addr_list0; - he0.h_addr_list[1] = NULL; - - ptr0 = &he0; - } - - if (getaddrinfo(name, NULL, NULL, &result) || result == NULL) { - return NULL; - } - - /* if getaddrinfo is successful, result->ai_canonname is non-NULL, - * (see newaddrinfo) but the string can still be empty */ - strlcpy(ptr0->h_name, *result->ai_canonname ? result->ai_canonname : name, - sizeof(h_name)); - - *((uint32_t *)ptr0->h_addr_list[0]) = result->ai_addr4->sin_addr.s_addr; - /* TODO: if result has ai_next, fit multiple entries for h_addr_list */ - - freeaddrinfo(result); - return ptr0; -} diff --git a/libc/dns/gethoststxt.c b/libc/dns/gethoststxt.c deleted file mode 100644 index eeca919b1..000000000 --- a/libc/dns/gethoststxt.c +++ /dev/null @@ -1,94 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/cxxabi.h" -#include "libc/dce.h" -#include "libc/dns/hoststxt.h" -#include "libc/dns/servicestxt.h" -#include "libc/intrin/pushpop.internal.h" -#include "libc/intrin/safemacros.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/thread/thread.h" - -static struct HostsTxt *g_hoststxt; -static struct HostsTxtInitialStaticMemory { - struct HostsTxt ht; - pthread_mutex_t lock; - struct HostsTxtEntry entries[8]; - char strings[64]; -} g_hoststxt_init; - -static const char *GetHostsTxtPath(char *path, size_t size) { - if (!IsWindows()) { - return "/etc/hosts"; - } else { - return GetSystemDirectoryPath(path, size, "drivers\\etc\\hosts"); - } -} - -/** - * Returns hosts.txt map. - * - * @note yoinking realloc() ensures there's no size limits - */ -const struct HostsTxt *GetHostsTxt(void) { - FILE *f; - char pathbuf[256]; - struct HostsTxtInitialStaticMemory *init; - init = &g_hoststxt_init; - pthread_mutex_lock(&init->lock); - if (!g_hoststxt) { - g_hoststxt = &init->ht; - init->ht.entries.n = pushpop(ARRAYLEN(init->entries)); - init->ht.entries.p = init->entries; - init->ht.strings.n = pushpop(ARRAYLEN(init->strings)); - init->ht.strings.p = init->strings; - __cxa_atexit((void *)FreeHostsTxt, &g_hoststxt, NULL); - if ((f = fopen(GetHostsTxtPath(pathbuf, sizeof(pathbuf)), "r"))) { - if (ParseHostsTxt(g_hoststxt, f) == -1) { - /* TODO(jart): Elevate robustness. */ - } - fclose(f); - } - } - pthread_mutex_unlock(&init->lock); - return g_hoststxt; -} - -/** - * Frees HOSTS.TXT data structure populated by ParseHostsTxt(). - */ -void FreeHostsTxt(struct HostsTxt **ht) { - if (*ht) { - if ((*ht)->entries.p != g_hoststxt_init.entries) { - free((*ht)->entries.p); - } - if ((*ht)->strings.p != g_hoststxt_init.strings) { - free((*ht)->strings.p); - } - if (*ht != &g_hoststxt_init.ht) { - free(*ht); - } - *ht = 0; - } -} diff --git a/libc/dns/getnameinfo.c b/libc/dns/getnameinfo.c deleted file mode 100644 index 7b8324efc..000000000 --- a/libc/dns/getnameinfo.c +++ /dev/null @@ -1,115 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/dns/consts.h" -#include "libc/dns/dns.h" -#include "libc/dns/hoststxt.h" -#include "libc/dns/resolvconf.h" -#include "libc/dns/servicestxt.h" -#include "libc/fmt/conv.h" -#include "libc/fmt/itoa.h" -#include "libc/intrin/safemacros.internal.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/inaddr.h" -#include "libc/sysv/errfuns.h" - -/** - * Resolves name/service for socket address. - * - * @param addr - * @param addrlen - * @param name - * @param namelen - * @param service - * @param servicelen - * @param flags - * - * @return 0 on success or EAI_xxx value - */ -int getnameinfo(const struct sockaddr *addr, uint32_t addrlen, char *name, - uint32_t namelen, char *service, uint32_t servicelen, - int flags) { - char *p, rdomain[1 + sizeof "255.255.255.255.in-addr.arpa"]; - char info[NI_MAXHOST + 1]; - int port; - uint8_t *ip; - unsigned int valid_flags; - - valid_flags = - (NI_NAMEREQD | NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN | NI_DGRAM); - - if (flags & ~(valid_flags)) return EAI_BADFLAGS; - if (!name && !service) return EAI_NONAME; - if (addr->sa_family != AF_INET || addrlen < sizeof(struct sockaddr_in)) - return EAI_FAMILY; - - ip = (uint8_t *)&(((struct sockaddr_in *)addr)->sin_addr); - p = rdomain; - p = FormatUint32(p, ip[3]), *p++ = '.'; - p = FormatUint32(p, ip[2]), *p++ = '.'; - p = FormatUint32(p, ip[1]), *p++ = '.'; - p = FormatUint32(p, ip[0]), stpcpy(p, ".in-addr.arpa"); - info[0] = '\0'; - if (name != NULL && namelen != 0) { - if ((flags & NI_NUMERICHOST) && (flags & NI_NAMEREQD)) return EAI_NONAME; - - if ((flags & NI_NUMERICHOST) && - inet_ntop(AF_INET, ip, info, sizeof(info)) == NULL) - return EAI_SYSTEM; - else if (!info[0] && ResolveHostsReverse(GetHostsTxt(), AF_INET, ip, info, - sizeof(info)) < 0) - return EAI_SYSTEM; - else if (!info[0] && ResolveDnsReverse(GetResolvConf(), AF_INET, rdomain, - info, sizeof(info)) < 0) - return EAI_SYSTEM; - else if (!info[0] && (flags & NI_NAMEREQD)) - return EAI_NONAME; - else if (!info[0] && inet_ntop(AF_INET, ip, info, sizeof(info)) == NULL) - return EAI_SYSTEM; - - if (strlen(info) + 1 > namelen) return EAI_OVERFLOW; - strcpy(name, info); - } - - port = ntohs(((struct sockaddr_in *)addr)->sin_port); - info[0] = '\0'; - if (service != NULL && servicelen != 0) { - if ((flags & NI_NUMERICSERV) || - LookupServicesByPort(port, ((flags & NI_DGRAM) ? "udp" : "tcp"), 4, - info, sizeof(info), NULL) == -1) { - FormatInt32(info, port); - } - if (strlen(info) + 1 > servicelen) { - return EAI_OVERFLOW; - } - strcpy(service, info); - } - - return 0; -} diff --git a/libc/dns/getntnameservers.c b/libc/dns/getntnameservers.c deleted file mode 100644 index ebf3827c6..000000000 --- a/libc/dns/getntnameservers.c +++ /dev/null @@ -1,93 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/calls/syscall_support-nt.internal.h" -#include "libc/dns/dns.h" -#include "libc/dns/resolvconf.h" -#include "libc/mem/arraylist.internal.h" -#include "libc/nt/enum/keyaccess.h" -#include "libc/nt/enum/reggetvalueflags.h" -#include "libc/nt/registry.h" -#include "libc/nt/runtime.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" - -/** - * Extracts DNS nameserver IPs from Windows Registry. - * - * @param resolv points to a ResolvConf object, which should be zero - * initialized by the caller; or if it already contains items, - * this function will append - * @return number of nameservers appended, or -1 w/ errno - */ -textwindows int GetNtNameServers(struct ResolvConf *resolv) { - int rc; - char value8[128]; - int64_t hkInterfaces; - char *state, *addr, *tmp; - struct sockaddr_in nameserver; - char16_t value[128], uuid[64]; - uint32_t i, keycount, valuebytes, uuidlen; - keycount = 0; - hkInterfaces = kNtInvalidHandleValue; - if (!RegOpenKeyEx( - kNtHkeyLocalMachine, - u"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces", - 0, kNtKeyRead, &hkInterfaces) && - !RegQueryInfoKey(hkInterfaces, 0, 0, 0, &keycount, 0, 0, 0, 0, 0, 0, 0)) { - nameserver.sin_family = AF_INET; - nameserver.sin_port = htons(DNS_PORT); - rc = 0; - for (i = 0; i < keycount; ++i) { - uuidlen = sizeof(uuid); - if (!RegEnumKeyEx(hkInterfaces, i, uuid, &uuidlen, 0, 0, 0, 0) && - ((!RegGetValue(hkInterfaces, uuid, u"DhcpIpAddress", - kNtRrfRtRegSz | kNtRrfRtRegMultiSz, NULL, value, - ((valuebytes = sizeof(value)), &valuebytes)) && - valuebytes > 2 * sizeof(char16_t)) || - (!RegGetValue(hkInterfaces, uuid, u"IpAddress", - kNtRrfRtRegSz | kNtRrfRtRegMultiSz, NULL, value, - ((valuebytes = sizeof(value)), &valuebytes)) && - valuebytes > 2 * sizeof(char16_t))) && - ((!RegGetValue(hkInterfaces, uuid, u"DhcpNameServer", - kNtRrfRtRegSz | kNtRrfRtRegMultiSz, NULL, value, - ((valuebytes = sizeof(value)), &valuebytes)) && - valuebytes > 2 * sizeof(char16_t)) || - (!RegGetValue(hkInterfaces, uuid, u"NameServer", - kNtRrfRtRegSz | kNtRrfRtRegMultiSz, NULL, value, - ((valuebytes = sizeof(value)), &valuebytes)) && - valuebytes > 2 * sizeof(char16_t)))) { - tprecode16to8(value8, sizeof(value8), value); - tmp = value8; - while ((addr = strtok_r(tmp, ", ", &state))) { - if (inet_pton(AF_INET, addr, &nameserver.sin_addr.s_addr) == 1) { - if (append(&resolv->nameservers, &nameserver) != -1) ++rc; - } - tmp = NULL; - } - } - } - } else { - rc = __winerr(); - } - RegCloseKey(hkInterfaces); - return rc; -} diff --git a/libc/dns/getntprotocolstxtpath.c b/libc/dns/getntprotocolstxtpath.c deleted file mode 100644 index 880e4b258..000000000 --- a/libc/dns/getntprotocolstxtpath.c +++ /dev/null @@ -1,37 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/dns/prototxt.h" -#include "libc/dns/servicestxt.h" - -const char *GetProtocolsTxtPath(char *buf, size_t size) { - if (!IsWindows()) { - return "/etc/protocols"; - } else { - return GetSystemDirectoryPath(buf, size, "drivers\\etc\\protocol"); - } -} diff --git a/libc/dns/getntservicestxtpath.c b/libc/dns/getntservicestxtpath.c deleted file mode 100644 index f952dfa0d..000000000 --- a/libc/dns/getntservicestxtpath.c +++ /dev/null @@ -1,36 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/dns/servicestxt.h" - -const char *GetServicesTxtPath(char *path, size_t size) { - if (!IsWindows()) { - return "/etc/services"; - } else { - return GetSystemDirectoryPath(path, size, "drivers\\etc\\services"); - } -} diff --git a/libc/dns/getprotobyname.c b/libc/dns/getprotobyname.c deleted file mode 100644 index 2735cf2b6..000000000 --- a/libc/dns/getprotobyname.c +++ /dev/null @@ -1,46 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" -#include "libc/dns/prototxt.h" -#include "libc/mem/mem.h" - -struct protoent *getprotobyname(const char *name) { - static struct protoent *ptr0, pe0; - static char p_name[DNS_NAME_MAX + 1]; - - if (!ptr0) { - pe0.p_name = p_name; - if (!(pe0.p_aliases = calloc(1, sizeof(char *)))) return NULL; - pe0.p_proto = -1; - ptr0 = &pe0; - } - - ptr0->p_proto = LookupProtoByName(name, ptr0->p_name, DNS_NAME_MAX, NULL); - if (ptr0->p_proto == -1) return NULL; - - return ptr0; -} diff --git a/libc/dns/getprotobynumber.c b/libc/dns/getprotobynumber.c deleted file mode 100644 index 23f2ea00b..000000000 --- a/libc/dns/getprotobynumber.c +++ /dev/null @@ -1,47 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" -#include "libc/dns/prototxt.h" -#include "libc/mem/mem.h" - -struct protoent *getprotobynumber(int proto) { - static struct protoent *ptr1, pe1; - static char p_name[DNS_NAME_MAX + 1]; - - if (!ptr1) { - pe1.p_name = p_name; - if (!(pe1.p_aliases = calloc(1, sizeof(char *)))) return NULL; - pe1.p_proto = -1; - ptr1 = &pe1; - } - - if (LookupProtoByNumber(proto, ptr1->p_name, DNS_NAME_MAX, NULL) == -1) - return NULL; - - ptr1->p_proto = proto; - return ptr1; -} diff --git a/libc/dns/getresolvconf.c b/libc/dns/getresolvconf.c deleted file mode 100644 index a2d75a856..000000000 --- a/libc/dns/getresolvconf.c +++ /dev/null @@ -1,83 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/cxxabi.h" -#include "libc/dce.h" -#include "libc/dns/resolvconf.h" -#include "libc/intrin/pushpop.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/sock/struct/sockaddr.h" -#include "libc/stdio/stdio.h" -#include "libc/thread/thread.h" - -static struct ResolvConf *g_resolvconf; -static struct ResolvConfInitialStaticMemory { - struct ResolvConf rv; - pthread_mutex_t lock; - struct sockaddr_in nameservers[3]; -} g_resolvconf_init; - -/** - * Returns singleton with DNS server address. - */ -const struct ResolvConf *GetResolvConf(void) { - int rc; - FILE *f; - struct ResolvConfInitialStaticMemory *init; - init = &g_resolvconf_init; - pthread_mutex_lock(&init->lock); - if (!g_resolvconf) { - g_resolvconf = &init->rv; - pushmov(&init->rv.nameservers.n, ARRAYLEN(init->nameservers)); - init->rv.nameservers.p = init->nameservers; - __cxa_atexit((void *)FreeResolvConf, &g_resolvconf, NULL); - if (!IsWindows()) { - if ((f = fopen("/etc/resolv.conf", "r"))) { - rc = ParseResolvConf(g_resolvconf, f); - } else { - rc = -1; - } - fclose(f); - } else { - rc = GetNtNameServers(g_resolvconf); - } - if (rc == -1 && !IsTiny()) { - /* TODO(jart): Elevate robustness. */ - } - } - pthread_mutex_unlock(&init->lock); - return g_resolvconf; -} - -/** - * Frees resolv.conf data structure populated by ParseResolvConf(). - */ -void FreeResolvConf(struct ResolvConf **rvp) { - if (*rvp) { - if ((*rvp)->nameservers.p != g_resolvconf_init.nameservers) { - free((*rvp)->nameservers.p); - } - if (*rvp != &g_resolvconf_init.rv) { - free(*rvp); - } - *rvp = 0; - } -} diff --git a/libc/dns/getservbyname.c b/libc/dns/getservbyname.c deleted file mode 100644 index 02a5cf9fd..000000000 --- a/libc/dns/getservbyname.c +++ /dev/null @@ -1,55 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" -#include "libc/dns/servicestxt.h" -#include "libc/mem/mem.h" -#include "libc/sock/sock.h" -#include "libc/str/str.h" - -struct servent *getservbyname(const char *name, const char *proto) { - static struct servent *ptr0, se0; - static char s_name[DNS_NAME_MAX + 1]; - static char localproto[DNS_NAME_MAX + 1]; - int p; - if (!ptr0) { - se0.s_name = s_name; - if (!(se0.s_aliases = calloc(1, sizeof(char *)))) return NULL; - se0.s_port = 0; - se0.s_proto = localproto; - ptr0 = &se0; - } - if (proto) { - strlcpy(localproto, proto, sizeof(localproto)); - } else { - *localproto = 0; - } - p = LookupServicesByName(name, ptr0->s_proto, DNS_NAME_MAX, ptr0->s_name, - DNS_NAME_MAX, NULL); - if (p == -1) return NULL; - ptr0->s_port = htons(p); - return ptr0; -} diff --git a/libc/dns/getservbyport.c b/libc/dns/getservbyport.c deleted file mode 100644 index ce0189060..000000000 --- a/libc/dns/getservbyport.c +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" -#include "libc/dns/servicestxt.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" - -struct servent *getservbyport(int port, const char *proto) { - static struct servent *ptr1, se1; - static char s_name[DNS_NAME_MAX + 1]; - static char localproto[DNS_NAME_MAX + 1]; - if (!ptr1) { - se1.s_name = s_name; - if (!(se1.s_aliases = calloc(1, sizeof(char *)))) return NULL; - se1.s_port = 0; - se1.s_proto = localproto; - ptr1 = &se1; - } - if (proto) { - if (!memccpy(localproto, proto, '\0', DNS_NAME_MAX)) { - return NULL; - } - } else { - *localproto = 0; - } - if (LookupServicesByPort(ntohs(port), ptr1->s_proto, DNS_NAME_MAX, - ptr1->s_name, DNS_NAME_MAX, NULL) == -1) { - return NULL; - } - ptr1->s_port = port; - return ptr1; -} diff --git a/libc/dns/herror.c b/libc/dns/herror.c deleted file mode 100644 index 5977b4fe2..000000000 --- a/libc/dns/herror.c +++ /dev/null @@ -1,36 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" -#include "libc/stdio/stdio.h" - -/** - * Prints `h_errno` description to stderr. - * @see perror() - */ -void herror(const char *s) { - fprintf(stderr, "%s%s%s\n", s ? s : "", s ? ": " : "", hstrerror(h_errno)); -} diff --git a/libc/dns/hostent.c b/libc/dns/hostent.c deleted file mode 100644 index 54b86d0a4..000000000 --- a/libc/dns/hostent.c +++ /dev/null @@ -1,37 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" - -struct hostent *gethostent(void) { - return NULL; -} - -void sethostent(int stayopen) { -} - -void endhostent(void) { -} diff --git a/libc/dns/hoststxt.h b/libc/dns/hoststxt.h deleted file mode 100644 index f413fea51..000000000 --- a/libc/dns/hoststxt.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_HOSTSTXT_H_ -#define COSMOPOLITAN_LIBC_DNS_HOSTSTXT_H_ -#include "libc/sock/struct/sockaddr.h" -#include "libc/stdio/stdio.h" -COSMOPOLITAN_C_START_ - -struct HostsTxtEntry { - uint8_t ip[4]; /* inet_ntop(AF_INET, he->ip, buf, size) */ - uint32_t name; /* &ht->strings.p[he->name] */ - uint32_t canon; /* &ht->strings.p[he->canon] */ -}; - -struct HostsTxtEntries { - size_t i, n; - struct HostsTxtEntry *p; -}; - -struct HostsTxtStrings { - size_t i, n; - char *p; -}; - -struct HostsTxt { - struct HostsTxtEntries entries; - struct HostsTxtStrings strings; -}; - -const struct HostsTxt *GetHostsTxt(void) returnsnonnull; -void FreeHostsTxt(struct HostsTxt **) paramsnonnull(); -int ParseHostsTxt(struct HostsTxt *, FILE *) paramsnonnull(); -int ResolveHostsTxt(const struct HostsTxt *, int, const char *, - struct sockaddr *, uint32_t, const char **) - paramsnonnull((1, 3)); -int ResolveHostsReverse(const struct HostsTxt *, int, const uint8_t *, char *, - size_t) paramsnonnull((1, 3)); - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_DNS_HOSTSTXT_H_ */ diff --git a/libc/dns/lookupprotobyname.c b/libc/dns/lookupprotobyname.c deleted file mode 100644 index 281b46004..000000000 --- a/libc/dns/lookupprotobyname.c +++ /dev/null @@ -1,101 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/dns/prototxt.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/intrin/safemacros.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" - -/** - * Opens and searches /etc/protocols to find number for a given name. - * - * @param protoname is a NULL-terminated string - * @param buf is a buffer to store the official name of the protocol - * @param bufsize is the size of buf - * @param filepath is the location of protocols file - * (if NULL, uses /etc/protocols) - * @return -1 on error, or - * positive protocol number - * - * @note aliases are read from file for comparison, but not returned. - * @see LookupProtoByNumber - */ -int LookupProtoByName(const char *protoname, char *buf, size_t bufsize, - const char *filepath) { - FILE *f; - char *line; - size_t linesize; - const char *path; - int found, result; - char pathbuf[256]; - char *name, *number, *alias, *comment, *tok; - if (!(path = filepath)) { - path = - !IsWindows() - ? "/etc/protocols" - : firstnonnull(GetProtocolsTxtPath(pathbuf, sizeof(pathbuf)), path); - } - if (bufsize == 0 || !(f = fopen(path, "r"))) { - return -1; - } - line = NULL; - linesize = 0; - found = 0; - result = -1; - while (found == 0 && getline(&line, &linesize, f) != -1) { - if ((comment = strchr(line, '#'))) *comment = '\0'; - name = strtok_r(line, " \t\r\n\v", &tok); - number = strtok_r(NULL, "/ \t\r\n\v", &tok); - if (name && number) { - alias = name; - while (alias && strcasecmp(alias, protoname)) { - alias = strtok_r(NULL, " \t\r\n\v", &tok); - } - if (alias) { /* alias matched with protoname */ - if (!memccpy(buf, name, '\0', bufsize)) { - if (bufsize) { - *buf = 0; - } - break; - } - result = atoi(number); - found = 1; - } - } - } - free(line); - if (ferror(f)) { - errno = ferror(f); - return -1; - } - fclose(f); - if (!found) return -1; - return result; -} diff --git a/libc/dns/lookupprotobynumber.c b/libc/dns/lookupprotobynumber.c deleted file mode 100644 index 7aac82ab3..000000000 --- a/libc/dns/lookupprotobynumber.c +++ /dev/null @@ -1,90 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/dns/prototxt.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" - -/** - * Opens and searches /etc/protocols to find name for a given number. - * - * The format of /etc/protocols is like this: - * - * # comment - * # NAME PROTOCOL ALIASES - * ip 0 IP - * icmp 1 ICMP - * - * @param protonum is the protocol number - * @param buf is a buffer to store the official name of the protocol - * @param bufsize is the size of buf - * @param path is the location of the protocols file, which may be NULL - * to use the system-wide default - * @return 0 on success, -1 on error - * @note aliases are not read from the file. - */ -int LookupProtoByNumber(const int protonum, char *buf, size_t bufsize, - const char *path) { - FILE *f; - int found; - char *line; - size_t linesize; - char pathbuf[256]; - char *name, *number, *comment, *tok; - if (!bufsize || - !(f = fopen(path ? path : GetProtocolsTxtPath(pathbuf, sizeof(pathbuf)), - "r"))) { - return -1; - } - line = NULL; - linesize = 0; - found = 0; - while (found == 0 && (getline(&line, &linesize, f)) != -1) { - if ((comment = strchr(line, '#'))) *comment = '\0'; - name = strtok_r(line, " \t\r\n\v", &tok); - number = strtok_r(NULL, " \t\r\n\v", &tok); - if (name && number && protonum == atoi(number)) { - if (!memccpy(buf, name, '\0', bufsize)) { - strcpy(buf, ""); - break; - } - found = 1; - } - } - free(line); - if (ferror(f)) { - errno = ferror(f); - fclose(f); - return -1; - } - fclose(f); - if (!found) return -1; - return 0; -} diff --git a/libc/dns/lookupservicesbyname.c b/libc/dns/lookupservicesbyname.c deleted file mode 100644 index d41ea3fcd..000000000 --- a/libc/dns/lookupservicesbyname.c +++ /dev/null @@ -1,107 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/dns/servicestxt.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/intrin/safemacros.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" - -/** - * Opens and searches /etc/services to find port for a given name. - * - * @param servname is a NULL-terminated string - * @param servproto is a NULL-terminated string (eg "tcp", "udp") - * (if servproto is an empty string, - * if is filled with the first matching - * protocol) - * @param servprotolen the size of servproto - * @param buf is a buffer to store the official name of the service - * (if NULL, the official name is not stored) - * @param bufsize is the size of buf - * @param path is the location of services file - * (if NULL, uses /etc/services) - * @return -1 on error, or positive port number - * @note aliases are read from file for comparison, but not returned. - * @see LookupServicesByPort - */ -int LookupServicesByName(const char *servname, char *servproto, - size_t servprotolen, char *buf, size_t bufsize, - const char *path) { - FILE *f; - char *line; - size_t linesize; - char pathbuf[256]; - int found, result; - char *name, *port, *proto, *alias, *comment, *tok; - if (servprotolen == 0 || - !(f = fopen(path ? path : GetServicesTxtPath(pathbuf, sizeof(pathbuf)), - "r"))) { - return -1; - } - line = NULL; - linesize = 0; - found = 0; - result = -1; - if (bufsize) strcpy(buf, ""); - while (found == 0 && (getline(&line, &linesize, f)) != -1) { - if ((comment = strchr(line, '#'))) *comment = '\0'; - name = strtok_r(line, " \t\r\n\v", &tok); - port = strtok_r(NULL, "/ \t\r\n\v", &tok); - proto = strtok_r(NULL, " \t\r\n\v", &tok); - if (name && port && proto) { - alias = name; - while (alias && strcasecmp(alias, servname) != 0) - alias = strtok_r(NULL, " \t\r\n\v", &tok); - if (alias) /* alias matched with servname */ - { - if (!servproto[0] || strncasecmp(proto, servproto, servprotolen) == 0) { - if (!servproto[0] && !memccpy(servproto, proto, '\0', servprotolen)) { - strcpy(servproto, ""); - break; - } - if (bufsize && !memccpy(buf, name, '\0', bufsize)) { - *buf = 0; - break; - } - result = atoi(port); - found = 1; - } - } - } - } - free(line); - if (ferror(f)) { - errno = ferror(f); - return -1; - } - fclose(f); - if (!found) return -1; - return result; -} diff --git a/libc/dns/lookupservicesbyport.c b/libc/dns/lookupservicesbyport.c deleted file mode 100644 index 42af3aefc..000000000 --- a/libc/dns/lookupservicesbyport.c +++ /dev/null @@ -1,107 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/dns/servicestxt.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/intrin/safemacros.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" - -/** - * Opens and searches /etc/services to find name for a given port. - * - * The format of /etc/services is like this: - * - * # comment - * # NAME PORT/PROTOCOL ALIASES - * ftp 21/tcp - * fsp 21/udp fspd - * ssh 22/tcp - * - * @param servport is the port number - * @param servproto is a NULL-terminated string (eg "tcp", "udp") - * (if servproto is an empty string, - * if is filled with the first matching - * protocol) - * @param servprotolen the size of servproto - * @param buf is a buffer to store the official name of the service - * @param bufsize is the size of buf - * @param path is the location of the services file, which may be NULL - * to use the system-wide default - * @return 0 on success, -1 on error - * @note aliases are not read from the file. - */ -int LookupServicesByPort(const int servport, char *servproto, - size_t servprotolen, char *buf, size_t bufsize, - const char *path) { - FILE *f; - int found; - char *line; - size_t linesize; - char pathbuf[256]; - char *name, *port, *proto, *comment, *tok; - if (!servprotolen || - !(f = fopen(path ? path : GetServicesTxtPath(pathbuf, sizeof(pathbuf)), - "r"))) { - return -1; - } - found = 0; - line = NULL; - linesize = 0; - if (bufsize) *buf = 0; - while (!found && (getline(&line, &linesize, f)) != -1) { - if ((comment = strchr(line, '#'))) *comment = '\0'; - name = strtok_r(line, " \t\r\n\v", &tok); - port = strtok_r(NULL, "/ \t\r\n\v", &tok); - proto = strtok_r(NULL, " \t\r\n\v", &tok); - if (name && port && proto && servport == atoi(port)) { - if (!servproto[0] || strncasecmp(proto, servproto, servprotolen) == 0) { - if (!servproto[0] && !memccpy(servproto, proto, '\0', servprotolen)) { - strcpy(servproto, ""); - break; - } - if (!memccpy(buf, name, '\0', bufsize)) { - if (bufsize) { - *buf = 0; - } - break; - } - found = 1; - } - } - } - free(line); - if (ferror(f)) { - errno = ferror(f); - return -1; - } - fclose(f); - if (!found) return -1; - return 0; -} diff --git a/libc/dns/netent.c b/libc/dns/netent.c deleted file mode 100644 index 91dbca0b8..000000000 --- a/libc/dns/netent.c +++ /dev/null @@ -1,45 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" - -struct netent *getnetent(void) { - return NULL; -} - -struct netent *getnetbyname(const char *name) { - return NULL; -} - -struct netent *getnetbyaddr(uint32_t net, int type) { - return NULL; -} - -void setnetent(int stayopen) { -} - -void endnetent(void) { -} diff --git a/libc/dns/newaddrinfo.c b/libc/dns/newaddrinfo.c deleted file mode 100644 index 76f918b34..000000000 --- a/libc/dns/newaddrinfo.c +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/sock/sock.h" -#include "libc/sysv/consts/af.h" - -#define SIZE sizeof(struct addrinfo) -#define ADDRLEN sizeof(struct sockaddr_in) - -struct addrinfo *newaddrinfo(uint16_t port) { - struct addrinfo *ai; - if ((ai = calloc(1, SIZE + ADDRLEN + DNS_NAME_MAX + 1))) { - ai->ai_family = AF_INET; - ai->ai_addrlen = ADDRLEN; - ai->ai_addr4 = (struct sockaddr_in *)((char *)ai + SIZE); - ai->ai_addr4->sin_family = AF_INET; - ai->ai_addr4->sin_port = htons(port); - ai->ai_canonname = (char *)ai + SIZE + ADDRLEN; - } - return ai; -} diff --git a/libc/dns/parsehoststxt.c b/libc/dns/parsehoststxt.c deleted file mode 100644 index 331722175..000000000 --- a/libc/dns/parsehoststxt.c +++ /dev/null @@ -1,72 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" -#include "libc/dns/hoststxt.h" -#include "libc/errno.h" -#include "libc/mem/arraylist.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" - -/** - * Parses HOSTS.TXT contents. - * - * Hostnames were invented by Peggy Karp; her format looks like this: - * - * # this is a comment - * # IP CANON [ALT...] - * 203.0.113.1 lol.example. lol - * 203.0.113.2 cat.example. cat - * - * @param htp points to a HostsTxt object, which should be zero - * initialized by the caller; or if it already contains items, - * this function will append - * @param f is the file content; see fopen() and fmemopen() - * @return 0 on success, or -1 w/ errno - * @see hoststxtsort() which is the logical next step - */ -int ParseHostsTxt(struct HostsTxt *ht, FILE *f) { - char *line; - size_t linesize; - struct HostsTxtEntry entry; - char *addr, *name, *tok, *comment; - line = NULL; - linesize = 0; - while ((getline(&line, &linesize, f)) != -1) { - if ((comment = strchr(line, '#'))) *comment = '\0'; - if ((addr = strtok_r(line, " \t\r\n\v", &tok)) && - inet_pton(AF_INET, addr, entry.ip) == 1) { - entry.canon = ht->strings.i; - while ((name = strtok_r(NULL, " \t\r\n\v", &tok))) { - entry.name = ht->strings.i; - concat(&ht->strings, name, strnlen(name, DNS_NAME_MAX) + 1); - append(&ht->entries, &entry); - } - } - } - free(line); - if (ferror(f)) { - errno = ferror(f); - return -1; - } - return 0; -} diff --git a/libc/dns/parseresolvconf.c b/libc/dns/parseresolvconf.c deleted file mode 100644 index f9a5cf05d..000000000 --- a/libc/dns/parseresolvconf.c +++ /dev/null @@ -1,68 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" -#include "libc/dns/resolvconf.h" -#include "libc/mem/arraylist.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/inaddr.h" - -/** - * Parses /etc/resolv.conf file. - * - * The content of the file usually looks like this: - * - * nameserver 8.8.8.8 - * nameserver 8.8.4.4 - * - * @param resolv points to a ResolvConf object, which should be zero - * initialized by the caller; or if it already contains items, - * this function will append - * @param f is an open stream with file content - * @return number of nameservers appended, or -1 w/ errno - */ -int ParseResolvConf(struct ResolvConf *resolv, struct FILE *f) { - /* TODO(jart): options ndots:5 */ - int rc; - char *line; - size_t linesize; - struct sockaddr_in nameserver; - char *directive, *value, *tok, *comment; - rc = 0; - line = 0; - linesize = 0; - nameserver.sin_family = AF_INET; - nameserver.sin_port = htons(DNS_PORT); - while (getline(&line, &linesize, f) != -1) { - if ((comment = strchr(line, '#'))) *comment = '\0'; - if ((directive = strtok_r(line, " \t\r\n\v", &tok)) && - (value = strtok_r(NULL, " \t\r\n\v", &tok))) { - if ((strcmp(directive, "nameserver") == 0 && - inet_pton(AF_INET, value, &nameserver.sin_addr.s_addr) == 1)) { - if (append(&resolv->nameservers, &nameserver) != -1) ++rc; - } - } - } - free(line); - return rc | ferror(f); -} diff --git a/libc/dns/pascalifydnsname.c b/libc/dns/pascalifydnsname.c deleted file mode 100644 index 966f3dc6a..000000000 --- a/libc/dns/pascalifydnsname.c +++ /dev/null @@ -1,58 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/dns.h" -#include "libc/str/str.h" -#include "libc/sysv/errfuns.h" - -/** - * Writes dotted hostname to DNS message wire. - * - * The wire format is basically a sequence of Pascal strings, for each - * label in the name. We only do enough validation to maintain protocol - * invariants. - * - * @param name is a dotted NUL-terminated hostname string - * @return bytes written (excluding NUL) or -1 w/ errno - */ -int PascalifyDnsName(uint8_t *buf, size_t size, const char *name) { - size_t i, j, k, namelen; - if ((namelen = strlen(name)) > DNS_NAME_MAX) return enametoolong(); - i = 0; - if (size || namelen) { - if (namelen + 1 > size) return enospc(); - buf[0] = '\0'; - j = 0; - for (;;) { - for (k = 0; name[j + k] && name[j + k] != '.'; ++k) { - buf[i + k + 1] = name[j + k]; - } - if (k) { - if (k > DNS_LABEL_MAX) return enametoolong(); - buf[i] = k; - i += k + 1; - } - j += k + 1; - if (!name[j - 1]) { - break; - } - } - buf[i] = '\0'; - } - return i; -} diff --git a/libc/dns/protoent.c b/libc/dns/protoent.c deleted file mode 100644 index 772bcecc6..000000000 --- a/libc/dns/protoent.c +++ /dev/null @@ -1,37 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/ent.h" - -struct protoent *getprotoent(void) { - return NULL; -} - -void setprotoent(int stayopen) { -} - -void endprotoent(void) { -} diff --git a/libc/dns/prototxt.h b/libc/dns/prototxt.h deleted file mode 100644 index 8746f044d..000000000 --- a/libc/dns/prototxt.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_PROTOTXT_H_ -#define COSMOPOLITAN_LIBC_DNS_PROTOTXT_H_ -#include "libc/sock/sock.h" -#include "libc/stdio/stdio.h" -COSMOPOLITAN_C_START_ - -const char *GetProtocolsTxtPath(char *, size_t); - -int LookupProtoByNumber(const int, char *, size_t, const char *) - paramsnonnull((2)); -int LookupProtoByName(const char *, char *, size_t, const char *) - paramsnonnull((1, 2)); - -/* TODO: implement like struct HostsTxt? */ - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_DNS_PROTOTXT_H_ */ diff --git a/libc/dns/resolvconf.h b/libc/dns/resolvconf.h deleted file mode 100644 index 46a24064f..000000000 --- a/libc/dns/resolvconf.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_DNS_RESOLVCONF_H_ -#define COSMOPOLITAN_LIBC_DNS_RESOLVCONF_H_ -#include "libc/sock/sock.h" -#include "libc/stdio/stdio.h" -COSMOPOLITAN_C_START_ - -struct Nameservers { - size_t i, n; - struct sockaddr_in *p; -}; - -struct ResolvConf { - struct Nameservers nameservers; -}; - -const struct ResolvConf *GetResolvConf(void) returnsnonnull; -int ParseResolvConf(struct ResolvConf *, struct FILE *) paramsnonnull(); -void FreeResolvConf(struct ResolvConf **) paramsnonnull(); -int GetNtNameServers(struct ResolvConf *) paramsnonnull(); - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_DNS_RESOLVCONF_H_ */ diff --git a/libc/dns/resolvedns.c b/libc/dns/resolvedns.c deleted file mode 100644 index 83c6c4ac7..000000000 --- a/libc/dns/resolvedns.c +++ /dev/null @@ -1,113 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/dns/consts.h" -#include "libc/dns/dns.h" -#include "libc/dns/dnsheader.h" -#include "libc/dns/dnsquestion.h" -#include "libc/dns/resolvconf.h" -#include "libc/serialize.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/internal.h" -#include "libc/sock/sock.h" -#include "libc/sock/struct/sockaddr.h" -#include "libc/stdio/rand.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/ipproto.h" -#include "libc/sysv/consts/sock.h" -#include "libc/sysv/errfuns.h" - -#define kMsgMax 512 - -/** - * Queries Domain Name System for address associated with name. - * - * @param resolvconf can be GetResolvConf() - * @param af can be AF_INET, AF_UNSPEC - * @param name can be a local or fully-qualified hostname - * @param addr should point to a struct sockaddr_in; if this function - * succeeds, its sin_family and sin_addr fields will be modified - * @param addrsize is the byte size of addr - * @return number of matches found, or -1 w/ errno - * @error EAFNOSUPPORT. ENETDOWN, ENAMETOOLONG, EBADMSG - */ -int ResolveDns(const struct ResolvConf *resolvconf, int af, const char *name, - struct sockaddr *addr, uint32_t addrsize) { - int rc, fd, n; - struct DnsQuestion q; - struct DnsHeader h, h2; - struct sockaddr_in *a4; - uint8_t *p, *pe, msg[512]; - uint16_t rtype, rclass, rdlength; - if (addrsize < kMinSockaddr4Size) return einval(); - if (af != AF_INET && af != AF_UNSPEC) return eafnosupport(); - if (!resolvconf->nameservers.i) return 0; - bzero(&h, sizeof(h)); - rc = ebadmsg(); - h.id = _rand64(); - h.bf1 = 1; /* recursion desired */ - h.qdcount = 1; - q.qname = name; - q.qtype = DNS_TYPE_A; - q.qclass = DNS_CLASS_IN; - bzero(msg, sizeof(msg)); - SerializeDnsHeader(msg, &h); - if ((n = SerializeDnsQuestion(msg + 12, 500, &q)) == -1) return -1; - if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) return -1; - if (sendto(fd, msg, 12 + n, 0, (struct sockaddr *)resolvconf->nameservers.p, - sizeof(*resolvconf->nameservers.p)) == 12 + n && - (n = read(fd, msg, 512)) >= 12) { - DeserializeDnsHeader(&h2, msg); - if (h2.id == h.id) { - rc = 0; - p = msg + 12; - pe = msg + n; - while (p < pe && h2.qdcount--) { - p += strnlen((char *)p, pe - p) + 1 + 4; - } - while (p < pe && h2.ancount--) { - if ((p[0] & 0xc0) == 0xc0) { /* name pointer */ - p += 2; - } else { - p += strnlen((char *)p, pe - p) + 1; - } - if (p + 10 <= pe) { - rtype = READ16BE(p); - rclass = READ16BE(p + 2); - // ttl = READ32BE(p + 4); - rdlength = READ16BE(p + 8); - if (p + 10 + rdlength <= pe && rdlength == 4 && - rclass == DNS_CLASS_IN && rtype == DNS_TYPE_A) { - rc = 1; - a4 = (struct sockaddr_in *)addr; - a4->sin_family = AF_INET; - memcpy(&a4->sin_addr.s_addr, p + 10, 4); - break; - } - p += 10 + rdlength; - } - } - } - } - close(fd); - return rc; -} diff --git a/libc/dns/resolvednsreverse.c b/libc/dns/resolvednsreverse.c deleted file mode 100644 index e6c4871df..000000000 --- a/libc/dns/resolvednsreverse.c +++ /dev/null @@ -1,128 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/dns/consts.h" -#include "libc/dns/dns.h" -#include "libc/dns/dnsheader.h" -#include "libc/dns/dnsquestion.h" -#include "libc/dns/resolvconf.h" -#include "libc/serialize.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/sock/struct/sockaddr.h" -#include "libc/stdio/rand.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/ipproto.h" -#include "libc/sysv/consts/sock.h" -#include "libc/sysv/errfuns.h" - -#define kMsgMax 512 - -/** - * Performs reverse DNS lookup with IP address. - * - * @param resolvconf can be GetResolvConf() - * @param af can be AF_INET, AF_UNSPEC - * @param name is a reversed IP address string ending with .in-addr.arpa - * @param buf to store the obtained hostname if any - * @param bufsize is size of buf - * @return 0 on success, or -1 w/ errno - * @error EAFNOSUPPORT, ENETDOWN, ENAMETOOLONG, EBADMSG - */ -int ResolveDnsReverse(const struct ResolvConf *resolvconf, int af, - const char *name, char *buf, size_t bufsize) { - int rc, fd, n; - struct DnsQuestion q; - struct DnsHeader h, h2; - uint8_t *p, *pe, msg[512]; - uint16_t rtype, rclass, rdlength; - if (af != AF_INET && af != AF_UNSPEC) return eafnosupport(); - if (!resolvconf->nameservers.i) return 0; - bzero(&h, sizeof(h)); - rc = ebadmsg(); - h.id = _rand64(); - h.bf1 = 1; /* recursion desired */ - h.qdcount = 1; - q.qname = name; - q.qtype = DNS_TYPE_PTR; - q.qclass = DNS_CLASS_IN; - bzero(msg, sizeof(msg)); - SerializeDnsHeader(msg, &h); - if ((n = SerializeDnsQuestion(msg + 12, 500, &q)) == -1) return -1; - if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) return -1; - if (sendto(fd, msg, 12 + n, 0, (struct sockaddr *)resolvconf->nameservers.p, - sizeof(*resolvconf->nameservers.p)) == 12 + n && - (n = read(fd, msg, 512)) >= 12) { - DeserializeDnsHeader(&h2, msg); - if (h2.id == h.id) { - rc = 0; - if (h2.ancount) { - p = msg + 12; - pe = msg + n; - while (p < pe && h2.qdcount) { - p += strnlen((char *)p, pe - p) + 1 + 4; - h2.qdcount--; - } - if (p + 1 < pe) { - if ((p[0] & 0b11000000) == 0b11000000) { /* name pointer */ - p += 2; - } else { - p += strnlen((char *)p, pe - p) + 1; - } - if (p + 2 + 2 + 4 + 2 < pe) { - rtype = READ16BE(p), p += 2; - rclass = READ16BE(p), p += 2; - /* ttl */ p += 4; - rdlength = READ16BE(p), p += 2; - if (p + rdlength <= pe && rtype == DNS_TYPE_PTR && - rclass == DNS_CLASS_IN) { - if (strnlen((char *)p, pe - p) + 1 > bufsize) - rc = -1; - else { - /* domain name starts with a letter */ - for (; !isalnum((char)(*p)) && p < pe; p++) rdlength--; - for (char *tmp = (char *)p; rdlength > 0 && *tmp != '\0'; - tmp++) { - /* each label is alphanumeric or hyphen - * any other character is assumed separator */ - if (!isalnum(*tmp) && *tmp != '-') *tmp = '.'; - rdlength--; - } - strcpy(buf, (char *)p); - } - } else - rc = -1; - } - } - } - } - } - close(fd); - return rc; -} diff --git a/libc/dns/resolvehostsreverse.c b/libc/dns/resolvehostsreverse.c deleted file mode 100644 index db21822c0..000000000 --- a/libc/dns/resolvehostsreverse.c +++ /dev/null @@ -1,52 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/consts.h" -#include "libc/dns/dns.h" -#include "libc/dns/hoststxt.h" -#include "libc/serialize.h" -#include "libc/mem/alg.h" -#include "libc/sock/sock.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/errfuns.h" - -/** - * Finds name associated with address in HOSTS.TXT table. - * - * @param ht can be GetHostsTxt() - * @param af can be AF_INET - * @param ip is IP address in binary (sin_addr) - * @param buf is buffer to store the name - * @param bufsize is length of buf - * @return 1 if found, 0 if not found, or -1 w/ errno - * @error EAFNOSUPPORT - */ -int ResolveHostsReverse(const struct HostsTxt *ht, int af, const uint8_t *ip, - char *buf, size_t bufsize) { - size_t i; - if (af != AF_INET && af != AF_UNSPEC) return eafnosupport(); - for (i = 0; i < ht->entries.i; ++i) { - if (READ32LE(ip) == READ32LE(ht->entries.p[i].ip)) { - if (memccpy(buf, ht->strings.p + ht->entries.p[i].name, 0, bufsize)) { - return 1; - } - } - } - return 0; -} diff --git a/libc/dns/resolvehoststxt.c b/libc/dns/resolvehoststxt.c deleted file mode 100644 index 96125e26f..000000000 --- a/libc/dns/resolvehoststxt.c +++ /dev/null @@ -1,62 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/consts.h" -#include "libc/dns/dns.h" -#include "libc/dns/hoststxt.h" -#include "libc/mem/alg.h" -#include "libc/sock/sock.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/errfuns.h" - -/** - * Finds address associated with name in HOSTS.TXT table. - * - * @param ht can be GetHostsTxt() - * @param af can be AF_INET, AF_UNSPEC - * @param name can be a local or fully-qualified hostname - * @param addr should point to a struct sockaddr_in; if this function - * succeeds, its sin_family and sin_addr fields will be modified - * @param addrsize is the byte size of addr - * @param canon be used to return a pointer to the canonical name - * @return number of matches found, or -1 w/ errno - * @error EAFNOSUPPORT - */ -int ResolveHostsTxt(const struct HostsTxt *ht, int af, const char *name, - struct sockaddr *addr, uint32_t addrsize, - const char **canon) { - size_t i; - struct sockaddr_in *addr4; - if (af != AF_INET && af != AF_UNSPEC) return eafnosupport(); - for (i = 0; i < ht->entries.i; ++i) { - if (!CompareDnsNames(name, ht->strings.p + ht->entries.p[i].name)) { - if (addr) { - if (addrsize < kMinSockaddr4Size) return einval(); - addr4 = (struct sockaddr_in *)addr; - addr4->sin_family = AF_INET; - memcpy(&addr4->sin_addr.s_addr, &ht->entries.p[i].ip[0], 4); - } - if (canon) { - *canon = ht->strings.p + ht->entries.p[i].canon; - } - return 1; - } - } - return 0; -} diff --git a/libc/dns/rfc0226.txt b/libc/dns/rfc0226.txt deleted file mode 100644 index 961828314..000000000 --- a/libc/dns/rfc0226.txt +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - -NETWORK WORKING GROUP PEGGY KARP -REQUEST FOR COMMENTS #226 MITRE -NIC #7625 20 SEPT 71 -CATEGORIES: D.3 -UPDATES: NONE -OBSOLETES: NONE - - STANDARDIZATION OF HOST MNEUMONICS - -IN EACH TELNET IMPLEMENTATION, A LIST OF HOST NMEUMONICS IS PROVIDED -FOR THE USER TO INDICATE THE SERVING HOST DESIRED. CURRENTLY, EACH -SITE EMPLOYS THEIR OWN SPECIAL LIST. RATHER THAN REQUIRE THE USER TO -BE COGNIZANT OF THE IDIOSYNCRASIES OF EACH LIST, ESPECIALLY WHEN -CHAINED THROUGH SEVERAL HOSTS VIA TELNET, IT HAS BEEN RECOMMENDED THAT -STANDARD HOST DESIGNATORS BE ADOPTED. - -THE FOLLOWING LIST OF SIX CHARACTER DESIGNATORS IS PROPOSED AS THE -STANDARD LIST. REGISTER ANY OBJECTIONS BY 3 OCTOBER TO P. KARP (703) -893-3500 X2391, X2318. AN OFFICIAL POLICY AND IMPLEMENTATION DATE WILL -BE SET ON 8 OCTOBER. - -THE LIST: - HOST # DESIGNATOR - 1 UCLA - 65 UCLA36 - 2 SRIARC - 66 SRIAI - 3 UCSB - 4 UTAH - 6 MULTCS - 70 MITDM - 7 RAND - 8 SDC - 9 HARV - 10 LNCTX2 - 74 LNC360 - 11 STAN - 12 ILL - 69 BBN - 133 BBNB - 144 AMES - 145 MITRE - 158 TIP - - [ This RFC was put into machine readable form for entry ] - [ into the online RFC archives by BBN Corp. under the ] - [ direction of Alex McKenzie. 12/96 ] - - - - - [Page 1] - diff --git a/libc/dns/rfc0247.txt b/libc/dns/rfc0247.txt deleted file mode 100644 index 57e320915..000000000 --- a/libc/dns/rfc0247.txt +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - -Network Working Group Peggy Karp -Request for Comments: #247 MITRE -NIC 7688 12 October 1971 -Categories: Policy, Telnet -Related: #226, 236, 239, 233, 237 -Obsoletes: #226 - - Proferred Set of Standard Host Names - - In RFC #226, BBN's TENEX list of Host names was set up as a strawman - set of standard Host names. Comments received since then (an RFC - actually generated comments!!!) have influenced me to propose the - following general rules for forming Host names. - - The Host names will be 8 characters in length. The general form is - - '-' - - will be at most 4 characters, formed as follows: - - (a) Use the keyword in the site name, if not more than - four characters, e.g., NASA Ames, Case Western - Reserve. ---- ---- - - (b) Use the standard acronym, if not more than four - characters, e.g., UCLA, RADC, NBS. - - (c) If a standard abbreviation exists, use it, e.g., Ill. - - (d) If none of the above apply, use the first four letters - in the site name, e.g., Burr, Mitr, Harv. - - (e) If none of the above is acceptable to the site, the - technical liaison should select the site mnemonic. - - will be at most 4 characters of the form - . - Examples of mfg. # are: - - IBM 360 2 digit model number - IBM 370 3 digit model number - PDP 1 - 2 digit model number - Burroughs 4 digits - CDC 4 digits - etc. - - - - - - - - [Page 1] - -RFC #247 - - - will be used when more than one machine of the same - type is located at a site (e.g., 2 PDP-10s at MIT, at SRI, and - at BBN). - - Limiting to 4 characters does not permit distinctions - to be made between machines with 4 digit mfg. #s. I expect - the situation will be handled in an ad hoc manner by the NIC if - it arises. - - TIPs are identified as 'TIP' rather than by '316'. If a Host - is not to be permanently addressable, the machine is identified - as 'TEST'. - - A list of Host names, formed according to these rules, is - attached. Alternate Host names should be provided, as - suggested by Jon Postel (RFC #236). RFC's 206, 233, and - 236 present lists with 4-character alternate names. The - Technical Liaison should select the alternate name for his - site and communicate the selection to the NIC. - - - The preceding rules and the attached list of Host names are - subject to the approval of the NWG. Hereafter, the list will - be generated and maintained by the NIC in cooperation with - the Technical Liaison at each site, as suggested in RFC #237. - Comments should be addressed to Dick Watson. - - - - - - - - [ This RFC was put into machine readable form for entry ] - - [ into the online RFC archives by BBN Corp. under the ] - - [ direction of Alex McKenzie. 12/96 ] - - - - - - - - - - - - [Page 2] - -RFC #247 -Attachment 1 - - NETWORK ADDRESS STANDARD NAME - --------------- ------------- - 1 UCLA-7 - 65 UCLA-91 - 2 SRI-10NI - 66 SRI-10AI - 3 UCSB-75 - 4 UTAH-10 - 5 BBN-516 - 69 BBN-10A - 133 BBN-10B - 6 MIT-645 - 70 MIT-10DM - 134 MIT-10AI - 7 RAND-65 - 71 RAND-10 - 8 SDC-75 - 9 HARV-10 - 73 HARV-1 - 137 HARV-11 - 10 LL-67 - 74 LL-TX2 - 138 LL-TSP - 11 SAIL-10 - 12 ILL-11 - 76 ILL-6500 - 13 CASE-10 - 14 CMU-10 - 15 BURR-6500 - 79 BURR-TEST - 16 AMES-67 - 144 AMES-TIP - 145 MITR-TIP - 18 RADC-645 - 146 RADC-TIP - 19 NBS-11 - 147 NBS-TIP - 148 ETAC-TIP - 21 TINK-418 - 22 MCCL-418 - 23 USC-44 - 151 USC-TIP - 152 GWC-TIP - 25 NCAR-7600 - 153 NCAR-TIP - 158 BBNX-TEST - - - - [Page 3] - -RFC #247 -Attachment 2 - - An Implementation Scheme - -If the standard Host names are formed according to the proposed -rules, the following implementation scheme, suggested by Steve -Crocker, can be used. - - Map into an 8-bit number, S and - map into an 8-bit number, M, - where - S + M = Network Address. - - S and M can be selected such that specification of - alone could cause a default to the "primary" Host at - the site. Note that this scheme depends on a unique - designator for each IMP. - -Some examples: - -If the "primary" Host at UCLA is the 91, let - UCLA -> S = X'41' - 7 -> M = X'40' - 91 -> M = X'00' -then for - UCLA-7, S + M = X'01' = 1 base 10 - UCLA-91,S + M = X'41' = 65 base 10 - -and - UCLA alone = X'41' = 65 base 10 - -If the primary Host at BBN is TENEX System A, let - BBN -> S = X'45' - 516 -> M = X'40' - 10A -> M = X'00' - 10B -> M = X'C0' -then for - BBN-516, S + M = X'05' = 5 base 10 - BBN-10A, S + M = X'45' = 69 base 10 - BBN-10B, S + M = X'85' = 133 base 10 - -and - BBN alone = X'45' = 69 base 10 - -The primary Host for each IMP would be designated by the -site and such information disseminated by the NIC. - - - - - - [Page 4] - diff --git a/libc/dns/rfc1035.txt b/libc/dns/rfc1035.txt deleted file mode 100644 index b1a9bf5a9..000000000 --- a/libc/dns/rfc1035.txt +++ /dev/null @@ -1,3077 +0,0 @@ -Network Working Group P. Mockapetris -Request for Comments: 1035 ISI - November 1987 -Obsoletes: RFCs 882, 883, 973 - - DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION - - -1. STATUS OF THIS MEMO - -This RFC describes the details of the domain system and protocol, and -assumes that the reader is familiar with the concepts discussed in a -companion RFC, "Domain Names - Concepts and Facilities" [RFC-1034]. - -The domain system is a mixture of functions and data types which are an -official protocol and functions and data types which are still -experimental. Since the domain system is intentionally extensible, new -data types and experimental behavior should always be expected in parts -of the system beyond the official protocol. The official protocol parts -include standard queries, responses and the Internet class RR data -formats (e.g., host addresses). Since the previous RFC set, several -definitions have changed, so some previous definitions are obsolete. - -Experimental or obsolete features are clearly marked in these RFCs, and -such information should be used with caution. - -The reader is especially cautioned not to depend on the values which -appear in examples to be current or complete, since their purpose is -primarily pedagogical. Distribution of this memo is unlimited. - - Table of Contents - - 1. STATUS OF THIS MEMO 1 - 2. INTRODUCTION 3 - 2.1. Overview 3 - 2.2. Common configurations 4 - 2.3. Conventions 7 - 2.3.1. Preferred name syntax 7 - 2.3.2. Data Transmission Order 8 - 2.3.3. Character Case 9 - 2.3.4. Size limits 10 - 3. DOMAIN NAME SPACE AND RR DEFINITIONS 10 - 3.1. Name space definitions 10 - 3.2. RR definitions 11 - 3.2.1. Format 11 - 3.2.2. TYPE values 12 - 3.2.3. QTYPE values 12 - 3.2.4. CLASS values 13 - - - -Mockapetris [Page 1] - -RFC 1035 Domain Implementation and Specification November 1987 - - - 3.2.5. QCLASS values 13 - 3.3. Standard RRs 13 - 3.3.1. CNAME RDATA format 14 - 3.3.2. HINFO RDATA format 14 - 3.3.3. MB RDATA format (EXPERIMENTAL) 14 - 3.3.4. MD RDATA format (Obsolete) 15 - 3.3.5. MF RDATA format (Obsolete) 15 - 3.3.6. MG RDATA format (EXPERIMENTAL) 16 - 3.3.7. MINFO RDATA format (EXPERIMENTAL) 16 - 3.3.8. MR RDATA format (EXPERIMENTAL) 17 - 3.3.9. MX RDATA format 17 - 3.3.10. NULL RDATA format (EXPERIMENTAL) 17 - 3.3.11. NS RDATA format 18 - 3.3.12. PTR RDATA format 18 - 3.3.13. SOA RDATA format 19 - 3.3.14. TXT RDATA format 20 - 3.4. ARPA Internet specific RRs 20 - 3.4.1. A RDATA format 20 - 3.4.2. WKS RDATA format 21 - 3.5. IN-ADDR.ARPA domain 22 - 3.6. Defining new types, classes, and special namespaces 24 - 4. MESSAGES 25 - 4.1. Format 25 - 4.1.1. Header section format 26 - 4.1.2. Question section format 28 - 4.1.3. Resource record format 29 - 4.1.4. Message compression 30 - 4.2. Transport 32 - 4.2.1. UDP usage 32 - 4.2.2. TCP usage 32 - 5. MASTER FILES 33 - 5.1. Format 33 - 5.2. Use of master files to define zones 35 - 5.3. Master file example 36 - 6. NAME SERVER IMPLEMENTATION 37 - 6.1. Architecture 37 - 6.1.1. Control 37 - 6.1.2. Database 37 - 6.1.3. Time 39 - 6.2. Standard query processing 39 - 6.3. Zone refresh and reload processing 39 - 6.4. Inverse queries (Optional) 40 - 6.4.1. The contents of inverse queries and responses 40 - 6.4.2. Inverse query and response example 41 - 6.4.3. Inverse query processing 42 - - - - - - -Mockapetris [Page 2] - -RFC 1035 Domain Implementation and Specification November 1987 - - - 6.5. Completion queries and responses 42 - 7. RESOLVER IMPLEMENTATION 43 - 7.1. Transforming a user request into a query 43 - 7.2. Sending the queries 44 - 7.3. Processing responses 46 - 7.4. Using the cache 47 - 8. MAIL SUPPORT 47 - 8.1. Mail exchange binding 48 - 8.2. Mailbox binding (Experimental) 48 - 9. REFERENCES and BIBLIOGRAPHY 50 - Index 54 - -2. INTRODUCTION - -2.1. Overview - -The goal of domain names is to provide a mechanism for naming resources -in such a way that the names are usable in different hosts, networks, -protocol families, internets, and administrative organizations. - -From the user's point of view, domain names are useful as arguments to a -local agent, called a resolver, which retrieves information associated -with the domain name. Thus a user might ask for the host address or -mail information associated with a particular domain name. To enable -the user to request a particular type of information, an appropriate -query type is passed to the resolver with the domain name. To the user, -the domain tree is a single information space; the resolver is -responsible for hiding the distribution of data among name servers from -the user. - -From the resolver's point of view, the database that makes up the domain -space is distributed among various name servers. Different parts of the -domain space are stored in different name servers, although a particular -data item will be stored redundantly in two or more name servers. The -resolver starts with knowledge of at least one name server. When the -resolver processes a user query it asks a known name server for the -information; in return, the resolver either receives the desired -information or a referral to another name server. Using these -referrals, resolvers learn the identities and contents of other name -servers. Resolvers are responsible for dealing with the distribution of -the domain space and dealing with the effects of name server failure by -consulting redundant databases in other servers. - -Name servers manage two kinds of data. The first kind of data held in -sets called zones; each zone is the complete database for a particular -"pruned" subtree of the domain space. This data is called -authoritative. A name server periodically checks to make sure that its -zones are up to date, and if not, obtains a new copy of updated zones - - - -Mockapetris [Page 3] - -RFC 1035 Domain Implementation and Specification November 1987 - - -from master files stored locally or in another name server. The second -kind of data is cached data which was acquired by a local resolver. -This data may be incomplete, but improves the performance of the -retrieval process when non-local data is repeatedly accessed. Cached -data is eventually discarded by a timeout mechanism. - -This functional structure isolates the problems of user interface, -failure recovery, and distribution in the resolvers and isolates the -database update and refresh problems in the name servers. - -2.2. Common configurations - -A host can participate in the domain name system in a number of ways, -depending on whether the host runs programs that retrieve information -from the domain system, name servers that answer queries from other -hosts, or various combinations of both functions. The simplest, and -perhaps most typical, configuration is shown below: - - Local Host | Foreign - | - +---------+ +----------+ | +--------+ - | | user queries | |queries | | | - | User |-------------->| |---------|->|Foreign | - | Program | | Resolver | | | Name | - | |<--------------| |<--------|--| Server | - | | user responses| |responses| | | - +---------+ +----------+ | +--------+ - | A | - cache additions | | references | - V | | - +----------+ | - | cache | | - +----------+ | - -User programs interact with the domain name space through resolvers; the -format of user queries and user responses is specific to the host and -its operating system. User queries will typically be operating system -calls, and the resolver and its cache will be part of the host operating -system. Less capable hosts may choose to implement the resolver as a -subroutine to be linked in with every program that needs its services. -Resolvers answer user queries with information they acquire via queries -to foreign name servers and the local cache. - -Note that the resolver may have to make several queries to several -different foreign name servers to answer a particular user query, and -hence the resolution of a user query may involve several network -accesses and an arbitrary amount of time. The queries to foreign name -servers and the corresponding responses have a standard format described - - - -Mockapetris [Page 4] - -RFC 1035 Domain Implementation and Specification November 1987 - - -in this memo, and may be datagrams. - -Depending on its capabilities, a name server could be a stand alone -program on a dedicated machine or a process or processes on a large -timeshared host. A simple configuration might be: - - Local Host | Foreign - | - +---------+ | - / /| | - +---------+ | +----------+ | +--------+ - | | | | |responses| | | - | | | | Name |---------|->|Foreign | - | Master |-------------->| Server | | |Resolver| - | files | | | |<--------|--| | - | |/ | | queries | +--------+ - +---------+ +----------+ | - -Here a primary name server acquires information about one or more zones -by reading master files from its local file system, and answers queries -about those zones that arrive from foreign resolvers. - -The DNS requires that all zones be redundantly supported by more than -one name server. Designated secondary servers can acquire zones and -check for updates from the primary server using the zone transfer -protocol of the DNS. This configuration is shown below: - - Local Host | Foreign - | - +---------+ | - / /| | - +---------+ | +----------+ | +--------+ - | | | | |responses| | | - | | | | Name |---------|->|Foreign | - | Master |-------------->| Server | | |Resolver| - | files | | | |<--------|--| | - | |/ | | queries | +--------+ - +---------+ +----------+ | - A |maintenance | +--------+ - | +------------|->| | - | queries | |Foreign | - | | | Name | - +------------------|--| Server | - maintenance responses | +--------+ - -In this configuration, the name server periodically establishes a -virtual circuit to a foreign name server to acquire a copy of a zone or -to check that an existing copy has not changed. The messages sent for - - - -Mockapetris [Page 5] - -RFC 1035 Domain Implementation and Specification November 1987 - - -these maintenance activities follow the same form as queries and -responses, but the message sequences are somewhat different. - -The information flow in a host that supports all aspects of the domain -name system is shown below: - - Local Host | Foreign - | - +---------+ +----------+ | +--------+ - | | user queries | |queries | | | - | User |-------------->| |---------|->|Foreign | - | Program | | Resolver | | | Name | - | |<--------------| |<--------|--| Server | - | | user responses| |responses| | | - +---------+ +----------+ | +--------+ - | A | - cache additions | | references | - V | | - +----------+ | - | Shared | | - | database | | - +----------+ | - A | | - +---------+ refreshes | | references | - / /| | V | - +---------+ | +----------+ | +--------+ - | | | | |responses| | | - | | | | Name |---------|->|Foreign | - | Master |-------------->| Server | | |Resolver| - | files | | | |<--------|--| | - | |/ | | queries | +--------+ - +---------+ +----------+ | - A |maintenance | +--------+ - | +------------|->| | - | queries | |Foreign | - | | | Name | - +------------------|--| Server | - maintenance responses | +--------+ - -The shared database holds domain space data for the local name server -and resolver. The contents of the shared database will typically be a -mixture of authoritative data maintained by the periodic refresh -operations of the name server and cached data from previous resolver -requests. The structure of the domain data and the necessity for -synchronization between name servers and resolvers imply the general -characteristics of this database, but the actual format is up to the -local implementor. - - - - -Mockapetris [Page 6] - -RFC 1035 Domain Implementation and Specification November 1987 - - -Information flow can also be tailored so that a group of hosts act -together to optimize activities. Sometimes this is done to offload less -capable hosts so that they do not have to implement a full resolver. -This can be appropriate for PCs or hosts which want to minimize the -amount of new network code which is required. This scheme can also -allow a group of hosts can share a small number of caches rather than -maintaining a large number of separate caches, on the premise that the -centralized caches will have a higher hit ratio. In either case, -resolvers are replaced with stub resolvers which act as front ends to -resolvers located in a recursive server in one or more name servers -known to perform that service: - - Local Hosts | Foreign - | - +---------+ | - | | responses | - | Stub |<--------------------+ | - | Resolver| | | - | |----------------+ | | - +---------+ recursive | | | - queries | | | - V | | - +---------+ recursive +----------+ | +--------+ - | | queries | |queries | | | - | Stub |-------------->| Recursive|---------|->|Foreign | - | Resolver| | Server | | | Name | - | |<--------------| |<--------|--| Server | - +---------+ responses | |responses| | | - +----------+ | +--------+ - | Central | | - | cache | | - +----------+ | - -In any case, note that domain components are always replicated for -reliability whenever possible. - -2.3. Conventions - -The domain system has several conventions dealing with low-level, but -fundamental, issues. While the implementor is free to violate these -conventions WITHIN HIS OWN SYSTEM, he must observe these conventions in -ALL behavior observed from other hosts. - -2.3.1. Preferred name syntax - -The DNS specifications attempt to be as general as possible in the rules -for constructing domain names. The idea is that the name of any -existing object can be expressed as a domain name with minimal changes. - - - -Mockapetris [Page 7] - -RFC 1035 Domain Implementation and Specification November 1987 - - -However, when assigning a domain name for an object, the prudent user -will select a name which satisfies both the rules of the domain system -and any existing rules for the object, whether these rules are published -or implied by existing programs. - -For example, when naming a mail domain, the user should satisfy both the -rules of this memo and those in RFC-822. When creating a new host name, -the old rules for HOSTS.TXT should be followed. This avoids problems -when old software is converted to use domain names. - -The following syntax will result in fewer problems with many - -applications that use domain names (e.g., mail, TELNET). - - ::= | " " - - ::=