/***********************************************************
Copyright (c) 1993, Oracle and/or its affiliates. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Digital not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

/*

Copyright 1987, 1988, 1994, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.

*/

/* Make sure all wm properties can make it out of the resource manager */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "IntrinsicI.h"
#include "StringDefs.h"
#include "CoreP.h"
#include "ShellP.h"
#include <stdio.h>
#include <X11/Xlocale.h>
#ifdef XTHREADS
#include <X11/Xthreads.h>
#endif
#ifndef WIN32
#define X_INCLUDE_PWD_H
#define XOS_USE_XT_LOCKING
#include <X11/Xos_r.h>
#endif

#include <stdlib.h>

#if (defined(SUNSHLIB) || defined(AIXSHLIB)) && defined(SHAREDCODE)
/*
 * If used as a shared library, generate code under a different name so that
 * the stub routines in sharedlib.c get loaded into the application binary.
 */
#define XtToolkitInitialize _XtToolkitInitialize
#define XtOpenApplication _XtOpenApplication
#define XtAppInitialize _XtAppInitialize
#define XtInitialize _XtInitialize
#endif                          /* (SUNSHLIB || AIXSHLIB) && SHAREDCODE */

/*
 * hpux
 * Hand-patched versions of HP-UX prior to version 7.0 can usefully add
 * -DUSE_UNAME in the appropriate config file to get long hostnames.
 */

#ifdef USG
#define USE_UNAME
#endif

#ifdef USE_UNAME
#include <sys/utsname.h>
#endif

/* some unspecified magic number of expected search levels for Xrm */
#define SEARCH_LIST_SIZE 1000

/*
 This is a set of default records describing the command line arguments that
 Xlib will parse and set into the resource data base.

 This list is applied before the users list to enforce these defaults.  This is
 policy, which the toolkit avoids but I hate differing programs at this level.
*/

/* *INDENT-OFF* */
static XrmOptionDescRec const opTable[] = {
{"+rv",               "*reverseVideo",     XrmoptionNoArg,   (XtPointer) "off"},
{"+synchronous",      "*synchronous",      XrmoptionNoArg,   (XtPointer) "off"},
{"-background",       "*background",       XrmoptionSepArg,  (XtPointer) NULL},
{"-bd",               "*borderColor",      XrmoptionSepArg,  (XtPointer) NULL},
{"-bg",               "*background",       XrmoptionSepArg,  (XtPointer) NULL},
{"-bordercolor",      "*borderColor",      XrmoptionSepArg,  (XtPointer) NULL},
{"-borderwidth",      ".borderWidth",      XrmoptionSepArg,  (XtPointer) NULL},
{"-bw",               ".borderWidth",      XrmoptionSepArg,  (XtPointer) NULL},
{"-display",          ".display",          XrmoptionSepArg,  (XtPointer) NULL},
{"-fg",               "*foreground",       XrmoptionSepArg,  (XtPointer) NULL},
{"-fn",               "*font",             XrmoptionSepArg,  (XtPointer) NULL},
{"-font",             "*font",             XrmoptionSepArg,  (XtPointer) NULL},
{"-foreground",       "*foreground",       XrmoptionSepArg,  (XtPointer) NULL},
{"-geometry",         ".geometry",         XrmoptionSepArg,  (XtPointer) NULL},
{"-iconic",           ".iconic",           XrmoptionNoArg,   (XtPointer) "on"},
{"-name",             ".name",             XrmoptionSepArg,  (XtPointer) NULL},
{"-reverse",          "*reverseVideo",     XrmoptionNoArg,   (XtPointer) "on"},
{"-rv",               "*reverseVideo",     XrmoptionNoArg,   (XtPointer) "on"},
{"-selectionTimeout", ".selectionTimeout", XrmoptionSepArg,  (XtPointer) NULL},
{"-synchronous",      "*synchronous",      XrmoptionNoArg,   (XtPointer) "on"},
{"-title",            ".title",            XrmoptionSepArg,  (XtPointer) NULL},
{"-xnllanguage",      ".xnlLanguage",      XrmoptionSepArg,  (XtPointer) NULL},
{"-xrm",              NULL,                XrmoptionResArg,  (XtPointer) NULL},
{"-xtsessionID",      ".sessionID",        XrmoptionSepArg,  (XtPointer) NULL},
};
/* *INDENT-ON* */

