]> git.saurik.com Git - apple/libc.git/commitdiff
Libc-1353.60.8.tar.gz macos-10152 macos-10153 v1353.60.8
authorApple <opensource@apple.com>
Thu, 2 Apr 2020 17:10:42 +0000 (17:10 +0000)
committerApple <opensource@apple.com>
Thu, 2 Apr 2020 17:10:42 +0000 (17:10 +0000)
25 files changed:
Libc.xcodeproj/project.pbxproj
include/struct.h
libdarwin/ctl.c
libdarwin/dirstat.c
libdarwin/h/bsd.h
libdarwin/h/cleanup.h
libdarwin/h/ctl.h
libdarwin/h/err.h
libdarwin/h/errno.h
libdarwin/h/mach_exception.h
libdarwin/h/mach_utils.h
libdarwin/h/stdio.h
libdarwin/h/stdlib.h
libdarwin/h/string.h
libdarwin/internal.h
libdarwin/stdio.c
libdarwin/stdlib.c
libdarwin/string.c
libdarwin/tapi.h [new file with mode: 0644]
os/api.h
os/assumes.h
tests/Makefile
tests/abort_tests.c
tests/darwin_bsd.c
tests/locale.c

index 8576016f7f0e507e09023f7d3ef64b460e49f602..2423e7a49440f67620394460fddddd9891a3b5cf 100644 (file)
                4B2C64A915519BC800342BFA /* assumes.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B2C64A215519BAF00342BFA /* assumes.c */; };
                4B2C64AA15519BCB00342BFA /* assumes.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B2C64A215519BAF00342BFA /* assumes.c */; };
                4B2C64BA1551B03700342BFA /* assumes.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B2C64A215519BAF00342BFA /* assumes.c */; };
+               4B2D551E231706F9003DAFCE /* tapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B2D551D2317040F003DAFCE /* tapi.h */; };
                4B450FFB211A56DD0029AF5D /* ctl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B450FFA211A56DC0029AF5D /* ctl.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4B450FFD211A56EC0029AF5D /* ctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B450FFC211A56EC0029AF5D /* ctl.c */; };
                4B4E643F2069E94A00C4D8D5 /* internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B2C50E41F8453FA005DA2B6 /* internal.h */; };
                4B6D181D206DEFBD00C00E37 /* mach_exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6D181C206DEFBD00C00E37 /* mach_exception.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4B6D181F206DF1E200C00E37 /* exception.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B6D181E206DF1E200C00E37 /* exception.c */; };
                4B782979208926A80070E1FF /* api.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B782978208926A70070E1FF /* api.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               4B8A6F3221C99ACC00D00D67 /* linker_set.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B8A6F3121C99A0E00D00D67 /* linker_set.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4BA6E55F202AB35900F38D3A /* string.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6E55E202AB35900F38D3A /* string.c */; };
                4BA6E562202AC06300F38D3A /* err.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6E561202AC06300F38D3A /* err.c */; };
                4BA6E566202AC94800F38D3A /* stdlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6E565202AC94800F38D3A /* stdlib.c */; };
                4B2C50E41F8453FA005DA2B6 /* internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = internal.h; sourceTree = "<group>"; };
                4B2C64A215519BAF00342BFA /* assumes.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = assumes.c; path = os/assumes.c; sourceTree = "<group>"; };
                4B2C64AB15519C3400342BFA /* assumes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = assumes.h; path = os/assumes.h; sourceTree = "<group>"; };
+               4B2D551D2317040F003DAFCE /* tapi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tapi.h; sourceTree = "<group>"; };
                4B450FFA211A56DC0029AF5D /* ctl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ctl.h; sourceTree = "<group>"; };
                4B450FFC211A56EC0029AF5D /* ctl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ctl.c; sourceTree = "<group>"; };
                4B69E81220800BE9008D13D2 /* libdarwin_init.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libdarwin_init.h; sourceTree = "<group>"; };
                                4B2C50DE1F8453A6005DA2B6 /* h */,
                                9280EA171E59BC8A007A6F58 /* AppleInternalVariant.plist */,
                                4B2C50E41F8453FA005DA2B6 /* internal.h */,
+                               4B2D551D2317040F003DAFCE /* tapi.h */,
                                4BA6E56B202AD02900F38D3A /* bsd.c */,
                                4B450FFC211A56EC0029AF5D /* ctl.c */,
                                4B6D181E206DF1E200C00E37 /* exception.c */,
                                4B0899BC2046258F001360A4 /* cleanup.h in Headers */,
                                4BCC350F20659AD500A4CBAA /* linker_set.h in Headers */,
                                4B20DB53202B81A4005C2327 /* stdlib.h in Headers */,
-                               4B8A6F3221C99ACC00D00D67 /* linker_set.h in Headers */,
                                4B09323421C9C08F006063D6 /* mach_utils.h in Headers */,
+                               4B2D551E231706F9003DAFCE /* tapi.h in Headers */,
                                4B69E81320800D47008D13D2 /* libdarwin_init.h in Headers */,
                                4B20DB54202B81A4005C2327 /* string.h in Headers */,
                                4B075C8E208BE9F200FD4F23 /* variant_private.h in Headers */,
                C9B53597138D9A690028D27C /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
-                               LastUpgradeCheck = 1000;
+                               LastUpgradeCheck = 1140;
                                TargetAttributes = {
                                        925E7FE619E8945900AC7889 = {
                                                CreatedOnToolsVersion = 6.1;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = "/bin/bash -e -x";
-                       shellScript = "mkdir -p ${TAPI_PUBLIC_HEADER_PATH}";
+                       shellScript = "mkdir -p ${TAPI_PUBLIC_HEADER_PATH}\n";
                        showEnvVarsInLog = 0;
                };
                3FF283291A4764370098AD2C /* Simulator Build Compat Symlink */ = {
                                        "$(DERIVED_FILES_DIR)/dtrace",
                                        "$(SRCROOT_SEARCH_PATHS)",
                                        "$(SYSTEM_FRAMEWORK_HEADERS)",
+                                       "$(PROJECT_DIR)/libdarwin",
+                                       "$(PROJECT_DIR)/libdarwin/h",
                                        "$(SDKROOT)/usr/local/include",
                                        "$(inherited)",
-                                       "$(PROJECT_DIR)/libdarwin/h",
                                );
                                LIBRARY_SEARCH_PATHS = /usr/lib/system;
                                LIBSYSTEM_DARWIN_LDFLAGS = "-all_load -nostdlib -L/usr/lib/system -umbrella System $(LIBCOMPILER_RT_LDFLAGS) $(LIBDYLD_LDFLAGS) $(LIBSYSCALL_LDFLAGS) $(LIBM_LDFLAGS) $(LIBMALLOC_LDFLAGS) $(LIBPLATFORM_LDFLAGS) $(LIBPTHREAD_LDFLAGS) $(LIBPLATFORM_LDFLAGS) $(LIBC_LDFLAGS) $(LIBDISPATCH_LDFLAGS) $(LIBXPC_LDFLAGS) -lmacho -ldyld -Wl,-upward-lsystem_trace";
                                PRODUCT_NAME = darwin;
                                SKIP_INSTALL = NO;
                                SUPPORTS_TEXT_BASED_API = YES;
+                               TAPI_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/libdarwin";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Debug;
                                        "$(DERIVED_FILES_DIR)/dtrace",
                                        "$(SRCROOT_SEARCH_PATHS)",
                                        "$(SYSTEM_FRAMEWORK_HEADERS)",
+                                       "$(PROJECT_DIR)/libdarwin",
+                                       "$(PROJECT_DIR)/libdarwin/h",
                                        "$(SDKROOT)/usr/local/include",
                                        "$(inherited)",
-                                       "$(PROJECT_DIR)/libdarwin/h",
                                );
                                LIBRARY_SEARCH_PATHS = /usr/lib/system;
                                LIBSYSTEM_DARWIN_LDFLAGS = "-all_load -nostdlib -L/usr/lib/system -umbrella System $(LIBCOMPILER_RT_LDFLAGS) $(LIBDYLD_LDFLAGS) $(LIBSYSCALL_LDFLAGS) $(LIBM_LDFLAGS) $(LIBMALLOC_LDFLAGS) $(LIBPLATFORM_LDFLAGS) $(LIBPTHREAD_LDFLAGS) $(LIBPLATFORM_LDFLAGS) $(LIBC_LDFLAGS) $(LIBDISPATCH_LDFLAGS) $(LIBXPC_LDFLAGS) -lmacho -ldyld -Wl,-upward-lsystem_trace";
                                OTHER_LDFLAGS = "$(LIBSYSTEM_DARWIN_LDFLAGS)";
-                               OTHER_TAPI_FLAGS = "$(inherited) -extra-public-header $(SRCROOT)/libdarwin/h/dirstat.h -extra-public-header $(SRCROOT)/libdarwin/internal.h -DDARWIN_TAPI=1 -extra-public-header $(SRCROOT)/os/variant_private.h";
+                               OTHER_TAPI_FLAGS = "$(inherited) -extra-public-header $(SRCROOT)/libdarwin/h/dirstat.h -extra-public-header $(SRCROOT)/libdarwin/tapi.h -DDARWIN_TAPI=1 -extra-public-header $(SRCROOT)/os/variant_private.h -extra-public-header $(SRCROOT)/os/api.h";
                                PRIVATE_HEADERS_FOLDER_PATH = "$(DARWIN_PRIVATE_HEADERS_FOLDER_PATH)";
                                PRODUCT_NAME = darwin;
                                SKIP_INSTALL = NO;
                                SUPPORTS_TEXT_BASED_API = YES;
+                               TAPI_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/libdarwin";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
index 4dff678dc15997ea77323abb03b9ac7a8b3f8c0e..c26849e4393a19f34c40d71f7b58215c2e9be9e6 100644 (file)
 #define        strbase(name, addr, field) \
        ((struct name *)((char *)(addr) - fldoff(name, field)))
 
+/*
+ * countof() cannot be safely used in a _Static_assert statement, so we provide
+ * an unsafe variant that does not verify the input array is statically-defined.
+ */
+#define countof_unsafe(arr) \
+       (sizeof(arr) / sizeof(arr[0]))
+
 /* Number of elements in a statically-defined array */
 #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && __GNUC__
 #define countof(arr) ({ \
 })
 #else
 #define countof(arr) \
-       (sizeof(arr) / sizeof(arr[0]))
+       countof_unsafe(arr)
 #endif
 
-/*
- * countof() cannot be safely used in a _Static_assert statement, so we provide
- * an unsafe variant that does not verify the input array is statically-defined.
- */
-#define countof_unsafe(arr) \
-       (sizeof(arr) / sizeof(arr[0]))
-
 /* Length of a statically-defined string (does not include null terminator) */
 #define lenof(str) \
        (sizeof(str) - 1)
 
+/* Last index of a statically-defined array */
+#define lastof(arr) \
+       (countof(arr) - 1)
+
 #endif /* !_STRUCT_H_ */
index 8015ba3e314cf774af5b9f9291c256ce59af4bf4..adcef271ba8fe823b0c2f9aac31209d0305417cc 100755 (executable)
 #define CTL_OUTPUT_LIST_PAD (4)
 #define SUBCOMMAND_LINKER_SET "__subcommands"
 
