int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
                                 NetClientDriver type, int max)
{
    NetClientState *nc;
    int ret = 0;

    QTAILQ_FOREACH(nc, &net_clients, next) {
        if (nc->info->type == type) {
            continue;
        }
        if (!id || !strcmp(nc->name, id)) {
            if (ret < max) {
                ncs[ret] = nc;
            }
            ret++;
        }
    }

    return ret;
}


NICState *qemu_get_nic(NetClientState *nc)
{
    NetClientState *nc0 = nc - nc->queue_index;

    return (NICState *)((void *)nc0 - nc->info->size);
}


void qemu_check_nic_model(NICInfo *nd, const char *model)
{
    const char *models[2];

    models[0] = model;
    models[1] = NULL;

    if (qemu_show_nic_models(nd->model, models))
        exit(0);
    if (qemu_find_nic_model(nd, models, model) < 0)
        exit(1);
}


static void qemu_net_client_destructor(NetClientState *nc)
{
    g_free(nc);
}


void *qemu_get_nic_opaque(NetClientState *nc)
{
    NICState *nic = qemu_get_nic(nc);

    return nic->opaque;
}


bool qemu_has_vnet_hdr_len(NetClientState *nc, int len)
{
    if (!nc || !nc->info->has_vnet_hdr_len) {
        return false;
    }

    return nc->info->has_vnet_hdr_len(nc, len);
}


int qemu_show_nic_models(const char *arg, const char *const *models)
{
    int i;

    if (!arg || !is_help_option(arg)) {
        return 0;
    }

    fprintf(stderr, "qemu: Supported NIC models: ");
    for (i = 0 ; models[i]; i++)
        fprintf(stderr, "%s%c", models[i], models[i+1] ? ',' : '\n');
    return 1;
}


ModuleExport void UnregisterSVGImage(void)
{
  (void) UnregisterMagickInfo("SVGZ");
  (void) UnregisterMagickInfo("SVG");
  (void) UnregisterMagickInfo("MSVG");
#if defined(MAGICKCORE_XML_DELEGATE)
  xmlCleanupParser();
#endif
}


static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
  const char *string)
{
  char
    *next_token,
    token[MaxTextExtent];

  const char
    *p;

  double
    value;

  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
  assert(string != (const char *) NULL);
  p=(const char *) string;
  GetNextToken(p,&p,MaxTextExtent,token);
  value=StringToDouble(token,&next_token);
  if (strchr(token,'%') != (char *) NULL)
    {
      double
        alpha,
        beta;

      if (type > 0)
        {
          if (svg_info->view_box.width == 0.0)
            return(0.0);
          return(svg_info->view_box.width*value/100.0);
        }
      if (type < 0)
        {
          if (svg_info->view_box.height == 0.0)
            return(0.0);
          return(svg_info->view_box.height*value/100.0);
        }
      alpha=value-svg_info->view_box.width;
      beta=value-svg_info->view_box.height;
      return(hypot(alpha,beta)/sqrt(2.0)/100.0);
    }
  GetNextToken(p,&p,MaxTextExtent,token);
  if (LocaleNCompare(token,"cm",2) == 0)
    return(72.0*svg_info->scale[0]/2.54*value);
  if (LocaleNCompare(token,"em",2) == 0)
    return(svg_info->pointsize*value);
  if (LocaleNCompare(token,"ex",2) == 0)
    return(svg_info->pointsize*value/2.0);
  if (LocaleNCompare(token,"in",2) == 0)
    return(72.0*svg_info->scale[0]*value);
  if (LocaleNCompare(token,"mm",2) == 0)
    return(72.0*svg_info->scale[0]/25.4*value);
  if (LocaleNCompare(token,"pc",2) == 0)
    return(72.0*svg_info->scale[0]/6.0*value);
  if (LocaleNCompare(token,"pt",2) == 0)
    return(svg_info->scale[0]*value);
  if (LocaleNCompare(token,"px",2) == 0)
    return(value);
  return(value);
}