/*
 * GetHostname - emulates gethostname() on non-bsd systems.
 */

static void
GetHostname(char *buf, int maxlen)
{
#ifdef USE_UNAME
    int len;
    struct utsname name;

    if (maxlen <= 0 || buf == NULL)
        return;

    uname(&name);
    len = strlen(name.nodename);
    if (len >= maxlen)
        len = maxlen;
    (void) strncpy(buf, name.nodename, len - 1);
    buf[len - 1] = '\0';
#else
    if (maxlen <= 0 || buf == NULL)
        return;

    buf[0] = '\0';
    (void) gethostname(buf, (size_t) maxlen);
    buf[maxlen - 1] = '\0';
#endif
}

#ifdef SUNSHLIB
void
_XtInherit(void)
{
    extern void __XtInherit();

    __XtInherit();
}

#define _XtInherit __XtInherit
#endif

#if defined (WIN32) || defined(__CYGWIN__)
/*
 * The Symbol _XtInherit is used in two different manners.
 * First it could be used as a generic function and second
 * as an absolute address reference, which will be used to
 * check the initialisation process of several other libraries.
 * Because of this the symbol must be accessable by all
 * client dll's and applications.  In unix environments
 * this is no problem, because the used shared libraries
 * format (elf) supports this immediatly.  Under Windows
 * this isn't true, because a functions address in a dll
 * is different from the same function in another dll or
 * applications, because the used Portable Executable
 * File adds a code stub to each client to provide the
 * exported symbol name.  This stub uses an indirect
 * pointer to get the original symbol address, which is
 * then jumped to, like in this example:
 *
 * --- client ---                                     --- dll ----
 *  ...
 *  call foo
 *
 * foo: jmp (*_imp_foo)               ---->           foo: ....
 *      nop
 *      nop
 *
 * _imp_foo: .long <index of foo in dll export table, is
 *                  set to the real address by the runtime linker>
 *
 * Now it is clear why the clients symbol foo isn't the same
 * as in the dll and we can think about how to deal which
 * this two above mentioned requirements, to export this
 * symbol to all clients and to allow calling this symbol
 * as a function.  The solution I've used exports the
 * symbol _XtInherit as data symbol, because global data
 * symbols are exported to all clients.  But how to deal
 * with the second requirement, that this symbol should
 * be used as function.  The Trick is to build a little
 * code stub in the data section in the exact manner as
 * above explained.  This is done with the assembler code
 * below.
 *
 * Ralf Habacker
 *
 * References:
 * msdn          http://msdn.microsoft.com/msdnmag/issues/02/02/PE/PE.asp
 * cygwin-xfree: http://www.cygwin.com/ml/cygwin-xfree/2003-10/msg00000.html
 */

#ifdef __x86_64__
asm(".section .trampoline, \"dwx\" \n\
 .globl _XtInherit        \n\
 _XtInherit:              \n\
    jmp *_y(%rip)         \n\
_y: .quad __XtInherit     \n\
    .text                 \n");
#else
asm(".data\n\
 .globl __XtInherit        \n\
 __XtInherit:      jmp *_y \n\
  _y: .long ___XtInherit   \n\
    .text                 \n");
#endif

#define _XtInherit __XtInherit
#endif

void
_XtInherit(void)
{
    XtErrorMsg("invalidProcedure", "inheritanceProc", XtCXtToolkitError,
               "Unresolved inheritance operation", NULL, NULL);
}

void
XtToolkitInitialize(void)
{
    static Boolean initialized = False;

    LOCK_PROCESS;
    if (initialized) {
        UNLOCK_PROCESS;
        return;
    }
    initialized = True;
    UNLOCK_PROCESS;
    /* Resource management initialization */
    XrmInitialize();
    _XtResourceListInitialize();

    /* Other intrinsic intialization */
    _XtConvertInitialize();
    _XtEventInitialize();
    _XtTranslateInitialize();

    /* Some apps rely on old (broken) XtAppPeekEvent behavior */
    if (getenv("XTAPPPEEKEVENT_SKIPTIMER"))
        XtAppPeekEvent_SkipTimer = True;
    else
        XtAppPeekEvent_SkipTimer = False;
}