+#define OS_SUBCOMMAND_OPTIONS_FOREACH(_osco_i, _osc, _which, _i) \
+               while (((_osco_i) = &osc->osc_ ## _which[(_i)]) && \
+                       ((_i) += 1, 1) && \
+                       !((_osco_i)->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR))
+
+#pragma mark Types
+OS_ENUM(os_subcommand_option_spec_fmt, uint64_t,
+       OS_SUBCOMMAND_OPTION_SPEC_SHORT,
+       OS_SUBCOMMAND_OPTION_SPEC_LONG,
+       OS_SUBCOMMAND_OPTION_SPEC_COMBINED,
+);
+
+#pragma mark Forward Declarations
+static void _print_header(
+               FILE *f,
+               const char *hdr,
+               bool *already_done);
+static const os_subcommand_t *_os_subcommand_find(
+               const char *name);
+static void _os_subcommand_print_usage(
+               const os_subcommand_t *osc,
+               FILE *f);
+static void _os_subcommand_print_help_line(
+               const os_subcommand_t *osc,
+               FILE *f);
+
 #pragma mark Module Globals
-static const os_subcommand_t _help_cmd;
+static const os_subcommand_t __help_cmd;
+static const os_subcommand_t *_help_cmd = &__help_cmd;
+
+static const os_subcommand_t __main_cmd;
+static const os_subcommand_t *_main_cmd = &__main_cmd;
+static const os_subcommand_t *_internal_main_cmd = &__main_cmd;
+
+#pragma mark Main Subcommand
+static int _main_invoke(const os_subcommand_t *osc,
+       int argc,
+       const char *argv[]);
+
+static const os_subcommand_option_t _main_positional[] = {
+       [0] = {
+               .osco_version = OS_SUBCOMMAND_OPTION_VERSION,
+               .osco_flags = 0,
+               .osco_option = NULL,
+               .osco_argument_usage = "subcommand",
+               .osco_argument_human = "The subcommand to invoke",
+       },
+       OS_SUBCOMMAND_OPTION_TERMINATOR,
+};
+
+static const os_subcommand_t __main_cmd = {
+       .osc_version = OS_SUBCOMMAND_VERSION,
+       .osc_flags = OS_SUBCOMMAND_FLAG_MAIN,
+       .osc_name = "_main",
+       .osc_desc = "main command",
+       .osc_optstring = NULL,
+       .osc_options = NULL,
+       .osc_required = NULL,
+       .osc_optional = NULL,
+       .osc_positional = _main_positional,
+       .osc_invoke = &_main_invoke,
+};
+
+static int
+_main_invoke(const os_subcommand_t *osc, int argc, const char *argv[])
+{
+       return 0;
+}
+
+#pragma mark Help Subcommand
+static int _help_invoke(const os_subcommand_t *osc,
+       int argc,
+       const char *argv[]);
+
+static const os_subcommand_option_t _help_positional[] = {
+       [0] = {
+               .osco_version = OS_SUBCOMMAND_OPTION_VERSION,
+               .osco_flags = 0,
+               .osco_option = NULL,
+               .osco_argument_usage = "subcommand",
+               .osco_argument_human = "The subcommand to query for help",
+       },
+       OS_SUBCOMMAND_OPTION_TERMINATOR,
+};
+
+static const os_subcommand_t __help_cmd = {
+       .osc_version = OS_SUBCOMMAND_VERSION,
+       .osc_flags = 0,
+       .osc_name = "help",
+       .osc_desc = "prints helpful information",
+       .osc_optstring = NULL,
+       .osc_options = NULL,
+       .osc_required = NULL,
+       .osc_optional = NULL,
+       .osc_positional = _help_positional,
+       .osc_invoke = &_help_invoke,
+};
+
+static int
+_help_invoke(const os_subcommand_t *osc, int argc, const char *argv[])
+{
+       int xit = -1;
+       const char *cmdname = NULL;
+       const os_subcommand_t *target = NULL;
+       FILE *f = stdout;
+
+       if (argc > 1) {
+               cmdname = argv[1];
+       }
+
+       if (cmdname) {
+               // Print usage information for the requested subcommand.
+               target = _os_subcommand_find(argv[1]);
+               if (!target) {
+                       os_subcommand_fprintf(osc, stderr, "unrecognized subcommand: %s",
+                                       argv[1]);
+                       xit = EX_USAGE;
+               } else {
+                       xit = 0;
+               }
+       } else {
+               // Print general, top-level usage followed by a list of subcommands.
+               target = _main_cmd;
+               xit = 0;
+       }
+
+       if (xit) {
+               f = stderr;
+       }
+
+       _os_subcommand_print_usage(target, f);
+
+       if (target == _main_cmd) {
+               const os_subcommand_t **oscip = NULL;
+               const os_subcommand_t *osci = NULL;
+               bool header_printed = false;
+
+               LINKER_SET_FOREACH(oscip, const os_subcommand_t **,
+                               SUBCOMMAND_LINKER_SET) {
+                       osci = *oscip;
+
+                       _print_header(f, "subcommands", &header_printed);
+                       _os_subcommand_print_help_line(osci, f);
+               }
+
+               // Print the help subcommand last.
+               _os_subcommand_print_help_line(osc, f);
+       }
+
+       return xit;
+}
+
+#pragma mark Utilities
+static void
+_print_header(FILE *f, const char *hdr, bool *already_done)
+{
+       if (*already_done) {
+               return;
+       }
+
+       crfprintf_np(f, "");
+       crfprintf_np(f, "%s:", hdr);
+       *already_done = true;
+}
 
 #pragma mark Module Routines
 static char *
-_os_subcommand_copy_optarg_usage(const os_subcommand_t *osc,
-               const struct option *opt, os_subcommand_optarg_format_t format,
-               os_subcommand_option_t *scopt)
+_os_subcommand_copy_option_spec_short(const os_subcommand_t *osc,
+               const os_subcommand_option_t *osco)
 {
+       const struct option *opt = osco->osco_option;
        char optbuff[64] = "";
        char argbuff[64] = "";
        char *final = NULL;
        int ret = -1;
 
-       snprintf(optbuff, sizeof(optbuff), "--%s", opt->name);
+       if (opt) {
+               snprintf(optbuff, sizeof(optbuff), "-%c", opt->val);
+
+               switch (opt->has_arg) {
+               case no_argument:
+                       break;
+               case optional_argument:
+                       snprintf(argbuff, sizeof(argbuff), "[%s]",
+                                       osco->osco_argument_usage);
+                       break;
+               case required_argument:
+                       snprintf(argbuff, sizeof(argbuff), "<%s>",
+                                       osco->osco_argument_usage);
+                       break;
+               default:
+                       __builtin_unreachable();
+               }
+       } else {
+               snprintf(optbuff, sizeof(optbuff), "%s", osco->osco_argument_usage);
+       }
 
-       if (osc->osc_info) {
-               osc->osc_info(osc, format, opt, scopt);
+       ret = asprintf(&final, "%s%s", optbuff, argbuff);
+       if (ret < 0) {
+               os_assert_zero(ret);
        }
 
-       switch (opt->has_arg) {
-       case no_argument:
-               break;
-       case optional_argument:
-               snprintf(argbuff, sizeof(argbuff), "[=%s]", scopt->osco_argdesc);
-               break;
-       case required_argument:
-               snprintf(argbuff, sizeof(argbuff), "=<%s>", scopt->osco_argdesc);
-               break;
-       default:
-               __builtin_unreachable();
+       return final;
+}
+
+static char *
+_os_subcommand_copy_option_spec_long(const os_subcommand_t *osc,
+               const os_subcommand_option_t *osco)
+{
+       const struct option *opt = osco->osco_option;
+       char optbuff[64] = "";
+       char argbuff[64] = "";
+       char *final = NULL;
+       int ret = -1;
+
+       if (opt) {
+               snprintf(optbuff, sizeof(optbuff), "--%s", opt->name);
+
+               switch (opt->has_arg) {
+               case no_argument:
+                       break;
+               case optional_argument:
+                       snprintf(argbuff, sizeof(argbuff), "[=%s]",
+                                       osco->osco_argument_usage);
+                       break;
+               case required_argument:
+                       snprintf(argbuff, sizeof(argbuff), "=<%s>",
+                                       osco->osco_argument_usage);
+                       break;
+               default:
+                       __builtin_unreachable();
+               }
+       } else {
+               snprintf(optbuff, sizeof(optbuff), "%s", osco->osco_argument_usage);
        }
 
        ret = asprintf(&final, "%s%s", optbuff, argbuff);
@@ -47,243 +250,363 @@ _os_subcommand_copy_optarg_usage(const os_subcommand_t *osc,
        return final;
 }
 
-static void
-_os_subcommand_print_optarg_usage(const os_subcommand_t *osc,
-               const struct option *opt, FILE *f)
+static char *
+_os_subcommand_copy_option_spec(const os_subcommand_t *osc,
+               const os_subcommand_option_t *osco, os_subcommand_option_spec_fmt_t fmt)
 {
-       os_subcommand_option_t scopt = {
-               .osco_flags = 0,
-               .osco_argdesc = opt->name,
-       };
-       char *__os_free usage = NULL;
-       char *braces[2] = {
-               "",
-               "",
-       };
-
-       usage = _os_subcommand_copy_optarg_usage(osc, opt,
-                       OS_SUBCOMMAND_OPTARG_USAGE, &scopt);
-       if (scopt.osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL) {
-               braces[0] = "[";
-               braces[1] = "]";
+       int ret = -1;
+       char *spec = NULL;
+       char *__os_free spec_old = NULL;
+
+       switch (fmt) {
+       case OS_SUBCOMMAND_OPTION_SPEC_SHORT:
+               _os_subcommand_copy_option_spec_short(osc, osco);
+               break;
+       case OS_SUBCOMMAND_OPTION_SPEC_LONG:
+               _os_subcommand_copy_option_spec_long(osc, osco);
+               break;
+       case OS_SUBCOMMAND_OPTION_SPEC_COMBINED:
+               spec = _os_subcommand_copy_option_spec_long(osc, osco);
+               if (osco->osco_option) {
+                       spec_old = spec;
+
+                       ret = asprintf(&spec, "%s | -%c", spec, osco->osco_option->val);
+                       if (ret < 0) {
+                               os_assert_zero(ret);
+                       }
+               }
+
+               break;
+       default:
+               __builtin_unreachable();
        }
 
-       fprintf(f, " %s%s%s", braces[0], usage, braces[1]);
+       return spec;
 }
 
-static void
-_os_subcommand_print_usage(const os_subcommand_t *osc, FILE *f)
+static char *
+_os_subcommand_copy_usage_line(const os_subcommand_t *osc)
 {
-       const struct option *opts = osc->osc_options;
-       const struct option *curopt = NULL;
+       char *usage_line = NULL;
        size_t i = 0;
+       const os_subcommand_option_t *osco_i = NULL;
+       const char *optional_spec = "";
+       char subcmd_name[64];
+       int ret = -1;
 
-       fprintf(f, "usage: %s %s", getprogname(), osc->osc_name);
-
-       while ((curopt = &opts[i]) && curopt->name) {
-               _os_subcommand_print_optarg_usage(osc, curopt, f);
-               i++;
+       // The usage line does not enumerate all possible optional options, just the
+       // required options. If there are optional options, then display that but
+       // otherwise leave them to be described by more extensive usage information.
+       if (osc->osc_optional) {
+               optional_spec = " [options]";
        }
 
-       fprintf(f, "\n");
-}
+       if (osc == _main_cmd) {
+               strlcpy(subcmd_name, "", sizeof(subcmd_name));
+       } else {
+               snprintf(subcmd_name, sizeof(subcmd_name), " %s", osc->osc_name);
+       }
 
-static void
-_os_subcommand_print_optarg_human(const os_subcommand_t *osc,
-               const struct option *opt, FILE *f)
-{
-       os_subcommand_option_t scopt = {
-               .osco_flags = 0,
-               .osco_argdesc = opt->name,
-       };
-       char *__os_free usage = NULL;
-       char *__os_free human = NULL;
-
-       usage = _os_subcommand_copy_optarg_usage(osc, opt,
-                       OS_SUBCOMMAND_OPTARG_USAGE, &scopt);
-       fprintf(f, "    %-24s", usage);
-
-       human = _os_subcommand_copy_optarg_usage(osc, opt,
-                       OS_SUBCOMMAND_OPTARG_HUMAN, &scopt);
-       wfprintf_np(f, -CTL_OUTPUT_OPTARG_PAD, CTL_OUTPUT_OPTARG_PAD,
-                       CTL_OUTPUT_WIDTH, "%s", scopt.osco_argdesc);
-}
+       ret = asprintf(&usage_line, "%s%s%s",
+                       getprogname(), subcmd_name, optional_spec);
+       if (ret < 0) {
+               os_assert_zero(ret);
+       }
 
-static void
-_os_subcommand_print_human(const os_subcommand_t *osc, FILE *f)
-{
-       const struct option *opts = osc->osc_options;
-       const struct option *curopt = NULL;
-       size_t i = 0;
+       i = 0;
+       OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, required, i) {
+               char *__os_free usage_line_old = NULL;
+               char *__os_free osco_spec = NULL;
 
-       _os_subcommand_print_usage(osc, f);
+               usage_line_old = usage_line;
 
-       while ((curopt = &opts[i]) && curopt->name) {
-               _os_subcommand_print_optarg_human(osc, curopt, f);
-               i++;
+               osco_spec = _os_subcommand_copy_option_spec_long(osc, osco_i);
+               ret = asprintf(&usage_line, "%s %s", usage_line, osco_spec);
+               if (ret < 0) {
+                       os_assert_zero(ret);
+               }
        }
-}
 
-static void
-_os_subcommand_print_list(const os_subcommand_t *osc, FILE *f)
-{
-       wfprintf_np(f, CTL_OUTPUT_LIST_PAD, 0, 0, "%-24s %s",
-                       osc->osc_name, osc->osc_desc);
-}
+       i = 0;
+       OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) {
+               char *__os_free usage_line_old = NULL;
+               char *__os_free osco_spec = NULL;
+               const char *braces[] = {
+                       "<",
+                       ">",
+               };
+
+               if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) {
+                       braces[0] = "[";
+                       braces[1] = "]";
+               }
 
-static const os_subcommand_t *
-_os_subcommand_find(const char *name)
-{
-       const os_subcommand_t **oscip = NULL;
+               usage_line_old = usage_line;
 
-       if (strcmp(_help_cmd.osc_name, name) == 0) {
-               return &_help_cmd;
+               osco_spec = _os_subcommand_copy_option_spec_long(osc, osco_i);
+               ret = asprintf(&usage_line, "%s %s%s%s",
+                               usage_line, braces[0], osco_spec, braces[1]);
+               if (ret < 0) {
+                       os_assert_zero(ret);
+               }
        }
 
-       LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
-               const os_subcommand_t *osci = *oscip;
+       if (osc == _main_cmd && osc != _internal_main_cmd) {
+               // Always include the positional subcommand when printing usage for the
+               // main subcommand. We do not expect it to be specified in a user-
+               // provided main subcommand.
+               char *__os_free usage_line_old = NULL;
+               char *__os_free osco_spec = NULL;
 
-               if (strcmp(osci->osc_name, name) == 0) {
-                       return osci;
+               usage_line_old = usage_line;
+
+               osco_spec = _os_subcommand_copy_option_spec_long(osc,
+                               &_main_positional[0]);
+               ret = asprintf(&usage_line, "%s <%s>", usage_line, osco_spec);
+               if (ret < 0) {
+                       os_assert_zero(ret);
                }
        }
 
-       return NULL;
+       return usage_line;
 }
 
-#pragma mark Default Usage
 static void
-_usage_default(FILE *f)
+_os_subcommand_print_option_usage(const os_subcommand_t *osc,
+               const os_subcommand_option_t *osco, FILE *f)
 {
-       const os_subcommand_t **oscip = NULL;
+       char *__os_free opt_spec = NULL;
+       ssize_t initpad = -CTL_OUTPUT_OPTARG_PAD;
 
-       crfprintf_np(f, "usage: %s <subcommand> [...] | help [subcommand]",
-                       getprogname());
-       crfprintf_np(f, "");
+       opt_spec = _os_subcommand_copy_option_spec(osc, osco,
+                       OS_SUBCOMMAND_OPTION_SPEC_COMBINED);
+       fprintf(f, "    %-24s", opt_spec);
 
-       crfprintf_np(f, "subcommands:");
-       LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
-               const os_subcommand_t *osci = *oscip;
-               _os_subcommand_print_list(osci, f);
+       // If the usage specifier is long, start the description on the next line.
+       if (strlen(opt_spec) >= CTL_OUTPUT_OPTARG_PAD) {
+               initpad = CTL_OUTPUT_OPTARG_PAD;
+               crfprintf_np(f, "");
        }
 
-       _os_subcommand_print_list(&_help_cmd, f);
+       wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_PAD,
+                       CTL_OUTPUT_WIDTH, "%s",
+                       osco->osco_argument_human);
 }
 
-static int
-_usage(FILE *f)
+static void
+_os_subcommand_print_help_line(const os_subcommand_t *osc, FILE *f)
 {
-       _usage_default(f);
-       return EX_USAGE;
-}
+       ssize_t initpad = -CTL_OUTPUT_OPTARG_PAD;
 
-#pragma mark Help Subcommand
-static int _help_invoke(const os_subcommand_t *osc,
-       int argc,
-       const char *argv[]
-);
+       fprintf(f, "    %-24s", osc->osc_name);
 
-static const os_subcommand_t _help_cmd = {
-       .osc_version = OS_SUBCOMMAND_VERSION,
-       .osc_flags = 0,
-       .osc_name = "help",
-       .osc_desc = "prints helpful information",
-       .osc_optstring = NULL,
-       .osc_options = NULL,
-       .osc_info = NULL,
-       .osc_invoke = &_help_invoke,
-};
+       // If the usage specifier is long, start the description on the next line.
+       if (strlen(osc->osc_name) >= CTL_OUTPUT_OPTARG_PAD) {
+               initpad = CTL_OUTPUT_OPTARG_PAD;
+               crfprintf_np(f, "");
+       }
 
-static void
-_help_print_subcommand(const os_subcommand_t *osc, FILE *f)
-{
-       wfprintf_np(f, 4, 4, 76, "%-16s%s", osc->osc_name, osc->osc_desc);
+       wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_PAD,
+                       CTL_OUTPUT_WIDTH, "%s",
+                       osc->osc_desc);
 }
 
 static void
-_help_print_all(FILE *f)
+_os_subcommand_print_usage(const os_subcommand_t *osc, FILE *f)
 {
-       const os_subcommand_t **oscip = NULL;
+       size_t i = 0;
+       const os_subcommand_option_t *osco_i = NULL;
+       char *__os_free usage_line = NULL;
+       bool header_printed = false;
 
-       _usage_default(f);
-       crfprintf_np(f, "");
+       usage_line = _os_subcommand_copy_usage_line(osc);
+       wfprintf_np(f, 0, 0, CTL_OUTPUT_WIDTH, "usage: %s", usage_line);
 
-       crfprintf_np(f, "subcommands:");
-       LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
-               const os_subcommand_t *osci = *oscip;
-               if (osci->osc_flags & OS_SUBCOMMAND_FLAG_HIDDEN) {
-                       continue;
+       if (osc->osc_required || osc->osc_positional || osc == _main_cmd) {
+               i = 0;
+               OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, required, i) {
+                       _print_header(f, "required", &header_printed);
+
+                       crfprintf_np(f, "");
+                       _os_subcommand_print_option_usage(osc, osco_i, f);
+               }
+
+               i = 0;
+               OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) {
+                       _print_header(f, "required", &header_printed);
+
+                       if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) {
+                               continue;
+                       }
+
+                       crfprintf_np(f, "");
+                       _os_subcommand_print_option_usage(osc, osco_i, f);
+               }
+
+               if (osc == _main_cmd && osc != _internal_main_cmd) {
+                       // We do not expect the user's main command to specify that a
+                       // subcommand must follow, so always defer to ours.
+                       _print_header(f, "required", &header_printed);
+
+                       crfprintf_np(f, "");
+                       _os_subcommand_print_option_usage(osc, &_main_positional[0], f);
+               }
+       }
+
+       header_printed = false;
+
+       if (osc->osc_optional || osc->osc_positional) {
+               i = 0;
+               OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, optional, i) {
+                       _print_header(f, "optional", &header_printed);
+
+                       crfprintf_np(f, "");
+                       _os_subcommand_print_option_usage(osc, osco_i, f);
+               }
+
+               i = 0;
+               OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) {
+                       if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) {
+                               _print_header(f, "optional", &header_printed);
+
+                               crfprintf_np(f, "");
+                               _os_subcommand_print_option_usage(osc, osco_i, f);
+                       }
                }
-               _help_print_subcommand(osci, f);
        }
 }
 