void MonCapGrant::expand_profile_mon(const EntityName& name) const
{
  if (profile == "mon") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_ALL));
    profile_grants.push_back(MonCapGrant("log", MON_CAP_ALL));
  }
  if (profile == "osd") {
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_ALL));
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("pg", MON_CAP_R | MON_CAP_W));
    profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
  }
  if (profile == "mds") {
    profile_grants.push_back(MonCapGrant("mds", MON_CAP_ALL));
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
    // This command grant is checked explicitly in MRemoveSnaps handling
    profile_grants.push_back(MonCapGrant("osd pool rmsnap"));
    profile_grants.push_back(MonCapGrant("osd blacklist"));
    profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
  }
  if (profile == "mgr") {
    profile_grants.push_back(MonCapGrant("mgr", MON_CAP_ALL));
    profile_grants.push_back(MonCapGrant("log", MON_CAP_R | MON_CAP_W));
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R | MON_CAP_W));
    profile_grants.push_back(MonCapGrant("mds", MON_CAP_R | MON_CAP_W));
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R | MON_CAP_W));
    profile_grants.push_back(MonCapGrant("auth", MON_CAP_R | MON_CAP_X));
    profile_grants.push_back(MonCapGrant("config-key", MON_CAP_R | MON_CAP_W));
    profile_grants.push_back(MonCapGrant("config", MON_CAP_R | MON_CAP_W));
  }
  if (profile == "osd" || profile == "mds" || profile == "mon" ||
      profile == "mgr") {
    StringConstraint constraint(StringConstraint::MATCH_TYPE_PREFIX,
                                string("daemon-private/") + stringify(name) +
                                string("/"));
    string prefix = string("daemon-private/") + stringify(name) + string("/");
    profile_grants.push_back(MonCapGrant("config-key get", "key", constraint));
    profile_grants.push_back(MonCapGrant("config-key put", "key", constraint));
    profile_grants.push_back(MonCapGrant("config-key set", "key", constraint));
    profile_grants.push_back(MonCapGrant("config-key exists", "key", constraint));
    profile_grants.push_back(MonCapGrant("config-key delete", "key", constraint));
  }
  if (profile == "bootstrap-osd") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));  // read monmap
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));  // read osdmap
    profile_grants.push_back(MonCapGrant("mon getmap"));
    profile_grants.push_back(MonCapGrant("osd new"));
    profile_grants.push_back(MonCapGrant("osd purge-new"));
  }
  if (profile == "bootstrap-mds") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));  // read monmap
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));  // read osdmap
    profile_grants.push_back(MonCapGrant("mon getmap"));
    profile_grants.push_back(MonCapGrant("auth get-or-create"));  // FIXME: this can expose other mds keys
    profile_grants.back().command_args["entity"] = StringConstraint(
      StringConstraint::MATCH_TYPE_PREFIX, "mds.");
    profile_grants.back().command_args["caps_mon"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "allow profile mds");
    profile_grants.back().command_args["caps_osd"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
    profile_grants.back().command_args["caps_mds"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "allow");
  }
  if (profile == "bootstrap-mgr") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));  // read monmap
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));  // read osdmap
    profile_grants.push_back(MonCapGrant("mon getmap"));
    profile_grants.push_back(MonCapGrant("auth get-or-create"));  // FIXME: this can expose other mgr keys
    profile_grants.back().command_args["entity"] = StringConstraint(
      StringConstraint::MATCH_TYPE_PREFIX, "mgr.");
    profile_grants.back().command_args["caps_mon"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "allow profile mgr");
  }
  if (profile == "bootstrap-rgw") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));  // read monmap
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));  // read osdmap
    profile_grants.push_back(MonCapGrant("mon getmap"));
    profile_grants.push_back(MonCapGrant("auth get-or-create"));  // FIXME: this can expose other mds keys
    profile_grants.back().command_args["entity"] = StringConstraint(
      StringConstraint::MATCH_TYPE_PREFIX, "client.rgw.");
    profile_grants.back().command_args["caps_mon"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "allow rw");
    profile_grants.back().command_args["caps_osd"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
  }
  if (profile == "bootstrap-rbd") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));  // read monmap
    profile_grants.push_back(MonCapGrant("auth get-or-create"));  // FIXME: this can expose other mds keys
    profile_grants.back().command_args["entity"] = StringConstraint(
      StringConstraint::MATCH_TYPE_PREFIX, "client.");
    profile_grants.back().command_args["caps_mon"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "profile rbd");
    profile_grants.back().command_args["caps_osd"] = StringConstraint(
      StringConstraint::MATCH_TYPE_REGEX,
      "^([ ,]*profile(=|[ ]+)['\"]?rbd[^ ,'\"]*['\"]?([ ]+pool(=|[ ]+)['\"]?[^,'\"]+['\"]?)?)+$");
  }
  if (profile == "fs-client") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("mds", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
  }
  if (profile == "simple-rados-client") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
  }
  if (profile == "rbd") {
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));

    // exclusive lock dead-client blacklisting (IP+nonce required)
    profile_grants.push_back(MonCapGrant("osd blacklist"));
    profile_grants.back().command_args["blacklistop"] = StringConstraint(
      StringConstraint::MATCH_TYPE_EQUAL, "add");
    profile_grants.back().command_args["addr"] = StringConstraint(
      StringConstraint::MATCH_TYPE_REGEX, "^[^/]+/[0-9]+$");
  }

  if (profile == "role-definer") {
    // grants ALL caps to the auth subsystem, read-only on the
    // monitor subsystem and nothing else.
    profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
    profile_grants.push_back(MonCapGrant("auth", MON_CAP_ALL));
  }
}


void MonCapGrant::expand_profile(int daemon_type, const EntityName& name) const
{
  // only generate this list once
  if (!profile_grants.empty())
    return;

  if (profile == "read-only") {
    // grants READ-ONLY caps monitor-wide
    // 'auth' requires MON_CAP_X even for RO, which we do not grant here.
    profile_grants.push_back(mon_rwxa_t(MON_CAP_R));
    return;
  }

  if (profile == "read-write") {
    // grants READ-WRITE caps monitor-wide
    // 'auth' requires MON_CAP_X for all operations, which we do not grant.
    profile_grants.push_back(mon_rwxa_t(MON_CAP_R | MON_CAP_W));
    return;
  }

  switch (daemon_type) {
  case CEPH_ENTITY_TYPE_MON:
    expand_profile_mon(name);
    return;
  case CEPH_ENTITY_TYPE_MGR:
    expand_profile_mgr(name);
    return;
  }
}


xf86PrivsElevated(void)
{
    static Bool privsTested = FALSE;
    static Bool privsElevated = TRUE;

    if (!privsTested) {
#if defined(WIN32)
        privsElevated = FALSE;
#else
        if ((getuid() != geteuid()) || (getgid() != getegid())) {
            privsElevated = TRUE;
        }
        else {
#if defined(HAVE_ISSETUGID)
            privsElevated = issetugid();
#elif defined(HAVE_GETRESUID)
            uid_t ruid, euid, suid;
            gid_t rgid, egid, sgid;

            if ((getresuid(&ruid, &euid, &suid) == 0) &&
                (getresgid(&rgid, &egid, &sgid) == 0)) {
                privsElevated = (euid != suid) || (egid != sgid);
            }
            else {
                printf("Failed getresuid or getresgid");
                /* Something went wrong, make defensive assumption */
                privsElevated = TRUE;
            }
#else
            if (getuid() == 0) {
                /* running as root: uid==euid==0 */
                privsElevated = FALSE;
            }
            else {
                /*
                 * If there are saved ID's the process might still be privileged
                 * even though the above test succeeded. If issetugid() and
                 * getresgid() aren't available, test this by trying to set
                 * euid to 0.
                 */
                unsigned int oldeuid;

                oldeuid = geteuid();

                if (seteuid(0) != 0) {
                    privsElevated = FALSE;
                }
                else {
                    if (seteuid(oldeuid) != 0) {
                        FatalError("Failed to drop privileges.  Exiting\n");
                    }
                    privsElevated = TRUE;
                }
            }
#endif
        }
#endif
        privsTested = TRUE;
    }
    return privsElevated;
}


LogPrintMarkers(void)
{
    /* Show what the message marker symbols mean. */
    LogWrite(0, "Markers: ");
    LogMessageVerb(X_PROBED, 0, "probed, ");
    LogMessageVerb(X_CONFIG, 0, "from config file, ");
    LogMessageVerb(X_DEFAULT, 0, "default setting,\n\t");
    LogMessageVerb(X_CMDLINE, 0, "from command line, ");
    LogMessageVerb(X_NOTICE, 0, "notice, ");
    LogMessageVerb(X_INFO, 0, "informational,\n\t");
    LogMessageVerb(X_WARNING, 0, "warning, ");
    LogMessageVerb(X_ERROR, 0, "error, ");
    LogMessageVerb(X_NOT_IMPLEMENTED, 0, "not implemented, ");
    LogMessageVerb(X_UNKNOWN, 0, "unknown.\n");
}