String
_XtGetUserName(_XtString dest, int len)
{
#ifdef WIN32
    String ptr = NULL;

    if ((ptr = getenv("USERNAME"))) {
        (void) strncpy(dest, ptr, len - 1);
        dest[len - 1] = '\0';
    }
    else
        *dest = '\0';
#else
#ifdef X_NEEDS_PWPARAMS
    _Xgetpwparams pwparams;
#endif
    struct passwd *pw;
    char *ptr;

    if ((ptr = getenv("USER"))) {
        (void) strncpy(dest, ptr, (size_t) (len - 1));
        dest[len - 1] = '\0';
    }
    else {
        if ((pw = _XGetpwuid(getuid(), pwparams)) != NULL) {
            (void) strncpy(dest, pw->pw_name, (size_t) (len - 1));
            dest[len - 1] = '\0';
        }
        else
            *dest = '\0';
    }
#endif
    return dest;
}

static String
GetRootDirName(_XtString dest, int len)
{
#ifdef WIN32
    register char *ptr1;
    register char *ptr2 = NULL;
    int len1 = 0, len2 = 0;

    if (ptr1 = getenv("HOME")) {        /* old, deprecated */
        len1 = strlen(ptr1);
    }
    else if ((ptr1 = getenv("HOMEDRIVE")) && (ptr2 = getenv("HOMEDIR"))) {
        len1 = strlen(ptr1);
        len2 = strlen(ptr2);
    }
    else if (ptr2 = getenv("USERNAME")) {
        len1 = strlen(ptr1 = "/users/");
        len2 = strlen(ptr2);
    }
    if ((len1 + len2 + 1) < len)
        sprintf(dest, "%s%s", ptr1, (ptr2) ? ptr2 : "");
    else
        *dest = '\0';
#else
#ifdef X_NEEDS_PWPARAMS
    _Xgetpwparams pwparams;
#endif
    static char *ptr;

    if (len <= 0 || dest == NULL)
        return NULL;

    if ((ptr = getenv("HOME"))) {
        (void) strncpy(dest, ptr, (size_t) (len - 1));
        dest[len - 1] = '\0';
    }
    else {
        struct passwd *pw;

        if ((ptr = getenv("USER")))
            pw = _XGetpwnam(ptr, pwparams);
        else
            pw = _XGetpwuid(getuid(), pwparams);
        if (pw != NULL) {
            (void) strncpy(dest, pw->pw_dir, (size_t) (len - 1));
            dest[len - 1] = '\0';
        }
        else
            *dest = '\0';
    }
#endif
    return dest;
}

static void
CombineAppUserDefaults(Display *dpy, XrmDatabase *pdb)
{
    char *filename;
    char *path = NULL;
    Boolean deallocate = False;

    if (!(path = getenv("XUSERFILESEARCHPATH"))) {
#if !defined(WIN32) || !defined(__MINGW32__)
        char *old_path;
        char homedir[PATH_MAX];

        GetRootDirName(homedir, PATH_MAX);
        if (!(old_path = getenv("XAPPLRESDIR"))) {
            XtAsprintf(&path,
                       "%s/%%L/%%N%%C:%s/%%l/%%N%%C:%s/%%N%%C:%s/%%L/%%N:%s/%%l/%%N:%s/%%N",
                       homedir, homedir, homedir, homedir, homedir, homedir);
        }
        else {
            XtAsprintf(&path,
                       "%s/%%L/%%N%%C:%s/%%l/%%N%%C:%s/%%N%%C:%s/%%N%%C:%s/%%L/%%N:%s/%%l/%%N:%s/%%N:%s/%%N",
                       old_path, old_path, old_path, homedir,
                       old_path, old_path, old_path, homedir);
        }
        deallocate = True;
#endif
    }

    filename = XtResolvePathname(dpy, NULL, NULL, NULL, path, NULL, 0, NULL);
    if (filename) {
        (void) XrmCombineFileDatabase(filename, pdb, False);
        XtFree(filename);
    }

    if (deallocate)
        XtFree(path);
}

static void
CombineUserDefaults(Display *dpy, XrmDatabase *pdb)
{
    char *dpy_defaults = XResourceManagerString(dpy);

    if (dpy_defaults) {
        XrmCombineDatabase(XrmGetStringDatabase(dpy_defaults), pdb, False);
    }
    else {
#ifdef __MINGW32__
        const char *slashDotXdefaults = "/Xdefaults";
#else
        const char *slashDotXdefaults = "/.Xdefaults";
#endif
        char filename[PATH_MAX];

        (void) GetRootDirName(filename,
                              PATH_MAX - (int) strlen(slashDotXdefaults) - 1);
        (void) strcat(filename, slashDotXdefaults);
        (void) XrmCombineFileDatabase(filename, pdb, False);
    }
}