-static int
-_help_invoke(const os_subcommand_t *osc, int argc, const char *argv[])
+static const os_subcommand_t *
+_os_subcommand_find(const char *name)
 {
-       const os_subcommand_t *target = NULL;
+       const os_subcommand_t **oscip = NULL;
 
-       if (argc == 1) {
-               _help_print_all(stdout);
-       } else {
-               target = _os_subcommand_find(argv[1]);
-               if (!target) {
-                       crfprintf_np(stderr, "unrecognized subcommand: %s", argv[1]);
-                       _usage_default(stderr);
-                       return EX_USAGE;
+       if (strcmp(_help_cmd->osc_name, name) == 0) {
+               return &__help_cmd;
+       }
+
+       LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
+               const os_subcommand_t *osci = *oscip;
+
+               if (osci->osc_flags & OS_SUBCOMMAND_FLAG_MAIN) {
+                       // The main subcommand cannot be invoked directly.
+                       continue;
                }
 
-               _os_subcommand_print_human(target, stdout);
+               if (strcmp(osci->osc_name, name) == 0) {
+                       return osci;
+               }
        }
 
-       return 0;
+       return NULL;
 }
 
 #pragma mark API
 int
-os_subcommand_main(int argc, const char *argv[])
+os_subcommand_main(int argc, const char *argv[],
+               os_subcommand_main_flags_t flags)
 {
-       int exitcode = -1;
+       int xit = -1;
        const char *cmdname = NULL;
-       const os_subcommand_t *osci = NULL;
+       const os_subcommand_t *osc = NULL;
+       const os_subcommand_t **oscip = NULL;
 
        if (argc < 2) {
-               exitcode = _usage(stderr);
+               os_subcommand_fprintf(NULL, stderr, "please provide a subcommand");
+               xit = EX_USAGE;
+               goto __out;
+       }
+
+       // Find the main subcommand if any exists. Otherwise we'll just use our pre-
+       // canned main subcommand.
+       LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
+               osc = *oscip;
+               if (osc->osc_flags & OS_SUBCOMMAND_FLAG_MAIN) {
+                       _main_cmd = osc;
+                       break;
+               }
+       }
+
+       osc = NULL;
+
+       // Invoke the main subcommand to snarf any global options. Our default
+       // implementation does nothing and just returns 0.
+       xit = _main_cmd->osc_invoke(_main_cmd, argc, argv);
+       if (xit) {
                goto __out;
        }
 
        // Advance argument pointer and make the subcommand argv[0].
-       argc -= 1;
-       argv += 1;
+       argc -= optind;
+       argv += optind;
        cmdname = argv[0];
 
-       osci = _os_subcommand_find(cmdname);
-       if (osci) {
-               if (osci->osc_flags & OS_SUBCOMMAND_FLAG_REQUIRE_ROOT) {
+       if (argc < 1) {
+               os_subcommand_fprintf(NULL, stderr, "please provide a subcommand");
+               xit = EX_USAGE;
+               goto __out;
+       }
+
+       osc = _os_subcommand_find(cmdname);
+       if (osc) {
+               if (osc->osc_flags & OS_SUBCOMMAND_FLAG_REQUIRE_ROOT) {
                        if (geteuid()) {
-                               crfprintf_np(stderr, "subcommand requires root: %s", cmdname);
-                               exitcode = EX_NOPERM;
+                               os_subcommand_fprintf(osc, stderr,
+                                               "subcommand requires root: %s",
+                                               cmdname);
+                               xit = EX_NOPERM;
                                goto __out;
                        }
                }
 
-               if (osci->osc_flags & OS_SUBCOMMAND_FLAG_TTYONLY) {
+               if (osc->osc_flags & OS_SUBCOMMAND_FLAG_TTYONLY) {
                        if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) {
-                               crfprintf_np(stderr, "subcommand requires a tty: %s", cmdname);
-                               exitcode = EX_UNAVAILABLE;
+                               os_subcommand_fprintf(osc, stderr,
+                                               "subcommand requires a tty: %s",
+                                               cmdname);
+                               xit = EX_UNAVAILABLE;
                                goto __out;
                        }
                }
 
-               exitcode = osci->osc_invoke(osci, argc, argv);
-               if (exitcode == EX_USAGE) {
-                       _os_subcommand_print_usage(osci, stderr);
-               }
+               xit = osc->osc_invoke(osc, argc, argv);
        } else {
-               crfprintf_np(stderr, "unrecognized subcommand: %s", cmdname);
-               exitcode = _usage(stderr);
+               os_subcommand_fprintf(NULL, stderr, "unknonwn subcommand: %s", cmdname);
+               xit = EX_USAGE;
        }
 
 __out:
-       return exitcode;
+       if (xit == EX_USAGE) {
+               if (!osc) {
+                       osc = _main_cmd;
+               }
+
+               _os_subcommand_print_usage(osc, stderr);
+       }
+
+       return xit;
+}
+
+void
+os_subcommand_fprintf(const os_subcommand_t *osc, FILE *f,
+               const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vcrfprintf_np(f, fmt, ap);
+       va_end(ap);
+}
+
+void
+os_subcommand_vfprintf(const os_subcommand_t *osc, FILE *f,
+               const char *fmt, va_list ap)
+{
+       if (!osc || (osc->osc_flags & OS_SUBCOMMAND_FLAG_MAIN)) {
+               fprintf(f, "%s: ", getprogname());
+       } else {
+               fprintf(f, "%s::%s: ", getprogname(), osc->osc_name);
+       }
+
+       vcrfprintf_np(f, fmt, ap);
 }
index 89b0a83115c552cfca854b016959c95e31544da6..d62a521f7a6b6f14fae8322ef9a23de766cfebb4 100644 (file)
@@ -335,8 +335,8 @@ fdirstat_fallback(int parent_fd, int flags, struct dirstat *ds)
                                                if (1 == attrs.link_count) {
                                                        ds->total_size += object_size;
                                                } else {
-                                                       bool new_fileid = _dirstat_fileid_set_add(fileid_seen, attrs.fileid);
-                                                       if (new_fileid) {
+                                                       bool seen_fileid = _dirstat_fileid_set_add(fileid_seen, attrs.fileid);
+                                                       if (!seen_fileid) {
                                                                ds->total_size += object_size;
                                                        } else {
                                                                DEBUGPRINT( "Skipping hardlinked file at %s/%s\n", path, name);
index 6fc247ed617fb7903677c9081cb6d9c06383b60a..a33a9e0b80581c47865c43a0bf1f235c8e78e802 100644 (file)
 #include <stdint.h>
 #include <stdbool.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 __BEGIN_DECLS;
 
 /*!
index 0b82c054a7bb08ec610ab6ea5ffc3c042f2bb8fd..cbbd045887d0a17659c462a359bd190da3765228 100644 (file)
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <dirent.h>
 #include <mach/mach_init.h>
 #include <mach/port.h>
 #include <mach/mach_port.h>
 #include <mach/kern_return.h>
 #include <mach/mach_right.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 __BEGIN_DECLS;
 
 #if __has_attribute(cleanup)
@@ -99,7 +104,7 @@ __os_cleanup_close(int *__fd)
  * @define __os_fclose
  * An attribute that may be applied to a variable's type. This attribute causes
  * the variable to be passed to fclose(3) when it goes out of scope. Applying
- * this attribute to variables that do not reference a valid FILE* will result
+ * this attribute to variables that do not reference a valid FILE * will result
  * in undefined behavior. If the variable's value is NULL upon going out-of-
  * scope, no cleanup is performed.
  */
@@ -120,6 +125,26 @@ __os_cleanup_fclose(FILE **__fp)
        }
 }
 
+/*!
+ * @define __os_closedir
+ * An attribute that may be applied to a variable's type. This attribute causes
+ * the variable to be passed to closedir(3) when it goes out of scope. Applying
+ * this attribute to variables that do not reference a valid DIR * will result
+ * in undefined behavior. If the variable's value is NULL upon going out-of-
+ * scope, no cleanup is performed.
+ */
+#define __os_closedir __attribute__((cleanup(__os_cleanup_closedir)))
+static inline void
+__os_cleanup_closedir(DIR **__dp)
+{
+       DIR *dp = *__dp;
+
+       if (!dp) {
+               return;
+       }
+       posix_assert_zero(closedir(dp));
+}
+
 /*!
  * @define __os_close_mach_recv
  * An attribute that may be applied to a variable's type. This attribute causes
@@ -212,7 +237,7 @@ __os_cleanup_os_release(void *__p)
 }
 #endif
 
-#if __COREFOUNDATION__
+#if DARWIN_CLEANUP_CF
 /*!
  * @define __os_cfrelease
  * An attribute that may be applied to a variable's type. This attribute causes
@@ -220,6 +245,9 @@ __os_cleanup_os_release(void *__p)
  * this attribute to a variable which does not reference a valid CoreFoundation
  * object will result in undefined behavior. If the variable's value is NULL
  * upon going out-of-scope, no cleanup is performed.
+ *
+ * In order to use, you must define the DARWIN_CLEANUP_CF macro to 1 prior to
+ * including this header.
  */
 #define __os_cfrelease __attribute__((cleanup(__os_cleanup_cfrelease)))
 static inline void
@@ -232,7 +260,68 @@ __os_cleanup_cfrelease(void *__p)
        }
        CFRelease(cf);
 }
-#endif // __COREFOUNDATION__
+#endif // DARWIN_CLEANUP_CF
+
+#if DARWIN_CLEANUP_IOKIT
+/*!
+ * @define __os_iorelease
+ * An attribute that may be applied to a variable's type. This attribute causes
+ * the variable to be passed to IOObjectRelease() when it goes out of scope.
+ * Applying this attribute to a variable which does not reference a valid IOKit
+ * object will result in undefined behavior. If the variable's value is
+ * IO_OBJECT_NULL upon going out-of-scope, no cleanup is performed.
+ *
+ *
+ * In order to use, you must define the DARWIN_CLEANUP_IOKIT macro to 1 prior to
+ * including this header.
+ */
+#define __os_iorelease __attribute__((cleanup(__os_cleanup_iorelease)))
+static inline void
+__os_cleanup_iorelease(void *__p)
+{
+       kern_return_t kr = KERN_FAILURE;
+       io_object_t *iop = (io_object_t *)__p;
+       io_object_t io = *iop;
+
+       if (io == IO_OBJECT_NULL) {
+               return;
+       }
+
+       kr = IOObjectRelease(io);
+       if (kr) {
+               os_crash("IOObjectRetain: %{mach.errno}d", kr);
+       }
+}
+
+/*!
+ * @define __os_ioclose
+ * An attribute that may be applied to a variable's type. This attribute causes
+ * the variable to be passed to IOServiceClose() when it goes out of scope.
+ * Applying this attribute to a variable which does not reference a valid IOKit
+ * connection will result in undefined behavior. If the variable's value is
+ * IO_OBJECT_NULL upon going out-of-scope, no cleanup is performed.
+ *
+ * In order to use, you must define the DARWIN_CLEANUP_IOKIT macro to 1 prior to
+ * including this header.
+ */
+#define __os_ioclose __attribute__((cleanup(__os_cleanup_ioclose)))
+static inline void
+__os_cleanup_ioclose(void *__p)
+{
+       kern_return_t kr = KERN_FAILURE;
+       io_connect_t *iop = (io_object_t *)__p;
+       io_connect_t io = *iop;
+
+       if (io == IO_OBJECT_NULL) {
+               return;
+       }
+
+       kr = IOServiceClose(io);
+       if (kr) {
+               os_crash("IOObjectRelease: %{mach.errno}d", kr);
+       }
+}
+#endif // DARWIN_CLEANUP_IOKIT
 
 /*!
  * @define __os_unfair_unlock
@@ -275,11 +364,14 @@ __os_cleanup_unfair_unlock(void *__p)
 #define __os_free __os_cleanup_unsupported
 #define __os_close __os_cleanup_unsupported
 #define __os_fclose __os_cleanup_unsupported
+#define __os_closedir __os_cleanup_unsupported
 #define __os_close_mach_recv __os_cleanup_unsupported
 #define __os_release_mach_send __os_cleanup_unsupported
 #define __os_preserve_errno __os_cleanup_unsupported
 #define __os_release __os_cleanup_unsupported
 #define __os_cfrelease __os_cleanup_unsupported
+#define __os_iorelease __os_cleanup_unsupported
+#define __os_ioclose __os_cleanup_unsupported
 #define __os_unfair_unlock __os_cleanup_unsupported
 #endif // __has_attribute(cleanup)
 
index 450f097cddb5c6f01eb0f14e002f132a1a370e37..2968b5a7a4de5b95aa28bdd5dd1d4e261a87a87a 100644 (file)
  *
  * The user may define each subcommand taken by the utility as:
  *
- *     static const os_subcommand_t _foo_cmd = {
- *         .osc_version = OS_SUBCOMMAND_VERSION,
- *         .osc_flags = 0,
- *         .osc_name = "foo",
- *         .osc_desc = "does a foo",
- *         .osc_optstring = NULL,
- *         .osc_options = NULL,
- *         .osc_info = NULL,
- *         .osc_invoke = &_foo_invoke,
+ *     static const struct option _template_opts[] = {
+ *         [0] = {
+ *             .name = "bar",
+ *             .has_arg = required_argument,
+ *             .flag = NULL,
+ *             .val = 'f',
+ *         }, {
+ *             .name = "baz",
+ *             .has_arg = optional_argument,
+ *             .flag = NULL,
+ *             .val = 'b',
+ *         }, {
+ *             .name = NULL,
+ *             .has_arg = 0,
+ *             .flag = NULL,
+ *             .val = 0,
+ *         },
  *     };
- *     OS_SUBCOMMAND_REGISTER(_foo_cmd);
  *
- *     static const os_subcommand_t _bar_cmd = {
- *         .osc_version = OS_SUBCOMMAND_VERSION,
- *         .osc_flags = 0,
- *         .osc_name = "bar",
- *         .osc_desc = "bars a foo",
- *         .osc_optstring = "x:q",
- *         .osc_options = _bar_opts,
- *         .osc_info = &_bar_optinfo,
- *         .osc_invoke = &_bar_invoke,
+ *     static const os_subcommand_option_t _template_required[] = {
+ *         [0] = {
+ *             .osco_template = OS_SUBCOMMAND_OPTION_VERSION,
+ *             .osco_flags = 0,
+ *             .osco_option = &_template_opts[0],
+ *             .osco_argument_usage = "thing-to-bar",
+ *             .osco_argument_human = "The thing to bar. May be specified as a "
+ *             "bar that has a baz. This baz should have a shnaz.",
+ *         },
+ *         OS_SUBCOMMAND_OPTION_TERMINATOR,
  *     };
- *     OS_SUBCOMMAND_REGISTER(_bar_cmd);
- * };
  *
- * Where the "bar" subcommand's option information is returned by the routine:
- *
- * static void
- * _bar_optinfo(const os_subcommand_t *osc,
- *         os_subcommand_optarg_format_t format, const struct option *opt,
- *         os_subcommand_option_t *scopt)
- * {
- *     switch (format) {
- *     case OS_SUBCOMMAND_OPTARG_USAGE:
- *         switch (opt->val) {
- *         case 'x':
- *             scopt->osco_flags |= OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL;
- *             scopt->osco_argdesc = "x-argument";
- *             break;
- *         case 'q':
- *             scopt->osco_argdesc = "q-argument";
- *             break;
- *         default:
- *             __builtin_unreachable();
- *         }
- *         break;
- *     case OS_SUBCOMMAND_OPTARG_HUMAN:
- *         switch (opt->val) {
- *         case 'x':
- *             scopt->osco_flags |= OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL;
- *             scopt->osco_argdesc = "argument describing x";
- *             break;
- *         case 'q':
- *             scopt->osco_argdesc = "Lorem ipsum dolor sit amet, consectetur "
- *             "adipiscing elit. Nullam a maximus lectus. Curabitur ornare "
- *             "convallis turpis, in porttitor augue tempus laoreet. Maecenas "
- *             "auctor mauris tortor, et tempor libero maximus id. Donec ac "
- *             "nunc et elit sagittis commodo. Donec tincidunt libero vehicula "
- *             "ex eleifend sagittis. Suspendisse consectetur cursus elit. "
- *             "Proin neque metus, commodo id rhoncus eu, cursus hendrerit ex. "
- *             "Etiam in fringilla nulla, vitae mollis eros.";
- *             break;
- *         default:
- *             __builtin_unreachable();
- *         }
- *         break;
+ *     static const os_subcommand_option_t _template_optional[] = {
+ *         [0] = {
+ *             .osco_template = OS_SUBCOMMAND_OPTION_VERSION,
+ *             .osco_flags = 0,
+ *             .osco_option = &_template_opts[1],
+ *             .osco_argument_usage = "thing-to-baz",
+ *             .osco_argument_human = "The baz of which to propagate a foo.",
+ *         },
+ *         OS_SUBCOMMAND_OPTION_TERMINATOR,
+ *     };
+ *
+ *     static const os_subcommand_option_t _template_positional[] = {
+ *         [0] = {
+ *             .osco_template = OS_SUBCOMMAND_OPTION_VERSION,
+ *             .osco_flags = 0,
+ *             .osco_option = NULL,
+ *             .osco_argument_usage = "positional-baz",
+ *             .osco_argument_human = "A baz specified by position.",
+ *         },
+ *         OS_SUBCOMMAND_OPTION_TERMINATOR,
+ *     };
+ *
+ *     static const os_subcommand_t _template_cmd = {
+ *         .osc_template = OS_SUBCOMMAND_VERSION,
+ *         .osc_flags = 0,
+ *         .osc_name = "foo",
+ *         .osc_desc = "foo a bar or maybe baz",
+ *         .osc_optstring = "f:b:",
+ *         .osc_options = _foo_opts,
+ *         .osc_required = _foo_required,
+ *         .osc_optional = _foo_optional,
+ *         .osc_positional = _template_positional,
+ *         .osc_invoke = &_foo_invoke,
  *     }
- * }
+ *     OS_SUBCOMMAND_REGISTER(_foo_cmd);
+ * };
  *
  * When {@link os_subcommand_main} is called, the tool's "help" subcommand will
  * display approximately the following:
  *
  * $ tool help
- * usage: playground <subcommand> [...] | help [subcommand]
+ * usage: tool <subcommand>
  *
  * subcommands:
- *     foo             does a foo
- *     bar             bars a foo
+ *     foo             foo a bar or maybe baz
+ *     help            Prints helpful information
  *
  * $ tool help foo
- * usage: tool foo
- *
- * $ tool help bar
- * usage: tool bar [--xarg] --qarg[=q-argument]
- *     --xarg                  argument describing x
- *     --qarg[=q-argument]     Lorem ipsum dolor sit amet, consectetur
- *                             adipiscing elit. Nullam a maximus lectus.
- *                             Curabitur ornare convallis turpis, in porttitor
- *                             augue tempus laoreet. Maecenas auctor mauris
- *                             tortor, et tempor libero maximus id. Donec ac
- *                             nunc et elit sagittis commodo. Donec tincidunt
- *                             libero vehicula ex eleifend sagittis. Suspendisse
- *                             consectetur cursus elit. Proin neque metus,
- *                             commodo id rhoncus eu, cursus hendrerit ex. Etiam
- *                             in fringilla nulla, vitae mollis eros.
+ * usage: tool foo [options] --bar=<thing-to-bar> <positional-baz>
+ *
+ * required options:
+ *     --bar=<thing-to-bar>    The thing to bar. May be specified as a bar that
+ *                             has a baz. This baz should have a shnaz.
+ *
+ *     positional-baz          A baz specified by position.
+ *
+ * optional options:
+ *     --baz[=thing-to-baz]    The baz of which to propagate a foo.
  */
 #ifndef __DARWIN_CTL_H
 #define __DARWIN_CTL_H
 
 #include <os/base.h>
 #include <os/api.h>
-
-#if DARWIN_TAPI
-#define LINKER_SET_ENTRY(_x, _y)
-#else
 #include <os/linker_set.h>
-#endif
-
 #include <sys/cdefs.h>
 #include <stdio.h>
 #include <getopt.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 __BEGIN_DECLS;
 
 /*!
@@ -169,31 +161,6 @@ typedef struct _os_subcommand os_subcommand_t;
  */
 #define OS_SUBCOMMAND_OPTION_VERSION ((os_struct_version_t)0)
 
-/*!
- * @typedef os_subcommand_optarg_format_t
- * A type describing a usage format for the argument taken by an option.
- *
- * @const OS_SUBCOMMAND_OPTION_USAGE
- * The short-form name of the argument given to the option. For example, if the
- * subcommand takes a "--file" option with a required argument, this might be
- * "file-path" and will be displayed as
- *
- *     --file=<file-path>
- *
- * @const OS_SUBCOMMAND_OPTION_HUMAN
- * The long-form description of the argument given to the option. Extending the
- * above example, this might be "The path to a file to take as input. This path
- * must be absolute; relative paths are not supported." and will be displayed as
- *
- *     --file     The path to a file to take as input. This path must be
- *                absolute; relative paths are not supported.
- */
-DARWIN_API_AVAILABLE_20181020
-OS_CLOSED_ENUM(os_subcommand_optarg_format, uint64_t,
-       OS_SUBCOMMAND_OPTARG_USAGE,
-       OS_SUBCOMMAND_OPTARG_HUMAN,
-);
-
 /*!
  * @typedef os_subcommand_option_flags_t
  * Flags describing an option for a subcommand.
@@ -201,66 +168,89 @@ OS_CLOSED_ENUM(os_subcommand_optarg_format, uint64_t,
  * @const OS_SUBCOMMAND_OPTION_FLAG_INIT
  * No flags set. This value is suitable for initialization purposes.
  *
- * @const OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL
- * The option does not need to be present in the subcommand invocation. By
- * default, options are considered required.
+ * @const OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR
+ * The option terminates an array of {@link os_subcommand_option_t} structures
+ * and does not contain any useful information.
+ *
+ * @const OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS
+ * The option is a positional option (that is, not identified by a long or short
+ * flag) and is not required for the subcommand to execute successfully.
  */
 DARWIN_API_AVAILABLE_20181020
 OS_CLOSED_ENUM(os_subcommand_option_flags, uint64_t,
        OS_SUBCOMMAND_OPTION_FLAG_INIT = 0,
-       OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL = (1 << 0),
+       OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR = (1 << 0),
+       OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS = (1 << 1),
 );
 
 /*!
  * @typedef os_subcommand_option_t
  * A structure describing human-readable information about a particular option
  * taken by a subcommand. This structure is to be returned when the
- * implementation queries about a command's options individually. This is done
- * when the implementation is synthesizing a usage string.
+ * implementation invokes a {@link os_subcommand_option_info_t} function to
+ * query about a command's options individually. This is done when the
+ * implementation is synthesizing a usage string.
  *
  * @field osco_version
  * The version of the structure. Initialize to
  * {@link OS_SUBCOMMAND_OPTION_VERSION}.
  *
  * @field osco_flags
- * On return from a {@link os_subcommand_option_info_t} function, a set of flags
- * describing information about the option.
+ * A set of flags describing information about the option.
+ *
+ * @field osco_option
+ * A pointer to the option structure ingested by getopt_long(3) which
+ * corresponds to this option.
  *
- * @field osco_argdesc
- * On return from a {@link os_subcommand_option_info_t} function, this should
- * point to a constant string describing the argument to the option.
+ * @field osco_argument_usage
+ * The short-form name of the argument given to the option, appropriate for
+ * display in a usage specifier. For example, if the subcommand takes a "--file"
+ * option with a required argument, this might be the string "FILE-PATH", and
+ * the resulting usage specifier would be
+ *
+ *     --file=<FILE-PATH>
+ *
+ * @field osco_argument_human
+ * The long-form description of the argument given to the option. Extending the
+ * above example, this might be the string "The path to a file to take as input.
+ * This path must be absolute; relative paths are not supported." and the
+ * resulting usage specifier would be
+ *
+ *     --file=<FILE-PATH> The path to a file to take as input. This path must be
+ *                        absolute; relative paths are not supported.
  */
-DARWIN_API_AVAILABLE_20181020
+DARWIN_API_AVAILABLE_20191015
 typedef struct _os_subcommand_option {
        const os_struct_version_t osco_version;
        os_subcommand_option_flags_t osco_flags;
-       const char *osco_argdesc;
+       const struct option *osco_option;
+       const char *osco_argument_usage;
+       const char *osco_argument_human;
 } os_subcommand_option_t;
 
 /*!
- * @typedef os_subcommand_option_info_t
- * A type describing a function which returns option information.
- *
- * @param osc
- * The subcommand to which the option belongs.
- *
- * @param format
- * The format of usage information required.
- *
- * @param opt
- * A pointer to the option structure for which to retrieve information.
- *
- * @param scopt
- * A pointer to a subcommand option structure to be populated with information
- * pertaining to the option. When passed to the callee, this structure is zero-
- * filled.
+ * @const OS_SUBCOMMAND_OPTION_TERMINATOR
+ * A convenience terminator for an array of {@link os_subcommand_option_t}
+ * structures.
  */
-DARWIN_API_AVAILABLE_20181020
-typedef void (*os_subcommand_option_info_t)(
-               const os_subcommand_t *osc,
-               os_subcommand_optarg_format_t format,
-               const struct option *opt,
-               os_subcommand_option_t *scopt);
+#define OS_SUBCOMMAND_OPTION_TERMINATOR (os_subcommand_option_t){ \
+       .osco_version = OS_SUBCOMMAND_OPTION_VERSION, \
+       .osco_flags = OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR, \
+       .osco_option = NULL, \
+       .osco_argument_usage = NULL, \
+       .osco_argument_human = NULL, \
+}
+
+/*!
+* @const OS_SUBCOMMAND_GETOPT_TERMINATOR
+* A convenience terminator for an array of getopt(3) option structures.
+*/
+#define OS_SUBCOMMAND_GETOPT_TERMINATOR (struct option){ \
+       .name = NULL, \
+       .has_arg = 0, \
+       .flag = NULL, \
+       .val = 0, \
+}
 
 /*!
  * @const OS_SUBCOMMAND_VERSION
@@ -288,6 +278,21 @@ typedef void (*os_subcommand_option_info_t)(
  *
  * @const OS_SUBCOMMAND_FLAG_HIDDEN
  * This subcommand should not be displayed in the list of subcommands.
+ *
+ * @const OS_SUBCOMMAND_FLAG_MAIN
+ * This subcommand is the "main" subcommand. Designating a main subcommand
+ * allows the program to specify and parse global options using an
+ * {@link os_subcommand_t} object and {@link os_subcommand_main}.
+ *
+ * This flag implies the behavior of {@link OS_SUBCOMMAND_FLAG_HIDDEN}.
+ *
+ * If the program specifies a main subcommand, that subcommand's invocation
+ * routine is unconditionally called before calling the subcommand invocation,
+ * if the user provided a subcommand. The invocation function for the main
+ * subcommand should not exit on success and should instead return 0.
+ *
+ * If multiple subcommands in the same program set
+ * {@link OS_SUBCOMMAND_FLAG_MAIN}, the implementation's behavior is undefined.
  */
 DARWIN_API_AVAILABLE_20181020
 OS_CLOSED_OPTIONS(os_subcommand_flags, uint64_t,
@@ -295,6 +300,7 @@ OS_CLOSED_OPTIONS(os_subcommand_flags, uint64_t,
        OS_SUBCOMMAND_FLAG_REQUIRE_ROOT = (1 << 0),
        OS_SUBCOMMAND_FLAG_TTYONLY = (1 << 1),
        OS_SUBCOMMAND_FLAG_HIDDEN = (1 << 2),
+       OS_SUBCOMMAND_FLAG_MAIN = (1 << 3),
 );
 
 /*!
@@ -312,17 +318,21 @@ OS_CLOSED_OPTIONS(os_subcommand_flags, uint64_t,
  * subcommand.
  *
  * @result
- * An exit code, preferably from sysexits(3). Note that exit codes should not
- * intersect with POSIX error codes from errno.h (cf. intro(2)).
+ * An exit code, preferably from sysexits(3). Do not return a POSIX error code
+ * directly from this routine.
  *
  * @discussion
- * You may exit directly from within the routine if you wish.
+ * You may exit directly on success or failure from this routine if desired. If
+ * the routine is the invocation for a main subcommand, then on success, the
+ * routine should return zero to the caller rather than exiting so that the
+ * implementation may continue parsing the command line arguments.
  */
 DARWIN_API_AVAILABLE_20181020
 typedef int (*os_subcommand_invoke_t)(
-               const os_subcommand_t *osc,
-               int argc,
-               const char *argv[]);
+       const os_subcommand_t *osc,
+       int argc,
+       const char *argv[]
+);
 
 /*!
  * @struct os_subcommand_t
@@ -361,7 +371,34 @@ typedef int (*os_subcommand_invoke_t)(
  *
  * @field osc_options
  * A pointer to an array of option structures describing the long options
- * recognized by the subcommand (cf. getopt_long(3)).
+ * recognized by the subcommand. This array must be terminated by a NULL entry
+ * as expected by getopt_long(3).
+ *
+ * @field osc_required
+ * A pointer to an array of subcommand option descriptors. The options described
+ * in this array are required for the subcommand to execute successfully. This
+ * array should be terminated with {@link OS_SUBCOMMAND_OPTION_TERMINATOR}.
+ *
+ * This array is consulted when printing usage information.
+ *
+ * @field osc_optional
+ * A pointer to an array of subcommand option descriptors. The options described
+ * in this array are parsed by the subcommand but not required for it to execute
+ * successfully. This array should be terminated with
+ * {@link OS_SUBCOMMAND_OPTION_TERMINATOR}.
+ *
+ * This array is consulted when printing usage information.
+ *
+ * @field osc_positional
+ * A pointer to an array of subcommand option descriptors. The options described
+ * in this array are expected to follow the required and optional arguments in
+ * the command line invocation, in the order given in this array. This array
+ * should be terminated with {@link OS_SUBCOMMAND_OPTION_TERMINATOR}.
+ *
+ * These options are expected to have their {@link osco_option} fields set to
+ * NULL.
+ *
+ * This array is consulted when printing usage information.
  *
  * @field osc_info
  * A pointer to a function which returns information about the subcommand's
@@ -370,7 +407,7 @@ typedef int (*os_subcommand_invoke_t)(
  * @field osc_invoke
  * The implementation for the subcommand.
  */