LogSWrite(int verb, const char *buf, size_t len, Bool end_line)
{
    static Bool newline = TRUE;
    int ret;

    if (verb < 0 || logVerbosity >= verb)
        ret = write(2, buf, len);

    if (verb < 0 || logFileVerbosity >= verb) {
        if (inSignalContext && logFileFd >= 0) {
            ret = write(logFileFd, buf, len);
#ifndef WIN32
            if (logFlush && logSync)
                fsync(logFileFd);
#endif
        }
        else if (!inSignalContext && logFile) {
            if (newline)
                fprintf(logFile, "[%10.3f] ", GetTimeInMillis() / 1000.0);
            newline = end_line;
            fwrite(buf, len, 1, logFile);
            if (logFlush) {
                fflush(logFile);
#ifndef WIN32
                if (logSync)
                    fsync(fileno(logFile));
#endif
            }
        }
        else if (!inSignalContext && needBuffer) {
            if (len > bufferUnused) {
                bufferSize += 1024;
                bufferUnused += 1024;
                saveBuffer = realloc(saveBuffer, bufferSize);
                if (!saveBuffer)
                    FatalError("realloc() failed while saving log messages\n");
            }
            bufferUnused -= len;
            memcpy(saveBuffer + bufferPos, buf, len);
            bufferPos += len;
        }
    }

    /* There's no place to log an error message if the log write
     * fails...
     */
    (void) ret;
}


AbortServer(void)
{
#ifdef XF86BIGFONT
    XF86BigfontCleanup();
#endif
    CloseWellKnownConnections();
    OsCleanup(TRUE);
    AbortDevices();
    AbortDDX(EXIT_ERR_ABORT);
    fflush(stderr);
    if (CoreDump)
        OsAbort();
    exit(1);
}


int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
        size_t pos = 0, idx = 0;
        _cleanup_strv_free_ char **names = NULL;
        int r;

        assert_return(optlen > 1, -ENODATA);
        assert_return(optval[optlen - 1] == '\0', -EINVAL);

        while (pos < optlen) {
                _cleanup_free_ char *ret = NULL;
                size_t n = 0, allocated = 0;
                bool first = true;

                for (;;) {
                        const char *label;
                        uint8_t c;

                        c = optval[pos++];

                        if (c == 0)
                                /* End of name */
                                break;
                        if (c > 63)
                                return -EBADMSG;

                        /* Literal label */
                        label = (const char *)&optval[pos];
                        pos += c;
                        if (pos >= optlen)
                                return -EMSGSIZE;

                        if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
                                return -ENOMEM;

                        if (first)
                                first = false;
                        else
                                ret[n++] = '.';

                        r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
                        if (r < 0)
                                return r;

                        n += r;
                }

                if (n == 0)
                        continue;

                if (!GREEDY_REALLOC(ret, allocated, n + 1))
                        return -ENOMEM;

                ret[n] = 0;

                r = strv_extend(&names, ret);
                if (r < 0)
                        return r;

                idx++;
        }

        *str_arr = TAKE_PTR(names);

        return idx;
}


int manager_rtnl_enumerate_addresses(Manager *m) {
        _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
        sd_netlink_message *addr;
        int r;

        assert(m);
        assert(m->rtnl);

        r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0);
        if (r < 0)
                return r;

        r = sd_netlink_message_request_dump(req, true);
        if (r < 0)
                return r;

        r = sd_netlink_call(m->rtnl, req, 0, &reply);
        if (r < 0)
                return r;

        for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
                int k;

                m->enumerating = true;

                k = manager_rtnl_process_address(m->rtnl, addr, m);
                if (k < 0)
                        r = k;

                m->enumerating = false;
        }

        return r;
}


static int manager_connect_rtnl(Manager *m) {
        int fd, r;

        assert(m);

        fd = systemd_netlink_fd();
        if (fd < 0)
                r = sd_netlink_open(&m->rtnl);
        else
                r = sd_netlink_open_fd(&m->rtnl, fd);
        if (r < 0)
                return r;

        r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE);
        if (r < 0)
                return r;

        r = sd_netlink_attach_event(m->rtnl, m->event, 0);
        if (r < 0)
                return r;

        r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
        if (r < 0)
                return r;

        r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, &manager_rtnl_process_link, m);
        if (r < 0)
                return r;

        r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &manager_rtnl_process_address, m);
        if (r < 0)
                return r;

        r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &manager_rtnl_process_address, m);
        if (r < 0)
                return r;

        r = sd_netlink_add_match(m->rtnl, RTM_NEWROUTE, &manager_rtnl_process_route, m);
        if (r < 0)
                return r;

        r = sd_netlink_add_match(m->rtnl, RTM_DELROUTE, &manager_rtnl_process_route, m);
        if (r < 0)
                return r;

        return 0;
}


bool link_ipv6ll_enabled(Link *link) {
        if (link->flags & IFF_LOOPBACK)
                return false;

        if (!link->network)
                return false;

        return link->network->link_local & ADDRESS_FAMILY_IPV6;
}


int manager_new(Manager **ret) {
        _cleanup_manager_free_ Manager *m = NULL;
        int r;

        m = new0(Manager, 1);
        if (!m)
                return -ENOMEM;

        m->state_file = strdup("/run/systemd/netif/state");
        if (!m->state_file)
                return -ENOMEM;

        r = sd_event_default(&m->event);
        if (r < 0)
                return r;

        sd_event_set_watchdog(m->event, true);

        sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
        sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);

        r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m);
        if (r < 0)
                return r;

        r = manager_connect_rtnl(m);
        if (r < 0)
                return r;

        r = manager_connect_udev(m);
        if (r < 0)
                return r;

        m->netdevs = hashmap_new(&string_hash_ops);
        if (!m->netdevs)
                return -ENOMEM;

        LIST_HEAD_INIT(m->networks);

        r = setup_default_address_pool(m);
        if (r < 0)
                return r;

        *ret = m;
        m = NULL;

        return 0;
}