static Bool
StoreDBEntry(XrmDatabase *db _X_UNUSED,
             XrmBindingList bindings,
             XrmQuarkList quarks,
             XrmRepresentation *type,
             XrmValuePtr value,
             XPointer data)
{
    XrmQPutResource((XrmDatabase *) data, bindings, quarks, *type, value);
    return False;
}

static XrmDatabase
CopyDB(XrmDatabase db)
{
    XrmDatabase copy = NULL;
    XrmQuark empty = NULLQUARK;

    XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
                         StoreDBEntry, (XPointer) &copy);
    return copy;
}

static String
_XtDefaultLanguageProc(Display *dpy _X_UNUSED,
                       String xnl,
                       XtPointer closure _X_UNUSED)
{
    if (!setlocale(LC_ALL, xnl))
        XtWarning("locale not supported by C library, locale unchanged");

    if (!XSupportsLocale()) {
        XtWarning("locale not supported by Xlib, locale set to C");
        setlocale(LC_ALL, "C");
    }
    if (!XSetLocaleModifiers(""))
        XtWarning("X locale modifiers not supported, using default");

    return setlocale(LC_ALL, NULL);     /* re-query in case overwritten */
}

XtLanguageProc
XtSetLanguageProc(XtAppContext app, XtLanguageProc proc, XtPointer closure)
{
    XtLanguageProc old;

    if (!proc) {
        proc = _XtDefaultLanguageProc;
        closure = NULL;
    }

    if (app) {
        LOCK_APP(app);
        LOCK_PROCESS;
        /* set langProcRec only for this application context */
        old = app->langProcRec.proc;
        app->langProcRec.proc = proc;
        app->langProcRec.closure = closure;
        UNLOCK_PROCESS;
        UNLOCK_APP(app);
    }
    else {
        /* set langProcRec for all application contexts */
        ProcessContext process;

        LOCK_PROCESS;
        process = _XtGetProcessContext();
        old = process->globalLangProcRec.proc;
        process->globalLangProcRec.proc = proc;
        process->globalLangProcRec.closure = closure;
        app = process->appContextList;
        while (app) {
            app->langProcRec.proc = proc;
            app->langProcRec.closure = closure;
            app = app->next;
        }
        UNLOCK_PROCESS;
    }
    return (old ? old : _XtDefaultLanguageProc);
}