-DARWIN_API_AVAILABLE_20181020
+DARWIN_API_AVAILABLE_20191015
 struct _os_subcommand {
        const os_struct_version_t osc_version;
        const os_subcommand_flags_t osc_flags;
@@ -378,10 +415,23 @@ struct _os_subcommand {
        const char *const osc_desc;
        const char *osc_optstring;
        const struct option *osc_options;
-       const os_subcommand_option_info_t osc_info;
+       const os_subcommand_option_t *osc_required;
+       const os_subcommand_option_t *osc_optional;
+       const os_subcommand_option_t *osc_positional;
        const os_subcommand_invoke_t osc_invoke;
 };
 
+/*!
+ * @typedef os_subcommand_main_flags_t
+ * Flags modifying the behavior of {@link os_subcommand_main}.
+ *
+ * @const OS_SUBCOMMAND_MAIN_FLAG_INIT
+ * No flags set. This value is suitable for initialization purposes.
+ */
+OS_CLOSED_OPTIONS(os_subcommand_main_flags, uint64_t,
+       OS_SUBCOMMAND_MAIN_FLAG_INIT,
+);
+
 /*!
  * @function os_subcommand_main
  * Dispatches the subcommand appropriate for the given arguments. All
@@ -394,6 +444,9 @@ struct _os_subcommand {
  * @param argv
  * The argument vector supplied to main().
  *
+ * @param flags
+ * Flags modifying the behavior of the implementation.
+ *
  * @result
  * The exit status from the subcommand's invocation function or an exit status
  * from the implementation indicating that the subcommand could not be
@@ -420,10 +473,70 @@ struct _os_subcommand {
  *
  * This routine implicitly implements a "help" subcommand.
  */
-DARWIN_API_AVAILABLE_20181020
+DARWIN_API_AVAILABLE_20191015
 OS_EXPORT OS_WARN_RESULT OS_NONNULL2
 int
-os_subcommand_main(int argc, const char *argv[]);
+os_subcommand_main(int argc, const char *argv[],
+               os_subcommand_main_flags_t flags);
+
+/*!
+ * @function os_subcommand_fprintf
+ * Prints a message in the context of a subcommand to the given output stream.
+ *
+ * @param osc
+ * The subcommand which represents the context of the message.
+ *
+ * @param f
+ * The stream to which the message shall be printed. If NULL, will be printed to
+ * stderr(4).
+ *
+ * @param fmt
+ * A printf(3)-style format string.
+ *
+ * @param ...
+ * The arguments corresponding to {@link fmt}.
+ *
+ * @discussion
+ * This routine provides a uniform method for printing messages in the context
+ * of a subcommand. It will ensure that the message is prefixed appropriately
+ * (e.g. with the program name and/or subcommand name).
+ *
+ * This routine should be used for printing messages intended for humans to
+ * read; the implementation makes no guarantees about the output format's
+ * stability. If any output is intended to be machine-parseable, it should be
+ * written with fprintf(3) et al.
+ */
+DARWIN_API_AVAILABLE_20191015
+OS_EXPORT OS_NONNULL3 OS_FORMAT_PRINTF(3, 4)
+void
+os_subcommand_fprintf(const os_subcommand_t *osc, FILE *f,
+               const char *fmt, ...);
+
+/*!
+ * @function os_subcommand_vfprintf
+ * Prints a message in the context of a subcommand to the given output stream.
+ *
+ * @param osc
+ * The subcommand which represents the context of the message.
+ *
+ * @param f
+ * The stream to which the message shall be printed. If NULL, will be printed to
+ * stderr(4).
+ *
+ * @param fmt
+ * A printf(3)-style format string.
+ *
+ * @param ap
+ * The argument list corresponding to {@link fmt}.
+ *
+ * @discussion
+ * See discussion for {@link os_subcommand_fprintf}.
+ */
+DARWIN_API_AVAILABLE_20191015
+OS_EXPORT OS_NONNULL3
+void
+os_subcommand_vfprintf(const os_subcommand_t *osc, FILE *f,
+               const char *fmt, va_list ap);
 
 __END_DECLS;
 
index c5cd955b0609732750a235d5da5e27f1f0026b0b..c912c53d4869ffcb133cf198909b4bb5cf44b998 100644 (file)
 #include <stdarg.h>
 #include <stdio.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 __BEGIN_DECLS;
 
 /*!
index 4370b1eeedc2fceb4e0a2de8b4605ce130cc6e0b..1f21f857171331628b4591a65495f831ae5a58bc 100644 (file)
 #include <sys/cdefs.h>
 #include <sysexits.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 /*!
  * @enum
  * Additional POSIX-flavor error codes that are meaningful to Darwin. These
index ef4a62e77c77c01b2ccf25dccaf1308c8df174ce..76d1f7a64258c6da609c16812efcd56fa7928f37 100644 (file)
 #include <mach/mach_port.h>
 #include <mach/kern_return.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 __BEGIN_DECLS;
 
 /*!
index 45ea439dd16248e0ba990aea9a1f1eaefa659fff..8b9575162b1e39e03b58dbcf1629581f163f30b6 100644 (file)
 #include <mach/mach_port.h>
 #include <mach/kern_return.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 /*!
  * @define OS_MIG_SUBSYSTEM_MAXSIZE
  * A macro that evaluates to the maximum size of a request or reply message in
 
 __BEGIN_DECLS;
 
+/*!
+ * @function os_vm_address_from_ptr
+ * Converts the given pointer to a vm_address_t.
+ *
+ * @param p
+ * The pointer to convert.
+ *
+ * @result
+ * The pointer as a vm_address_t.
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_ALWAYS_INLINE OS_WARN_RESULT
+static inline vm_address_t
+os_vm_address_from_ptr(const void *p)
+{
+       return (vm_address_t)(uintptr_t)p;
+}
+
+/*!
+ * @function os_mach_vm_address_from_ptr
+ * Converts the given pointer to a mach_vm_address_t.
+ *
+ * @param p
+ * The pointer to convert.
+ *
+ * @result
+ * The pointer as a mach_vm_address_t.
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_ALWAYS_INLINE OS_WARN_RESULT
+static inline mach_vm_address_t
+os_mach_vm_address_from_ptr(const void *p)
+{
+       return (mach_vm_address_t)(uintptr_t)p;
+}
+
 /*!
  * @function os_mach_msg_get_trailer
  * Obtains the trailer for the received Mach message.
index 11e1c117af9dcb6eb71d269af9e8d2967e36b093..dd7f2711336e7cef82e3ccbc47d9406ab38418ca 100644 (file)
 #include <sys/cdefs.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <unistd.h>
+#include <sys/types.h>
 
-// TAPI and the compiler don't agree about header search paths, so if TAPI found
-// our header in the SDK, help it out.
-#if DARWIN_TAPI && DARWIN_API_VERSION < 20180727
-#define DARWIN_API_AVAILABLE_20180727
+#if __has_include(<sys/guarded.h>)
+#include <sys/guarded.h>
+#else
+typedef uint64_t guardid_t;
+#endif
+
+#if DARWIN_TAPI
+#include "tapi.h"
 #endif
 
 __BEGIN_DECLS;
@@ -68,6 +74,24 @@ os_fd_valid(os_fd_t fd)
        return (fd >= STDIN_FILENO);
 }
 
+/*!
+ * @function os_guardid_from_ptr
+ * Converts the given pointer to a guardid_t.
+ *
+ * @param p
+ * The pointer to convert.
+ *
+ * @result
+ * The pointer as a guardid_t.
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_ALWAYS_INLINE OS_WARN_RESULT
+static inline guardid_t
+os_guardid_from_ptr(const void *p)
+{
+       return (guardid_t)(uintptr_t)p;
+}
+
 /*!
  * @function fcheck_np
  * Checks the status of an fread(3) or fwrite(3) operation to a FILE.
@@ -116,6 +140,97 @@ OS_EXPORT OS_WARN_RESULT
 os_fd_t
 dup_np(os_fd_t fd);
 
+/*!
+ * @function claimfd_np
+ * Claims the given file descriptor for the caller's exclusive use by applying a
+ * guard and invalidating the given storage.
+ *
+ * @param fdp
+ * A pointer to the storage for the descriptor to claim. Upon return, a known-
+ * invalid value is written into this memory.
+ *
+ * @param gdid
+ * The optional guard value to enforce the caller's claim on the descriptor.
+ *
+ * @param gdflags
+ * The guard flags to enforce the caller's claim on the descriptor.
+ *
+ * @result
+ * The given descriptor with the guard applied.
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_EXPORT OS_WARN_RESULT OS_NONNULL1
+os_fd_t
+claimfd_np(os_fd_t *fdp, const guardid_t *gdid, u_int gdflags);
+
+/*!
+ * @function xferfd_np
+ * Transfers ownership from the given file descriptor back to the general public
+ * by clearing the guard associated with it.
+ *
+ * @param fdp
+ * A pointer to the storage for the descriptor to claim. Upon return, a known-
+ * invalid value is written into this memory.
+ *
+ * @param gdid
+ * The optional guard value to reliquish ownership on the descriptor.
+ *
+ * @param gdflags
+ * The guard flags to relinquish.
+ *
+ * @result
+ * The given descriptor with the guard cleared. This descriptor is suitable for
+ * claiming with {@link claimfd_np}.
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_EXPORT OS_WARN_RESULT OS_NONNULL1
+os_fd_t
+xferfd_np(os_fd_t *fdp, const guardid_t *gdid, u_int gdflags);
+
+/*!
+ * @function close_drop_np
+ * Variant of close(2) which transfers ownership from the caller and performs
+ * the close(2) operation. These semantics are useful for ensuring that a
+ * descriptor is not erroneously re-used after it has been closed. To achieve
+ * these semantics, this variant will clear the memory in which the descriptor
+ * resides and replace it with a known-invalid value before returning.
+ *
+ * @param fdp
+ * A pointer to the storage for the descriptor to close. Upon return, a known-
+ * invalid value is written into this memory.
+ *
+ * @param gdid
+ * The optional guard. If the descriptor is not guarded, pass NULL.
+ *
+ * @discussion
+ * If the implementation encounters a failure to close a valid descriptor
+ * number, the caller will be terminated.
+ */
+OS_EXPORT OS_NONNULL1
+void
+close_drop_np(os_fd_t *fdp, const guardid_t *gdid);
+
+/*!
+ * @function close_drop_optional_np
+ * Variant of {@link close_drop} which will not attempt to close an invalid
+ * descriptor. Otherwise all semantics are the same.
+ *
+ * @param fdp
+ * A pointer to the storage for the descriptor to close. Upon return, a known-
+ * invalid value is written into this memory.
+ *
+ * @param gdid
+ * The optional guard. If the descriptor is not guarded, pass NULL.
+ *
+ * @discussion
+ * If the implementation encounters a failure to close a valid descriptor
+ * number, the caller will be terminated. The implementation will not attempt to
+ * close the descriptor if its value is -1.
+ */
+OS_EXPORT OS_NONNULL1
+void
+close_drop_optional_np(os_fd_t *fdp, const guardid_t *gdid);
+
 /*!
  * @function zsnprintf_np
  * snprintf(3) variant which returns the numnber of bytes written less the null
index 25d524e020f94370c9205b2979bf4e262b1aaf62..2d59ba3317337a37b936cb1db5118bf9f680023f 100644 (file)
 #include <dispatch/private.h>
 
 #include <stdlib.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/cdefs.h>
 #include <sys/syslimits.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 __BEGIN_DECLS;
 
 /*!
@@ -237,6 +242,122 @@ _os_strdup_known(const char *str)
 #define os_strdup(__size) __os_requires_experimental_libtrace
 #endif
 
+/*!
+ * @function os_setflag
+ * Sets the given flag in a manner which is compatible with strongly-typed
+ * enumerations.
+ *
+ * @param _bf
+ * The bitfield in which to set the flag.
+ *
+ * @param _f
+ * The flag to set.
+ *
+ * @result
+ * The result of setting {@link _f} in {@link _bf}.
+ */
+#define os_setflag(_bf, _f) (typeof(_bf))((_bf) & (_f))
+
+/*!
+ * @function os_clrflag
+ * Clears the given flag in a manner which is compatible with strongly-typed
+ * enumerations.
+ *
+ * @param _bf
+ * The bitfield in which to clear the flag.
+ *
+ * @param _f
+ * The flag to clear.
+ *
+ * @result
+ * The result of clearing {@link _f} from {@link _bf}.
+ *
+ * @discussion
+ * clrbit() will produce errors when given types smaller than a pointer such as
+ * int because it casts to char *; thus this implementation is required to deal
+ * properly with flags defined via {@link OS_OPTIONS} or similar.
+ */
+#define os_clrflag(_bf, _f) (typeof(_bf))((_bf) & (typeof(_bf))(~(_f)))
+
+/*!
+ * @function switch_posix
+ * Macro which expands to a switch() statement for handling both the success
+ * case as well as errno values set by POSIX and POSIX-y APIs that return -1 and
+ * set errno.
+ *
+ * @example
+ *
+ *     int ret = dup(fd);
+ *     switch_posix (ret) {
+ *     case 0:
+ *         // success
+ *         break;
+ *     case EINTR:
+ *         // interrupted system call
+ *         break;
+ *     case EBADF:
+ *         // bad file descriptor
+ *         break;
+ *     }
+ *
+ * @discussion
+ * This statement cannot be used with APIs that return positive values on
+ * failure.
+ */
+#define switch_posix(_ret) if ((_ret) < 0 || (errno = 0, 1)) switch (errno)
+
+/*!
+ * @function size_unsigned
+ * Converts a signed size quantity into an unsigned size quantity after
+ * verifying the former can be represented as the latter.
+ *
+ * @param ss
+ * The signed size quantity.
+ *
+ * @result
+ * The unsigned representation of {@link ss}.
+ *
+ * @discussion
+ * This routine is useful for passing a signed value (such as the size of a file
+ * from a stat(2) structure) into a routine which accepts unsigned input
+ * (e.g. write(2)).
+ */
+OS_ALWAYS_INLINE OS_WARN_RESULT
+static inline size_t
+size_unsigned(ssize_t ss)
+{
+       if (ss < 0) {
+               os_crash("value not representable as size_t");
+       }
+       return (size_t)ss;
+}
+
+/*!
+ * @function size_signed
+ * Converts an unsigned size quantity into a signed size quantity after
+ * verifying the former can be represented as the latter.
+ *
+ * @param un
+ * The unsigned size quantity.
+ *
+ * @result
+ * The signed representation of {@link un}.
+ *
+ * @discussion
+ * This routine is useful for comparing an unsigned value (such as a number of
+ * bytes) to the result of a routine which returns a signed type but only ever
+ * returns a negative number in the event of an error (e.g. read(2)).
+ */
+OS_ALWAYS_INLINE OS_WARN_RESULT
+static inline ssize_t
+size_signed(size_t un)
+{
+       if (un > SSIZE_MAX) {
+               os_crash("value not representable as ssize_t");
+       }
+       return (ssize_t)un;
+}
+
 /*!
  * @function os_localtime_file
  * A routine to generate a time stamp that is suitable for embedding in a file
@@ -372,6 +493,51 @@ OS_EXPORT OS_WARN_RESULT
 errno_t
 realpath_np(os_fd_t fd, char buff[static PATH_MAX]);
 
+/*!
+ * @function memdup_np
+ * Copies the given range of bytes into a new allocation.
+ *
+ * @param _new
+ * Upon successful return, a pointer to a new allocation which has had the given
+ * source bytes copied into it. The caller is responsible for calling free(3) on
+ * this object when it is no longer needed.
+ *
+ * @param src
+ * The bytes to copy.
+ *
+ * @param len
+ * The number of bytes to copy.
+ *
+ * @result
+ * On success, zero is returned. Otherwise, the implementation may return any
+ * error that can be returned by malloc(3).
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_EXPORT OS_WARN_RESULT OS_NONNULL1 OS_NONNULL2
+errno_t
+memdup_np(void **_new, const void *src, size_t len);
+
+/*!
+ * @function memdup2_np
+ * Variant of {@link memdup_np} which guarantees that memory duplication will
+ * either succeed or not return (terminating the caller).
+ *
+ * @param src
+ * The bytes to copy.
+ *
+ * @param len
+ * The number of bytes to copy.
+ *
+ * @result
+ * On success, a pointer to the new allocation which has had the given source
+ * bytes copied into it. The caller is responsible for calling free(3) on this
+ * object when it is no longer needed.
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_EXPORT OS_WARN_RESULT OS_MALLOC OS_NONNULL1
+void *
+memdup2_np(const void *src, size_t len);
+
 __END_DECLS;
 
 #endif // __DARWIN_STDLIB_H
index 23c0098e8eeebd794178603f32c398eef344fb69..b4db82ebbaa47bc7e1afcad274251e668f2c0cae 100644 (file)
 #include <os/api.h>
 #include <sys/cdefs.h>
 
+#if DARWIN_TAPI
+#include "tapi.h"
+#endif
+
 __BEGIN_DECLS;
 
 /*!
@@ -93,6 +97,21 @@ OS_EXPORT OS_COLD OS_WARN_RESULT OS_PURE
 const char *
 strerror_np(int code);
 
+/*!
+ * @function strexit_np
+ * Returns a human-readable string for the given sysexits(3) code.
+ *
+ * @param code
+ * The exit code for which to obtain the string.
+ *
+ * @result
+ * A human-readable string describing the exit condition.
+ */
+DARWIN_API_AVAILABLE_20190830
+OS_EXPORT OS_COLD OS_WARN_RESULT OS_PURE
+const char *
+strexit_np(int code);
+
 /*!
  * @function symerror_np
  * Returns the token name of the given {@link errno_t} or POSIX error
index a16d86658d57470ab0403974ea9ad075df3210c5..25a29ba0d0c87ebadd9c9610da1faabde1c4dc85 100644 (file)
 #include "h/stdlib.h"
 #include "h/string.h"
 
-#if DARWIN_TAPI
-#undef os_assert_mach
-#undef os_assert_mach_port_status
-
-// Duplicate declarations to make TAPI happy. This header is included in the
-// TAPI build as an extra public header.
-API_AVAILABLE(macos(10.14), ios(12.0), tvos(12.0), watchos(5.0))
-OS_EXPORT OS_NONNULL1
-void
-(os_assert_mach)(const char *op, kern_return_t kr);
-
-API_AVAILABLE(macos(10.14), ios(12.0), tvos(12.0), watchos(5.0))
-OS_EXPORT
-void
-os_assert_mach_port_status(const char *desc, mach_port_t p,
-               mach_port_status_t *expected);
-#endif
-
 #endif //__DARWIN_INTERNAL_H
index c1d1585e73521e5e04af1c1d4ffce973203a0eb8..d7c44078f4fa3f68c6dec7f95c46ba8f6a236b48 100644 (file)
@@ -66,6 +66,63 @@ dup_np(os_fd_t fd)
        return dfd;
 }
 
+os_fd_t
+claimfd_np(os_fd_t *fdp, const guardid_t *gdid, u_int gdflags)
+{
+       int ret = -1;
+       int fd = *fdp;
+
+       if (gdid) {
+               ret = change_fdguard_np(fd, NULL, 0, gdid, gdflags, NULL);
+               if (ret) {
+                       os_crash("change_fdguard_np: %{darwin.errno}d", errno);
+               }
+       }
+
+       *fdp = -1;
+       return fd;
+}
+
+os_fd_t
+xferfd_np(os_fd_t *fdp, const guardid_t *gdid, u_int gdflags)
+{
+       int ret = -1;
+       int fd = *fdp;
+
+       ret = change_fdguard_np(fd, gdid, gdflags, NULL, 0, NULL);
+       if (ret) {
+               os_crash("change_fdguard_np: %{darwin.errno}d", errno);
+       }
+
+       *fdp = -1;
+       return fd;
+}
+
+void
+close_drop_np(os_fd_t *fdp, const guardid_t *gdid)
+{
+       int ret = -1;
+       int fd = *fdp;
+
+       if (gdid) {
+               ret = guarded_close_np(fd, gdid);
+       } else {
+               ret = close(fd);
+       }
+
+       posix_assert_zero(ret);
+       *fdp = -1;
+}
+
+void
+close_drop_optional_np(os_fd_t *fdp, const guardid_t *gdid)
+{
+       if (!os_fd_valid(*fdp)) {
+               return;
+       }
+       close_drop_np(fdp, gdid);
+}
+
 size_t
 zsnprintf_np(char *buff, size_t len, const char *fmt, ...)
 {
index d5ba78698b1b4980e48171c6d3ca789e0afced8e..1c22fb31d57c4642545d5c8a38e93b324290756a 100644 (file)
@@ -158,3 +158,26 @@ realpath_np(os_fd_t fd, char buff[static PATH_MAX])
 
        return error;
 }
+
+errno_t
+memdup_np(void **_new, const void *src, size_t len)
+{
+       void *mynew = NULL;
+
+       mynew = malloc(len);
+       if (!mynew) {
+               return errno;
+       }
+
+       memcpy(mynew, src, len);
+       *_new = mynew;
+       return 0;
+}
+
+void *
+memdup2_np(const void *src, size_t len)
+{
+       void *_new = os_malloc(len);
+       memcpy(_new, src, len);
+       return _new;
+}
index 7dbf10507755270f9f9a08b3bd96310cd0a22fdc..79208e4218310852e9a53ba36a25e99c9fd74827 100644 (file)
@@ -71,6 +71,14 @@ typedef struct _errno_desc {
        }
 
 #pragma mark Top-Level Statics
+static const errno_desc_t _zero = {
+       .ed_error = 0,
+       .ed_sysexit = 0,
+       .ed_flags = 0,
+       .ed_sym = "0",
+       .ed_str = "successful termination",
+};
+
 static const errno_desc_t _negative_one = {
        .ed_error = __ENEG_ONE,
        .ed_sysexit = EXIT_FAILURE,
@@ -306,6 +314,10 @@ _find_error(int code)
 static const errno_desc_t *
 _find_sysexit(int code)
 {
+       if (code == 0) {
+               return &_zero;
+       }
+
        if (code == EX_BADRECEIPT_NP) {
                return &_badreceipt;
        }
@@ -352,10 +364,17 @@ strerror_np(int code)
        return _find_error(code)->ed_str;
 }
 
+const char *
+strexit_np(int code)
+{
+       const errno_desc_t *de = _find_sysexit(code);
+       return de->ed_str;
+}
+
 const char *
 symerror_np(int code)
 {
-       const errno_desc_t *de = _find_error(code);;
+       const errno_desc_t *de = _find_error(code);
        return de->ed_sym;
 }
 
diff --git a/libdarwin/tapi.h b/libdarwin/tapi.h
new file mode 100644 (file)
index 0000000..1779311
--- /dev/null
@@ -0,0 +1,59 @@
+/*!
+ * @header
+ * TAPI-specific header to ensure project builds properly in installapi.
+ */
+#ifndef __DARWIN_TAPI_H
+#define __DARWIN_TAPI_H
+
+#if !DARWIN_TAPI
+#error "This header is for the installapi action only"
+#endif
+
+#include <os/base.h>
+#include <os/availability.h>
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/mach_port.h>
+
+#undef os_assert_mach
+#undef os_assert_mach_port_status
+
+// Duplicate declarations to make TAPI happy.
+API_AVAILABLE(macos(10.14), ios(12.0), tvos(12.0), watchos(5.0))
+OS_EXPORT OS_NONNULL1
+void
+os_assert_mach(const char *op, kern_return_t kr);
+
+API_AVAILABLE(macos(10.14), ios(12.0), tvos(12.0), watchos(5.0))
+OS_EXPORT
+void
+os_assert_mach_port_status(const char *desc, mach_port_t p,
+               mach_port_status_t *expected);
+
+// TAPI and the compiler don't agree about header search paths, so if TAPI found
+// our header in the SDK, and we've increased the API version, help it out.
+#if DARWIN_API_VERSION < 20170407
+#define DARWIN_API_AVAILABLE_20170407
+#endif
+
+#if DARWIN_API_VERSION < 20180727
+#define DARWIN_API_AVAILABLE_20180727
+#endif
+
+#if DARWIN_API_VERSION < 20181020
+#define DARWIN_API_AVAILABLE_20181020
+#endif
+
+#if DARWIN_API_VERSION < 20190830
+#define DARWIN_API_AVAILABLE_20190830
+#endif
+
+#if DARWIN_API_VERSION < 20191015
+#define DARWIN_API_AVAILABLE_20191015
+#endif
+
+#if !defined(LINKER_SET_ENTRY)
+#define LINKER_SET_ENTRY(_x, _y)
+#endif
+
+#endif // __DARWIN_TAPI_H
index d68f1756ace7406211fed09b308e534734178050..43a37d0922f6963b03ca84232c2e25524294a173 100644 (file)
--- a/os/api.h
+++ b/os/api.h
@@ -71,7 +71,7 @@
  * individual preprocessor macros in this header that declare new behavior as
  * required.
  */
-#define DARWIN_API_VERSION 20181020u
+#define DARWIN_API_VERSION 20191015u
 
 #if !DARWIN_BUILDING_LIBSYSTEM_DARWIN
 #define DARWIN_API_AVAILABLE_20170407 \
                API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0), watchos(6.0))
 #define DARWIN_API_AVAILABLE_20181020 \
                API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0), watchos(6.0))
