gs_lib_ctx_stash_sanitized_arg(gs_lib_ctx_t *ctx, const char *arg)
{
    gs_lib_ctx_core_t *core;
    size_t len;
    const char *p;
    int elide = 0;

    if (ctx == NULL || ctx->core == NULL || arg == NULL)
        return 0;

    /* Sanitize arg */
    switch(*arg)
    {
    case '-':
        switch (arg[1])
        {
        case 0:   /* We can let - through unchanged */
        case '-': /* Need to check for permitted file lists */
            /* By default, we want to keep the key, but lose the value */
            p = arg+2;
            while (*p && *p != '=')
                p++;
            if (*p == '=')
                p++;
            if (*p == 0)
                break; /* No value to elide */
            /* Check for our blocked values here */
#define ARG_MATCHES(STR, ARG, LEN) \
    (strlen(STR) == LEN && !memcmp(STR, ARG, LEN))
            if (ARG_MATCHES("permit-file-read", arg+2, p-arg-3))
                elide=1;
            if (ARG_MATCHES("permit-file-write", arg+2, p-arg-3))
                elide=1;
            if (ARG_MATCHES("permit-file-control", arg+2, p-arg-3))
                elide=1;
            if (ARG_MATCHES("permit-file-all", arg+2, p-arg-3))
                elide=1;
#undef ARG_MATCHES
            /* Didn't match a blocked value, so allow it. */
            break;
        case 'd': /* We can let -dFoo=<whatever> through unchanged */
        case 'D': /* We can let -DFoo=<whatever> through unchanged */
        case 'r': /* We can let -r through unchanged */
        case 'Z': /* We can let -Z through unchanged */
        case 'g': /* We can let -g through unchanged */
        case 'P': /* We can let -P through unchanged */
        case '+': /* We can let -+ through unchanged */
        case '_': /* We can let -_ through unchanged */
        case 'u': /* We can let -u through unchanged */
        case 'q': /* We can let -q through unchanged */
            break;
        case 'I': /* Let through the I, but hide anything else */
        case 'f': /* Let through the I, but hide anything else */
            if (arg[2] == 0)
                break;
            p = arg+2;
            while (*p == 32)
                p++;
            elide = 1;
            break;
        case 's':
        case 'S':
            /* By default, we want to keep the key, but lose the value */
            p = arg+2;
            while (*p && *p != '=')
                p++;
            if (*p == '=')
                p++;
            if (*p == 0)
                break; /* No value to elide */
            /* Check for our whitelisted values here */
#define ARG_MATCHES(STR, ARG, LEN) \
    (strlen(STR) == LEN && !memcmp(STR, ARG, LEN))
            if (ARG_MATCHES("DEFAULTPAPERSIZE", arg+2, p-arg-3))
                break;
            if (ARG_MATCHES("DEVICE", arg+2, p-arg-3))
                break;
            if (ARG_MATCHES("PAPERSIZE", arg+2, p-arg-3))
                break;
            if (ARG_MATCHES("SUBSTFONT", arg+2, p-arg-3))
                break;
            if (ARG_MATCHES("ColorConversionStrategy", arg+2, p-arg-3))
                break;
            if (ARG_MATCHES("NupControl", arg+2, p-arg-3))
                break;
            if (ARG_MATCHES("PageList", arg+2, p-arg-3))
                break;
            if (ARG_MATCHES("ProcessColorModel", arg+2, p-arg-3))
                break;
#undef ARG_MATCHES
            /* Didn't match a whitelisted value, so elide it. */
            elide = 1;
            break;
        default:
            /* Shouldn't happen, but elide it just in case */
            arg = "?";
            break;
        }
        break;
    case '@':
        /* Shouldn't happen */
    default:
        /* Anything else should be elided */
        arg = "?";
        break;
    }

    core = ctx->core;
    if (elide)
        len = p-arg;
    else
        len = strlen(arg);

    if (core->arg_max == core->argc) {
        char **argv;
        int newlen = core->arg_max * 2;
        if (newlen == 0)
            newlen = 4;
        argv = (char **)gs_alloc_bytes(ctx->core->memory, sizeof(char *) * newlen,
                                       "gs_lib_ctx_args");
        if (argv == NULL)
            return gs_error_VMerror;
        if (core->argc > 0) {
            memcpy(argv, core->argv, sizeof(char *) * core->argc);
            gs_free_object(ctx->memory, core->argv, "gs_lib_ctx_args");
        }
        core->argv = argv;
        core->arg_max = newlen;
    }

    core->argv[core->argc] = (char *)gs_alloc_bytes(ctx->core->memory, len+1+elide,
                                                    "gs_lib_ctx_arg");
    if (core->argv[core->argc] == NULL)
        return gs_error_VMerror;
    memcpy(core->argv[core->argc], arg, len);
    if (elide) {
        core->argv[core->argc][len] = '?';
    }
    core->argv[core->argc][len+elide] = 0;
    core->argc++;

    return 0;
}