diff --git a/build.zig b/build.zig index 28e76aa1..eb165690 100644 --- a/build.zig +++ b/build.zig @@ -229,6 +229,7 @@ fn linkHtml5Ever(b: *Build, mod: *Build.Module) !void { "src/html5ever/lib.rs", "src/html5ever/sink.rs", "src/html5ever/types.rs", + "src/html5ever/url.rs", }) |path| { exec_cargo.addFileInput(b.path(path)); } @@ -312,13 +313,6 @@ fn linkCurl(b: *Build, mod: *Build.Module, is_tsan: bool) !void { const boringssl = buildBoringSsl(b, target, mod.optimize.?); for (boringssl) |lib| curl.root_module.linkLibrary(lib); - const libidn2 = buildLibidn2(b, target, mod.optimize.?, is_tsan); - curl.root_module.linkLibrary(libidn2); - // Also expose libidn2 to the consuming module so src/sys/idna.zig's - // @cImport of resolves. Without this, lightpanda_module only - // sees idn2.h transitively if a system libidn2 happens to be installed. - mod.linkLibrary(libidn2); - switch (target.result.os.tag) { .macos => { // needed for proxying on mac @@ -473,168 +467,6 @@ fn buildNghttp2(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.O return lib; } -fn buildLibidn2( - b: *Build, - target: Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode, - is_tsan: bool, -) *Build.Step.Compile { - const dep = b.dependency("libidn2", .{}); - - const os = target.result.os.tag; - const is_darwin = os.isDarwin(); - - const mod = b.createModule(.{ - .target = target, - .optimize = optimize, - .link_libc = true, - .sanitize_thread = is_tsan, - }); - - // libidn2's autoconf+gnulib stack expects a config.h with hundreds of - // HAVE_*/_GL_ATTRIBUTE_* defines — including ~800 lines of attribute- - // detection macros emitted from gnulib-common.m4 via AH_VERBATIM. We - // vendor a single autoconf-generated config.h rather than try to - // reproduce that machinery in the Zig build system. - mod.addIncludePath(b.path("vendor/libidn2")); - - // Substitute the gnulib-style .in.h templates. All @VAR@ in them are - // either DLL-visibility markers (empty for static POSIX) or - // HAVE_UNISTRING_WOE32DLL_H (0). - inline for (.{ "unitypes", "unistr", "uniconv", "unictype", "uninorm" }) |name| { - mod.addConfigHeader(renderUnistringHeader(b, dep, name)); - } - - mod.addIncludePath(dep.path("lib")); - mod.addIncludePath(dep.path("unistring")); - // gl/ holds gnulib helpers — only malloca and version-etc headers are - // referenced from the sources we compile; we don't need the full gl/ shim - // layer (system header replacements). - mod.addIncludePath(dep.path("gl")); - - const lib = b.addLibrary(.{ .name = "idn2", .root_module = mod }); - lib.installHeader(dep.path("lib/idn2.h"), "idn2.h"); - - if (is_darwin) { - // unistring's striconveh.c calls real iconv_*, which on macOS lives - // in libiconv (separate from libSystem). On glibc Linux iconv is in - // libc itself; on musl it would also need a separate -liconv. - mod.linkSystemLibrary("iconv", .{}); - - // libidn2's lib/lookup.c calls strchrnul() without including - // ; the prototype is declared in vendor/libidn2/config.h - // alongside the existing strverscmp shim. macOS libc lacked the - // symbol entirely before 15.4 — provide it here so the link - // succeeds. Mirrors how gl/strverscmp.c is wired up below. - lib.addCSourceFile(.{ - .file = b.path("vendor/libidn2/darwin/strchrnul.c"), - .flags = &.{}, - }); - } - - lib.addCSourceFiles(.{ - .root = dep.path("lib"), - .flags = &.{ "-DHAVE_CONFIG_H", "-DIDN2_STATIC" }, - .files = &.{ - "bidi.c", "context.c", "data.c", "decode.c", - "error.c", "free.c", "idna.c", "lookup.c", - "punycode.c", "register.c", "tables.c", "tr46map.c", - "version.c", - }, - }); - lib.addCSourceFiles(.{ - .root = dep.path("gl"), - .flags = &.{"-DHAVE_CONFIG_H"}, - // malloca.c provides striconveha's stack-or-heap allocator; strverscmp - // is a glibc extension absent on macOS that lib/version.c needs. - .files = &.{ "malloca.c", "strverscmp.c" }, - }); - lib.addCSourceFiles(.{ - .root = dep.path("unistring"), - .flags = &.{"-DHAVE_CONFIG_H"}, - .files = &.{ - "c-ctype.c", "c-strcasecmp.c", "c-strncasecmp.c", - "free.c", "iconv.c", "iconv_close.c", - "iconv_open.c", "localcharset.c", "stdlib.c", - "striconveh.c", "striconveha.c", "unistd.c", - "uniconv/u8-conv-from-enc.c", "uniconv/u8-strconv-from-enc.c", "uniconv/u8-strconv-from-locale.c", - "uniconv/u8-strconv-to-enc.c", "uniconv/u8-strconv-to-locale.c", "unictype/bidi_of.c", - "unictype/categ_M.c", "unictype/categ_none.c", "unictype/categ_of.c", - "unictype/categ_test.c", "unictype/combiningclass.c", "unictype/joiningtype_of.c", - "unictype/scripts.c", "uninorm/canonical-decomposition.c", "uninorm/composition.c", - "uninorm/decompose-internal.c", "uninorm/decomposition-table.c", "uninorm/nfc.c", - "uninorm/nfd.c", "uninorm/u32-normalize.c", "unistr/u32-cmp.c", - "unistr/u32-cpy-alloc.c", "unistr/u32-cpy.c", "unistr/u32-mbtouc-unsafe.c", - "unistr/u32-strlen.c", "unistr/u32-to-u8.c", "unistr/u32-uctomb.c", - "unistr/u8-check.c", "unistr/u8-mblen.c", "unistr/u8-mbtouc.c", - "unistr/u8-mbtouc-aux.c", "unistr/u8-mbtouc-unsafe.c", "unistr/u8-mbtouc-unsafe-aux.c", - "unistr/u8-mbtoucr.c", "unistr/u8-prev.c", "unistr/u8-strlen.c", - "unistr/u8-to-u32.c", "unistr/u8-uctomb.c", "unistr/u8-uctomb-aux.c", - }, - }); - - return lib; -} - -/// Process one of unistring's `.in.h` template headers into a real `.h`. -/// All `@VAR@` substitutions in these headers are either DLL-visibility markers -/// (empty for static POSIX builds) or `HAVE_UNISTRING_WOE32DLL_H` (0). -fn renderUnistringHeader(b: *Build, dep: *Build.Dependency, name: []const u8) *Build.Step.ConfigHeader { - const in_rel = b.fmt("unistring/{s}.in.h", .{name}); - const out_name = b.fmt("{s}.h", .{name}); - const lazy = dep.path(in_rel); - const path = lazy.getPath3(b, null); - - const file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |e| { - std.debug.panic("openFile {s}: {s}", .{ path.sub_path, @errorName(e) }); - }; - defer file.close(); - const contents = file.readToEndAlloc(b.allocator, 4 << 20) catch @panic("OOM"); - - const ch = b.addConfigHeader(.{ - .include_path = out_name, - .style = .{ .autoconf_at = lazy }, - }, .{}); - - var seen = std.StringHashMap(void).init(b.allocator); - var i: usize = 0; - while (std.mem.indexOfScalarPos(u8, contents, i, '@')) |s| { - const a = s + 1; - const e = std.mem.indexOfScalarPos(u8, contents, a, '@') orelse break; - const var_name = contents[a..e]; - if (!isAtConfigName(var_name)) { - // Stray '@' (e.g. an email address in a comment); advance past it - // alone so we don't mis-pair with a later '@'. - i = s + 1; - continue; - } - const owned = b.allocator.dupe(u8, var_name) catch @panic("OOM"); - const gop = seen.getOrPut(owned) catch @panic("OOM"); - if (!gop.found_existing) { - if (std.mem.eql(u8, var_name, "HAVE_UNISTRING_WOE32DLL_H")) { - ch.addValue(owned, c_int, 0); - } else { - ch.addValue(owned, []const u8, ""); - } - } - i = e + 1; - } - return ch; -} - -fn isAtConfigName(s: []const u8) bool { - if (s.len == 0) return false; - for (s, 0..) |c, idx| { - const ok = switch (c) { - 'A'...'Z', '_' => true, - '0'...'9' => idx > 0, - else => false, - }; - if (!ok) return false; - } - return true; -} - fn buildCurl( b: *Build, target: Build.ResolvedTarget, @@ -711,11 +543,11 @@ fn buildCurl( ._FILE_OFFSET_BITS = 64, .USE_IPV6 = true, - // Route IDN hostnames through libidn2 (vendored, see buildLibidn2). - // Without this, libcurl ships UTF-8 host bytes to SNI/cert validation - // and breaks for non-ASCII hostnames like räksmörgås.se. - .HAVE_LIBIDN2 = true, - .HAVE_IDN2_H = true, + // IDN is handled before libcurl (HttpClient calls URL.ensureHostAscii, + // backed by rust-url), so libcurl always receives an ASCII host and + // does not link libidn2. + .HAVE_LIBIDN2 = false, + .HAVE_IDN2_H = false, .CURL_OS = switch (os) { .linux => if (is_android) "\"android\"" else "\"linux\"", else => std.fmt.allocPrint(b.allocator, "\"{s}\"", .{@tagName(os)}) catch @panic("OOM"), diff --git a/build.zig.zon b/build.zig.zon index cd5bef8a..ae7966b1 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -34,10 +34,6 @@ .url = "https://github.com/allyourcodebase/sqlite3/archive/8f840560eae88ab66668c6827c64ffbd0d74ef37.tar.gz", .hash = "sqlite3-3.51.0-DMxLWssOAABZ8cAvU_LfBIbp0kZjm824PU8sSLXpEDdr", }, - .libidn2 = .{ - .url = "https://ftp.gnu.org/gnu/libidn/libidn2-2.3.8.tar.gz", - .hash = "N-V-__8AABGOuAC_dhAN07kfoP4dycCFi8Bka4O-tuhriNH8", - }, .zenai = .{ .url = "git+https://github.com/lightpanda-io/zenai.git#120f5fd2a2d29779fbc44584aaa472ad83c6a153", .hash = "zenai-0.0.0-iOY_VP_EAwBATbpytTro7tmzT1aASeBvKPPvJEO2tEzB", diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index fa48874d..15c65bf4 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -560,8 +560,14 @@ pub fn navigate(self: *Frame, request_url: [:0]const u8, opts: NavigateOpts) !vo self.origin = try URL.getOrigin(self.arena, request_url[5.. :0]); } else if (self.parent) |parent| { self.origin = parent.origin; + if (is_about_blank) { + self.base_url = parent.base(); + } } else if (self.window._opener) |opener| { self.origin = opener._frame.origin; + if (is_about_blank) { + self.base_url = opener._frame.base(); + } } else { self.origin = null; } diff --git a/src/browser/ImportMap.zig b/src/browser/ImportMap.zig new file mode 100644 index 00000000..3066b95d --- /dev/null +++ b/src/browser/ImportMap.zig @@ -0,0 +1,535 @@ +// Copyright (C) 2023-2026 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Parsed - diff --git a/src/browser/tests/element/html/base.html b/src/browser/tests/element/html/base.html new file mode 100644 index 00000000..f3f966e9 --- /dev/null +++ b/src/browser/tests/element/html/base.html @@ -0,0 +1,27 @@ + + + + + + + diff --git a/src/browser/tests/navigator/navigator.html b/src/browser/tests/navigator/navigator.html index 09419d69..10ed2bcc 100644 --- a/src/browser/tests/navigator/navigator.html +++ b/src/browser/tests/navigator/navigator.html @@ -139,16 +139,9 @@ diff --git a/src/browser/tests/url.html b/src/browser/tests/url.html index 60f991ea..f1e626e4 100644 --- a/src/browser/tests/url.html +++ b/src/browser/tests/url.html @@ -97,6 +97,28 @@ testing.expectEqual('http://example.com/a/b/foo', url.toString()); } + { + // IDN hosts are converted to punycode (UTS#46). + const url = new URL('https://räksmörgås.se/x'); + testing.expectEqual('xn--rksmrgs-5wao1o.se', url.hostname); + testing.expectEqual('https://xn--rksmrgs-5wao1o.se/x', url.href); + } + + { + // Valid punycode passes through unchanged. + const url = new URL('https://xn--rksmrgs-5wao1o.se/x'); + testing.expectEqual('xn--rksmrgs-5wao1o.se', url.hostname); + } + + { + // An invalid domain (malformed punycode) is a parse failure -> TypeError. + testing.withError((err) => { + testing.expectEqual(true, err.toString().includes('TypeError')); + }, () => { + const url = new URL('https://xn--0.pt/x'); + }); + } + { const base = 'http://example.com/a/b/c/d'; const url = new URL('../../../../../foo', base); diff --git a/src/browser/webapi/Navigator.zig b/src/browser/webapi/Navigator.zig index 958fd109..e1e56eb5 100644 --- a/src/browser/webapi/Navigator.zig +++ b/src/browser/webapi/Navigator.zig @@ -139,11 +139,6 @@ pub fn getModelContext(_: *const Navigator, frame: *Frame) *ModelContext { return &frame.window._model_context; } -pub fn getBattery(_: *const Navigator, frame: *Frame) !js.Promise { - log.info(.not_implemented, "navigator.getBattery", .{}); - return frame.js.local.?.rejectErrorPromise(.{ .dom_exception = .{ .err = error.NotSupported } }); -} - pub fn registerProtocolHandler(_: *const Navigator, scheme: []const u8, url: [:0]const u8, frame: *const Frame) !void { try validateProtocolHandlerScheme(scheme); try validateProtocolHandlerURL(url, frame); @@ -248,7 +243,6 @@ pub const JsApi = struct { // Methods pub const javaEnabled = bridge.function(Navigator.javaEnabled, .{}); - pub const getBattery = bridge.function(Navigator.getBattery, .{}); pub const permissions = bridge.accessor(Navigator.getPermissions, null, .{}); pub const storage = bridge.accessor(Navigator.getStorage, null, .{}); pub const userAgentData = bridge.accessor(Navigator.getUserAgentData, null, .{}); diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index 261a5de7..c32a7eb1 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -21,6 +21,7 @@ const lp = @import("lightpanda"); const builtin = @import("builtin"); const js = @import("../js/js.zig"); +const URL = @import("../URL.zig"); const Frame = @import("../Frame.zig"); const Console = @import("Console.zig"); const History = @import("History.zig"); @@ -506,6 +507,14 @@ pub fn open(self: *Window, url_: ?[]const u8, target_: ?[]const u8, features_: ? const no_opener = hasFeatureToken(features, "noopener") or hasFeatureToken(features, "noreferrer"); + if (raw_url.len > 0) { + // Per spec, we should validate the url + _ = URL.resolve(frame.call_arena, frame.base(), raw_url, .{}) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => return error.SyntaxError, + }; + } + // _self / _parent / _top navigate the current browsing context. if (std.ascii.eqlIgnoreCase(target, "_self") or std.ascii.eqlIgnoreCase(target, "_parent") or @@ -610,9 +619,7 @@ pub fn close(self: *Window) void { // eval whose parser is still holding the Frame. Destroying the context // now leaves dangling pointers in the unwinding script eval (load event // dispatch, runMacrotasks, etc.). Defer to Page.deinit instead. - page.queued_close.append(page.frame_arena, frame) catch |err| { - log.err(.frame, "queue popup close", .{ .err = err }); - }; + page.session.queueFrameDestruction(frame); } pub fn postMessage(self: *Window, message: js.Value.Temp, target_origin: ?[]const u8, transfer: ?[]const *MessagePort, frame: *Frame) !void { @@ -1001,7 +1008,7 @@ pub const JsApi = struct { pub const opener = bridge.accessor(Window.getOpener, null, .{}); pub const closed = bridge.accessor(Window.getClosed, null, .{}); pub const name = bridge.accessor(Window.getName, Window.setName, .{}); - pub const open = bridge.function(Window.open, .{}); + pub const open = bridge.function(Window.open, .{ .dom_exception = true }); pub const close = bridge.function(Window.close, .{}); pub const alert = bridge.function(struct { diff --git a/src/browser/webapi/element/html/Anchor.zig b/src/browser/webapi/element/html/Anchor.zig index 62223112..65acd009 100644 --- a/src/browser/webapi/element/html/Anchor.zig +++ b/src/browser/webapi/element/html/Anchor.zig @@ -165,6 +165,28 @@ pub fn setProtocol(self: *Anchor, value: []const u8, frame: *Frame) !void { try setHref(self, new_href, frame); } +pub fn getUsername(self: *Anchor, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getUsername(href); +} + +pub fn setUsername(self: *Anchor, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setUsername(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getPassword(self: *Anchor, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getPassword(href); +} + +pub fn setPassword(self: *Anchor, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setPassword(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + pub fn getType(self: *Anchor) []const u8 { return self.asElement().getAttributeSafe(comptime .wrap("type")) orelse ""; } @@ -221,6 +243,8 @@ pub const JsApi = struct { pub const protocol = bridge.accessor(Anchor.getProtocol, Anchor.setProtocol, .{ .ce_reactions = true }); pub const host = bridge.accessor(Anchor.getHost, Anchor.setHost, .{ .ce_reactions = true }); pub const hostname = bridge.accessor(Anchor.getHostname, Anchor.setHostname, .{ .ce_reactions = true }); + pub const username = bridge.accessor(Anchor.getUsername, Anchor.setUsername, .{ .ce_reactions = true }); + pub const password = bridge.accessor(Anchor.getPassword, Anchor.setPassword, .{ .ce_reactions = true }); pub const port = bridge.accessor(Anchor.getPort, Anchor.setPort, .{ .ce_reactions = true }); pub const pathname = bridge.accessor(Anchor.getPathname, Anchor.setPathname, .{ .ce_reactions = true }); pub const search = bridge.accessor(Anchor.getSearch, Anchor.setSearch, .{ .ce_reactions = true }); diff --git a/src/browser/webapi/element/html/Area.zig b/src/browser/webapi/element/html/Area.zig index a74e705c..d00e120e 100644 --- a/src/browser/webapi/element/html/Area.zig +++ b/src/browser/webapi/element/html/Area.zig @@ -1,4 +1,23 @@ +// Copyright (C) 2023-2026 Lightpanda (Selecy SAS) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); const js = @import("../../../js/js.zig"); +const Frame = @import("../../../Frame.zig"); + +const URL = @import("../../../URL.zig"); const Node = @import("../../Node.zig"); const Element = @import("../../Element.zig"); const HtmlElement = @import("../Html.zig"); @@ -14,6 +33,155 @@ pub fn asNode(self: *Area) *Node { return self.asElement().asNode(); } +pub fn getHref(self: *Area, frame: *Frame) ![]const u8 { + const href = self.asElement().getAttributeSafe(comptime .wrap("href")) orelse return ""; + if (href.len == 0) { + return ""; + } + return self.asNode().resolveURL(href, frame, .{}); +} + +pub fn setHref(self: *Area, value: []const u8, frame: *Frame) !void { + try self.asElement().setAttributeSafe(comptime .wrap("href"), .wrap(value), frame); +} + +pub fn getOrigin(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return (try URL.getOrigin(frame.call_arena, href)) orelse "null"; +} + +pub fn getHost(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + const host = URL.getHost(href); + const protocol = URL.getProtocol(href); + const port = URL.getPort(href); + + // Strip default ports + if (port.len > 0) { + if ((std.mem.eql(u8, protocol, "https:") and std.mem.eql(u8, port, "443")) or + (std.mem.eql(u8, protocol, "http:") and std.mem.eql(u8, port, "80"))) + { + return URL.getHostname(href); + } + } + + return host; +} + +pub fn setHost(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setHost(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getHostname(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getHostname(href); +} + +pub fn setHostname(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setHostname(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getUsername(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getUsername(href); +} + +pub fn setUsername(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setUsername(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getPassword(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getPassword(href); +} + +pub fn setPassword(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setPassword(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getPort(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + const port = URL.getPort(href); + const protocol = URL.getProtocol(href); + + // Return empty string for default ports + if (port.len > 0) { + if ((std.mem.eql(u8, protocol, "https:") and std.mem.eql(u8, port, "443")) or + (std.mem.eql(u8, protocol, "http:") and std.mem.eql(u8, port, "80"))) + { + return ""; + } + } + + return port; +} + +pub fn setPort(self: *Area, value: ?[]const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setPort(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getSearch(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getSearch(href); +} + +pub fn setSearch(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setSearch(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getHash(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getHash(href); +} + +pub fn setHash(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setHash(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getPathname(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getPathname(href); +} + +pub fn setPathname(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setPathname(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +pub fn getProtocol(self: *Area, frame: *Frame) ![]const u8 { + const href = try getResolvedHref(self, frame) orelse return ""; + return URL.getProtocol(href); +} + +pub fn setProtocol(self: *Area, value: []const u8, frame: *Frame) !void { + const href = try getResolvedHref(self, frame) orelse return; + const new_href = try URL.setProtocol(href, value, frame.call_arena); + try setHref(self, new_href, frame); +} + +fn getResolvedHref(self: *Area, frame: *Frame) !?[:0]const u8 { + const href = self.asElement().getAttributeSafe(comptime .wrap("href")) orelse return null; + if (href.len == 0) { + return null; + } + return try self.asNode().resolveURL(href, frame, .{}); +} + pub const JsApi = struct { pub const bridge = js.Bridge(Area); @@ -22,4 +190,17 @@ pub const JsApi = struct { pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; }; + + pub const href = bridge.accessor(Area.getHref, Area.setHref, .{ .ce_reactions = true }); + pub const origin = bridge.accessor(Area.getOrigin, null, .{}); + pub const protocol = bridge.accessor(Area.getProtocol, Area.setProtocol, .{ .ce_reactions = true }); + pub const host = bridge.accessor(Area.getHost, Area.setHost, .{ .ce_reactions = true }); + pub const hostname = bridge.accessor(Area.getHostname, Area.setHostname, .{ .ce_reactions = true }); + pub const username = bridge.accessor(Area.getUsername, Area.setUsername, .{ .ce_reactions = true }); + pub const password = bridge.accessor(Area.getPassword, Area.setPassword, .{ .ce_reactions = true }); + pub const port = bridge.accessor(Area.getPort, Area.setPort, .{ .ce_reactions = true }); + pub const pathname = bridge.accessor(Area.getPathname, Area.setPathname, .{ .ce_reactions = true }); + pub const search = bridge.accessor(Area.getSearch, Area.setSearch, .{ .ce_reactions = true }); + pub const hash = bridge.accessor(Area.getHash, Area.setHash, .{ .ce_reactions = true }); + pub const toString = bridge.function(Area.getHref, .{}); }; diff --git a/src/browser/webapi/element/html/Base.zig b/src/browser/webapi/element/html/Base.zig index 0c2f2e8a..bb5616c8 100644 --- a/src/browser/webapi/element/html/Base.zig +++ b/src/browser/webapi/element/html/Base.zig @@ -1,4 +1,7 @@ const js = @import("../../../js/js.zig"); +const URL = @import("../../../URL.zig"); +const Frame = @import("../../../Frame.zig"); + const Node = @import("../../Node.zig"); const Element = @import("../../Element.zig"); const HtmlElement = @import("../Html.zig"); @@ -14,6 +17,44 @@ pub fn asNode(self: *Base) *Node { return self.asElement().asNode(); } +pub fn getHref(self: *Base, frame: *Frame) ![]const u8 { + const element = self.asElement(); + const href = element.getAttributeSafe(comptime .wrap("href")) orelse return ""; + if (href.len == 0) { + return ""; + } + return URL.resolve(frame.call_arena, frame.url, href, .{}); +} + +pub fn setHref(self: *Base, value: []const u8, frame: *Frame) !void { + const element = self.asElement(); + try element.setAttributeSafe(comptime .wrap("href"), .wrap(value), frame); + + // Per HTML spec, the document's base URL is the href of the FIRST + // element in tree order that has an href attribute — not necessarily this + // one. Re-derive from scratch so that setting href on a non-authoritative + // , or clearing href on the authoritative one, both work correctly. + const node = element.asNode(); + if (!node.isConnected()) { + return; + } + + const owner = node.ownerFrame(frame); + const first = (try owner.document.querySelector(comptime .wrap("base[href]"), owner)) orelse { + owner.base_url = null; + return; + }; + const href = first.getAttributeSafe(comptime .wrap("href")) orelse { + owner.base_url = null; + return; + }; + if (href.len == 0) { + owner.base_url = null; + return; + } + owner.base_url = try URL.resolve(owner.arena, owner.url, href, .{}); +} + pub const JsApi = struct { pub const bridge = js.Bridge(Base); @@ -22,4 +63,11 @@ pub const JsApi = struct { pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; }; + + pub const href = bridge.accessor(Base.getHref, Base.setHref, .{ .ce_reactions = true }); }; + +const testing = @import("../../../../testing.zig"); +test "WebApi: HTML.Base" { + try testing.htmlRunner("element/html/base.html", .{}); +} diff --git a/src/browser/webapi/event/CookieChangeEvent.zig b/src/browser/webapi/event/CookieChangeEvent.zig index 0b4252fe..9a194e67 100644 --- a/src/browser/webapi/event/CookieChangeEvent.zig +++ b/src/browser/webapi/event/CookieChangeEvent.zig @@ -80,7 +80,9 @@ pub fn initSingle( const item = try arena.create(CookieStore.CookieListItem); item.* = .{ .name = try String.init(arena, snapshot.name, .{}), - .value = try String.init(arena, snapshot.value, .{}), + // Deletions report no value (the `deleted` accessor serializes the + // resulting null as undefined); changes carry the new value. + .value = if (kind == .deleted) null else try String.init(arena, snapshot.value, .{}), .domain = if (snapshot.domain.len > 0 and snapshot.domain[0] == '.') try String.init(arena, snapshot.domain[1..], .{}) else @@ -89,9 +91,9 @@ pub fn initSingle( .expires = null, .secure = snapshot.secure, .sameSite = switch (snapshot.same_site) { - .strict => .strict, - .lax => .lax, - .none => .none, + .strict => "strict", + .lax => "lax", + .none => "none", }, .partitioned = false, }; @@ -133,5 +135,6 @@ pub const JsApi = struct { pub const constructor = bridge.constructor(CookieChangeEvent.init, .{}); pub const changed = bridge.accessor(CookieChangeEvent.getChanged, null, .{}); - pub const deleted = bridge.accessor(CookieChangeEvent.getDeleted, null, .{}); + // null_as_undefined trickles down to the serialization of the CookieListItem fields + pub const deleted = bridge.accessor(CookieChangeEvent.getDeleted, null, .{ .null_as_undefined = true }); }; diff --git a/src/browser/webapi/storage/CookieStore.zig b/src/browser/webapi/storage/CookieStore.zig index cf365537..983d7acc 100644 --- a/src/browser/webapi/storage/CookieStore.zig +++ b/src/browser/webapi/storage/CookieStore.zig @@ -33,10 +33,6 @@ const Allocator = std.mem.Allocator; const Execution = js.Execution; const String = lp.String; -pub fn registerTypes() []const type { - return &.{ CookieStore, CookieListItem }; -} - // https://developer.mozilla.org/en-US/docs/Web/API/CookieStore const CookieStore = @This(); @@ -259,7 +255,7 @@ pub fn get(_: *CookieStore, input: GetInput, exec: *const Execution) !js.Promise }; if (items.len == 0) { - return local.resolvePromise(@as(?*CookieListItem, null)); + return local.resolvePromise(@as(?CookieListItem, null)); } return local.resolvePromise(items[0]); } @@ -289,7 +285,7 @@ pub fn set(_: *CookieStore, input: SetInput, value: ?[]const u8, exec: *const Ex }, }; - storeCookie(exec, init) catch |err| { + storeCookie(exec, init, false) catch |err| { return local.rejectPromise(.{ .type_error = @errorName(err) }); }; @@ -315,7 +311,7 @@ pub fn delete(_: *CookieStore, input: DeleteInput, exec: *const Execution) !js.P .path = opts.path, .sameSite = .strict, .partitioned = opts.partitioned, - }) catch |err| { + }, true) catch |err| { return local.rejectPromise(.{ .type_error = @errorName(err) }); }; @@ -346,7 +342,7 @@ fn matchCookies( name: ?[]const u8, url: ?[]const u8, first_only: bool, -) ![]*CookieListItem { +) ![]CookieListItem { const session = exec.session; const url_resolved = try resolveQueryUrl(exec, url); @@ -359,7 +355,7 @@ fn matchCookies( session.cookie_jar.removeExpired(null); - var items: std.ArrayList(*CookieListItem) = .empty; + var items: std.ArrayList(CookieListItem) = .empty; for (session.cookie_jar.cookies.items) |*cookie| { // CookieStore exposes only cookies that script would see for the // current document. HttpOnly cookies stay hidden. @@ -368,8 +364,7 @@ fn matchCookies( if (!std.mem.eql(u8, cookie.name, n)) continue; } - const item = try exec.arena.create(CookieListItem); - item.* = .{ + try items.append(exec.call_arena, .{ .name = String.wrap(cookie.name), .value = String.wrap(cookie.value), .domain = if (cookie.domain.len > 0 and cookie.domain[0] == '.') @@ -380,32 +375,73 @@ fn matchCookies( .expires = if (cookie.expires) |e| e * 1000.0 else null, .secure = cookie.secure, .sameSite = switch (cookie.same_site) { - .strict => .strict, - .lax => .lax, - .none => .none, + .strict => "strict", + .lax => "lax", + .none => "none", }, .partitioned = false, - }; - try items.append(exec.call_arena, item); + }); if (first_only) break; } return items.items; } -fn storeCookie(exec: *const Execution, init: CookieInit) !void { +fn storeCookie(exec: *const Execution, init_: CookieInit, is_delete: bool) !void { const session = exec.session; const url = exec.url.*; + var init = init_; + + init.name = std.mem.trim(u8, init.name, " \t"); + init.value = std.mem.trim(u8, init.value, " \t"); + + // delete() may legitimately target a nameless cookie — its value is always empty. + if (!is_delete and init.name.len == 0) { + if (init.value.len == 0) { + return error.InvalidCookieName; + } + if (std.mem.indexOfScalar(u8, init.value, '=') != null) { + return error.InvalidCookieName; + } + } + // Reject inputs the cookie model can't represent. `=` is allowed in - // values but not in names; `;`/CR/LF/NUL break the cookie wire format - // everywhere and so are forbidden in every field. - if (init.name.len == 0) return error.InvalidCookieName; - if (std.mem.indexOfAny(u8, init.name, "=;\r\n\x00") != null) return error.InvalidCookieName; - if (std.mem.indexOfAny(u8, init.value, ";\r\n\x00") != null) return error.InvalidCookieValue; - if (std.mem.indexOfAny(u8, init.path, ";\r\n\x00") != null) return error.InvalidCookiePath; + // values but not in names; `;` and the control characters (U+0000–U+001F, + // U+007F) break the cookie wire format and so are forbidden in both. + if (std.mem.indexOfScalar(u8, init.name, '=') != null) { + return error.InvalidCookieName; + } + if (hasForbiddenChar(init.name)) { + return error.InvalidCookieName; + } + if (hasForbiddenChar(init.value)) { + return error.InvalidCookieValue; + } + + // A path attribute, when given, must be absolute. The Cookie path/domain + // attribute values are also capped at 1024 bytes per spec. + // https://cookiestore.spec.whatwg.org/#cookie-maximum-attribute-value-size + if (init.path.len > 0 and init.path[0] != '/') { + return error.InvalidCookiePath; + } + if (init.path.len > 1024) { + return error.InvalidCookiePath; + } + if (std.mem.indexOfAny(u8, init.path, ";\r\n\x00") != null) { + return error.InvalidCookiePath; + } if (init.domain) |d| { - if (std.mem.indexOfAny(u8, d, ";\r\n\x00") != null) return error.InvalidCookieDomain; + // CookieStore (unlike the HTTP cookie syntax) rejects a leading dot. + if (d.len > 0 and d[0] == '.') { + return error.InvalidCookieDomain; + } + if (d.len > 1024) { + return error.InvalidCookieDomain; + } + if (std.mem.indexOfAny(u8, d, ";\r\n\x00") != null) { + return error.InvalidCookieDomain; + } } const is_https = URL.isSecure(url); @@ -413,18 +449,32 @@ fn storeCookie(exec: *const Execution, init: CookieInit) !void { // marks any cookie written from an HTTPS document as Secure. const secure = is_https or init.sameSite == .none; + // The `__Http-` and `__Host-Http-` prefixes are reserved for HTTP-state + // cookies; the (script) CookieStore API can never set them, on any origin. + if (std.ascii.startsWithIgnoreCase(init.name, "__Http-") or std.ascii.startsWithIgnoreCase(init.name, "__Host-Http-")) { + return error.InvalidPrefixedCookie; + } + // Cookie-name-prefix rules — match Cookie.parse, case-insensitive to // catch impersonation attempts (e.g. "__HoSt-"). // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#name-cookie-name-prefixes if (std.ascii.startsWithIgnoreCase(init.name, "__Host-")) { - if (!is_https) return error.InvalidPrefixedCookie; + if (!is_https) { + return error.InvalidPrefixedCookie; + } if (init.domain) |d| { - if (d.len > 0) return error.InvalidPrefixedCookie; + if (d.len > 0) { + return error.InvalidPrefixedCookie; + } } const effective_path = if (init.path.len > 0) init.path else "/"; - if (!std.mem.eql(u8, effective_path, "/")) return error.InvalidPrefixedCookie; + if (!std.mem.eql(u8, effective_path, "/")) { + return error.InvalidPrefixedCookie; + } } else if (std.ascii.startsWithIgnoreCase(init.name, "__Secure-")) { - if (!is_https) return error.InvalidPrefixedCookie; + if (!is_https) { + return error.InvalidPrefixedCookie; + } } // The errdefer only protects construction failures. Once we `break :blk` @@ -465,6 +515,18 @@ fn storeCookie(exec: *const Execution, init: CookieInit) !void { try session.cookie_jar.add(cookie, std.time.timestamp(), false); } +// Control characters (U+0000–U+001F and U+007F DEL) and `;` cannot appear in +// a cookie name or value. The whitespace chars TAB and SPACE are trimmed +// before this check, so the surviving controls are all genuinely invalid. +fn hasForbiddenChar(s: []const u8) bool { + for (s) |c| { + if (c <= 0x1F or c == 0x7F or c == ';') { + return true; + } + } + return false; +} + pub const JsApi = struct { pub const bridge = js.Bridge(CookieStore); @@ -481,59 +543,21 @@ pub const JsApi = struct { pub const onchange = bridge.accessor(CookieStore.getOnChange, CookieStore.setOnChange, .{}); }; -// CookieListItem: per CookieStore.get / getAll return shape, documented inline on -// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore +// CookieListItem is an plain JavaScript object, not an interface. The bridge +// automatically translate a Zig struct -> JS Object This should _not_ have a +// JsApi. pub const CookieListItem = struct { name: String, - value: String, + // Optional because a deletion change-event reports the removed cookie with + // `value` omitted (serialized as undefined via the `deleted` accessor's + // null_as_undefined). For get/getAll and `changed` items it is always set. + value: ?String, domain: ?String, path: String, expires: ?f64, secure: bool, - sameSite: SameSite, + sameSite: []const u8, partitioned: bool, - - fn getName(self: *const CookieListItem) String { - return self.name; - } - fn getValue(self: *const CookieListItem) String { - return self.value; - } - fn getDomain(self: *const CookieListItem) ?String { - return self.domain; - } - fn getPath(self: *const CookieListItem) String { - return self.path; - } - fn getExpires(self: *const CookieListItem) ?f64 { - return self.expires; - } - fn getSecure(self: *const CookieListItem) bool { - return self.secure; - } - fn getSameSite(self: *const CookieListItem) []const u8 { - return @tagName(self.sameSite); - } - fn getPartitioned(self: *const CookieListItem) bool { - return self.partitioned; - } - - pub const JsApi = struct { - pub const bridge = js.Bridge(CookieListItem); - pub const Meta = struct { - pub const name = "CookieListItem"; - pub const prototype_chain = bridge.prototypeChain(); - pub var class_id: bridge.ClassId = undefined; - }; - pub const name = bridge.accessor(CookieListItem.getName, null, .{}); - pub const value = bridge.accessor(CookieListItem.getValue, null, .{}); - pub const domain = bridge.accessor(CookieListItem.getDomain, null, .{}); - pub const path = bridge.accessor(CookieListItem.getPath, null, .{}); - pub const expires = bridge.accessor(CookieListItem.getExpires, null, .{}); - pub const secure = bridge.accessor(CookieListItem.getSecure, null, .{}); - pub const sameSite = bridge.accessor(CookieListItem.getSameSite, null, .{}); - pub const partitioned = bridge.accessor(CookieListItem.getPartitioned, null, .{}); - }; }; const testing = @import("../../../testing.zig"); diff --git a/src/html5ever/Cargo.lock b/src/html5ever/Cargo.lock index 89a69176..d1646acb 100644 --- a/src/html5ever/Cargo.lock +++ b/src/html5ever/Cargo.lock @@ -30,6 +30,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -61,6 +72,109 @@ dependencies = [ "markup5ever", ] +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "libc" version = "0.2.172" @@ -73,6 +187,7 @@ version = "0.1.0" dependencies = [ "encoding_rs", "html5ever", + "idna", "string_cache", "tikv-jemalloc-ctl", "tikv-jemallocator", @@ -80,6 +195,12 @@ dependencies = [ "xml5ever", ] +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + [[package]] name = "lock_api" version = "0.4.13" @@ -181,6 +302,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + [[package]] name = "precomputed-hash" version = "0.1.1" @@ -198,9 +328,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -258,6 +388,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "string_cache" version = "0.9.0" @@ -294,6 +430,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tendril" version = "0.5.0" @@ -335,6 +482,16 @@ dependencies = [ "tikv-jemalloc-sys", ] +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "typed-arena" version = "2.0.2" @@ -353,6 +510,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "web_atoms" version = "0.2.3" @@ -429,6 +592,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + [[package]] name = "xml5ever" version = "0.39.0" @@ -438,3 +607,80 @@ dependencies = [ "log", "markup5ever", ] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/html5ever/Cargo.toml b/src/html5ever/Cargo.toml index 70c434af..2cc913cf 100644 --- a/src/html5ever/Cargo.toml +++ b/src/html5ever/Cargo.toml @@ -16,6 +16,7 @@ tikv-jemallocator = {version = "0.6.1", features = ["stats"]} tikv-jemalloc-ctl = {version = "0.6.1", features = ["stats"]} xml5ever = "0.39.0" encoding_rs = "0.8" +idna = "1.1.0" [profile.release] lto = true diff --git a/src/html5ever/lib.rs b/src/html5ever/lib.rs index 0fccf9de..709d508c 100644 --- a/src/html5ever/lib.rs +++ b/src/html5ever/lib.rs @@ -18,6 +18,7 @@ mod sink; mod types; +mod url; #[cfg(debug_assertions)] #[global_allocator] @@ -159,8 +160,7 @@ pub extern "C" fn html5ever_parse_document_with_encoding( }; // Parse directly from decoded string - parse_document(sink, Default::default()) - .one(StrTendril::from(decoded.as_ref())); + parse_document(sink, Default::default()).one(StrTendril::from(decoded.as_ref())); } // === Encoding API for TextDecoder === @@ -180,10 +180,7 @@ pub struct EncodingInfo { /// Look up an encoding by its label (case-insensitive, whitespace-trimmed) #[no_mangle] -pub extern "C" fn encoding_for_label( - label: *const c_uchar, - label_len: usize, -) -> EncodingInfo { +pub extern "C" fn encoding_for_label(label: *const c_uchar, label_len: usize) -> EncodingInfo { if label.is_null() || label_len == 0 { return EncodingInfo { found: 0, diff --git a/src/html5ever/url.rs b/src/html5ever/url.rs new file mode 100644 index 00000000..98a83b60 --- /dev/null +++ b/src/html5ever/url.rs @@ -0,0 +1,83 @@ +// Copyright (C) 2023-2026 Lightpanda (Selecy SAS) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// WHATWG "domain to ASCII" backed by the `idna` crate (UTS#46, the same engine +// rust-url/Servo use). Pairs with src/sys/idna.zig. Replaced libidn2, whose +// IDNA-2008 behavior diverged from the spec. Value-in / value-out: a UTF-8 +// host string becomes its punycode form, or an error. + +use std::os::raw::c_uchar; +use std::slice; + +fn str_from(ptr: *const c_uchar, len: usize) -> Option<&'static str> { + // Zig hands empty slices a non-null but dangling pointer, so length must + // be checked before forming a slice from raw parts. + if ptr.is_null() || len == 0 { + return Some(""); + } + let bytes = unsafe { slice::from_raw_parts(ptr, len) }; + std::str::from_utf8(bytes).ok() +} + +// Catch any panic from the IDNA code so it never unwinds across the extern "C" +// boundary and aborts the whole process; a panic becomes error code 1. +fn ffi_guard i32>(f: F) -> i32 { + std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).unwrap_or(1) +} + +/// WHATWG "domain to ASCII" (UTS#46, non-transitional, beStrict=false). Writes +/// a NUL-terminated owned buffer to *out_ptr / *out_len (caller frees with +/// lpurl_free). Returns 0 on success, 1 if `host` is not a valid domain. +#[no_mangle] +pub extern "C" fn lpurl_domain_to_ascii( + host_ptr: *const c_uchar, + host_len: usize, + out_ptr: *mut *mut c_uchar, + out_len: *mut usize, +) -> i32 { + ffi_guard(move || { + let host = match str_from(host_ptr, host_len) { + Some(s) => s, + None => return 1, + }; + let ascii = match idna::domain_to_ascii(host) { + Ok(s) => s, + Err(_) => return 1, + }; + let len = ascii.len(); + let mut bytes = ascii.into_bytes(); + bytes.push(0); + let boxed = bytes.into_boxed_slice(); + unsafe { + *out_ptr = Box::into_raw(boxed) as *mut c_uchar; + *out_len = len; + } + 0 + }) +} + +/// Free a NUL-terminated buffer handed out by lpurl_domain_to_ascii. +#[no_mangle] +pub extern "C" fn lpurl_free(ptr: *mut c_uchar, len: usize) { + if ptr.is_null() { + return; + } + // The buffer included a NUL terminator, so its length is len + 1 and its + // capacity matches exactly (it was a boxed slice). + unsafe { + let slice = std::ptr::slice_from_raw_parts_mut(ptr, len + 1); + drop(Box::from_raw(slice)); + } +} diff --git a/src/sys/idna.zig b/src/sys/idna.zig index 368928e5..39e8c65f 100644 --- a/src/sys/idna.zig +++ b/src/sys/idna.zig @@ -15,12 +15,19 @@ const std = @import("std"); -const c = @cImport({ - @cInclude("idn2.h"); -}); - const Allocator = std.mem.Allocator; -pub const Error = error{Idna} || Allocator.Error; + +// WHATWG "domain to ASCII" lives in the rust-url FFI (src/html5ever/url.rs), +// which uses the UTS#46-conformant `idna` crate — the same engine rust-url +// itself uses. +extern "c" fn lpurl_domain_to_ascii( + host_ptr: [*]const u8, + host_len: usize, + out_ptr: *?[*]u8, + out_len: *usize, +) i32; + +extern "c" fn lpurl_free(ptr: ?[*]u8, len: usize) void; /// True if `host` contains any non-ASCII byte and therefore needs IDNA /// processing. Pure-ASCII hostnames are returned unchanged by `toAscii`, @@ -35,21 +42,16 @@ pub fn needsAscii(host: []const u8) bool { } /// Convert a UTF-8 hostname to its ASCII (Punycode) form per UTS#46 -/// IDNA 2008 with non-transitional processing — the algorithm WHATWG URL -/// invokes as "domain to ASCII". Returns an allocator-owned slice. -pub fn toAscii(allocator: Allocator, host: []const u8) Error![]u8 { - const host_z = try allocator.dupeZ(u8, host); - defer allocator.free(host_z); - - var out_ptr: [*c]u8 = undefined; - const flags: c_int = c.IDN2_NFC_INPUT | c.IDN2_NONTRANSITIONAL; - const rc = c.idn2_to_ascii_8z(host_z.ptr, &out_ptr, flags); - if (rc != c.IDN2_OK) { +/// non-transitional processing — the algorithm WHATWG URL invokes as +/// "domain to ASCII". Returns an allocator-owned slice. +pub fn toAscii(allocator: Allocator, host: []const u8) ![]u8 { + var out_len: usize = 0; + var out_ptr: ?[*]u8 = null; + if (lpurl_domain_to_ascii(host.ptr, host.len, &out_ptr, &out_len) != 0) { return error.Idna; } - defer c.idn2_free(out_ptr); - - return try allocator.dupe(u8, std.mem.span(@as([*:0]const u8, @ptrCast(out_ptr)))); + defer lpurl_free(out_ptr, out_len); + return allocator.dupe(u8, out_ptr.?[0..out_len]); } const testing = @import("../testing.zig"); @@ -74,3 +76,39 @@ test "idna: German sharp s with non-transitional processing" { defer testing.allocator.free(out); try testing.expectString("xn--fa-hia.de", out); } + +test "idna: needsAscii" { + try testing.expectEqual(false, needsAscii("")); + try testing.expectEqual(false, needsAscii("xn--fa-hia.de")); + try testing.expectEqual(true, needsAscii("faß.de")); + try testing.expectEqual(true, needsAscii("\xff")); +} + +test "idna: UTS#46 lowercases ASCII" { + const out = try toAscii(testing.allocator, "EXAMPLE.COM"); + defer testing.allocator.free(out); + try testing.expectString("example.com", out); +} + +test "idna: already-punycode is idempotent" { + const out = try toAscii(testing.allocator, "xn--rksmrgs-5wao1o.se"); + defer testing.allocator.free(out); + try testing.expectString("xn--rksmrgs-5wao1o.se", out); +} + +test "idna: mixed ASCII and non-ASCII labels" { + const out = try toAscii(testing.allocator, "münchen.example.com"); + defer testing.allocator.free(out); + try testing.expectString("xn--mnchen-3ya.example.com", out); +} + +test "idna: multi-label CJK" { + const out = try toAscii(testing.allocator, "日本.jp"); + defer testing.allocator.free(out); + try testing.expectString("xn--wgv71a.jp", out); +} + +test "idna: invalid domain returns error" { + // U+FFFD (REPLACEMENT CHARACTER) is disallowed under UTS#46. + try testing.expectError(error.Idna, toAscii(testing.allocator, "\u{FFFD}.com")); +} diff --git a/vendor/libidn2/config.h b/vendor/libidn2/config.h deleted file mode 100644 index 539177ab..00000000 --- a/vendor/libidn2/config.h +++ /dev/null @@ -1,1915 +0,0 @@ -/* config.h. Generated from config.h.in by configure. */ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Witness that has been included. */ -#define _GL_CONFIG_H_INCLUDED 1 - - -/* Define to the number of bits in type 'ptrdiff_t'. */ -/* #undef BITSIZEOF_PTRDIFF_T */ - -/* Define to the number of bits in type 'sig_atomic_t'. */ -/* #undef BITSIZEOF_SIG_ATOMIC_T */ - -/* Define to the number of bits in type 'size_t'. */ -/* #undef BITSIZEOF_SIZE_T */ - -/* Define to the number of bits in type 'wchar_t'. */ -/* #undef BITSIZEOF_WCHAR_T */ - -/* Define to the number of bits in type 'wint_t'. */ -/* #undef BITSIZEOF_WINT_T */ - -/* Define to 1 if using 'alloca.c'. */ -/* #undef C_ALLOCA */ - -/* Define to 1 if // is a file system root distinct from /. */ -/* #undef DOUBLE_SLASH_IS_DISTINCT_ROOT */ - -/* Define to 1 if translation of program messages to the user's native - language is requested. */ -/* #undef ENABLE_NLS */ - -/* Define this to 1 if F_DUPFD behavior does not match POSIX */ -/* #undef FCNTL_DUPFD_BUGGY */ - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module close shall be considered present. */ -#define GNULIB_CLOSE 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module fscanf shall be considered present. */ -#define GNULIB_FSCANF 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module fstat shall be considered present. */ -#define GNULIB_FSTAT 1 - -/* Define to the directory where to find the localizations of the translation - domain 'gnulib', as a C string. */ -#define GNULIB_LOCALEDIR "/usr/local/share/locale" - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module msvc-nothrow shall be considered present. */ -#define GNULIB_MSVC_NOTHROW 1 - -/* Disable VLA usage in gettext.h. */ -#define GNULIB_NO_VLA 1 - -/* Define to 1 if printf and friends should be labeled with attribute - "__gnu_printf__" instead of "__printf__" */ -/* #undef GNULIB_PRINTF_ATTRIBUTE_FLAVOR_GNU */ - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module scanf shall be considered present. */ -#define GNULIB_SCANF 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module stat shall be considered present. */ -#define GNULIB_STAT 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module strerror shall be considered present. */ -#define GNULIB_STRERROR 1 - -/* Define to 1 when the gnulib module cloexec should be tested. */ -#define GNULIB_TEST_CLOEXEC 1 - -/* Define to 1 when the gnulib module close should be tested. */ -#define GNULIB_TEST_CLOSE 1 - -/* Define to 1 when the gnulib module dup2 should be tested. */ -#define GNULIB_TEST_DUP2 1 - -/* Define to 1 when the gnulib module fcntl should be tested. */ -#define GNULIB_TEST_FCNTL 1 - -/* Define to 1 when the gnulib module fgetc should be tested. */ -#define GNULIB_TEST_FGETC 1 - -/* Define to 1 when the gnulib module fgets should be tested. */ -#define GNULIB_TEST_FGETS 1 - -/* Define to 1 when the gnulib module fprintf should be tested. */ -#define GNULIB_TEST_FPRINTF 1 - -/* Define to 1 when the gnulib module fputc should be tested. */ -#define GNULIB_TEST_FPUTC 1 - -/* Define to 1 when the gnulib module fputs should be tested. */ -#define GNULIB_TEST_FPUTS 1 - -/* Define to 1 when the gnulib module fread should be tested. */ -#define GNULIB_TEST_FREAD 1 - -/* Define to 1 when the gnulib module free-posix should be tested. */ -#define GNULIB_TEST_FREE_POSIX 1 - -/* Define to 1 when the gnulib module fscanf should be tested. */ -#define GNULIB_TEST_FSCANF 1 - -/* Define to 1 when the gnulib module fstat should be tested. */ -#define GNULIB_TEST_FSTAT 1 - -/* Define to 1 when the gnulib module fwrite should be tested. */ -#define GNULIB_TEST_FWRITE 1 - -/* Define to 1 when the gnulib module getc should be tested. */ -#define GNULIB_TEST_GETC 1 - -/* Define to 1 when the gnulib module getchar should be tested. */ -#define GNULIB_TEST_GETCHAR 1 - -/* Define to 1 when the gnulib module getdelim should be tested. */ -#define GNULIB_TEST_GETDELIM 1 - -/* Define to 1 when the gnulib module getdtablesize should be tested. */ -#define GNULIB_TEST_GETDTABLESIZE 1 - -/* Define to 1 when the gnulib module getline should be tested. */ -#define GNULIB_TEST_GETLINE 1 - -/* Define to 1 when the gnulib module getopt-posix should be tested. */ -#define GNULIB_TEST_GETOPT_POSIX 1 - -/* Define to 1 when the gnulib module getprogname should be tested. */ -#define GNULIB_TEST_GETPROGNAME 1 - -/* Define to 1 when the gnulib module open should be tested. */ -#define GNULIB_TEST_OPEN 1 - -/* Define to 1 when the gnulib module printf should be tested. */ -#define GNULIB_TEST_PRINTF 1 - -/* Define to 1 when the gnulib module putc should be tested. */ -#define GNULIB_TEST_PUTC 1 - -/* Define to 1 when the gnulib module putchar should be tested. */ -#define GNULIB_TEST_PUTCHAR 1 - -/* Define to 1 when the gnulib module puts should be tested. */ -#define GNULIB_TEST_PUTS 1 - -/* Define to 1 when the gnulib module rawmemchr should be tested. */ -#define GNULIB_TEST_RAWMEMCHR 1 - -/* Define to 1 when the gnulib module scanf should be tested. */ -#define GNULIB_TEST_SCANF 1 - -/* Define to 1 when the gnulib module stat should be tested. */ -#define GNULIB_TEST_STAT 1 - -/* Define to 1 when the gnulib module strchrnul should be tested. */ -#define GNULIB_TEST_STRCHRNUL 1 - -/* Define to 1 when the gnulib module strerror should be tested. */ -#define GNULIB_TEST_STRERROR 1 - -/* Define to 1 when the gnulib module strverscmp should be tested. */ -#define GNULIB_TEST_STRVERSCMP 1 - -/* Define to 1 when the gnulib module uninorm/u32-normalize should be tested. - */ -#define GNULIB_TEST_UNINORM_U32_NORMALIZE 1 - -/* Define to 1 when the gnulib module vfprintf should be tested. */ -#define GNULIB_TEST_VFPRINTF 1 - -/* Define to 1 when the gnulib module vprintf should be tested. */ -#define GNULIB_TEST_VPRINTF 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module unistr/u32-mbtouc-unsafe shall be considered - present. */ -#define GNULIB_UNISTR_U32_MBTOUC_UNSAFE 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module unistr/u32-uctomb shall be considered present. */ -#define GNULIB_UNISTR_U32_UCTOMB 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module unistr/u8-mbtouc shall be considered present. */ -#define GNULIB_UNISTR_U8_MBTOUC 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module unistr/u8-mbtoucr shall be considered present. */ -#define GNULIB_UNISTR_U8_MBTOUCR 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module unistr/u8-mbtouc-unsafe shall be considered - present. */ -#define GNULIB_UNISTR_U8_MBTOUC_UNSAFE 1 - -/* Define to a C preprocessor expression that evaluates to 1 or 0, depending - whether the gnulib module unistr/u8-uctomb shall be considered present. */ -#define GNULIB_UNISTR_U8_UCTOMB 1 - -/* Define to 1 if you have 'alloca' after including , a header that - may be supplied by this distribution. */ -#define HAVE_ALLOCA 1 - -/* Define to 1 if works. */ -#define HAVE_ALLOCA_H 1 - -/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the - CoreFoundation framework. */ -#define HAVE_CFLOCALECOPYCURRENT 1 - -/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in - the CoreFoundation framework. */ -#define HAVE_CFPREFERENCESCOPYAPPVALUE 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_CRTDEFS_H */ - -/* Define to 1 if bool, true and false work as per C2023. */ -/* #undef HAVE_C_BOOL */ - -/* Define to 1 if the static_assert keyword works. */ -/* #undef HAVE_C_STATIC_ASSERT */ - -/* Define to 1 if C supports variable-length arrays. */ -#define HAVE_C_VARARRAYS 1 - -/* Define if the GNU dcgettext() function is already present or preinstalled. - */ -/* #undef HAVE_DCGETTEXT */ - -/* Define to 1 if you have the declaration of `ecvt', and to 0 if you don't. - */ -#define HAVE_DECL_ECVT 1 - -/* Define to 1 if you have the declaration of `execvpe', and to 0 if you - don't. */ -#define HAVE_DECL_EXECVPE 0 - -/* Define to 1 if you have the declaration of `fcloseall', and to 0 if you - don't. */ -#define HAVE_DECL_FCLOSEALL 0 - -/* Define to 1 if you have the declaration of `fcvt', and to 0 if you don't. - */ -#define HAVE_DECL_FCVT 1 - -/* Define to 1 if you have the declaration of `gcvt', and to 0 if you don't. - */ -#define HAVE_DECL_GCVT 1 - -/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you - don't. */ -#define HAVE_DECL_GETC_UNLOCKED 1 - -/* Define to 1 if you have the declaration of `getdelim', and to 0 if you - don't. */ -#define HAVE_DECL_GETDELIM 1 - -/* Define to 1 if you have the declaration of `getdtablesize', and to 0 if you - don't. */ -#define HAVE_DECL_GETDTABLESIZE 1 - -/* Define to 1 if you have the declaration of `getline', and to 0 if you - don't. */ -#define HAVE_DECL_GETLINE 1 - -/* Define to 1 if you have the declaration of `getw', and to 0 if you don't. - */ -#define HAVE_DECL_GETW 1 - -/* Define to 1 if you have the declaration of `program_invocation_name', and - to 0 if you don't. */ -#define HAVE_DECL_PROGRAM_INVOCATION_NAME 0 - -/* Define to 1 if you have the declaration of `program_invocation_short_name', - and to 0 if you don't. */ -#define HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME 0 - -/* Define to 1 if you have the declaration of `putw', and to 0 if you don't. - */ -#define HAVE_DECL_PUTW 1 - -/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you - don't. */ -#define HAVE_DECL_STRERROR_R 1 - -/* Define to 1 if you have the declaration of `wcsdup', and to 0 if you don't. - */ -#define HAVE_DECL_WCSDUP 1 - -/* Define to 1 if you have the declaration of `__argv', and to 0 if you don't. - */ -/* #undef HAVE_DECL___ARGV */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 - -/* Define to 1 if you have the `error' function. */ -/* #undef HAVE_ERROR */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ERROR_H */ - -/* Define to 1 if you have the `fcntl' function. */ -#define HAVE_FCNTL 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_FEATURES_H */ - -/* Define to 1 if you have the `flockfile' function. */ -#define HAVE_FLOCKFILE 1 - -/* Define if the 'free' function is guaranteed to preserve errno. */ -/* #undef HAVE_FREE_POSIX */ - -/* Define to 1 if you have the `funlockfile' function. */ -#define HAVE_FUNLOCKFILE 1 - -/* Define to 1 if you have the `getdelim' function. */ -#define HAVE_GETDELIM 1 - -/* Define to 1 if you have the `getdtablesize' function. */ -#define HAVE_GETDTABLESIZE 1 - -/* Define to 1 if you have the `getexecname' function. */ -/* #undef HAVE_GETEXECNAME */ - -/* Define to 1 if you have the `getline' function. */ -#define HAVE_GETLINE 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_GETOPT_H 1 - -/* Define to 1 if you have the `getopt_long_only' function. */ -#define HAVE_GETOPT_LONG_ONLY 1 - -/* Define to 1 if you have the `getprogname' function. */ -#define HAVE_GETPROGNAME 1 - -/* Define if the GNU gettext() function is already present or preinstalled. */ -/* #undef HAVE_GETTEXT */ - -/* Define if you have the iconv() function and it works. */ -/* #undef HAVE_ICONV */ - -/* Define to 1 if you have the header file. */ -#define HAVE_ICONV_H 1 - -/* Define to 1 if the compiler supports one of the keywords 'inline', - '__inline__', '__inline' and effectively inlines functions marked as such. - */ -#define HAVE_INLINE 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define if you have and nl_langinfo(CODESET). */ -#define HAVE_LANGINFO_CODESET 1 - -/* Define if you have the libunistring library. */ -/* #undef HAVE_LIBUNISTRING */ - -/* Define to 1 if you have the header file. */ -#define HAVE_LIMITS_H 1 - -/* Define to 1 if the system has the type 'long long int'. */ -#define HAVE_LONG_LONG_INT 1 - -/* Define to 1 if you have the `lstat' function. */ -#define HAVE_LSTAT 1 - -/* Define to 1 if malloc (0) returns nonnull. */ -#define HAVE_MALLOC_0_NONNULL 1 - -/* Define if malloc, realloc, and calloc set errno on allocation failure. */ -#define HAVE_MALLOC_POSIX 1 - -/* Define to 1 if malloc-like functions do not allocate objects larger than - PTRDIFF_MAX bytes. */ -#define HAVE_MALLOC_PTRDIFF 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_MINIX_CONFIG_H */ - -/* Define to 1 on MSVC platforms that have the "invalid parameter handler" - concept. */ -/* #undef HAVE_MSVC_INVALID_PARAMETER_HANDLER */ - -/* Define to 1 if you have the `rawmemchr' function. */ -/* #undef HAVE_RAWMEMCHR */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SDKDDKVER_H */ - -/* Define to 1 if you have the `setdtablesize' function. */ -/* #undef HAVE_SETDTABLESIZE */ - -/* Define to 1 if 'sig_atomic_t' is a signed integer type. */ -/* #undef HAVE_SIGNED_SIG_ATOMIC_T */ - -/* Define to 1 if 'wchar_t' is a signed integer type. */ -/* #undef HAVE_SIGNED_WCHAR_T */ - -/* Define to 1 if 'wint_t' is a signed integer type. */ -/* #undef HAVE_SIGNED_WINT_T */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDBOOL_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDCKDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strchrnul' function. */ -#define HAVE_STRCHRNUL 1 - -/* Define if you have `strerror_r'. */ -#define HAVE_STRERROR_R 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if `st_atimensec' is a member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_ATIMENSEC */ - -/* Define to 1 if `st_atimespec.tv_nsec' is a member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC 1 - -/* Define to 1 if `st_atim.st__tim.tv_nsec' is a member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC */ - -/* Define to 1 if `st_atim.tv_nsec' is a member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC */ - -/* Define to 1 if `st_birthtimensec' is a member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC */ - -/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1 - -/* Define to 1 if `st_birthtim.tv_nsec' is a member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC */ - -/* Define to 1 if you have the `strverscmp' function. */ -/* #undef HAVE_STRVERSCMP */ - -/* Define to 1 if you have the `symlink' function. */ -#define HAVE_SYMLINK 1 - -/* The toolchain supports aliases and .symver. */ -/* #undef HAVE_SYMVER_ALIAS_SUPPORT */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_BITYPES_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_INTTYPES_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_PARAM_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SOCKET_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TIME_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_UNISTRING_WOE32DLL_H */ - -/* Define to 1 if the system has the type 'unsigned long long int'. */ -#define HAVE_UNSIGNED_LONG_LONG_INT 1 - -/* Define if you have a global __progname variable */ -/* #undef HAVE_VAR___PROGNAME */ - -/* Define to 1 or 0, depending whether the compiler supports simple visibility - declarations. */ -#define HAVE_VISIBILITY 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_WCHAR_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WINSOCK2_H */ - -/* Define if you have the 'wint_t' type. */ -#define HAVE_WINT_T 1 - -/* Define to 1 if O_NOATIME works. */ -#define HAVE_WORKING_O_NOATIME 1 - -/* Define to 1 if O_NOFOLLOW works. */ -#define HAVE_WORKING_O_NOFOLLOW 1 - -/* Define to 1 if you have the `_set_invalid_parameter_handler' function. */ -/* #undef HAVE__SET_INVALID_PARAMETER_HANDLER */ - -/* Define to 1 if ctype.h defines __header_inline. */ -#define HAVE___HEADER_INLINE 1 - -/* Please see the Gnulib manual for how to use these macros. - - Suppress extern inline with HP-UX cc, as it appears to be broken; see - . - - Suppress extern inline with Sun C in standards-conformance mode, as it - mishandles inline functions that call each other. E.g., for 'inline void f - (void) { } inline void g (void) { f (); }', c99 incorrectly complains - 'reference to static identifier "f" in extern inline function'. - This bug was observed with Oracle Developer Studio 12.6 - (Sun C 5.15 SunOS_sparc 2017/05/30). - - Suppress extern inline (with or without __attribute__ ((__gnu_inline__))) - on configurations that mistakenly use 'static inline' to implement - functions or macros in standard C headers like . For example, - if isdigit is mistakenly implemented via a static inline function, - a program containing an extern inline function that calls isdigit - may not work since the C standard prohibits extern inline functions - from calling static functions (ISO C 99 section 6.7.4.(3). - This bug is known to occur on: - - OS X 10.8 and earlier; see: - https://lists.gnu.org/r/bug-gnulib/2012-12/msg00023.html - - DragonFly; see - http://muscles.dragonflybsd.org/bulk/clang-master-potential/20141111_102002/logs/ah-tty-0.3.12.log - - FreeBSD; see: - https://lists.gnu.org/r/bug-gnulib/2014-07/msg00104.html - - OS X 10.9 has a macro __header_inline indicating the bug is fixed for C and - for clang but remains for g++; see . - Assume DragonFly and FreeBSD will be similar. - - GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99 - inline semantics, unless -fgnu89-inline is used. It defines a macro - __GNUC_STDC_INLINE__ to indicate this situation or a macro - __GNUC_GNU_INLINE__ to indicate the opposite situation. - GCC 4.2 with -std=c99 or -std=gnu99 implements the GNU C inline - semantics but warns, unless -fgnu89-inline is used: - warning: C99 inline functions are not supported; using GNU89 - warning: to disable this warning use -fgnu89-inline or the gnu_inline function attribute - It defines a macro __GNUC_GNU_INLINE__ to indicate this situation. - */ -#if (((defined __APPLE__ && defined __MACH__) \ - || defined __DragonFly__ || defined __FreeBSD__) \ - && (defined HAVE___HEADER_INLINE \ - ? (defined __cplusplus && defined __GNUC_STDC_INLINE__ \ - && ! defined __clang__) \ - : ((! defined _DONT_USE_CTYPE_INLINE_ \ - && (defined __GNUC__ || defined __cplusplus)) \ - || (defined _FORTIFY_SOURCE && 0 < _FORTIFY_SOURCE \ - && defined __GNUC__ && ! defined __cplusplus)))) -# define _GL_EXTERN_INLINE_STDHEADER_BUG -#endif -#if ((__GNUC__ \ - ? (defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ \ - && !defined __PCC__) \ - : (199901L <= __STDC_VERSION__ \ - && !defined __HP_cc \ - && !defined __PGI \ - && !(defined __SUNPRO_C && __STDC__))) \ - && !defined _GL_EXTERN_INLINE_STDHEADER_BUG) -# define _GL_INLINE inline -# define _GL_EXTERN_INLINE extern inline -# define _GL_EXTERN_INLINE_IN_USE -#elif (2 < __GNUC__ + (7 <= __GNUC_MINOR__) && !defined __STRICT_ANSI__ \ - && !defined __PCC__ \ - && !defined _GL_EXTERN_INLINE_STDHEADER_BUG) -# if defined __GNUC_GNU_INLINE__ && __GNUC_GNU_INLINE__ - /* __gnu_inline__ suppresses a GCC 4.2 diagnostic. */ -# define _GL_INLINE extern inline __attribute__ ((__gnu_inline__)) -# else -# define _GL_INLINE extern inline -# endif -# define _GL_EXTERN_INLINE extern -# define _GL_EXTERN_INLINE_IN_USE -#else -# define _GL_INLINE _GL_UNUSED static -# define _GL_EXTERN_INLINE _GL_UNUSED static -#endif - -/* In GCC 4.6 (inclusive) to 5.1 (exclusive), - suppress bogus "no previous prototype for 'FOO'" - and "no previous declaration for 'FOO'" diagnostics, - when FOO is an inline function in the header; see - and - . */ -#if __GNUC__ == 4 && 6 <= __GNUC_MINOR__ -# if defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ -# define _GL_INLINE_HEADER_CONST_PRAGMA -# else -# define _GL_INLINE_HEADER_CONST_PRAGMA \ - _Pragma ("GCC diagnostic ignored \"-Wsuggest-attribute=const\"") -# endif -# define _GL_INLINE_HEADER_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wmissing-prototypes\"") \ - _Pragma ("GCC diagnostic ignored \"-Wmissing-declarations\"") \ - _GL_INLINE_HEADER_CONST_PRAGMA -# define _GL_INLINE_HEADER_END \ - _Pragma ("GCC diagnostic pop") -#else -# define _GL_INLINE_HEADER_BEGIN -# define _GL_INLINE_HEADER_END -#endif - -/* Define to 1 if the compiler supports the keyword '__inline'. */ -#define HAVE___INLINE 1 - -/* Define as const if the declaration of iconv() needs const. */ -#define ICONV_CONST - -/* Define to a symbolic name denoting the flavor of iconv_open() - implementation. */ -/* #undef ICONV_FLAVOR */ - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -#define LT_OBJDIR ".libs/" - -/* Use GNU style printf and scanf. */ -#ifndef __USE_MINGW_ANSI_STDIO -# define __USE_MINGW_ANSI_STDIO 1 -#endif - - -/* Define to 1 on musl libc. */ -/* #undef MUSL_LIBC */ - -/* Define to 1 if open() fails to recognize a trailing slash. */ -/* #undef OPEN_TRAILING_SLASH_BUG */ - -/* Name of package */ -#define PACKAGE "libidn2" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "help-libidn@gnu.org" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "Libidn2" - -/* String identifying the packager of this software */ -/* #undef PACKAGE_PACKAGER */ - -/* Packager info for bug reports (URL/e-mail/...) */ -/* #undef PACKAGE_PACKAGER_BUG_REPORTS */ - -/* Packager-specific version information */ -/* #undef PACKAGE_PACKAGER_VERSION */ - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "Libidn2 2.3.8" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "libidn2" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "https://www.gnu.org/software/libidn/#libidn2" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "2.3.8" - -/* Define to the type that is the result of default argument promotions of - type mode_t. */ -#define PROMOTED_MODE_T int - -/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type - 'ptrdiff_t'. */ -/* #undef PTRDIFF_T_SUFFIX */ - -/* Define to 1 if stat needs help when passed a file name with a trailing - slash */ -/* #undef REPLACE_FUNC_STAT_FILE */ - -/* Define to 1 if strerror(0) does not return a message implying success. */ -#define REPLACE_STRERROR_0 1 - -/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type - 'sig_atomic_t'. */ -/* #undef SIG_ATOMIC_T_SUFFIX */ - -/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type - 'size_t'. */ -/* #undef SIZE_T_SUFFIX */ - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at runtime. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ -/* #undef STACK_DIRECTION */ - -/* Define to 1 if the `S_IS*' macros in do not work properly. */ -/* #undef STAT_MACROS_BROKEN */ - -/* Define to 1 if all of the C90 standard headers exist (not just the ones - required in a freestanding environment). This macro is provided for - backward compatibility; new code need not use it. */ -#define STDC_HEADERS 1 - -/* Define to 1 if strerror_r returns char *. */ -/* #undef STRERROR_R_CHAR_P */ - -/* Define to 1 if the type of the st_atim member of a struct stat is struct - timespec. */ -/* #undef TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC */ - -/* Define to enable the declarations of ISO C 23 Annex K types and functions. */ -#if !(defined __STDC_WANT_LIB_EXT1__ && __STDC_WANT_LIB_EXT1__) -#undef/**/__STDC_WANT_LIB_EXT1__ -#define __STDC_WANT_LIB_EXT1__ 1 -#endif - - -/* Enable extensions on AIX 3, Interix. */ -#ifndef _ALL_SOURCE -# define _ALL_SOURCE 1 -#endif -/* Enable general extensions on macOS. */ -#ifndef _DARWIN_C_SOURCE -# define _DARWIN_C_SOURCE 1 -#endif -/* Enable general extensions on Solaris. */ -#ifndef __EXTENSIONS__ -# define __EXTENSIONS__ 1 -#endif -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif -/* Enable X/Open compliant socket functions that do not require linking - with -lxnet on HP-UX 11.11. */ -#ifndef _HPUX_ALT_XOPEN_SOCKET_API -# define _HPUX_ALT_XOPEN_SOCKET_API 1 -#endif -/* Identify the host operating system as Minix. - This macro does not affect the system headers' behavior. - A future release of Autoconf may stop defining this macro. */ -#ifndef _MINIX -/* # undef _MINIX */ -#endif -/* Enable general extensions on NetBSD. - Enable NetBSD compatibility extensions on Minix. */ -#ifndef _NETBSD_SOURCE -# define _NETBSD_SOURCE 1 -#endif -/* Enable OpenBSD compatibility extensions on NetBSD. - Oddly enough, this does nothing on OpenBSD. */ -#ifndef _OPENBSD_SOURCE -# define _OPENBSD_SOURCE 1 -#endif -/* Define to 1 if needed for POSIX-compatible behavior. */ -#ifndef _POSIX_SOURCE -/* # undef _POSIX_SOURCE */ -#endif -/* Define to 2 if needed for POSIX-compatible behavior. */ -#ifndef _POSIX_1_SOURCE -/* # undef _POSIX_1_SOURCE */ -#endif -/* Enable POSIX-compatible threading on Solaris. */ -#ifndef _POSIX_PTHREAD_SEMANTICS -# define _POSIX_PTHREAD_SEMANTICS 1 -#endif -/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ -#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ -# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1 -#endif -/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ -#ifndef __STDC_WANT_IEC_60559_BFP_EXT__ -# define __STDC_WANT_IEC_60559_BFP_EXT__ 1 -#endif -/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ -#ifndef __STDC_WANT_IEC_60559_DFP_EXT__ -# define __STDC_WANT_IEC_60559_DFP_EXT__ 1 -#endif -/* Enable extensions specified by C23 Annex F. */ -#ifndef __STDC_WANT_IEC_60559_EXT__ -# define __STDC_WANT_IEC_60559_EXT__ 1 -#endif -/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ -#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ -# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1 -#endif -/* Enable extensions specified by C23 Annex H and ISO/IEC TS 18661-3:2015. */ -#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ -# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1 -#endif -/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ -#ifndef __STDC_WANT_LIB_EXT2__ -# define __STDC_WANT_LIB_EXT2__ 1 -#endif -/* Enable extensions specified by ISO/IEC 24747:2009. */ -#ifndef __STDC_WANT_MATH_SPEC_FUNCS__ -# define __STDC_WANT_MATH_SPEC_FUNCS__ 1 -#endif -/* Enable extensions on HP NonStop. */ -#ifndef _TANDEM_SOURCE -# define _TANDEM_SOURCE 1 -#endif -/* Enable X/Open extensions. Define to 500 only if necessary - to make mbstate_t available. */ -#ifndef _XOPEN_SOURCE -/* # undef _XOPEN_SOURCE */ -#endif - - -/* Version number of package */ -#define VERSION "2.3.8" - -/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type - 'wchar_t'. */ -/* #undef WCHAR_T_SUFFIX */ - -/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type - 'wint_t'. */ -/* #undef WINT_T_SUFFIX */ - -/* Number of bits in a file offset, on hosts where this is settable. */ -/* #undef _FILE_OFFSET_BITS */ - -/* True if the compiler says it groks GNU C version MAJOR.MINOR. - Except that - - clang groks GNU C 4.2, even on Windows, where it does not define - __GNUC__. - - The OpenMandriva-modified clang compiler pretends that it groks - GNU C version 13.1, but it doesn't: It does not support - __attribute__ ((__malloc__ (f, i))), nor does it support - __attribute__ ((__warning__ (message))) on a function redeclaration. - - Users can make clang lie as well, through the -fgnuc-version option. */ -#if defined __GNUC__ && defined __GNUC_MINOR__ && !defined __clang__ -# define _GL_GNUC_PREREQ(major, minor) \ - ((major) < __GNUC__ + ((minor) <= __GNUC_MINOR__)) -#elif defined __clang__ - /* clang really only groks GNU C 4.2. */ -# define _GL_GNUC_PREREQ(major, minor) \ - ((major) < 4 + ((minor) <= 2)) -#else -# define _GL_GNUC_PREREQ(major, minor) 0 -#endif - - -/* Define to enable the declarations of ISO C 11 types and functions. */ -/* #undef _ISOC11_SOURCE */ - -/* Define to 1 on platforms where this makes off_t a 64-bit type. */ -/* #undef _LARGE_FILES */ - -/* Define so that AIX headers are more compatible with GNU/Linux. */ -#define _LINUX_SOURCE_COMPAT 1 - -/* The _Noreturn keyword of C11. */ -#ifndef _Noreturn -# if (defined __cplusplus \ - && ((201103 <= __cplusplus && !(__GNUC__ == 4 && __GNUC_MINOR__ == 7)) \ - || (defined _MSC_VER && 1900 <= _MSC_VER)) \ - && 0) - /* [[noreturn]] is not practically usable, because with it the syntax - extern _Noreturn void func (...); - would not be valid; such a declaration would only be valid with 'extern' - and '_Noreturn' swapped, or without the 'extern' keyword. However, some - AIX system header files and several gnulib header files use precisely - this syntax with 'extern'. */ -# define _Noreturn [[noreturn]] -# elif (defined __clang__ && __clang_major__ < 16 \ - && defined _GL_WORK_AROUND_LLVM_BUG_59792) - /* Compile with -D_GL_WORK_AROUND_LLVM_BUG_59792 to work around - that rare LLVM bug, though you may get many false-alarm warnings. */ -# define _Noreturn -# elif ((!defined __cplusplus || defined __clang__) \ - && (201112 <= (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) \ - || (!defined __STRICT_ANSI__ \ - && (_GL_GNUC_PREREQ (4, 7) \ - || (defined __apple_build_version__ \ - ? 6000000 <= __apple_build_version__ \ - : 3 < __clang_major__ + (5 <= __clang_minor__)))))) - /* _Noreturn works as-is. */ -# elif _GL_GNUC_PREREQ (2, 8) || defined __clang__ || 0x5110 <= __SUNPRO_C -# define _Noreturn __attribute__ ((__noreturn__)) -# elif 1200 <= (defined _MSC_VER ? _MSC_VER : 0) -# define _Noreturn __declspec (noreturn) -# else -# define _Noreturn -# endif -#endif - - -/* Number of bits in time_t, on hosts where this is settable. */ -/* #undef _TIME_BITS */ - -/* For standard stat data types on VMS. */ -#define _USE_STD_STAT 1 - -/* Define to rpl_ if the getopt replacement functions and variables should be - used. */ -#define __GETOPT_PREFIX rpl_ - -/* Define to 1 on platforms where this makes time_t a 64-bit type. */ -/* #undef __MINGW_USE_VC2005_COMPAT */ - -/* Define to 1 if the system predates C++11. */ -/* #undef __STDC_CONSTANT_MACROS */ - -/* Define to 1 if the system predates C++11. */ -/* #undef __STDC_LIMIT_MACROS */ - -/* Define to 1 if C does not support variable-length arrays, and if the - compiler does not already define this. */ -/* #undef __STDC_NO_VLA__ */ - -/* The _GL_ASYNC_SAFE marker should be attached to functions that are - signal handlers (for signals other than SIGABRT, SIGPIPE) or can be - invoked from such signal handlers. Such functions have some restrictions: - * All functions that it calls should be marked _GL_ASYNC_SAFE as well, - or should be listed as async-signal-safe in POSIX - - section 2.4.3. Note that malloc(), sprintf(), and fwrite(), in - particular, are NOT async-signal-safe. - * All memory locations (variables and struct fields) that these functions - access must be marked 'volatile'. This holds for both read and write - accesses. Otherwise the compiler might optimize away stores to and - reads from such locations that occur in the program, depending on its - data flow analysis. For example, when the program contains a loop - that is intended to inspect a variable set from within a signal handler - while (!signal_occurred) - ; - the compiler is allowed to transform this into an endless loop if the - variable 'signal_occurred' is not declared 'volatile'. - Additionally, recall that: - * A signal handler should not modify errno (except if it is a handler - for a fatal signal and ends by raising the same signal again, thus - provoking the termination of the process). If it invokes a function - that may clobber errno, it needs to save and restore the value of - errno. */ -#define _GL_ASYNC_SAFE - - -/* Attributes. */ -/* Define _GL_HAS_ATTRIBUTE only once, because on FreeBSD, with gcc < 5, if - gets included once again after , __has_attribute(x) - expands to 0 always, and redefining _GL_HAS_ATTRIBUTE would turn off all - attributes. */ -#ifndef _GL_HAS_ATTRIBUTE -# if (defined __has_attribute \ - && (!defined __clang_minor__ \ - || (defined __apple_build_version__ \ - ? 7000000 <= __apple_build_version__ \ - : 5 <= __clang_major__))) -# define _GL_HAS_ATTRIBUTE(attr) __has_attribute (__##attr##__) -# else -# define _GL_HAS_ATTRIBUTE(attr) _GL_ATTR_##attr -# define _GL_ATTR_alloc_size _GL_GNUC_PREREQ (4, 3) -# define _GL_ATTR_always_inline _GL_GNUC_PREREQ (3, 2) -# define _GL_ATTR_artificial _GL_GNUC_PREREQ (4, 3) -# define _GL_ATTR_cold _GL_GNUC_PREREQ (4, 3) -# define _GL_ATTR_const _GL_GNUC_PREREQ (2, 95) -# define _GL_ATTR_deprecated _GL_GNUC_PREREQ (3, 1) -# define _GL_ATTR_diagnose_if 0 -# define _GL_ATTR_error _GL_GNUC_PREREQ (4, 3) -# define _GL_ATTR_externally_visible _GL_GNUC_PREREQ (4, 1) -# define _GL_ATTR_fallthrough _GL_GNUC_PREREQ (7, 0) -# define _GL_ATTR_format _GL_GNUC_PREREQ (2, 7) -# define _GL_ATTR_leaf _GL_GNUC_PREREQ (4, 6) -# define _GL_ATTR_malloc _GL_GNUC_PREREQ (3, 0) -# ifdef _ICC -# define _GL_ATTR_may_alias 0 -# else -# define _GL_ATTR_may_alias _GL_GNUC_PREREQ (3, 3) -# endif -# define _GL_ATTR_noinline _GL_GNUC_PREREQ (3, 1) -# define _GL_ATTR_nonnull _GL_GNUC_PREREQ (3, 3) -# define _GL_ATTR_nonstring _GL_GNUC_PREREQ (8, 0) -# define _GL_ATTR_nothrow _GL_GNUC_PREREQ (3, 3) -# define _GL_ATTR_packed _GL_GNUC_PREREQ (2, 7) -# define _GL_ATTR_pure _GL_GNUC_PREREQ (2, 96) -# define _GL_ATTR_reproducible 0 /* not yet supported, as of GCC 14 */ -# define _GL_ATTR_returns_nonnull _GL_GNUC_PREREQ (4, 9) -# define _GL_ATTR_sentinel _GL_GNUC_PREREQ (4, 0) -# define _GL_ATTR_unsequenced 0 /* not yet supported, as of GCC 14 */ -# define _GL_ATTR_unused _GL_GNUC_PREREQ (2, 7) -# define _GL_ATTR_warn_unused_result _GL_GNUC_PREREQ (3, 4) -# endif -#endif - -/* Use __has_c_attribute if available. However, do not use with - pre-C23 GCC, which can issue false positives if -Wpedantic. */ -#if (defined __has_c_attribute \ - && ! (_GL_GNUC_PREREQ (4, 6) \ - && (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) <= 201710)) -# define _GL_HAVE___HAS_C_ATTRIBUTE 1 -#else -# define _GL_HAVE___HAS_C_ATTRIBUTE 0 -#endif - -/* Attributes in bracket syntax [[...]] vs. attributes in __attribute__((...)) - syntax, in function declarations. There are two problems here. - (Last tested with gcc/g++ 14 and clang/clang++ 18.) - - 1) We want that the _GL_ATTRIBUTE_* can be cumulated on the same declaration - in any order. - =========================== foo.c = foo.cc =========================== - __attribute__ ((__deprecated__)) [[__nodiscard__]] int bar1 (int); - [[__nodiscard__]] __attribute__ ((__deprecated__)) int bar2 (int); - ====================================================================== - This gives a syntax error - - in C mode with gcc - , and - - in C++ mode with clang++ version < 16, and - - in C++ mode, inside extern "C" {}, still in newer clang++ versions - . - */ -/* Define if, in a function declaration, the attributes in bracket syntax - [[...]] must come before the attributes in __attribute__((...)) syntax. - If this is defined, it is best to avoid the bracket syntax, so that the - various _GL_ATTRIBUTE_* can be cumulated on the same declaration in any - order. */ -#ifdef __cplusplus -# if defined __clang__ -# define _GL_BRACKET_BEFORE_ATTRIBUTE 1 -# endif -#else -# if defined __GNUC__ && !defined __clang__ -# define _GL_BRACKET_BEFORE_ATTRIBUTE 1 -# endif -#endif -/* - 2) We want that the _GL_ATTRIBUTE_* can be placed in a declaration - - without 'extern', in C as well as in C++, - - with 'extern', in C, - - with 'extern "C"', in C++ - in the same position. That is, we don't want to be forced to use a - macro which arranges for the attribute to come before 'extern' in - one case and after 'extern' in the other case, because such a macro - would make the source code of .h files pretty ugly. - =========================== foo.c = foo.cc =========================== - #ifdef __cplusplus - # define CC "C" - #else - # define CC - #endif - - #define ND [[__nodiscard__]] - #define WUR __attribute__((__warn_unused_result__)) - - #ifdef __cplusplus - extern "C" { - #endif - // gcc clang g++ clang++ - - ND int foo (int); - int ND foo (int); // warn error warn error - int foo ND (int); - int foo (int) ND; // warn error warn error - - WUR int foo (int); - int WUR foo (int); - int fo1 WUR (int); // error error error error - int foo (int) WUR; - - #ifdef __cplusplus - } - #endif - - // gcc clang g++ clang++ - - ND extern CC int foo (int); // error error - extern CC ND int foo (int); // error error - extern CC int ND foo (int); // warn error warn error - extern CC int foo ND (int); - extern CC int foo (int) ND; // warn error warn error - - WUR extern CC int foo (int); // warn - extern CC WUR int foo (int); - extern CC int WUR foo (int); - extern CC int foo WUR (int); // error error error error - extern CC int foo (int) WUR; - - ND EXTERN_C_FUNC int foo (int); // error error - EXTERN_C_FUNC ND int foo (int); - EXTERN_C_FUNC int ND foo (int); // warn error warn error - EXTERN_C_FUNC int foo ND (int); - EXTERN_C_FUNC int foo (int) ND; // warn error warn error - - WUR EXTERN_C_FUNC int foo (int); // warn - EXTERN_C_FUNC WUR int foo (int); - EXTERN_C_FUNC int WUR foo (int); - EXTERN_C_FUNC int fo2 WUR (int); // error error error error - EXTERN_C_FUNC int foo (int) WUR; - ====================================================================== - So, if we insist on using the 'extern' keyword ('extern CC' idiom): - * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]] - in both C and C++, there is one available position: - - between the function name and the parameter list. - * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax - in both C and C++, there are several available positions: - - before the return type, - - between return type and function name, - - at the end of the declaration. - * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]] in C and to - __attribute__((...)) syntax in C++, there is no available position: - it would need to come before 'extern' in C but after 'extern "C"' - in C++. - * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax in C and - to bracket syntax [[...]] in C++, there is one available position: - - before the return type. - Whereas, if we use the 'EXTERN_C_FUNC' idiom, which conditionally - omits the 'extern' keyword: - * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]] - in both C and C++, there are two available positions: - - before the return type, - - between the function name and the parameter list. - * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax - in both C and C++, there are several available positions: - - before the return type, - - between return type and function name, - - at the end of the declaration. - * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]] in C and to - __attribute__((...)) syntax in C++, there is one available position: - - before the return type. - * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax in C and - to bracket syntax [[...]] in C++, there is one available position: - - before the return type. - The best choice is therefore to use the 'EXTERN_C_FUNC' idiom and - put the attributes before the return type. This works regardless - to what the _GL_ATTRIBUTE_* macros expand. - */ - -/* Attributes in bracket syntax [[...]] vs. attributes in __attribute__((...)) - syntax, in static/inline function definitions. - - There are similar constraints as for function declarations. However, here, - we cannot omit the storage-class specifier. Therefore, the following rule - applies: - * The macros - _GL_ATTRIBUTE_CONST - _GL_ATTRIBUTE_DEPRECATED - _GL_ATTRIBUTE_MAYBE_UNUSED - _GL_ATTRIBUTE_NODISCARD - _GL_ATTRIBUTE_PURE - _GL_ATTRIBUTE_REPRODUCIBLE - _GL_ATTRIBUTE_UNSEQUENCED - which may expand to bracket syntax [[...]], must come first, before the - storage-class specifier. - * Other _GL_ATTRIBUTE_* macros, that expand to __attribute__((...)) syntax, - are better placed between the storage-class specifier and the return - type. - */ - -/* Attributes in bracket syntax [[...]] vs. attributes in __attribute__((...)) - syntax, in variable declarations. - - At which position can they be placed? - (Last tested with gcc/g++ 14 and clang/clang++ 18.) - - =========================== foo.c = foo.cc =========================== - #ifdef __cplusplus - # define CC "C" - #else - # define CC - #endif - - #define BD [[__deprecated__]] - #define AD __attribute__ ((__deprecated__)) - - // gcc clang g++ clang++ - - BD extern CC int var; // error error - extern CC BD int var; // error error - extern CC int BD var; // warn error warn error - extern CC int var BD; - - AD extern CC int var; // warn - extern CC AD int var; - extern CC int AD var; - extern CC int var AD; - - BD extern CC int z[]; // error error - extern CC BD int z[]; // error error - extern CC int BD z[]; // warn error warn error - extern CC int z1 BD []; - extern CC int z[] BD; // warn error error - - AD extern CC int z[]; // warn - extern CC AD int z[]; - extern CC int AD z[]; - extern CC int z2 AD []; // error error error error - extern CC int z[] AD; - ====================================================================== - - * For non-array variables, the only good position is after the variable name, - that is, at the end of the declaration. - * For array variables, you will need to distinguish C and C++: - - In C, before the 'extern' keyword. - - In C++, between the 'extern "C"' and the variable's type. - */ - -/* _GL_ATTRIBUTE_ALLOC_SIZE ((N)) declares that the Nth argument of the function - is the size of the returned memory block. - _GL_ATTRIBUTE_ALLOC_SIZE ((M, N)) declares that the Mth argument multiplied - by the Nth argument of the function is the size of the returned memory block. - */ -/* Applies to: functions, pointer to functions, function types. */ -#ifndef _GL_ATTRIBUTE_ALLOC_SIZE -# if _GL_HAS_ATTRIBUTE (alloc_size) -# define _GL_ATTRIBUTE_ALLOC_SIZE(args) __attribute__ ((__alloc_size__ args)) -# else -# define _GL_ATTRIBUTE_ALLOC_SIZE(args) -# endif -#endif - -/* _GL_ATTRIBUTE_ALWAYS_INLINE tells that the compiler should always inline the - function and report an error if it cannot do so. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_ALWAYS_INLINE -# if _GL_HAS_ATTRIBUTE (always_inline) -# define _GL_ATTRIBUTE_ALWAYS_INLINE __attribute__ ((__always_inline__)) -# else -# define _GL_ATTRIBUTE_ALWAYS_INLINE -# endif -#endif - -/* _GL_ATTRIBUTE_ARTIFICIAL declares that the function is not important to show - in stack traces when debugging. The compiler should omit the function from - stack traces. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_ARTIFICIAL -# if _GL_HAS_ATTRIBUTE (artificial) -# define _GL_ATTRIBUTE_ARTIFICIAL __attribute__ ((__artificial__)) -# else -# define _GL_ATTRIBUTE_ARTIFICIAL -# endif -#endif - -/* _GL_ATTRIBUTE_COLD declares that the function is rarely executed. */ -/* Applies to: functions. */ -/* Avoid __attribute__ ((cold)) on MinGW; see thread starting at - . - Also, Oracle Studio 12.6 requires 'cold' not '__cold__'. */ -#ifndef _GL_ATTRIBUTE_COLD -# if _GL_HAS_ATTRIBUTE (cold) && !defined __MINGW32__ -# ifndef __SUNPRO_C -# define _GL_ATTRIBUTE_COLD __attribute__ ((__cold__)) -# else -# define _GL_ATTRIBUTE_COLD __attribute__ ((cold)) -# endif -# else -# define _GL_ATTRIBUTE_COLD -# endif -#endif - -/* _GL_ATTRIBUTE_CONST declares: - It is OK for a compiler to move calls to the function and to omit - calls to the function if another call has the same arguments or the - result is not used. - This attribute is safe for a function that neither depends on - nor affects state, and always returns exactly once - - e.g., does not raise an exception, call longjmp, or loop forever. - (This attribute is stricter than _GL_ATTRIBUTE_PURE because the - function cannot observe state. It is stricter than - _GL_ATTRIBUTE_UNSEQUENCED because the function must return exactly - once and cannot depend on state addressed by its arguments.) */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_CONST -# if _GL_HAS_ATTRIBUTE (const) -# define _GL_ATTRIBUTE_CONST __attribute__ ((__const__)) -# else -# define _GL_ATTRIBUTE_CONST _GL_ATTRIBUTE_UNSEQUENCED -# endif -#endif - -/* _GL_ATTRIBUTE_DEALLOC (F, I) declares that the function returns pointers - that can be freed by passing them as the Ith argument to the - function F. - _GL_ATTRIBUTE_DEALLOC_FREE declares that the function returns pointers that - can be freed via 'free'; it can be used only after declaring 'free'. */ -/* Applies to: functions. Cannot be used on inline functions. */ -#ifndef _GL_ATTRIBUTE_DEALLOC -# if _GL_GNUC_PREREQ (11, 0) -# define _GL_ATTRIBUTE_DEALLOC(f, i) __attribute__ ((__malloc__ (f, i))) -# else -# define _GL_ATTRIBUTE_DEALLOC(f, i) -# endif -#endif -/* If gnulib's or has already defined this macro, continue - to use this earlier definition, since may not have been included - yet. */ -#ifndef _GL_ATTRIBUTE_DEALLOC_FREE -# if defined __cplusplus && defined __GNUC__ && !defined __clang__ -/* Work around GCC bug */ -# define _GL_ATTRIBUTE_DEALLOC_FREE \ - _GL_ATTRIBUTE_DEALLOC ((void (*) (void *)) free, 1) -# else -# define _GL_ATTRIBUTE_DEALLOC_FREE \ - _GL_ATTRIBUTE_DEALLOC (free, 1) -# endif -#endif - -/* _GL_ATTRIBUTE_DEPRECATED: Declares that an entity is deprecated. - The compiler may warn if the entity is used. */ -/* Applies to: - - function, variable, - - struct, union, struct/union member, - - enumeration, enumeration item, - - typedef, - in C++ also: namespace, class, template specialization. */ -#ifndef _GL_ATTRIBUTE_DEPRECATED -# ifndef _GL_BRACKET_BEFORE_ATTRIBUTE -# if _GL_HAVE___HAS_C_ATTRIBUTE -# if __has_c_attribute (__deprecated__) -# define _GL_ATTRIBUTE_DEPRECATED [[__deprecated__]] -# endif -# endif -# endif -# if !defined _GL_ATTRIBUTE_DEPRECATED && _GL_HAS_ATTRIBUTE (deprecated) -# define _GL_ATTRIBUTE_DEPRECATED __attribute__ ((__deprecated__)) -# endif -# ifndef _GL_ATTRIBUTE_DEPRECATED -# define _GL_ATTRIBUTE_DEPRECATED -# endif -#endif - -/* _GL_ATTRIBUTE_ERROR(msg) requests an error if a function is called and - the function call is not optimized away. - _GL_ATTRIBUTE_WARNING(msg) requests a warning if a function is called and - the function call is not optimized away. */ -/* Applies to: functions. */ -#if !(defined _GL_ATTRIBUTE_ERROR && defined _GL_ATTRIBUTE_WARNING) -# if _GL_HAS_ATTRIBUTE (error) -# define _GL_ATTRIBUTE_ERROR(msg) __attribute__ ((__error__ (msg))) -# define _GL_ATTRIBUTE_WARNING(msg) __attribute__ ((__warning__ (msg))) -# elif _GL_HAS_ATTRIBUTE (diagnose_if) -# define _GL_ATTRIBUTE_ERROR(msg) __attribute__ ((__diagnose_if__ (1, msg, "error"))) -# define _GL_ATTRIBUTE_WARNING(msg) __attribute__ ((__diagnose_if__ (1, msg, "warning"))) -# else -# define _GL_ATTRIBUTE_ERROR(msg) -# define _GL_ATTRIBUTE_WARNING(msg) -# endif -#endif - -/* _GL_ATTRIBUTE_EXTERNALLY_VISIBLE declares that the entity should remain - visible to debuggers etc., even with '-fwhole-program'. */ -/* Applies to: functions, variables. */ -#ifndef _GL_ATTRIBUTE_EXTERNALLY_VISIBLE -# if _GL_HAS_ATTRIBUTE (externally_visible) -# define _GL_ATTRIBUTE_EXTERNALLY_VISIBLE __attribute__ ((externally_visible)) -# else -# define _GL_ATTRIBUTE_EXTERNALLY_VISIBLE -# endif -#endif - -/* _GL_ATTRIBUTE_FALLTHROUGH declares that it is not a programming mistake if - the control flow falls through to the immediately following 'case' or - 'default' label. The compiler should not warn in this case. */ -/* Applies to: Empty statement (;), inside a 'switch' statement. */ -/* Always expands to something. */ -#ifndef _GL_ATTRIBUTE_FALLTHROUGH -# if _GL_HAVE___HAS_C_ATTRIBUTE -# if __has_c_attribute (__fallthrough__) -# define _GL_ATTRIBUTE_FALLTHROUGH [[__fallthrough__]] -# endif -# endif -# if !defined _GL_ATTRIBUTE_FALLTHROUGH && _GL_HAS_ATTRIBUTE (fallthrough) -# define _GL_ATTRIBUTE_FALLTHROUGH __attribute__ ((__fallthrough__)) -# endif -# ifndef _GL_ATTRIBUTE_FALLTHROUGH -# define _GL_ATTRIBUTE_FALLTHROUGH ((void) 0) -# endif -#endif - -/* _GL_ATTRIBUTE_FORMAT ((ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK)) - declares that the STRING-INDEXth function argument is a format string of - style ARCHETYPE, which is one of: - printf, gnu_printf - scanf, gnu_scanf, - strftime, gnu_strftime, - strfmon, - or the same thing prefixed and suffixed with '__'. - If FIRST-TO-CHECK is not 0, arguments starting at FIRST-TO_CHECK - are suitable for the format string. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_FORMAT -# if _GL_HAS_ATTRIBUTE (format) -# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) -# else -# define _GL_ATTRIBUTE_FORMAT(spec) -# endif -#endif - -/* _GL_ATTRIBUTE_LEAF declares that if the function is called from some other - compilation unit, it executes code from that unit only by return or by - exception handling. This declaration lets the compiler optimize that unit - more aggressively. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_LEAF -# if _GL_HAS_ATTRIBUTE (leaf) -# define _GL_ATTRIBUTE_LEAF __attribute__ ((__leaf__)) -# else -# define _GL_ATTRIBUTE_LEAF -# endif -#endif - -/* _GL_ATTRIBUTE_MALLOC declares that the function returns a pointer to freshly - allocated memory. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_MALLOC -# if _GL_HAS_ATTRIBUTE (malloc) -# define _GL_ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) -# else -# define _GL_ATTRIBUTE_MALLOC -# endif -#endif - -/* _GL_ATTRIBUTE_MAY_ALIAS declares that pointers to the type may point to the - same storage as pointers to other types. Thus this declaration disables - strict aliasing optimization. */ -/* Applies to: types. */ -/* Oracle Studio 12.6 mishandles may_alias despite __has_attribute OK. */ -#ifndef _GL_ATTRIBUTE_MAY_ALIAS -# if _GL_HAS_ATTRIBUTE (may_alias) && !defined __SUNPRO_C -# define _GL_ATTRIBUTE_MAY_ALIAS __attribute__ ((__may_alias__)) -# else -# define _GL_ATTRIBUTE_MAY_ALIAS -# endif -#endif - -/* _GL_ATTRIBUTE_MAYBE_UNUSED declares that it is not a programming mistake if - the entity is not used. The compiler should not warn if the entity is not - used. */ -/* Applies to: - - function, variable, - - struct, union, struct/union member, - - enumeration, enumeration item, - - typedef, - in C++ also: class. */ -/* In C++ and C23, this is spelled [[__maybe_unused__]]. - GCC's syntax is __attribute__ ((__unused__)). - clang supports both syntaxes. Except that with clang ≥ 6, < 10, in C++ mode, - __has_c_attribute (__maybe_unused__) yields true but the use of - [[__maybe_unused__]] nevertheless produces a warning. */ -#ifndef _GL_ATTRIBUTE_MAYBE_UNUSED -# ifndef _GL_BRACKET_BEFORE_ATTRIBUTE -# if defined __clang__ && defined __cplusplus -# if !defined __apple_build_version__ && __clang_major__ >= 10 -# define _GL_ATTRIBUTE_MAYBE_UNUSED [[__maybe_unused__]] -# endif -# elif _GL_HAVE___HAS_C_ATTRIBUTE -# if __has_c_attribute (__maybe_unused__) -# define _GL_ATTRIBUTE_MAYBE_UNUSED [[__maybe_unused__]] -# endif -# endif -# endif -# ifndef _GL_ATTRIBUTE_MAYBE_UNUSED -# define _GL_ATTRIBUTE_MAYBE_UNUSED _GL_ATTRIBUTE_UNUSED -# endif -#endif -/* Alternative spelling of this macro, for convenience and for - compatibility with glibc/include/libc-symbols.h. */ -#define _GL_UNUSED _GL_ATTRIBUTE_MAYBE_UNUSED -/* Earlier spellings of this macro. */ -#define _UNUSED_PARAMETER_ _GL_ATTRIBUTE_MAYBE_UNUSED - -/* _GL_ATTRIBUTE_NODISCARD declares that the caller of the function should not - discard the return value. The compiler may warn if the caller does not use - the return value, unless the caller uses something like ignore_value. */ -/* Applies to: function, enumeration, class. */ -#ifndef _GL_ATTRIBUTE_NODISCARD -# ifndef _GL_BRACKET_BEFORE_ATTRIBUTE -# if defined __clang__ && defined __cplusplus - /* With clang up to 15.0.6 (at least), in C++ mode, [[__nodiscard__]] produces - a warning. - The 1000 below means a yet unknown threshold. When clang++ version X - starts supporting [[__nodiscard__]] without warning about it, you can - replace the 1000 with X. */ -# if __clang_major__ >= 1000 -# define _GL_ATTRIBUTE_NODISCARD [[__nodiscard__]] -# endif -# elif _GL_HAVE___HAS_C_ATTRIBUTE -# if __has_c_attribute (__nodiscard__) -# define _GL_ATTRIBUTE_NODISCARD [[__nodiscard__]] -# endif -# endif -# endif -# if !defined _GL_ATTRIBUTE_NODISCARD && _GL_HAS_ATTRIBUTE (warn_unused_result) -# define _GL_ATTRIBUTE_NODISCARD __attribute__ ((__warn_unused_result__)) -# endif -# ifndef _GL_ATTRIBUTE_NODISCARD -# define _GL_ATTRIBUTE_NODISCARD -# endif -#endif - -/* _GL_ATTRIBUTE_NOINLINE tells that the compiler should not inline the - function. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_NOINLINE -# if _GL_HAS_ATTRIBUTE (noinline) -# define _GL_ATTRIBUTE_NOINLINE __attribute__ ((__noinline__)) -# else -# define _GL_ATTRIBUTE_NOINLINE -# endif -#endif - -/* _GL_ATTRIBUTE_NONNULL ((N1, N2,...)) declares that the arguments N1, N2,... - must not be NULL. - _GL_ATTRIBUTE_NONNULL () declares that all pointer arguments must not be - null. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_NONNULL -# if _GL_HAS_ATTRIBUTE (nonnull) -# define _GL_ATTRIBUTE_NONNULL(args) __attribute__ ((__nonnull__ args)) -# else -# define _GL_ATTRIBUTE_NONNULL(args) -# endif -#endif - -/* _GL_ATTRIBUTE_NONSTRING declares that the contents of a character array is - not meant to be NUL-terminated. */ -/* Applies to: struct/union members and variables that are arrays of element - type '[[un]signed] char'. */ -#ifndef _GL_ATTRIBUTE_NONSTRING -# if _GL_HAS_ATTRIBUTE (nonstring) -# define _GL_ATTRIBUTE_NONSTRING __attribute__ ((__nonstring__)) -# else -# define _GL_ATTRIBUTE_NONSTRING -# endif -#endif - -/* There is no _GL_ATTRIBUTE_NORETURN; use _Noreturn instead. */ - -/* _GL_ATTRIBUTE_NOTHROW declares that the function does not throw exceptions. - */ -/* Applies to: functions. */ -/* After a function's parameter list, this attribute must come first, before - other attributes. */ -#ifndef _GL_ATTRIBUTE_NOTHROW -# if defined __cplusplus -# if _GL_GNUC_PREREQ (2, 8) || __clang_major__ >= 4 -# if __cplusplus >= 201103L -# define _GL_ATTRIBUTE_NOTHROW noexcept (true) -# else -# define _GL_ATTRIBUTE_NOTHROW throw () -# endif -# else -# define _GL_ATTRIBUTE_NOTHROW -# endif -# else -# if _GL_HAS_ATTRIBUTE (nothrow) -# define _GL_ATTRIBUTE_NOTHROW __attribute__ ((__nothrow__)) -# else -# define _GL_ATTRIBUTE_NOTHROW -# endif -# endif -#endif - -/* _GL_ATTRIBUTE_PACKED declares: - For struct members: The member has the smallest possible alignment. - For struct, union, class: All members have the smallest possible alignment, - minimizing the memory required. */ -/* Applies to: struct members, struct, union, - in C++ also: class. */ -#ifndef _GL_ATTRIBUTE_PACKED -/* Oracle Studio 12.6 miscompiles code with __attribute__ ((__packed__)) despite - __has_attribute OK. */ -# if _GL_HAS_ATTRIBUTE (packed) && !defined __SUNPRO_C -# define _GL_ATTRIBUTE_PACKED __attribute__ ((__packed__)) -# else -# define _GL_ATTRIBUTE_PACKED -# endif -#endif - -/* _GL_ATTRIBUTE_PURE declares: - It is OK for a compiler to move calls to the function and to omit - calls to the function if another call has the same arguments or the - result is not used, and if observable state is the same. - This attribute is safe for a function that does not affect observable state - and always returns exactly once. - (This attribute is looser than _GL_ATTRIBUTE_CONST because the function - can depend on observable state. It is stricter than - _GL_ATTRIBUTE_REPRODUCIBLE because the function must return exactly - once and cannot affect state addressed by its arguments.) */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_PURE -# if _GL_HAS_ATTRIBUTE (pure) -# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__)) -# else -# define _GL_ATTRIBUTE_PURE _GL_ATTRIBUTE_REPRODUCIBLE -# endif -#endif - -/* _GL_ATTRIBUTE_REPRODUCIBLE declares: - It is OK for a compiler to move calls to the function and to omit duplicate - calls to the function with the same arguments, so long as the state - addressed by its arguments is the same and is updated in time for - the rest of the program. - This attribute is safe for a function that is effectless and idempotent; see - ISO C 23 § 6.7.12.7 for a definition of these terms. - (This attribute is looser than _GL_ATTRIBUTE_UNSEQUENCED because - the function need not be stateless and idempotent. It is looser - than _GL_ATTRIBUTE_PURE because the function need not return - exactly once and can affect state addressed by its arguments.) - See also and - . - ATTENTION! Efforts are underway to change the meaning of this attribute. - See . */ -/* Applies to: functions, pointer to functions, function types. */ -#ifndef _GL_ATTRIBUTE_REPRODUCIBLE -/* This may be revisited when gcc and clang support [[reproducible]] or possibly - __attribute__ ((__reproducible__)). */ -# ifndef _GL_BRACKET_BEFORE_ATTRIBUTE -# if _GL_HAS_ATTRIBUTE (reproducible) -# define _GL_ATTRIBUTE_REPRODUCIBLE [[reproducible]] -# endif -# endif -# ifndef _GL_ATTRIBUTE_REPRODUCIBLE -# define _GL_ATTRIBUTE_REPRODUCIBLE -# endif -#endif - -/* _GL_ATTRIBUTE_RETURNS_NONNULL declares that the function's return value is - a non-NULL pointer. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_RETURNS_NONNULL -# if _GL_HAS_ATTRIBUTE (returns_nonnull) -# define _GL_ATTRIBUTE_RETURNS_NONNULL __attribute__ ((__returns_nonnull__)) -# else -# define _GL_ATTRIBUTE_RETURNS_NONNULL -# endif -#endif - -/* _GL_ATTRIBUTE_SENTINEL(pos) declares that the variadic function expects a - trailing NULL argument. - _GL_ATTRIBUTE_SENTINEL () - The last argument is NULL (requires C99). - _GL_ATTRIBUTE_SENTINEL ((N)) - The (N+1)st argument from the end is NULL. */ -/* Applies to: functions. */ -#ifndef _GL_ATTRIBUTE_SENTINEL -# if _GL_HAS_ATTRIBUTE (sentinel) -# define _GL_ATTRIBUTE_SENTINEL(pos) __attribute__ ((__sentinel__ pos)) -# else -# define _GL_ATTRIBUTE_SENTINEL(pos) -# endif -#endif - -/* _GL_ATTRIBUTE_UNSEQUENCED declares: - It is OK for a compiler to move calls to the function and to omit duplicate - calls to the function with the same arguments, so long as the state - addressed by its arguments is the same. - This attribute is safe for a function that is effectless, idempotent, - stateless, and independent; see ISO C 23 § 6.7.12.7 for a definition of - these terms. - (This attribute is stricter than _GL_ATTRIBUTE_REPRODUCIBLE because - the function must be stateless and independent. It is looser than - _GL_ATTRIBUTE_CONST because the function need not return exactly - once and can depend on state addressed by its arguments.) - See also and - . - ATTENTION! Efforts are underway to change the meaning of this attribute. - See . */ -/* Applies to: functions, pointer to functions, function types. */ -#ifndef _GL_ATTRIBUTE_UNSEQUENCED -/* This may be revisited when gcc and clang support [[unsequenced]] or possibly - __attribute__ ((__unsequenced__)). */ -# ifndef _GL_BRACKET_BEFORE_ATTRIBUTE -# if _GL_HAS_ATTRIBUTE (unsequenced) -# define _GL_ATTRIBUTE_UNSEQUENCED [[unsequenced]] -# endif -# endif -# ifndef _GL_ATTRIBUTE_UNSEQUENCED -# define _GL_ATTRIBUTE_UNSEQUENCED -# endif -#endif - -/* A helper macro. Don't use it directly. */ -#ifndef _GL_ATTRIBUTE_UNUSED -# if _GL_HAS_ATTRIBUTE (unused) -# define _GL_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -# else -# define _GL_ATTRIBUTE_UNUSED -# endif -#endif - - -/* _GL_UNUSED_LABEL; declares that it is not a programming mistake if the - immediately preceding label is not used. The compiler should not warn - if the label is not used. */ -/* Applies to: label (both in C and C++). */ -/* Note that g++ < 4.5 does not support the '__attribute__ ((__unused__)) ;' - syntax. But clang does. */ -#ifndef _GL_UNUSED_LABEL -# if !(defined __cplusplus && !_GL_GNUC_PREREQ (4, 5)) || defined __clang__ -# define _GL_UNUSED_LABEL _GL_ATTRIBUTE_UNUSED -# else -# define _GL_UNUSED_LABEL -# endif -#endif - -/* The following attributes enable detection of multithread-safety problems - and resource leaks at compile-time, by clang ≥ 15, when the warning option - -Wthread-safety is enabled. For usage, see - . */ -#ifndef _GL_ATTRIBUTE_CAPABILITY_TYPE -# if __clang_major__ >= 15 -# define _GL_ATTRIBUTE_CAPABILITY_TYPE(concept) \ - __attribute__ ((__capability__ (concept))) -# else -# define _GL_ATTRIBUTE_CAPABILITY_TYPE(concept) -# endif -#endif -#ifndef _GL_ATTRIBUTE_ACQUIRE_CAPABILITY -# if __clang_major__ >= 15 -# define _GL_ATTRIBUTE_ACQUIRE_CAPABILITY(resource) \ - __attribute__ ((__acquire_capability__ (resource))) -# else -# define _GL_ATTRIBUTE_ACQUIRE_CAPABILITY(resource) -# endif -#endif -#ifndef _GL_ATTRIBUTE_RELEASE_CAPABILITY -# if __clang_major__ >= 15 -# define _GL_ATTRIBUTE_RELEASE_CAPABILITY(resource) \ - __attribute__ ((__release_capability__ (resource))) -# else -# define _GL_ATTRIBUTE_RELEASE_CAPABILITY(resource) -# endif -#endif - - -/* In C++, there is the concept of "language linkage", that encompasses - name mangling and function calling conventions. - The following macros start and end a block of "C" linkage. */ -#ifdef __cplusplus -# define _GL_BEGIN_C_LINKAGE extern "C" { -# define _GL_END_C_LINKAGE } -#else -# define _GL_BEGIN_C_LINKAGE -# define _GL_END_C_LINKAGE -#endif - - -/* A replacement for va_copy, if needed. */ -#define gl_va_copy(a,b) ((a) = (b)) - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -/* #undef inline */ -#endif - -/* Work around a bug in Apple GCC 4.0.1 build 5465: In C99 mode, it supports - the ISO C 99 semantics of 'extern inline' (unlike the GNU C semantics of - earlier versions), but does not display it by setting __GNUC_STDC_INLINE__. - __APPLE__ && __MACH__ test for Mac OS X. - __APPLE_CC__ tests for the Apple compiler and its version. - __STDC_VERSION__ tests for the C99 mode. */ -#if defined __APPLE__ && defined __MACH__ && __APPLE_CC__ >= 5465 && !defined __cplusplus && __STDC_VERSION__ >= 199901L && !defined __GNUC_STDC_INLINE__ -# define __GNUC_STDC_INLINE__ 1 -#endif - -/* _GL_CMP (n1, n2) performs a three-valued comparison on n1 vs. n2, where - n1 and n2 are expressions without side effects, that evaluate to real - numbers (excluding NaN). - It returns - 1 if n1 > n2 - 0 if n1 == n2 - -1 if n1 < n2 - The naïve code (n1 > n2 ? 1 : n1 < n2 ? -1 : 0) produces a conditional - jump with nearly all GCC versions up to GCC 10. - This variant (n1 < n2 ? -1 : n1 > n2) produces a conditional with many - GCC versions up to GCC 9. - The better code (n1 > n2) - (n1 < n2) from Hacker's Delight § 2-9 - avoids conditional jumps in all GCC versions >= 3.4. */ -#define _GL_CMP(n1, n2) (((n1) > (n2)) - ((n1) < (n2))) - - -/* Define to `int' if does not define. */ -/* #undef mode_t */ - -/* Define to the type of st_nlink in struct stat, or a supertype. */ -/* #undef nlink_t */ - -/* Define as a signed integer type capable of holding a process identifier. */ -/* #undef pid_t */ - -/* Define to the equivalent of the C99 'restrict' keyword, or to - nothing if this is not supported. Do not define if restrict is - supported only directly. */ -#define restrict __restrict__ -/* Work around a bug in older versions of Sun C++, which did not - #define __restrict__ or support _Restrict or __restrict__ - even though the corresponding Sun C compiler ended up with - "#define restrict _Restrict" or "#define restrict __restrict__" - in the previous line. This workaround can be removed once - we assume Oracle Developer Studio 12.5 (2016) or later. */ -#if defined __SUNPRO_CC && !defined __RESTRICT && !defined __restrict__ -# define _Restrict -# define __restrict__ -#endif - -/* Define to `unsigned int' if does not define. */ -/* #undef size_t */ - -/* Define as a signed type of the same size as size_t. */ -/* #undef ssize_t */ - - - /* This definition is a duplicate of the one in unitypes.h. - It is here so that we can cope with an older version of unitypes.h - that does not contain this definition and that is pre-installed among - the public header files. */ - # if defined __restrict \ - || 2 < __GNUC__ + (95 <= __GNUC_MINOR__) \ - || __clang_major__ >= 3 - # define _UC_RESTRICT __restrict - # elif 199901L <= __STDC_VERSION__ || defined restrict - # define _UC_RESTRICT restrict - # else - # define _UC_RESTRICT - # endif - - -/* Define as a macro for copying va_list variables. */ -/* #undef va_copy */ - -#if !(defined __cplusplus \ - ? 1 \ - : (defined __clang__ \ - ? __STDC_VERSION__ >= 202000L && __clang_major__ >= 15 \ - : (defined __GNUC__ \ - ? __STDC_VERSION__ >= 202000L && __GNUC__ >= 13 \ - : defined HAVE_C_BOOL))) -# if !defined __cplusplus && !defined __bool_true_false_are_defined -# if HAVE_STDBOOL_H -# include -# else -# if defined __SUNPRO_C -# error " is not usable with this configuration. To make it usable, add -D_STDC_C99= to $CC." -# else -# error " does not exist on this platform. Use gnulib module 'stdbool-c99' instead of gnulib module 'stdbool'." -# endif -# endif -# endif -# if !true -# define true (!false) -# endif -#endif - -#if (!(defined __clang__ \ - ? (defined __cplusplus \ - ? __cplusplus >= 201703L \ - : __STDC_VERSION__ >= 202000L && __clang_major__ >= 16 \ - && !defined __sun) \ - : (defined __GNUC__ \ - ? (defined __cplusplus \ - ? __cplusplus >= 201103L && __GNUG__ >= 6 \ - : __STDC_VERSION__ >= 202000L && __GNUC__ >= 13 \ - && !defined __sun) \ - : defined HAVE_C_STATIC_ASSERT)) \ - && !defined assert \ - && (!defined __cplusplus \ - || (__cpp_static_assert < 201411 \ - && __GNUG__ < 6 && __clang_major__ < 6))) - #include - #undef/**/assert - #ifdef __sgi - #undef/**/__ASSERT_H__ - #endif - /* Solaris 11.4 defines static_assert as a macro with 2 arguments. - We need it also to be invocable with a single argument. - Haiku 2022 does not define static_assert at all. */ - #if (__STDC_VERSION__ - 0 >= 201112L) && !defined __cplusplus - #undef/**/static_assert - #define static_assert _Static_assert - #endif -#endif - -/* lightpanda: macOS's does not declare strverscmp (a glibc - extension). gnulib normally declares it via its replacement - shim, which we don't pull in. lib/strverscmp.c provides the definition. */ -#ifndef _LIBIDN2_LP_DECLS -#define _LIBIDN2_LP_DECLS -extern int strverscmp(const char *, const char *); - -/* lightpanda: lib/lookup.c calls strchrnul() without including , - so the prototype must reach it through this header. macOS libc also - lacked the symbol entirely before 15.4 — build.zig::buildLibidn2 adds - vendor/libidn2/darwin/strchrnul.c on Darwin to provide the definition. */ -#ifdef __APPLE__ -extern char *strchrnul(const char *, int); -#endif -#endif diff --git a/vendor/libidn2/darwin/strchrnul.c b/vendor/libidn2/darwin/strchrnul.c deleted file mode 100644 index d2a5c3ca..00000000 --- a/vendor/libidn2/darwin/strchrnul.c +++ /dev/null @@ -1,20 +0,0 @@ -/* Darwin-only strchrnul shim for libidn2. - - strchrnul is a glibc extension. macOS libc lacks it before 15.4, and - libidn2's lib/lookup.c never includes — so even on newer - macOS the declaration would not reach the call site. The matching - prototype is declared next to the strverscmp shim in - vendor/libidn2/config.h (within the _LIBIDN2_LP_DECLS block, gated on - __APPLE__), so callers compile; this file provides the symbol so - they link. - - gnulib's strchrnul.c falls through to rawmemchr() when the search byte - is NUL — also a glibc extension. libidn2 only ever searches for '.', so - a straight byte scan is enough and avoids dragging in a second shim. */ - -char *strchrnul(const char *s, int c_in) { - const unsigned char c = (unsigned char) c_in; - const unsigned char *p = (const unsigned char *) s; - while (*p && *p != c) p++; - return (char *) p; -}