+#define DARWIN_API_AVAILABLE_20181020 \
+               API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0), watchos(6.0))
+#define DARWIN_API_AVAILABLE_20190830 \
+               API_AVAILABLE(macos(10.15.2), ios(13.3), tvos(13.3), watchos(6.1.1))
+#define DARWIN_API_AVAILABLE_20191015 \
+               API_AVAILABLE(macos(10.15.2), ios(13.3), tvos(13.3), watchos(6.1.1))
 #else
 #define DARWIN_API_AVAILABLE_20170407
 #define DARWIN_API_AVAILABLE_20180727
 #define DARWIN_API_AVAILABLE_20181020
+#define DARWIN_API_AVAILABLE_20190830
+#define DARWIN_API_AVAILABLE_20191015
 #endif
 
 /*!
index ac76db5097d79485e399e72c2feb7d20a1f5f5ed..c5bca695b2a5e49096981c64119b1329023fbb38 100644 (file)
@@ -133,6 +133,28 @@ os_assert_sprintf(int ret, size_t buff_size)
        }
 }
 
+/*!
+ * @function os_assert_asprintf
+ * A routine to assert the result of a call to {v}asprintf(3).
+ *
+ * @param ret
+ * The return value from {v}asnprintf(3).
+ *
+ * @discussion
+ * If ret is less than zero, the routine will abort the caller with a message
+ * indicating the nature of the failure in the Application Specific Information
+ * section of the resulting crash log.
+ */
+API_AVAILABLE(macos(10.15.2), ios(13.3), tvos(13.3), watchos(6.1.1))
+OS_ALWAYS_INLINE OS_COLD
+static inline void
+os_assert_asprintf(int ret)
+{
+       if (ret < 0) {
+               os_crash("error printing buffer: %s", strerror(errno));
+       }
+}
+
 /*!
  * @function os_assert_malloc
  * A routine to assert the result of allocations which may fail.
index e6cbbed6e63ecc709fad900a67a798bae246b0d5..3f6b561a457ada0f1c01d010c8c0137c34efc017 100644 (file)
@@ -9,7 +9,7 @@ endif
 
 include $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.common
 
-ifeq ($(PLATFORM),BridgeOS)
+ifneq ($(PLATFORM),MacOSX)
 EXCLUDED_SOURCES += locale.c
 endif
 
@@ -20,7 +20,7 @@ WARNING_CFLAGS := -Weverything \
        -Wno-partial-availability -Wno-used-but-marked-unused \
        -Wno-reserved-id-macro -fmacro-backtrace-limit=0 \
        -Wno-c++98-compat -Wno-extra-semi -Wno-language-extension-token
-OTHER_CFLAGS := -DDARWINTEST --std=gnu11 $(FRAMEWORK_CFLAGS) $(WARNING_CFLAGS)
+OTHER_CFLAGS := -DDARWINTEST --std=gnu11 $(FRAMEWORK_CFLAGS) $(WARNING_CFLAGS) -I$(SDK_SYSTEM_FRAMEWORK_HEADERS)
 DT_LDFLAGS += -ldarwintest_utils
 ASAN_DYLIB_PATH := /usr/local/lib/sanitizers/
 
index f4306fa4c0c0bc00eb1a307e6d2b9f43498bce67..cae43f39cf05fd3d22cf24c4a782519319862043 100644 (file)
@@ -6,30 +6,46 @@
 #include <stdlib.h>
 #include <dispatch/dispatch.h>
 
+typedef enum { PTHREAD, WORKQUEUE } thread_type_t;
+
+typedef enum {
+       NO_CORRUPTION,
+       SIG_CORRUPTION,
+       FULL_CORRUPTION,
+} corrupt_type_t;
+
 static void *
-body(void *corrupt)
+body(void *ctx)
 {
-       T_LOG("Helper thread running: %d", (bool)corrupt);
-       if (corrupt) {
-               // The pthread_t is stored at the top of the stack and could be
-               // corrupted because of a stack overflow. To make the test more
-               // reliable, we will manually smash the pthread struct directly.
-               pthread_t self = pthread_self();
+       corrupt_type_t corrupt_type = (corrupt_type_t)ctx;
+       pthread_t self = pthread_self();
+
+       T_LOG("Helper thread running: %d", corrupt_type);
+
+       // The pthread_t is stored at the top of the stack and could be
+       // corrupted because of a stack overflow. To make the test more
+       // reliable, we will manually smash the pthread struct directly.
+       switch (corrupt_type) {
+       case NO_CORRUPTION:
+               break;
+       case SIG_CORRUPTION:
+               memset(self, 0x41, 128);
+               break;
+       case FULL_CORRUPTION: /* includes TSD */
                memset(self, 0x41, 4096);