XrmDatabase
XtScreenDatabase(Screen *screen)
{
    int scrno;
    Bool doing_def;
    XrmDatabase db, olddb;
    XtPerDisplay pd;
    Status do_fallback;
    char *scr_resources;
    Display *dpy = DisplayOfScreen(screen);

    DPY_TO_APPCON(dpy);
    if (dpy == NULL) {
        XtErrorMsg("nullDisplay",
                   "XtScreenDatabase", XtCXtToolkitError,
                   "XtScreenDatabase requires a non-NULL display",
                   NULL, NULL);
    }

    LOCK_APP(app);
    LOCK_PROCESS;
    if (screen == DefaultScreenOfDisplay(dpy)) {
        scrno = DefaultScreen(dpy);
        doing_def = True;
    }
    else {
        scrno = XScreenNumberOfScreen(screen);
        doing_def = False;
    }
    pd = _XtGetPerDisplay(dpy);
    if ((db = pd->per_screen_db[scrno])) {
        UNLOCK_PROCESS;
        UNLOCK_APP(app);
        return (doing_def ? XrmGetDatabase(dpy) : db);
    }
    scr_resources = XScreenResourceString(screen);

    if (ScreenCount(dpy) == 1) {
        db = pd->cmd_db;
        pd->cmd_db = NULL;
    }
    else {
        db = CopyDB(pd->cmd_db);
    }
    {                           /* Environment defaults */
        char filenamebuf[PATH_MAX];
        char *filename;

        if (!(filename = getenv("XENVIRONMENT"))) {
            int len;

#ifdef __MINGW32__
            const char *slashDotXdefaultsDash = "/Xdefaults-";
#else
            const char *slashDotXdefaultsDash = "/.Xdefaults-";
#endif

            (void) GetRootDirName(filename = filenamebuf,
                                  PATH_MAX -
                                  (int) strlen(slashDotXdefaultsDash) - 1);
            (void) strcat(filename, slashDotXdefaultsDash);
            len = (int) strlen(filename);
            GetHostname(filename + len, PATH_MAX - len);
        }
        (void) XrmCombineFileDatabase(filename, &db, False);
    }
    if (scr_resources) {        /* Screen defaults */
        XrmCombineDatabase(XrmGetStringDatabase(scr_resources), &db, False);
        XFree(scr_resources);
    }
    /* Server or host defaults */
    if (!pd->server_db)
        CombineUserDefaults(dpy, &db);
    else {
        (void) XrmCombineDatabase(pd->server_db, &db, False);
        pd->server_db = NULL;
    }

    if (!db)
        db = XrmGetStringDatabase("");
    pd->per_screen_db[scrno] = db;
    olddb = XrmGetDatabase(dpy);
    /* set database now, for XtResolvePathname to use */
    XrmSetDatabase(dpy, db);
    CombineAppUserDefaults(dpy, &db);
    do_fallback = 1;
    {                           /* System app-defaults */
        char *filename;

        if ((filename = XtResolvePathname(dpy, "app-defaults",
                                          NULL, NULL, NULL, NULL, 0, NULL))) {
            do_fallback = !XrmCombineFileDatabase(filename, &db, False);
            XtFree(filename);
        }
    }
    /* now restore old database, if need be */
    if (!doing_def)
        XrmSetDatabase(dpy, olddb);
    if (do_fallback && pd->appContext->fallback_resources) {    /* Fallback defaults */
        XrmDatabase fdb = NULL;
        String *res;

        for (res = pd->appContext->fallback_resources; *res; res++)
            XrmPutLineResource(&fdb, *res);
        (void) XrmCombineDatabase(fdb, &db, False);
    }
    UNLOCK_PROCESS;
    UNLOCK_APP(app);
    return db;
}

/*
 * Merge two option tables, allowing the second to over-ride the first,
 * so that ambiguous abbreviations can be noticed.  The merge attempts
 * to make the resulting table lexicographically sorted, but succeeds
 * only if the first source table is sorted.  Though it _is_ recommended
 * (for optimizations later in XrmParseCommand), it is not required
 * that either source table be sorted.
 *
 * Caller is responsible for freeing the returned option table.
 */

static void
_MergeOptionTables(const XrmOptionDescRec *src1,
                   Cardinal num_src1,
                   const XrmOptionDescRec *src2,
                   Cardinal num_src2,
                   XrmOptionDescRec **dst,
                   Cardinal *num_dst)
{
    XrmOptionDescRec *table, *endP;
    XrmOptionDescRec *opt1, *dstP;
    const XrmOptionDescRec *opt2;
    int i1;
    Cardinal i2;
    int dst_len, order;
    enum { Check, NotSorted, IsSorted } sort_order = Check;

    *dst = table = (XrmOptionDescRec *)
        __XtMalloc((Cardinal)
                   (sizeof(XrmOptionDescRec) * (num_src1 + num_src2)));

    (void) memmove(table, src1, sizeof(XrmOptionDescRec) * num_src1);
    if (num_src2 == 0) {
        *num_dst = num_src1;
        return;
    }
    endP = &table[dst_len = (int) num_src1];
    for (opt2 = src2, i2 = 0; i2 < num_src2; opt2++, i2++) {
        XrmOptionDescRec *whereP;
        Boolean found;

        found = False;
        whereP = endP - 1;      /* assume new option goes at the end */
        for (opt1 = table, i1 = 0; i1 < dst_len; opt1++, i1++) {
            /* have to walk the entire new table so new list is ordered
               (if src1 was ordered) */
            if (sort_order == Check && i1 > 0
                && strcmp(opt1->option, (opt1 - 1)->option) < 0)
                sort_order = NotSorted;
            if ((order = strcmp(opt1->option, opt2->option)) == 0) {
                /* same option names; just overwrite opt1 with opt2 */
                *opt1 = *opt2;
                found = True;
                break;
            }
            /* else */
            if (sort_order == IsSorted && order > 0) {
                /* insert before opt1 to preserve order */
                /* shift rest of table forward to make room for new entry */
                for (dstP = endP++; dstP > opt1; dstP--)
                    *dstP = *(dstP - 1);
                *opt1 = *opt2;
                dst_len++;
                found = True;
                break;
            }
            /* else */
            if (order < 0)
                /* opt2 sorts after opt1, so remember this position */
                whereP = opt1;
        }
        if (sort_order == Check && i1 == dst_len)
            sort_order = IsSorted;
        if (!found) {
            /* when we get here, whereP points to the last entry in the
               destination that sorts before "opt2".  Shift rest of table
               forward and insert "opt2" after whereP. */
            whereP++;
            for (dstP = endP++; dstP > whereP; dstP--)
                *dstP = *(dstP - 1);
            *whereP = *opt2;
            dst_len++;
        }
    }
    *num_dst = (Cardinal) dst_len;
}