static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
        Manager *m = userdata;
        Link *link = NULL;
        NetDev *netdev = NULL;
        uint16_t type;
        const char *name;
        int r, ifindex;

        assert(rtnl);
        assert(message);
        assert(m);

        if (sd_netlink_message_is_error(message)) {
                r = sd_netlink_message_get_errno(message);
                if (r < 0)
                        log_warning_errno(r, "rtnl: Could not receive link: %m");

                return 0;
        }

        r = sd_netlink_message_get_type(message, &type);
        if (r < 0) {
                log_warning_errno(r, "rtnl: Could not get message type: %m");
                return 0;
        } else if (type != RTM_NEWLINK && type != RTM_DELLINK) {
                log_warning("rtnl: Received unexpected message type when processing link");
                return 0;
        }

        r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
        if (r < 0) {
                log_warning_errno(r, "rtnl: Could not get ifindex from link: %m");
                return 0;
        } else if (ifindex <= 0) {
                log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
                return 0;
        } else
                link_get(m, ifindex, &link);

        r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
        if (r < 0) {
                log_warning_errno(r, "rtnl: Received link message without ifname: %m");
                return 0;
        } else
                netdev_get(m, name, &netdev);

        switch (type) {
        case RTM_NEWLINK:
                if (!link) {
                        /* link is new, so add it */
                        r = link_add(m, message, &link);
                        if (r < 0) {
                                log_warning_errno(r, "Could not add new link: %m");
                                return 0;
                        }
                }

                if (netdev) {
                        /* netdev exists, so make sure the ifindex matches */
                        r = netdev_set_ifindex(netdev, message);
                        if (r < 0) {
                                log_warning_errno(r, "Could not set ifindex on netdev: %m");
                                return 0;
                        }
                }

                r = link_update(link, message);
                if (r < 0)
                        return 0;

                break;

        case RTM_DELLINK:
                link_drop(link);
                netdev_drop(netdev);

                break;

        default:
                assert_not_reached("Received invalid RTNL message type.");
        }

        return 1;
}


static int link_put_carrier(Link *link, Link *carrier, Hashmap **h) {
        int r;

        assert(link);
        assert(carrier);

        if (link == carrier)
                return 0;

        if (hashmap_get(*h, INT_TO_PTR(carrier->ifindex)))
                return 0;

        r = hashmap_ensure_allocated(h, NULL);
        if (r < 0)
                return r;

        r = hashmap_put(*h, INT_TO_PTR(carrier->ifindex), carrier);
        if (r < 0)
                return r;

        return 0;
}


int manager_rtnl_enumerate_links(Manager *m) {
        _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
        sd_netlink_message *link;
        int r;

        assert(m);
        assert(m->rtnl);

        r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
        if (r < 0)
                return r;

        r = sd_netlink_message_request_dump(req, true);
        if (r < 0)
                return r;

        r = sd_netlink_call(m->rtnl, req, 0, &reply);
        if (r < 0)
                return r;

        for (link = reply; link; link = sd_netlink_message_next(link)) {
                int k;

                m->enumerating = true;

                k = manager_rtnl_process_link(m->rtnl, link, m);
                if (k < 0)
                        r = k;

                m->enumerating = false;
        }

        return r;
}


static int setup_default_address_pool(Manager *m) {
        AddressPool *p;
        int r;

        assert(m);

        /* Add in the well-known private address ranges. */

        r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7);
        if (r < 0)
                return r;

        r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16);
        if (r < 0)
                return r;

        r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12);
        if (r < 0)
                return r;

        r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8);
        if (r < 0)
                return r;

        return 0;
}


bool manager_should_reload(Manager *m) {
        return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
}


static int set_timezone_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
        _cleanup_link_unref_ Link *link = userdata;
        const sd_bus_error *e;

        assert(m);
        assert(link);

        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                return 1;

        e = sd_bus_message_get_error(m);
        if (e)
                log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set timezone: %s", e->message);

        return 1;
}


void manager_free(Manager *m) {
        Network *network;
        NetDev *netdev;
        Link *link;
        AddressPool *pool;

        if (!m)
                return;

        free(m->state_file);

        while ((link = hashmap_first(m->links)))
                link_unref(link);
        hashmap_free(m->links);

        while ((network = m->networks))
                network_free(network);

        hashmap_free(m->networks_by_name);

        while ((netdev = hashmap_first(m->netdevs)))
                netdev_unref(netdev);
        hashmap_free(m->netdevs);

        while ((pool = m->address_pools))
                address_pool_free(pool);

        sd_netlink_unref(m->rtnl);
        sd_event_unref(m->event);

        sd_event_source_unref(m->udev_event_source);
        udev_monitor_unref(m->udev_monitor);
        udev_unref(m->udev);

        sd_bus_unref(m->bus);
        sd_bus_slot_unref(m->prepare_for_sleep_slot);
        sd_event_source_unref(m->bus_retry_event_source);

        free(m);
}


static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
        _cleanup_free_ struct in_addr *addresses = NULL;
        size_t n_addresses = 0, n_allocated = 0;
        char **a;

        log_debug("Copying DNS server information from %s", link->ifname);

        if (!link->network)
                return 0;

        STRV_FOREACH(a, link->network->dns) {
                struct in_addr ia;

                /* Only look for IPv4 addresses */
                if (inet_pton(AF_INET, *a, &ia) <= 0)
                        continue;

                if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
                        return log_oom();

                addresses[n_addresses++] = ia;
        }

        if (link->network->dhcp_dns &&
            link->dhcp_lease) {
                const struct in_addr *da = NULL;
                int n;

                n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
                if (n > 0) {

                        if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
                                return log_oom();

                        memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr));
                        n_addresses += n;
                }
        }

        if (n_addresses <= 0)
                return 0;

        return sd_dhcp_server_set_dns(s, addresses, n_addresses);
}


int ndisc_configure(Link *link) {
        int r;

        assert_return(link, -EINVAL);

        r = sd_ndisc_new(&link->ndisc_router_discovery);
        if (r < 0)
                return r;

        r = sd_ndisc_attach_event(link->ndisc_router_discovery, NULL, 0);
        if (r < 0)
                return r;

        r = sd_ndisc_set_mac(link->ndisc_router_discovery, &link->mac);
        if (r < 0)
                return r;

        r = sd_ndisc_set_index(link->ndisc_router_discovery, link->ifindex);
        if (r < 0)
                return r;

        r = sd_ndisc_set_callback(link->ndisc_router_discovery,
                                  ndisc_router_handler,
                                  NULL,
                                  NULL,
                                  ndisc_handler,
                                  link);

        return r;
}


static int manager_reset_all(Manager *m) {
        Link *link;
        Iterator i;
        int r;

        assert(m);

        HASHMAP_FOREACH(link, m->links, i) {
                r = link_carrier_reset(link);
                if (r < 0)
                        log_link_warning_errno(link, r, "Could not reset carrier: %m");
        }

        return 0;
}


static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
        _cleanup_link_unref_ Link *link = userdata;
        const sd_bus_error *e;

        assert(m);
        assert(link);

        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                return 1;

        e = sd_bus_message_get_error(m);
        if (e)
                log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set hostname: %s", e->message);

        return 1;
}