+               break;
        }
+
        // Expected behavior is that if a thread calls abort, the process should
        // abort promptly.
        abort();
        T_FAIL("Abort didn't?");
 }
 
-typedef enum { PTHREAD, WORKQUEUE } thread_type_t;
-
 static void
-abort_test(thread_type_t type, int expected_signal)
+abort_test(thread_type_t type, corrupt_type_t corrupt_type)
 {
        pid_t child = fork();
-       bool corrupt = expected_signal == SIGSEGV;
 
        if (child == 0) {
                T_LOG("Child running");
@@ -38,41 +54,54 @@ abort_test(thread_type_t type, int expected_signal)
                        pthread_t tid;
                        T_QUIET;
                        T_ASSERT_POSIX_ZERO(
-                                       pthread_create(&tid, NULL, body, (void *)corrupt), NULL);
+                                       pthread_create(&tid, NULL, body, (void *)corrupt_type), NULL);
                        break;
                }
                case WORKQUEUE: {
                        dispatch_async_f(dispatch_get_global_queue(
                                                                         DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
-                                       (void *)corrupt, &body);
+                                       (void *)corrupt_type, &body);
                        break;
                }
                }
                sleep(5);
                T_FAIL("Child didn't abort");
                exit(-1);
-       } else {
-               // Wait and check the exit status of the child
-               int status = 0;
-               pid_t pid = wait(&status);
-               T_QUIET;
-               T_ASSERT_EQ(pid, child, NULL);
-               T_QUIET;
-               T_EXPECT_FALSE(WIFEXITED(status), "WIFEXITED Status: %x", status);
-               T_QUIET;
-               T_EXPECT_TRUE(WIFSIGNALED(status), "WIFSIGNALED Status: %x", status);
-               T_QUIET;
-               T_EXPECT_FALSE(WIFSTOPPED(status), "WIFSTOPPED Status: %x", status);
-               // This test is successful if we trigger a SIGSEGV|SIGBUS or SIGABRT
-               // since both will promptly terminate the program
-               int signal = WTERMSIG(status);
+       }
+
+       // Wait and check the exit status of the child
+       int status = 0;
+       pid_t pid = wait(&status);
+       T_QUIET;
+       T_ASSERT_EQ(pid, child, NULL);
+       T_QUIET;
+       T_EXPECT_FALSE(WIFEXITED(status), "WIFEXITED Status: %x", status);
+       T_QUIET;
+       T_EXPECT_TRUE(WIFSIGNALED(status), "WIFSIGNALED Status: %x", status);
+       T_QUIET;
+       T_EXPECT_FALSE(WIFSTOPPED(status), "WIFSTOPPED Status: %x", status);
+
+       // This test is successful if we trigger a SIGSEGV|SIGBUS or SIGABRT
+       // since both will promptly terminate the program
+       int signal = WTERMSIG(status);
+
+#if defined(__i386__) || defined(__x86_64__)
+       // on intel pthread_self() reads a TSD so FULL corruption results
+       // in SIGSEGV/SIGBUS
+       if (corrupt_type == FULL_CORRUPTION) {
+               // any of these signals may happen depending on which libpthread
+               // you're running on.
                if (signal == SIGBUS) {
-                       // rdar://53269061
                        T_LOG("Converting %d to SIGSEGV", signal);
                        signal = SIGSEGV;
                }
-               T_EXPECT_EQ(signal, expected_signal, NULL);
+               T_EXPECT_EQ(signal, SIGSEGV, NULL);
+               T_END;
        }