/* NOTE: name, class, and type must be permanent strings */
static Boolean
_GetResource(Display *dpy,
             XrmSearchList list,
             String name,
             String class,
             String type,
             XrmValue *value)
{
    XrmRepresentation db_type;
    XrmValue db_value;
    XrmName Qname = XrmPermStringToQuark(name);
    XrmClass Qclass = XrmPermStringToQuark(class);
    XrmRepresentation Qtype = XrmPermStringToQuark(type);

    if (XrmQGetSearchResource(list, Qname, Qclass, &db_type, &db_value)) {
        if (db_type == Qtype) {
            if (Qtype == _XtQString)
                *(String *) value->addr = db_value.addr;
            else
                (void) memmove(value->addr, db_value.addr, value->size);
            return True;
        }
        else {
            WidgetRec widget;   /* hack, hack */

            memset(&widget, 0, sizeof(widget));
            widget.core.self = &widget;
            widget.core.widget_class = coreWidgetClass;
            widget.core.screen = (Screen *) DefaultScreenOfDisplay(dpy);
            XtInitializeWidgetClass(coreWidgetClass);
            if (_XtConvert(&widget, db_type, &db_value, Qtype, value, NULL)) {
                return True;
            }
        }
    }
    return False;
}

XrmDatabase
_XtPreparseCommandLine(XrmOptionDescRec *urlist,
                       Cardinal num_urs,
                       int argc,
                       _XtString *argv, /* return */
                       String *applName,
                       String *displayName,
                       String *language)
{
    XrmDatabase db = NULL;
    XrmOptionDescRec *options;
    Cardinal num_options;
    XrmName name_list[3];
    XrmName class_list[3];
    XrmRepresentation type;
    XrmValue val;
    _XtString *targv;
    int targc = argc;

    targv = (_XtString *)
        __XtMalloc((Cardinal) (sizeof(_XtString *) * (size_t) argc));
    (void) memmove(targv, argv, sizeof(char *) * (size_t) argc);
    _MergeOptionTables(opTable, XtNumber(opTable), urlist, num_urs,
                       &options, &num_options);
    name_list[0] = class_list[0] = XrmPermStringToQuark(".");
    name_list[2] = class_list[2] = NULLQUARK;
    XrmParseCommand(&db, options, (int) num_options, ".", &targc, targv);
    if (applName) {
        name_list[1] = XrmPermStringToQuark("name");
        if (XrmQGetResource(db, name_list, name_list, &type, &val) &&
            type == _XtQString)
            *applName = val.addr;
    }
    if (displayName) {
        name_list[1] = XrmPermStringToQuark("display");
        if (XrmQGetResource(db, name_list, name_list, &type, &val) &&
            type == _XtQString)
            *displayName = val.addr;
    }
    if (language) {
        name_list[1] = XrmPermStringToQuark("xnlLanguage");
        class_list[1] = XrmPermStringToQuark("XnlLanguage");
        if (XrmQGetResource(db, name_list, class_list, &type, &val) &&
            type == _XtQString)
            *language = val.addr;
    }

    XtFree((char *) targv);
    XtFree((char *) options);
    return db;
}