static int link_set_ipv6_hop_limit(Link *link) {
        char buf[DECIMAL_STR_MAX(unsigned) + 1];
        const char *p = NULL;
        int r;

        /* Make this a NOP if IPv6 is not available */
        if (!socket_ipv6_is_supported())
                return 0;

        if (link->flags & IFF_LOOPBACK)
                return 0;

        if (link->network->ipv6_hop_limit < 0)
                return 0;

        p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/hop_limit");

        xsprintf(buf, "%u", link->network->ipv6_hop_limit);

        r = write_string_file(p, buf, 0);
        if (r < 0) {
                /* If the right value is set anyway, don't complain */
                if (verify_one_line_file(p, buf) > 0)
                        return 0;

                log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m");
        }

        return 0;
}


zstopped(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;

    check_op(1);
    /* Mark the execution stack, and push the default result */
    /* in case control returns normally. */
    check_estack(5);
    push_mark_estack(es_stopped, no_cleanup);
    ++esp;
    make_false(esp);		/* save the result */
    ++esp;
    make_int(esp, 1);		/* save the signal mask */
    push_op_estack(stopped_push);
    push_op_estack(zexec);	/* execute the operand */
    return o_push_estack;
}


push_execstack(i_ctx_t *i_ctx_p, os_ptr op1, bool include_marks,
               op_proc_t cont)
{
    uint size;
    /*
     * We can't do this directly, because the interpreter
     * might have cached some state.  To force the interpreter
     * to update the stored state, we push a continuation on
     * the exec stack; the continuation is executed immediately,
     * and does the actual transfer.
     */
    uint depth;

    if (!r_is_array(op1))
        return_op_typecheck(op1);
    /* Check the length before the write access per CET 28-03 */
    size = r_size(op1);
    depth = count_exec_stack(i_ctx_p, include_marks);
    if (depth > size)
        return_error(gs_error_rangecheck);
    check_write(*op1);
    {
        int code = ref_stack_store_check(&e_stack, op1, size, 0);

        if (code < 0)
            return code;
    }
    check_estack(1);
    r_set_size(op1, depth);
    push_op_estack(cont);
    return o_push_estack;
}


zcurrentfile(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    ref *fp;

    push(1);
    /* Check the cache first */
    if (esfile != 0) {
#ifdef DEBUG
        /* Check that esfile is valid. */
        ref *efp = zget_current_file(i_ctx_p);

        if (esfile != efp) {
            lprintf2("currentfile: esfile=0x%lx, efp=0x%lx\n",
                     (ulong) esfile, (ulong) efp);
            ref_assign(op, efp);
        } else
#endif
            ref_assign(op, esfile);
    } else if ((fp = zget_current_file(i_ctx_p)) == 0) {	/* Return an invalid file object. */
        /* This doesn't make a lot of sense to me, */
        /* but it's what the PostScript manual specifies. */
        make_invalid_file(i_ctx_p, op);
    } else {
        ref_assign(op, fp);
        esfile_set_cache(fp);
    }
    /* Make the returned value literal. */
    r_clear_attrs(op, a_executable);
    return 0;
}


static MagickBooleanType ProcessMSLScript(const ImageInfo *image_info,
  Image **image,ExceptionInfo *exception)
{
  char
    message[MagickPathExtent];

  Image
    *msl_image;

  int
    status;

  ssize_t
    n;

  MSLInfo
    msl_info;

  xmlSAXHandler
    sax_modules;

  xmlSAXHandlerPtr
    sax_handler;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(image != (Image **) NULL);
  msl_image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,msl_image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
        msl_image->filename);
      msl_image=DestroyImageList(msl_image);
      return(MagickFalse);
    }
  msl_image->columns=1;
  msl_image->rows=1;
  /*
    Parse MSL file.
  */
  (void) memset(&msl_info,0,sizeof(msl_info));
  msl_info.exception=exception;
  msl_info.image_info=(ImageInfo **) AcquireMagickMemory(
    sizeof(*msl_info.image_info));
  msl_info.draw_info=(DrawInfo **) AcquireMagickMemory(
    sizeof(*msl_info.draw_info));
  /* top of the stack is the MSL file itself */
  msl_info.image=(Image **) AcquireMagickMemory(sizeof(*msl_info.image));
  msl_info.attributes=(Image **) AcquireMagickMemory(
    sizeof(*msl_info.attributes));
  msl_info.group_info=(MSLGroupInfo *) AcquireMagickMemory(
    sizeof(*msl_info.group_info));
  if ((msl_info.image_info == (ImageInfo **) NULL) ||
      (msl_info.draw_info == (DrawInfo **) NULL) ||
      (msl_info.image == (Image **) NULL) ||
      (msl_info.attributes == (Image **) NULL) ||
      (msl_info.group_info == (MSLGroupInfo *) NULL))
    ThrowFatalException(ResourceLimitFatalError,"UnableToInterpretMSLImage");
  *msl_info.image_info=CloneImageInfo(image_info);
  *msl_info.draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
  *msl_info.attributes=AcquireImage(image_info,exception);
  msl_info.group_info[0].numImages=0;
  /* the first slot is used to point to the MSL file image */
  *msl_info.image=msl_image;
  if (*image != (Image *) NULL)
    MSLPushImage(&msl_info,*image);
  (void) xmlSubstituteEntitiesDefault(1);
  (void) memset(&sax_modules,0,sizeof(sax_modules));
  sax_modules.internalSubset=MSLInternalSubset;
  sax_modules.isStandalone=MSLIsStandalone;
  sax_modules.hasInternalSubset=MSLHasInternalSubset;
  sax_modules.hasExternalSubset=MSLHasExternalSubset;
  sax_modules.resolveEntity=MSLResolveEntity;
  sax_modules.getEntity=MSLGetEntity;
  sax_modules.entityDecl=MSLEntityDeclaration;
  sax_modules.notationDecl=MSLNotationDeclaration;
  sax_modules.attributeDecl=MSLAttributeDeclaration;
  sax_modules.elementDecl=MSLElementDeclaration;
  sax_modules.unparsedEntityDecl=MSLUnparsedEntityDeclaration;
  sax_modules.setDocumentLocator=MSLSetDocumentLocator;
  sax_modules.startDocument=MSLStartDocument;
  sax_modules.endDocument=MSLEndDocument;
  sax_modules.startElement=MSLStartElement;
  sax_modules.endElement=MSLEndElement;
  sax_modules.reference=MSLReference;
  sax_modules.characters=MSLCharacters;
  sax_modules.ignorableWhitespace=MSLIgnorableWhitespace;
  sax_modules.processingInstruction=MSLProcessingInstructions;
  sax_modules.comment=MSLComment;
  sax_modules.warning=MSLWarning;
  sax_modules.error=MSLError;
  sax_modules.fatalError=MSLError;
  sax_modules.getParameterEntity=MSLGetParameterEntity;
  sax_modules.cdataBlock=MSLCDataBlock;
  sax_modules.externalSubset=MSLExternalSubset;
  sax_handler=(&sax_modules);
  msl_info.parser=xmlCreatePushParserCtxt(sax_handler,&msl_info,(char *) NULL,0,
    msl_image->filename);
  while (ReadBlobString(msl_image,message) != (char *) NULL)
  {
    n=(ssize_t) strlen(message);
    if (n == 0)
      continue;
    status=xmlParseChunk(msl_info.parser,message,(int) n,MagickFalse);
    if (status != 0)
      break;
    (void) xmlParseChunk(msl_info.parser," ",1,MagickFalse);
    if (msl_info.exception->severity >= ErrorException)
      break;
  }
  if (msl_info.exception->severity == UndefinedException)
    (void) xmlParseChunk(msl_info.parser," ",1,MagickTrue);
  /*
    Free resources.
  */
  xmlFreeParserCtxt(msl_info.parser);
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
  if (*image == (Image *) NULL)
    *image=CloneImage(*msl_info.image,0,0,MagickTrue,exception);
  while (msl_info.n >= 0)
  {
    msl_info.image[msl_info.n]=DestroyImage(msl_info.image[msl_info.n]);
    msl_info.attributes[msl_info.n]=DestroyImage(
      msl_info.attributes[msl_info.n]);
    msl_info.draw_info[msl_info.n]=DestroyDrawInfo(
      msl_info.draw_info[msl_info.n]);
    msl_info.image_info[msl_info.n]=DestroyImageInfo(
      msl_info.image_info[msl_info.n]);
    msl_info.n--;
  }
  msl_info.draw_info=(DrawInfo **) RelinquishMagickMemory(msl_info.draw_info);
  msl_info.image=(Image **) RelinquishMagickMemory(msl_info.image);
  msl_info.attributes=(Image **) RelinquishMagickMemory(msl_info.attributes);
  msl_info.image_info=(ImageInfo **) RelinquishMagickMemory(
    msl_info.image_info);
  msl_info.group_info=(MSLGroupInfo *) RelinquishMagickMemory(
    msl_info.group_info);
  if (msl_info.exception->severity != UndefinedException)
    return(MagickFalse);
  return(MagickTrue);
}