+#endif
+
+       /* pthread calls abort_with_reason if only the signature is corrupt */
+       T_EXPECT_EQ(signal, SIGABRT, NULL);
 }
 
 static void
@@ -82,38 +111,48 @@ signal_handler(int signo)
        T_FAIL("Unexpected signal: %d\n", signo);
 }
 
-T_DECL(abort_pthread_corrupt_test, "Tests abort")
+T_DECL(abort_pthread_corrupt_test_full, "Tests abort")
 {
-       abort_test(PTHREAD, SIGSEGV);
+       abort_test(PTHREAD, FULL_CORRUPTION);
 }
 
-T_DECL(abort_workqueue_corrupt_test, "Tests abort")
+T_DECL(abort_workqueue_corrupt_test_full, "Tests abort")
 {
-       abort_test(WORKQUEUE, SIGSEGV);
+       abort_test(WORKQUEUE, FULL_CORRUPTION);
 }
 
-T_DECL(abort_pthread_handler_test, "Tests abort")
+T_DECL(abort_pthread_handler_test_full, "Tests abort")
 {
        // rdar://52892057
        T_SKIP("Abort hangs if the user registers their own SIGSEGV handler");
        signal(SIGSEGV, signal_handler);
-       abort_test(PTHREAD, SIGSEGV);
+       abort_test(PTHREAD, FULL_CORRUPTION);
 }
 
