/***********************************************************************
 *                                                                      *
 *               This software is part of the ast package               *
 *          Copyright (c) 1997-2011 AT&T Intellectual Property          *
 *                      and is licensed under the                       *
 *                 Eclipse Public License, Version 1.0                  *
 *                    by AT&T Intellectual Property                     *
 *                                                                      *
 *                A copy of the License is available at                 *
 *          http://www.eclipse.org/org/documents/epl-v10.html           *
 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
 *                                                                      *
 *              Information and Software Systems Research               *
 *                            AT&T Research                             *
 *                           Florham Park NJ                            *
 *                                                                      *
 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
 *                                                                      *
 ***********************************************************************/
//
// Glenn Fowler
// AT&T Research
//
#include "config_ast.h"  // IWYU pragma: keep

#ifndef __EXTENSIONS__
#define __EXTENSIONS__ 1
#endif

#include <dlfcn.h>
#include <string.h>  // IWYU pragma: keep

#if _hdr_rld_interface
#include <rld_interface.h>
#endif

#include "dlldefs.h"
#include "dlllib.h"  // IWYU pragma: keep

//
// return a handle for the next layer down,
// i.e., the next layer that has symbols covered
// by the main prog and dll's loaded so far
//
// intentionally light on external lib calls
// so this routine can be used early in process
// startup
//

#ifdef _DLL_RLD_SYM

#define DEBUG 1

#ifdef DEBUG

typedef ssize_t (*Write_f)(int, const void *, size_t);

#endif

#undef dllnext

void *_dll_next(int flags, _DLL_RLD_SYM_TYPE *here) {
    char *vp;
    void *lp;
    int found = 0;
    char *s;
    char *b;
    char *e;
    char dummy[256];
#ifdef DEBUG
    Write_f wr = 0;
    Write_f xr;
    char buf[1024];
#endif

#ifdef DEBUG
    if (getenv("DLL_DEBUG") && (vp = (char *)_rld_new_interface(_RLD_FIRST_PATHNAME))) {
        do {
            if (strcmp(vp, "MAIN") && (lp = dllopen(vp, flags))) {
                xr = (Write_f)dlsym(lp, "write") if (xr) wr = xr;
            }
        } while (vp = (char *)_rld_new_interface(_RLD_NEXT_PATHNAME));
    }
#endif
    vp = (char *)_rld_new_interface(_RLD_FIRST_PATHNAME);
    if (vp) {
        do {
            lp = dllopen(strcmp(vp, "MAIN") ? vp : NULL, flags);
            if (lp) {
                if (found) {
                    b = e = 0;
                    s = vp;
                    for (;;) {
                        switch (*s++) {
                            case 0:
                                break;
                            case '/':
                                b = s;
                                e = 0;
                                continue;
                            case '.':
                                if (!e) e = s - 1;
                                continue;
                            default:
                                continue;
                        }
                        break;
                    }
                    if (b && e) {
                        s = dummy;
                        *s++ = '_';
                        *s++ = '_';
                        while (b < e) *s++ = *b++;
                        b = "_dummy";
                        while (*s++ = *b++)
                            ;
                        if (dlsym(lp, dummy)) {
                            dlclose(lp);
                            lp = 0;
                        }
                    }
                    if (lp) {
#ifdef DEBUG
                        if (wr) (*wr)(2, buf, sfsprintf(buf, sizeof(buf), "dll: next %s\n", vp));
#endif
                        return lp;
                    }
#ifdef DEBUG
                    else if (wr) {
                        (*wr)(2, buf, sfsprintf(buf, sizeof(buf), "dll: skip %s\n", vp));
                    }
#endif
                } else if ((_DLL_RLD_SYM_TYPE *)dlsym(lp, _DLL_RLD_SYM_STR) == here) {
#ifdef DEBUG
                    if (wr) (*wr)(2, buf, sfsprintf(buf, sizeof(buf), "dll: this %s\n", vp));
#endif
                    found = 1;
                }
            }
        } while (vp = (char *)_rld_new_interface(_RLD_NEXT_PATHNAME));
    }
    return dllnext(flags);
}

#endif

#ifndef RTLD_NEXT
#if _dll_DYNAMIC

#include <link.h>

extern struct link_dynamic _DYNAMIC;

#endif
#endif

void *dllnext(int flags) {
    void *dll;
#ifndef RTLD_NEXT
#if _dll_DYNAMIC
    struct link_map *map;
    char *s;
    char *b;
#endif
    char *ver;
    char *path;

    static char next[] = {_DLL_NEXT_PATH};
#endif

#ifdef RTLD_NEXT
    UNUSED(flags);

    dll = RTLD_NEXT;
#else
    path = next;
#if _dll_DYNAMIC
    for (map = _DYNAMIC.ld_un.ld_1->ld_loaded; map; map = map->lm_next) {
        b = 0;
        s = map->lm_name;
        while (*s) {
            if (*s++ == '/') b = s;
        }
        if (b && b[0] == 'l' && b[1] == 'i' && b[2] == 'b' && b[3] == 'c' && b[4] == '.') {
            path = map->lm_name;
            break;
        }
    }
#endif
    ver = path + strlen(path);
    while (!(dll = dllopen(path, flags))) {
        do {
            if (ver <= path) return 0;
        } while (*--ver != '.');
        if (*(ver + 1) <= '0' || *(ver + 1) >= '9') return 0;
        *ver = 0;
    }
#endif
    return dll;
}
