/*******************************************************************/ /* sltdl: a surrogate ltdl implementation */ /* Copyright (C) 2019 Z. Gilboa */ /* Released under the Standard MIT License; see COPYING.SLTDL. */ /*******************************************************************/ #include #include #include #include #include #include #include #include #include "sltdl_core.h" static off_t lt_plen; static off_t lt_plocs; static char * lt_upath; static char * lt_vpath; static char ** lt_vmark; static char ** lt_pathv; const char * lt_dlgetsearchpath(void) { return lt_upath; } static int lt_dlsetsearchpath_locked(const char * path) { const char * ch; char * uch; char * vch; char ** pathv; off_t elements; if (path[0] != '/') return 1; elements = 1; for (ch=&path[1]; *ch; ch++) { if (*ch == ':') { if (ch[1] != '/') return 1; elements++; } } if (++elements > lt_plocs) { if (lt_pathv) { free(lt_pathv); lt_pathv = 0; } lt_plocs = elements; lt_plocs += 0x3f; lt_plocs |= 0x3f; lt_plocs ^= 0x3f; if (!(lt_pathv = calloc(lt_plocs,sizeof(char *)))) return 1; } if ((ch - path) > lt_plen) { if (lt_upath) { free(lt_upath); lt_upath = 0; } lt_plen = ((ch - path + 1) <= 0x800) ? 0x800 : ch - path + 1; lt_plen += 0x7ff; lt_plen |= 0x7ff; lt_plen ^= 0x7ff; if (!(lt_upath = calloc(2*lt_plen,1))) return 1; lt_vpath = <_upath[lt_plen]; } pathv = lt_pathv; *pathv++ = lt_vpath; for (ch=path, uch=lt_upath, vch=lt_vpath; *ch; ch++) { if (*ch == ':') { *uch++ = ch[0]; *uch++ = ch[1]; *vch++ = 0; *pathv = vch; *vch++ = ch[1]; ch++; pathv++; } else { *uch++ = *ch; *vch++ = *ch; } } lt_vmark = pathv; return 0; } int lt_dlsetsearchpath(const char * path) { lt_slock(); return lt_sunlock(lt_dlsetsearchpath_locked(path)); } int lt_dladdsearchdir(const char * path) { int ret; const char * ch; char * buf; off_t alen; off_t plen; if (path[0] != '/') return 1; for (ch=path; *ch; ch++) if (*ch == ':') return 1; lt_slock(); alen = strlen(path); plen = strlen(lt_upath); /* no allocation needed? */ if (!lt_pathv[lt_plocs - 2] && ((plen + 1 + alen + 1) <= lt_plen)) { lt_upath[plen] = ':'; lt_vpath[plen] = 0; plen++; strcpy(<_upath[plen],path); strcpy(<_vpath[plen],path); *lt_vmark++ = <_vpath[plen]; return lt_sunlock(0); } /* (allocation needed) */ if (!(buf = malloc(plen + 1 + alen + 1))) return lt_sunlock(1); sprintf(buf,"%s:%s",lt_upath,path); ret = lt_dlsetsearchpath_locked(buf); free(buf); return lt_sunlock(ret); } int lt_dlinsertsearchdir(const char * mark, const char * path) { int ret; const char * ch; char * buf; char * dst; off_t alen; off_t plen; off_t slen; off_t offset; char ** pathv; if (path[0] != '/') return 1; if (!mark) return lt_dladdsearchdir(path); for (ch=path; *ch; ch++) if (*ch == ':') return 1; lt_slock(); alen = strlen(path); plen = strlen(lt_upath); if ((mark < lt_upath) || (mark >= <_upath[plen])) return lt_sunlock(1); if ((mark > lt_upath) && (mark[-1] != ':')) return lt_sunlock(1); mark = <_vpath[mark - lt_upath]; /* no allocation needed? */ if (!lt_pathv[lt_plocs - 2] && ((plen + 1 + alen + 1) <= lt_plen)) { for (pathv=lt_vmark; pathv>=lt_pathv; pathv--) { slen = strlen(pathv[-1]); offset = pathv[-1] - lt_vpath; offset += alen + 1; memcpy(<_upath[offset],pathv[-1],slen); memcpy(<_vpath[offset],<_upath[offset],slen); if (pathv < lt_vmark) lt_upath[offset + slen] = ':'; pathv[0] = pathv[-1]; if (pathv[-1] == mark) { offset = mark - lt_vpath; strcpy(<_vpath[offset],path); memcpy(<_upath[offset],path,alen); lt_upath[offset+alen] = ':'; lt_vmark++; return lt_sunlock(0); } } } /* (allocation needed) */ if (!(buf = malloc(plen + 1 + alen + 1))) return lt_sunlock(1); for (dst=buf, pathv=lt_pathv; *pathv; pathv++) { if (*pathv == mark) dst += sprintf(dst,"%s:",path); if (pathv[1]) dst += sprintf(dst,"%s:",*pathv); else dst += sprintf(dst,"%s",*pathv); } ret = lt_dlsetsearchpath_locked(buf); free(buf); return lt_sunlock(ret); } int lt_dlpathopen(const char * module, const char ** extv) { int fdat; int fdmod; char ** ppath; const char ** pext; size_t mlen; size_t elen; char path[1024]; if ((mlen = strlen(module)) >= sizeof(path)) return -1; memcpy(path,module,mlen); lt_slock(); for (ppath=lt_pathv; *ppath; ppath++) { fdat = open(*ppath,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0); if (fdat >= 0) { for (pext=extv; *pext; pext++) { if (mlen + (elen = strlen(*pext)) >= (sizeof(path))) { close(fdat); return (-1); } memcpy(&path[mlen],*pext,elen); path[mlen+elen] = 0; fdmod = openat(fdat,path,O_EXEC|O_CLOEXEC,0); if (fdmod >= 0) { close(fdat); return lt_sunlock(fdmod); } } close(fdat); } } return lt_sunlock(-1); }