-T_DECL(abort_workqueue_handler_test, "Tests abort")
+T_DECL(abort_workqueue_handler_test_full, "Tests abort")
 {
        // rdar://52892057
        T_SKIP("Abort hangs if the user registers their own SIGSEGV handler");
        signal(SIGSEGV, signal_handler);
-       abort_test(WORKQUEUE, SIGSEGV);
+       abort_test(WORKQUEUE, FULL_CORRUPTION);
+}
+
+T_DECL(abort_pthread_corrupt_test_sig, "Tests abort")
+{
+       abort_test(PTHREAD, SIG_CORRUPTION);
+}
+
+T_DECL(abort_workqueue_corrupt_test_sig, "Tests abort")
+{
+       abort_test(WORKQUEUE, SIG_CORRUPTION);
 }
 
 T_DECL(abort_pthread_test, "Tests abort")
 {
-       abort_test(PTHREAD, SIGABRT);
+       abort_test(PTHREAD, NO_CORRUPTION);
 }
 
 T_DECL(abort_workqueue_test, "Tests abort")
 {
-       abort_test(WORKQUEUE, SIGABRT);
+       abort_test(WORKQUEUE, NO_CORRUPTION);
 }
index 56efa8e2f2c01861041d3bef0f30488b3ebf4fc0..10d4d13f0e6553f59e2261ca357036fc7278a645 100644 (file)
@@ -1,5 +1,9 @@
 #include <darwintest.h>
 
+#if !defined(DARWIN_API_AVAILABLE_20190830)
+#define DARWIN_API_AVAILABLE_20190830
+#endif
+
 #include "../libdarwin/bsd.c"
 
 static struct test_case {
index d3aa4bf19f429cd936baf03be9cdda43f414875d..8d188e98d46692a9807b9ea2181f186f27ff335d 100644 (file)
@@ -1,3 +1,4 @@
+#include <TargetConditionals.h>
 #include <locale.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -7,6 +8,7 @@
 
 #include <darwintest.h>
 
+#if TARGET_OS_OSX
 T_DECL(locale_PR_23679075, "converts a cyrillic a to uppercase")
 {
        locale_t loc = newlocale(LC_COLLATE_MASK|LC_CTYPE_MASK, "ru_RU", 0);
@@ -32,3 +34,4 @@ T_DECL(locale_PR_28774201, "return code on bad locale")
     T_EXPECT_NULL(newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, "foobar", NULL), NULL);
     T_EXPECT_EQ(errno, ENOENT, NULL);
 }
+#endif