static void
GetLanguage(Display *dpy, XtPerDisplay pd)
{
    XrmRepresentation type;
    XrmValue value;
    XrmName name_list[3];
    XrmName class_list[3];

    LOCK_PROCESS;
    if (!pd->language) {
        name_list[0] = pd->name;
        name_list[1] = XrmPermStringToQuark("xnlLanguage");
        class_list[0] = pd->class;
        class_list[1] = XrmPermStringToQuark("XnlLanguage");
        name_list[2] = class_list[2] = NULLQUARK;
        if (!pd->server_db)
            CombineUserDefaults(dpy, &pd->server_db);
        if (pd->server_db &&
            XrmQGetResource(pd->server_db, name_list, class_list, &type, &value)
            && type == _XtQString)
            pd->language = (char *) value.addr;
    }

    if (pd->appContext->langProcRec.proc) {
        if (!pd->language)
            pd->language = "";
        pd->language = (*pd->appContext->langProcRec.proc)
            (dpy, pd->language, pd->appContext->langProcRec.closure);
    }
    else if (!pd->language || pd->language[0] == '\0')  /* R4 compatibility */
        pd->language = getenv("LANG");

    if (pd->language)
        pd->language = XtNewString(pd->language);
    UNLOCK_PROCESS;
}

static void
ProcessInternalConnection(XtPointer client_data,
                          int *fd,
                          XtInputId *id _X_UNUSED)
{
    XProcessInternalConnection((Display *) client_data, *fd);
}

static void
ConnectionWatch(Display *dpy,
                XPointer client_data,
                int fd,
                Bool opening,
                XPointer *watch_data)
{
    XtInputId *iptr;
    XtAppContext app = XtDisplayToApplicationContext(dpy);

    if (opening) {
        iptr = (XtInputId *) __XtMalloc(sizeof(XtInputId));
        *iptr = XtAppAddInput(app, fd, (XtPointer) XtInputReadMask,
                              ProcessInternalConnection, client_data);
        *watch_data = (XPointer) iptr;
    }
    else {
        iptr = (XtInputId *) *watch_data;
        XtRemoveInput(*iptr);
        (void) XtFree(*watch_data);
    }
}

void
_XtDisplayInitialize(Display *dpy,
                     XtPerDisplay pd,
                     _Xconst char *name,
                     XrmOptionDescRec *urlist,
                     Cardinal num_urs,
                     int *argc,
                     char **argv)
{
    Boolean tmp_bool;
    XrmValue value;
    XrmOptionDescRec *options;
    Cardinal num_options;
    XrmDatabase db;
    XrmName name_list[2];
    XrmClass class_list[2];
    XrmHashTable *search_list;
    int search_list_size = SEARCH_LIST_SIZE;

    GetLanguage(dpy, pd);

    /* Parse the command line and remove Xt arguments from argv */
    _MergeOptionTables(opTable, XtNumber(opTable), urlist, num_urs,
                       &options, &num_options);
    XrmParseCommand(&pd->cmd_db, options, (int) num_options, name, argc, argv);

    db = XtScreenDatabase(DefaultScreenOfDisplay(dpy));

    if (!(search_list = (XrmHashTable *)
          ALLOCATE_LOCAL(SEARCH_LIST_SIZE * sizeof(XrmHashTable))))
        _XtAllocError(NULL);
    name_list[0] = pd->name;
    class_list[0] = pd->class;
    name_list[1] = NULLQUARK;
    class_list[1] = NULLQUARK;

    while (!XrmQGetSearchList(db, name_list, class_list,
                              search_list, search_list_size)) {
        XrmHashTable *old = search_list;
        Cardinal size =
            (Cardinal) ((size_t) (search_list_size *= 2) *
                        sizeof(XrmHashTable));
        if (!(search_list = (XrmHashTable *) ALLOCATE_LOCAL(size)))
            _XtAllocError(NULL);
        (void) memmove((char *) search_list, (char *) old, (size >> 1));
        DEALLOCATE_LOCAL(old);
    }

    value.size = sizeof(tmp_bool);
    value.addr = (XtPointer) &tmp_bool;
    if (_GetResource(dpy, search_list, "synchronous", "Synchronous",
                     XtRBoolean, &value)) {
        int i;
        Display **dpyP = pd->appContext->list;

        pd->appContext->sync = tmp_bool;
        for (i = pd->appContext->count; i; dpyP++, i--) {
            (void) XSynchronize(*dpyP, (Bool) tmp_bool);
        }
    }
    else {
        (void) XSynchronize(dpy, (Bool) pd->appContext->sync);
    }

    if (_GetResource(dpy, search_list, "reverseVideo", "ReverseVideo",
                     XtRBoolean, &value)
        && tmp_bool) {
        pd->rv = True;
    }

    value.size = sizeof(pd->multi_click_time);
    value.addr = (XtPointer) &pd->multi_click_time;
    if (!_GetResource(dpy, search_list,
                      "multiClickTime", "MultiClickTime", XtRInt, &value)) {
        pd->multi_click_time = 200;
    }

    value.size = sizeof(pd->appContext->selectionTimeout);
    value.addr = (XtPointer) &pd->appContext->selectionTimeout;
    (void) _GetResource(dpy, search_list,
                        "selectionTimeout", "SelectionTimeout", XtRInt, &value);

#ifndef NO_IDENTIFY_WINDOWS
    value.size = sizeof(pd->appContext->identify_windows);
    value.addr = (XtPointer) &pd->appContext->identify_windows;
    (void) _GetResource(dpy, search_list,
                        "xtIdentifyWindows", "XtDebug", XtRBoolean, &value);
#endif

    XAddConnectionWatch(dpy, ConnectionWatch, (XPointer) dpy);

    XtFree((XtPointer) options);
    DEALLOCATE_LOCAL(search_list);
}