pack_unpack1(VALUE str, VALUE fmt)
{
    return pack_unpack_internal(str, fmt, UNPACK_1);
}


pack_pack(int argc, VALUE *argv, VALUE ary)
{
    static const char nul10[] = "\0\0\0\0\0\0\0\0\0\0";
    static const char spc10[] = "          ";
    const char *p, *pend;
    VALUE fmt, opt = Qnil, res, from, associates = 0, buffer = 0;
    char type;
    long len, idx, plen;
    const char *ptr;
    int enc_info = 1;		/* 0 - BINARY, 1 - US-ASCII, 2 - UTF-8 */
#ifdef NATINT_PACK
    int natint;		/* native integer */
#endif
    int integer_size, bigendian_p;

    rb_scan_args(argc, argv, "10:", &fmt, &opt);

    StringValue(fmt);
    p = RSTRING_PTR(fmt);
    pend = p + RSTRING_LEN(fmt);
    if (!NIL_P(opt)) {
	static ID keyword_ids[1];
	if (!keyword_ids[0])
	    CONST_ID(keyword_ids[0], "buffer");

	rb_get_kwargs(opt, keyword_ids, 0, 1, &buffer);

	if (buffer != Qundef && !RB_TYPE_P(buffer, T_STRING))
	    rb_raise(rb_eTypeError, "buffer must be String, not %s", rb_obj_classname(buffer));
    }
    if (buffer)
	res = buffer;
    else
	res = rb_str_buf_new(0);

    idx = 0;

#define TOO_FEW (rb_raise(rb_eArgError, toofew), 0)
#define MORE_ITEM (idx < RARRAY_LEN(ary))
#define THISFROM (MORE_ITEM ? RARRAY_AREF(ary, idx) : TOO_FEW)
#define NEXTFROM (MORE_ITEM ? RARRAY_AREF(ary, idx++) : TOO_FEW)

    while (p < pend) {
	int explicit_endian = 0;
	if (RSTRING_PTR(fmt) + RSTRING_LEN(fmt) != pend) {
	    rb_raise(rb_eRuntimeError, "format string modified");
	}
	type = *p++;		/* get data type */
#ifdef NATINT_PACK
	natint = 0;
#endif

	if (ISSPACE(type)) continue;
	if (type == '#') {
	    while ((p < pend) && (*p != '\n')) {
		p++;
	    }
	    continue;
	}

	{
          modifiers:
	    switch (*p) {
	      case '_':
	      case '!':
		if (strchr(natstr, type)) {
#ifdef NATINT_PACK
		    natint = 1;
#endif
		    p++;
		}
		else {
		    rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
		}
		goto modifiers;

	      case '<':
	      case '>':
		if (!strchr(endstr, type)) {
		    rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
		}
		if (explicit_endian) {
		    rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
		}
		explicit_endian = *p++;
		goto modifiers;
	    }
	}

	if (*p == '*') {	/* set data length */
	    len = strchr("@Xxu", type) ? 0
                : strchr("PMm", type) ? 1
                : RARRAY_LEN(ary) - idx;
	    p++;
	}
	else if (ISDIGIT(*p)) {
	    errno = 0;
	    len = STRTOUL(p, (char**)&p, 10);
	    if (errno) {
		rb_raise(rb_eRangeError, "pack length too big");
	    }
	}
	else {
	    len = 1;
	}

	switch (type) {
	  case 'U':
	    /* if encoding is US-ASCII, upgrade to UTF-8 */
	    if (enc_info == 1) enc_info = 2;
	    break;
	  case 'm': case 'M': case 'u':
	    /* keep US-ASCII (do nothing) */
	    break;
	  default:
	    /* fall back to BINARY */
	    enc_info = 0;
	    break;
	}
	switch (type) {
	  case 'A': case 'a': case 'Z':
	  case 'B': case 'b':
	  case 'H': case 'h':
	    from = NEXTFROM;
	    if (NIL_P(from)) {
		ptr = "";
		plen = 0;
	    }
	    else {
		StringValue(from);
		ptr = RSTRING_PTR(from);
		plen = RSTRING_LEN(from);
		OBJ_INFECT(res, from);
	    }

	    if (p[-1] == '*')
		len = plen;

	    switch (type) {
	      case 'a':		/* arbitrary binary string (null padded)  */
	      case 'A':         /* arbitrary binary string (ASCII space padded) */
	      case 'Z':         /* null terminated string  */
		if (plen >= len) {
		    rb_str_buf_cat(res, ptr, len);
		    if (p[-1] == '*' && type == 'Z')
			rb_str_buf_cat(res, nul10, 1);
		}
		else {
		    rb_str_buf_cat(res, ptr, plen);
		    len -= plen;
		    while (len >= 10) {
			rb_str_buf_cat(res, (type == 'A')?spc10:nul10, 10);
			len -= 10;
		    }
		    rb_str_buf_cat(res, (type == 'A')?spc10:nul10, len);
		}
		break;

#define castchar(from) (char)((from) & 0xff)

	      case 'b':		/* bit string (ascending) */
		{
		    int byte = 0;
		    long i, j = 0;

		    if (len > plen) {
			j = (len - plen + 1)/2;
			len = plen;
		    }
		    for (i=0; i++ < len; ptr++) {
			if (*ptr & 1)
			    byte |= 128;
			if (i & 7)
			    byte >>= 1;
			else {
			    char c = castchar(byte);
			    rb_str_buf_cat(res, &c, 1);
			    byte = 0;
			}
		    }
		    if (len & 7) {
			char c;
			byte >>= 7 - (len & 7);
			c = castchar(byte);
			rb_str_buf_cat(res, &c, 1);
		    }
		    len = j;
		    goto grow;
		}
		break;

	      case 'B':		/* bit string (descending) */
		{
		    int byte = 0;
		    long i, j = 0;

		    if (len > plen) {
			j = (len - plen + 1)/2;
			len = plen;
		    }
		    for (i=0; i++ < len; ptr++) {
			byte |= *ptr & 1;
			if (i & 7)
			    byte <<= 1;
			else {
			    char c = castchar(byte);
			    rb_str_buf_cat(res, &c, 1);
			    byte = 0;
			}
		    }
		    if (len & 7) {
			char c;
			byte <<= 7 - (len & 7);
			c = castchar(byte);
			rb_str_buf_cat(res, &c, 1);
		    }
		    len = j;
		    goto grow;
		}
		break;

	      case 'h':		/* hex string (low nibble first) */
		{
		    int byte = 0;
		    long i, j = 0;

		    if (len > plen) {
			j = (len + 1) / 2 - (plen + 1) / 2;
			len = plen;
		    }
		    for (i=0; i++ < len; ptr++) {
			if (ISALPHA(*ptr))
			    byte |= (((*ptr & 15) + 9) & 15) << 4;
			else
			    byte |= (*ptr & 15) << 4;
			if (i & 1)
			    byte >>= 4;
			else {
			    char c = castchar(byte);
			    rb_str_buf_cat(res, &c, 1);
			    byte = 0;
			}
		    }
		    if (len & 1) {
			char c = castchar(byte);
			rb_str_buf_cat(res, &c, 1);
		    }
		    len = j;
		    goto grow;
		}
		break;

	      case 'H':		/* hex string (high nibble first) */
		{
		    int byte = 0;
		    long i, j = 0;

		    if (len > plen) {
			j = (len + 1) / 2 - (plen + 1) / 2;
			len = plen;
		    }
		    for (i=0; i++ < len; ptr++) {
			if (ISALPHA(*ptr))
			    byte |= ((*ptr & 15) + 9) & 15;
			else
			    byte |= *ptr & 15;
			if (i & 1)
			    byte <<= 4;
			else {
			    char c = castchar(byte);
			    rb_str_buf_cat(res, &c, 1);
			    byte = 0;
			}
		    }
		    if (len & 1) {
			char c = castchar(byte);
			rb_str_buf_cat(res, &c, 1);
		    }
		    len = j;
		    goto grow;
		}
		break;
	    }
	    break;

	  case 'c':		/* signed char */
	  case 'C':		/* unsigned char */
            integer_size = 1;
            bigendian_p = BIGENDIAN_P(); /* not effective */
            goto pack_integer;

	  case 's':		/* s for int16_t, s! for signed short */
            integer_size = NATINT_LEN(short, 2);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'S':		/* S for uint16_t, S! for unsigned short */
            integer_size = NATINT_LEN(short, 2);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'i':		/* i and i! for signed int */
            integer_size = (int)sizeof(int);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'I':		/* I and I! for unsigned int */
            integer_size = (int)sizeof(int);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'l':		/* l for int32_t, l! for signed long */
            integer_size = NATINT_LEN(long, 4);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'L':		/* L for uint32_t, L! for unsigned long */
            integer_size = NATINT_LEN(long, 4);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'q':		/* q for int64_t, q! for signed long long */
	    integer_size = NATINT_LEN_Q;
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'Q':		/* Q for uint64_t, Q! for unsigned long long */
	    integer_size = NATINT_LEN_Q;
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

	  case 'j':		/* j for intptr_t */
	    integer_size = sizeof(intptr_t);
	    bigendian_p = BIGENDIAN_P();
	    goto pack_integer;

	  case 'J':		/* J for uintptr_t */
	    integer_size = sizeof(uintptr_t);
	    bigendian_p = BIGENDIAN_P();
	    goto pack_integer;

	  case 'n':		/* 16 bit (2 bytes) integer (network byte-order)  */
            integer_size = 2;
            bigendian_p = 1;
            goto pack_integer;

	  case 'N':		/* 32 bit (4 bytes) integer (network byte-order) */
            integer_size = 4;
            bigendian_p = 1;
            goto pack_integer;

	  case 'v':		/* 16 bit (2 bytes) integer (VAX byte-order) */
            integer_size = 2;
            bigendian_p = 0;
            goto pack_integer;

	  case 'V':		/* 32 bit (4 bytes) integer (VAX byte-order) */
            integer_size = 4;
            bigendian_p = 0;
            goto pack_integer;

          pack_integer:
	    if (explicit_endian) {
		bigendian_p = explicit_endian == '>';
	    }
            if (integer_size > MAX_INTEGER_PACK_SIZE)
                rb_bug("unexpected intger size for pack: %d", integer_size);
            while (len-- > 0) {
                char intbuf[MAX_INTEGER_PACK_SIZE];

                from = NEXTFROM;
                rb_integer_pack(from, intbuf, integer_size, 1, 0,
                    INTEGER_PACK_2COMP |
                    (bigendian_p ? INTEGER_PACK_BIG_ENDIAN : INTEGER_PACK_LITTLE_ENDIAN));
                rb_str_buf_cat(res, intbuf, integer_size);
            }
	    break;

	  case 'f':		/* single precision float in native format */
	  case 'F':		/* ditto */
	    while (len-- > 0) {
		float f;

		from = NEXTFROM;
		f = (float)RFLOAT_VALUE(rb_to_float(from));
		rb_str_buf_cat(res, (char*)&f, sizeof(float));
	    }
	    break;

	  case 'e':		/* single precision float in VAX byte-order */
	    while (len-- > 0) {
		FLOAT_CONVWITH(tmp);

		from = NEXTFROM;
		tmp.f = (float)RFLOAT_VALUE(rb_to_float(from));
		HTOVF(tmp);
		rb_str_buf_cat(res, tmp.buf, sizeof(float));
	    }
	    break;

	  case 'E':		/* double precision float in VAX byte-order */
	    while (len-- > 0) {
		DOUBLE_CONVWITH(tmp);
		from = NEXTFROM;
		tmp.d = RFLOAT_VALUE(rb_to_float(from));
		HTOVD(tmp);
		rb_str_buf_cat(res, tmp.buf, sizeof(double));
	    }
	    break;

	  case 'd':		/* double precision float in native format */
	  case 'D':		/* ditto */
	    while (len-- > 0) {
		double d;

		from = NEXTFROM;
		d = RFLOAT_VALUE(rb_to_float(from));
		rb_str_buf_cat(res, (char*)&d, sizeof(double));
	    }
	    break;

	  case 'g':		/* single precision float in network byte-order */
	    while (len-- > 0) {
		FLOAT_CONVWITH(tmp);
		from = NEXTFROM;
		tmp.f = (float)RFLOAT_VALUE(rb_to_float(from));
		HTONF(tmp);
		rb_str_buf_cat(res, tmp.buf, sizeof(float));
	    }
	    break;

	  case 'G':		/* double precision float in network byte-order */
	    while (len-- > 0) {
		DOUBLE_CONVWITH(tmp);

		from = NEXTFROM;
		tmp.d = RFLOAT_VALUE(rb_to_float(from));
		HTOND(tmp);
		rb_str_buf_cat(res, tmp.buf, sizeof(double));
	    }
	    break;

	  case 'x':		/* null byte */
	  grow:
	    while (len >= 10) {
		rb_str_buf_cat(res, nul10, 10);
		len -= 10;
	    }
	    rb_str_buf_cat(res, nul10, len);
	    break;

	  case 'X':		/* back up byte */
	  shrink:
	    plen = RSTRING_LEN(res);
	    if (plen < len)
		rb_raise(rb_eArgError, "X outside of string");
	    rb_str_set_len(res, plen - len);
	    break;

	  case '@':		/* null fill to absolute position */
	    len -= RSTRING_LEN(res);
	    if (len > 0) goto grow;
	    len = -len;
	    if (len > 0) goto shrink;
	    break;

	  case '%':
	    rb_raise(rb_eArgError, "%% is not supported");
	    break;

	  case 'U':		/* Unicode character */
	    while (len-- > 0) {
		SIGNED_VALUE l;
		char buf[8];
		int le;

		from = NEXTFROM;
		from = rb_to_int(from);
		l = NUM2LONG(from);
		if (l < 0) {
		    rb_raise(rb_eRangeError, "pack(U): value out of range");
		}
		le = rb_uv_to_utf8(buf, l);
		rb_str_buf_cat(res, (char*)buf, le);
	    }
	    break;

	  case 'u':		/* uuencoded string */
	  case 'm':		/* base64 encoded string */
	    from = NEXTFROM;
	    StringValue(from);
	    ptr = RSTRING_PTR(from);
	    plen = RSTRING_LEN(from);
	    OBJ_INFECT(res, from);

	    if (len == 0 && type == 'm') {
		encodes(res, ptr, plen, type, 0);
		ptr += plen;
		break;
	    }
	    if (len <= 2)
		len = 45;
	    else if (len > 63 && type == 'u')
		len = 63;
	    else
		len = len / 3 * 3;
	    while (plen > 0) {
		long todo;

		if (plen > len)
		    todo = len;
		else
		    todo = plen;
		encodes(res, ptr, todo, type, 1);
		plen -= todo;
		ptr += todo;
	    }
	    break;

	  case 'M':		/* quoted-printable encoded string */
	    from = rb_obj_as_string(NEXTFROM);
	    OBJ_INFECT(res, from);
	    if (len <= 1)
		len = 72;
	    qpencode(res, from, len);
	    break;

	  case 'P':		/* pointer to packed byte string */
	    from = THISFROM;
	    if (!NIL_P(from)) {
		StringValue(from);
		if (RSTRING_LEN(from) < len) {
		    rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
			     RSTRING_LEN(from), len);
		}
	    }
	    len = 1;
	    /* FALL THROUGH */
	  case 'p':		/* pointer to string */
	    while (len-- > 0) {
		char *t;
		from = NEXTFROM;
		if (NIL_P(from)) {
		    t = 0;
		}
		else {
		    t = StringValuePtr(from);
		    OBJ_INFECT(res, from);
		    rb_obj_taint(from);
		}
		if (!associates) {
		    associates = rb_ary_new();
		}
		rb_ary_push(associates, from);
		rb_str_buf_cat(res, (char*)&t, sizeof(char*));
	    }
	    break;

	  case 'w':		/* BER compressed integer  */
	    while (len-- > 0) {
		VALUE buf = rb_str_new(0, 0);
                size_t numbytes;
                int sign;
                char *cp;

		from = NEXTFROM;
                from = rb_to_int(from);
                numbytes = rb_absint_numwords(from, 7, NULL);
                if (numbytes == 0)
                    numbytes = 1;
                buf = rb_str_new(NULL, numbytes);

                sign = rb_integer_pack(from, RSTRING_PTR(buf), RSTRING_LEN(buf), 1, 1, INTEGER_PACK_BIG_ENDIAN);

                if (sign < 0)
                    rb_raise(rb_eArgError, "can't compress negative numbers");
                if (sign == 2)
                    rb_bug("buffer size problem?");

                cp = RSTRING_PTR(buf);
                while (1 < numbytes) {
                  *cp |= 0x80;
                  cp++;
                  numbytes--;
                }

                rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf));
	    }
	    break;

	  default: {
	    char unknown[5];
	    if (ISPRINT(type)) {
		unknown[0] = type;
		unknown[1] = '\0';
	    }
	    else {
		snprintf(unknown, sizeof(unknown), "\\x%.2x", type & 0xff);
	    }
	    rb_warning("unknown pack directive '%s' in '% "PRIsVALUE"'",
		       unknown, fmt);
	    break;
	  }
	}
    }

    if (associates) {
	str_associate(res, associates);
    }
    OBJ_INFECT(res, fmt);
    switch (enc_info) {
      case 1:
	ENCODING_CODERANGE_SET(res, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
	break;
      case 2:
	rb_enc_set_index(res, rb_utf8_encindex());
	break;
      default:
	/* do nothing, keep ASCII-8BIT */
	break;
    }
    return res;
}