/*      Function Name: XtAppSetFallbackResources
 *      Description: Sets the fallback resource list that will be loaded
 *                   at display initialization time.
 *      Arguments: app_context - the app context.
 *                 specification_list - the resource specification list.
 *      Returns: none.
 */

void
XtAppSetFallbackResources(XtAppContext app_context, String *specification_list)
{
    LOCK_APP(app_context);
    app_context->fallback_resources = specification_list;
    UNLOCK_APP(app_context);
}

Widget
XtOpenApplication(XtAppContext *app_context_return,
                  _Xconst char *application_class,
                  XrmOptionDescRec *options,
                  Cardinal num_options,
                  int *argc_in_out,
                  _XtString *argv_in_out,
                  String *fallback_resources,
                  WidgetClass widget_class,
                  ArgList args_in,
                  Cardinal num_args_in)
{
    XtAppContext app_con;
    Display *dpy;
    register int saved_argc = *argc_in_out;
    Widget root;
    Arg args[3], *merged_args;
    Cardinal num = 0;

    XtToolkitInitialize();      /* cannot be moved into _XtAppInit */

    dpy = _XtAppInit(&app_con, (String) application_class, options, num_options,
                     argc_in_out, &argv_in_out, fallback_resources);

    LOCK_APP(app_con);
    XtSetArg(args[num], XtNscreen, DefaultScreenOfDisplay(dpy));
    num++;
    XtSetArg(args[num], XtNargc, saved_argc);
    num++;
    XtSetArg(args[num], XtNargv, argv_in_out);
    num++;

    merged_args = XtMergeArgLists(args_in, num_args_in, args, num);
    num += num_args_in;

    root = XtAppCreateShell(NULL, application_class, widget_class, dpy,
                            merged_args, num);

    if (app_context_return)
        *app_context_return = app_con;

    XtFree((XtPointer) merged_args);
    XtFree((XtPointer) argv_in_out);
    UNLOCK_APP(app_con);
    return root;
}

Widget
XtAppInitialize(XtAppContext *app_context_return,
                _Xconst char *application_class,
                XrmOptionDescRec *options,
                Cardinal num_options,
                int *argc_in_out,
                _XtString *argv_in_out,
                String *fallback_resources,
                ArgList args_in,
                Cardinal num_args_in)
{
    return XtOpenApplication(app_context_return, application_class,
                             options, num_options,
                             argc_in_out, argv_in_out, fallback_resources,
                             applicationShellWidgetClass, args_in, num_args_in);
}

Widget
XtInitialize(_Xconst _XtString name _X_UNUSED,
             _Xconst _XtString classname,
             XrmOptionDescRec *options,
             Cardinal num_options,
             int *argc,
             _XtString *argv)
{
    Widget root;
    XtAppContext app_con;
    register ProcessContext process = _XtGetProcessContext();

    root = XtAppInitialize(&app_con, classname, options, num_options,
                           argc, argv, NULL, NULL, (Cardinal) 0);

    LOCK_PROCESS;
    process->defaultAppContext = app_con;
    UNLOCK_PROCESS;
    return root;
}
