parent
56f13a7744
commit
c117f3ed1a
@ -0,0 +1,14 @@
|
||||
language: python
|
||||
|
||||
env:
|
||||
global:
|
||||
- PYTHONUNBUFFERED=1
|
||||
|
||||
stages:
|
||||
- name: build_ext
|
||||
- name: test
|
||||
|
||||
python:
|
||||
- '2.7'
|
||||
- '3.6'
|
||||
|
@ -0,0 +1,39 @@
|
||||
|
||||
MACOSPY = $(shell find "$(HOME)/Library/Python/" -name bin -type d -maxdepth 2 -exec echo -n "{}:" \; 2>/dev/null)
|
||||
WINPY = $(shell find "/$(SYSTEMDRIVE)/"Python* -name bin -type d -maxdepth 2 -exec echo -n "{}:" \; 2>/dev/null)
|
||||
PATHPREP = $(PATH):$(HOME)/.local/bin:$(MACOSPY):$(WINPY)
|
||||
PYTHON := $(shell PATH="$(PATHPREP)" which python3 python2 python | head -n1)
|
||||
PATH := $(PATHPREP)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
BINEXT = .exe
|
||||
else
|
||||
BINEXT =
|
||||
endif
|
||||
|
||||
apportable_demo: apportable.c apportable_demo.c
|
||||
$(CC) -liconv -DAPPORTABLE -o apportable_demo apportable.c apportable_demo.c
|
||||
|
||||
apportable_demo.exe: apportable.c apportable_demo.c
|
||||
$(CC) -DAPPORTABLE -o apportable_demo.exe apportable.c apportable_demo.c
|
||||
|
||||
demo: apportable_demo$(BINEXT)
|
||||
|
||||
all: demo
|
||||
|
||||
build/lib/.build_stamp: setup.py apportable.c apportable_pyext.c
|
||||
mkdir -p build
|
||||
$(PYTHON) setup.py build_ext -b build/lib
|
||||
touch build/lib/.build_stamp
|
||||
|
||||
build_ext: build/lib/.build_stamp
|
||||
|
||||
test: build_ext
|
||||
PYTHONPATH=build/lib $(PYTHON) test_apportable.py
|
||||
|
||||
clean:
|
||||
rm -f apportable build/lib/* build/lib/.build_stamp apportable_demo apportable_demo.exe
|
||||
|
||||
|
||||
|
||||
.PHONY: all test build_ext
|
||||
|
@ -1,2 +1,102 @@
|
||||
# apportable
|
||||
C library to help making applications portable
|
||||
|
||||
**This project is not finished yet; use with care.**
|
||||
|
||||
C library to help making applications portable.
|
||||
|
||||
## Motivation
|
||||
|
||||
There is a distinction between portable applications and portable source-code;
|
||||
the latter is source code with an inherent flexibility which lets us compile
|
||||
the program on different platforms The former instead allows to copy
|
||||
applications from one similar system to another, without the need to place
|
||||
dependencies in system-default, and thus particularly guarded locations -
|
||||
i.e. without installation. This library is about offering the functionality
|
||||
to make binaries portable, providing itself portable source-code, so that it
|
||||
is particularly interesting for applications with already have portable source
|
||||
code.
|
||||
|
||||
Installed applications can easily locate their files on absolute paths,
|
||||
once they know the distributions conventions. Configuration files for example are
|
||||
usually considered to be under **$(sysconfdir)**, which the maintainer can define
|
||||
at compile time to be - usually - **/etc/**. Portable applications instead can not
|
||||
be accommodated with a fixed absolute path.
|
||||
|
||||
Different strategies exist, but all depend on a carefully prepared environment,
|
||||
which is normally not realistic. Neither environment variables are reliably set,
|
||||
nor can a regular user set the working directory for a binary.
|
||||
|
||||
Portable applications need a way to compute the paths to their resources
|
||||
relative to the binary file. This is surprisingly a hard problem, especially
|
||||
cross-platform. The code differs not only by platform, but also for the
|
||||
compiler and the specific C library implementation.
|
||||
|
||||
|
||||
## Architecture
|
||||
|
||||
This library is designed such that existing build systems can be leveraged, without
|
||||
too much code change. It is also designed to be turned off by default, so that adding
|
||||
apportable to the build system in not enough to introduce additional security problems,
|
||||
except if requested explicitely by the maintainers.
|
||||
|
||||
To use apportable add `apportable.c` to the linked objects, and somehow add `apportable.h` to
|
||||
the necessary source files. It should not hurt to add `apportable.h` globally, as
|
||||
all variables are prefixed with `apportable_`. Finally you need to enable the code
|
||||
explicitely by defining `APPORTABLE` on the compiler.
|
||||
|
||||
Once you have integrated apportable in the build and enabled it with APPORTABLE,
|
||||
the basic idea is that whenever the build system expects you to define a static path
|
||||
|
||||
```Makefile
|
||||
CONFIGFILE = "/etc/apportable.conf"
|
||||
```
|
||||
|
||||
it can be replaced by a function call:
|
||||
|
||||
```Makefile
|
||||
CONFIGFILE = apportable_template("$ORIGIN/../etc/apportable.conf")
|
||||
```
|
||||
|
||||
This is possible as static strings are basically pointers to an array of characters (`char *`),
|
||||
thus replacing the code with a function which is guaranteed to return a valid pointer
|
||||
is normally fine.
|
||||
|
||||
**Note**: this works, as long as the string is not concatenated to other strings:
|
||||
|
||||
```c
|
||||
FILE * cf = open(PREFIX CONFIGFILE, "r");
|
||||
```
|
||||
|
||||
In such a case, the source code needs to be modified directly:
|
||||
|
||||
```c
|
||||
FILE * cf = open(apportable_template("$ORIGIN/../etc/apportable.conf"), "r");
|
||||
```
|
||||
|
||||
## Source code compatibility
|
||||
|
||||
This should be written in POSIX 2008 compatible C99, to make it interesting
|
||||
and usable by most existing software.
|
||||
|
||||
It was tested to compile on:
|
||||
|
||||
* Apple LLVM version 9.0.0
|
||||
* gcc (Debian 6.3.0-18) 6.3.0
|
||||
* Visual Studio 2008
|
||||
|
||||
For testig (and a programming exercise in handling Unicode and Python), a
|
||||
Python extension has been added, which is tested with the same compilers and
|
||||
Python 2.7 and Python 3.2+.
|
||||
|
||||
|
||||
## Encoding (UTF-8, UTF-16, UTF-32)
|
||||
|
||||
Functions for both ASCII compatible UTF-8 (`char *`) and native *wide
|
||||
character* (`wchar_t`, UTF-16, UTF-32) arguments are provided, the latter are
|
||||
prefixed with a `w` (like in `wutf8`). All the necessary conversions are done
|
||||
with platform standard means, that is for now either libiconv or the Windows API.
|
||||
|
||||
The testing infrastructure cross-checks the internal conversion against
|
||||
Python's conversion between the formats, in the hope that this further
|
||||
validates the function's design.
|
||||
|
||||
|
@ -0,0 +1,756 @@
|
||||
/* apportable.c - Self-awareness, cross-platform
|
||||
*
|
||||
* Copyright (C) 2018 Claudio Luck
|
||||
*
|
||||
* This file is part of apportable.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of either
|
||||
*
|
||||
* - the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or
|
||||
*
|
||||
* - the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or both in parallel, as here.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if defined APPORTABLE
|
||||
|
||||
#if defined __APPLE__
|
||||
# define APPORTABLE_SUPPORTED
|
||||
# include <mach-o/dyld.h>
|
||||
# include <mach-o/nlist.h>
|
||||
# include <sys/syslimits.h>
|
||||
# include <iconv.h>
|
||||
# ifdef __LP64__
|
||||
typedef struct mach_header_64 mach_header_t;
|
||||
typedef struct segment_command_64 segment_command_t;
|
||||
typedef struct nlist_64 nlist_t;
|
||||
# else
|
||||
typedef struct mach_header mach_header_t;
|
||||
typedef struct segment_command segment_command_t;
|
||||
typedef struct nlist nlist_t;
|
||||
# endif
|
||||
|
||||
#elif defined _WIN32
|
||||
# define APPORTABLE_SUPPORTED
|
||||
# include <Windows.h>
|
||||
|
||||
#elif defined __BIONIC__ // Android
|
||||
// # define PORTABLE_H_ENABLED
|
||||
# include <linker/linker.h>
|
||||
|
||||
#elif defined __GNUC__
|
||||
# define _GNU_SOURCE
|
||||
# ifdef __linux__
|
||||
# define APPORTABLE_SUPPORTED
|
||||
# include <linux/limits.h>
|
||||
# include <link.h>
|
||||
# include <dlfcn.h>
|
||||
# include <iconv.h>
|
||||
# endif
|
||||
|
||||
#elif defined __UCLIBC__
|
||||
// # define PORTABLE_H_ENABLED
|
||||
# include <ldso.h>
|
||||
|
||||
#endif /* platforms */
|
||||
|
||||
#if defined _WIN32
|
||||
#define DIRSEP_S "\\"
|
||||
#define DIRSEP_C '\\'
|
||||
#define PATHSEP_S ";"
|
||||
#define PATHSEP_C ';'
|
||||
# if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
|
||||
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
# endif
|
||||
#else
|
||||
#define DIRSEP_S "/"
|
||||
#define DIRSEP_C '/'
|
||||
#define PATHSEP_S ":"
|
||||
#define PATHSEP_C ':'
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
// Debugging
|
||||
#include <stdio.h>
|
||||
|
||||
#include "apportable.h"
|
||||
|
||||
#define mem_calloc(a, t, c) (a)->_calloc((c), sizeof(t))
|
||||
#define mem_free(a, v) (a)->_free((v))
|
||||
#define APPORTABLE_STATE(a) apportable_getstate((a))
|
||||
|
||||
static apportable_t apportable_global_state = {0, 0};
|
||||
|
||||
|
||||
void apportable_init (
|
||||
apportable a,
|
||||
int enabled
|
||||
)
|
||||
{
|
||||
/* figure out endianness */
|
||||
int le;
|
||||
#if !defined _WIN32
|
||||
volatile uint32_t le_i;
|
||||
|
||||
le_i = 0x01234567;
|
||||
le = ((*((uint8_t*)(&le_i))) == 0x67);
|
||||
#else /*_WIN32*/
|
||||
le = 1;
|
||||
#endif
|
||||
if (sizeof(wchar_t) == 4)
|
||||
a->_iconv_wchar_t = le ? "UTF-32LE" : "UTF-32BE";
|
||||
else if (sizeof(wchar_t) == 2)
|
||||
a->_iconv_wchar_t = le ? "UTF-16LE" : "UTF-32BE";
|
||||
|
||||
a->_calloc = calloc;
|
||||
a->_free = free;
|
||||
|
||||
a->_strndup = &apportable_strndup;
|
||||
a->_wcsndup = &apportable_wcsndup;
|
||||
|
||||
a->wutf8 = &apportable_wutf8;
|
||||
a->wutf8_free = &apportable_wutf8_free;
|
||||
a->uwchar_t = &apportable_uwchar_t;
|
||||
a->uwchar_t_free = &apportable_uwchar_t_free;
|
||||
|
||||
a->ugetenv = &apportable_ugetenv;
|
||||
a->wugetenv = &apportable_wugetenv;
|
||||
|
||||
a->progfile = &apportable_progfile;
|
||||
a->pathexp = &apportable_pathexp;
|
||||
a->whereis = &apportable_whereis;
|
||||
a->enabled = enabled;
|
||||
a->initialized = 1;
|
||||
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
apportable apportable_getstate (apportable a)
|
||||
{
|
||||
apportable self;
|
||||
int default_enabled = 0;
|
||||
if (a != NULL)
|
||||
self = a;
|
||||
else
|
||||
{
|
||||
self = &apportable_global_state;
|
||||
default_enabled = 1;
|
||||
}
|
||||
if (self->initialized)
|
||||
return self;
|
||||
apportable_init(self, default_enabled);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* UTF-8 -> wchar_t -> UTF-8 */
|
||||
char * apportable_strndup(apportable a, const char * str, size_t syms)
|
||||
{
|
||||
// WORKS
|
||||
|
||||
apportable self;
|
||||
wchar_t * wstr, * buffer;
|
||||
char * ret;
|
||||
size_t buflen;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
wstr = self->uwchar_t(self, str);
|
||||
buflen = wcslen(wstr);
|
||||
buffer = self->_wcsndup(self, wstr, syms);
|
||||
|
||||
ret = self->wutf8(self, buffer);
|
||||
|
||||
// // return ret;
|
||||
// char * x = malloc(1000);
|
||||
// sprintf(x, "%zu %zu L<%ls> <%s>", syms, buflen, buffer, ret);
|
||||
// return x;
|
||||
|
||||
free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
wchar_t * apportable_wcsndup(apportable a, const wchar_t * str, size_t syms)
|
||||
{
|
||||
apportable self;
|
||||
wchar_t * buffer;
|
||||
size_t buflen;
|
||||
size_t copysyms;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
buflen = wcslen(str);
|
||||
if (syms == 0)
|
||||
syms = buflen;
|
||||
if (!(copysyms = syms))
|
||||
copysyms = (syms > buflen ? buflen : syms);
|
||||
if ((buffer = self->_calloc(sizeof(wchar_t), buflen + 1)))
|
||||
wcsncpy(buffer, str, copysyms);
|
||||
// sprintf(buffer, L"%d %d", strlen(buffer), strlen(str));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
char * apportable_wutf8 (apportable a, const wchar_t * s)
|
||||
{
|
||||
apportable self;
|
||||
char * b;
|
||||
size_t s_l, b_l;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
s_l = wcslen(s);
|
||||
b_l = WideCharToMultiByte(CP_UTF8, 0, s, s_l, NULL, 0, NULL, NULL);
|
||||
b = self->_calloc(sizeof(wchar_t), b_l + 1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, s, s_l, b, b_l, NULL, NULL);
|
||||
return b;
|
||||
}
|
||||
|
||||
char * apportable_wutf8_free(apportable a, wchar_t * s)
|
||||
{
|
||||
apportable self;
|
||||
char * ret;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
ret = self->wutf8(self, s);
|
||||
self->_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
wchar_t * apportable_uwchar_t (apportable a, const char * s)
|
||||
{
|
||||
apportable self;
|
||||
char * b;
|
||||
size_t s_l, b_l;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
s_l = strlen(s) + 1;
|
||||
b_l = MultiByteToWideChar(CP_UTF8, 0, s, s_l, NULL, 0);
|
||||
b = self->_calloc(sizeof(wchar_t), b_l + 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, s, s_l, b, b_l);
|
||||
return b;
|
||||
}
|
||||
|
||||
wchar_t * apportable_uwchar_t_free (apportable a, const char * s)
|
||||
{
|
||||
apportable self;
|
||||
char * ret;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
ret = self->uwchar_t(self, s);
|
||||
self->_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
char * apportable_ugetenv (apportable a, const char * var)
|
||||
{
|
||||
apportable self;
|
||||
wchar_t * v;
|
||||
size_t var_l, v_l;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
var_l = strlen(var) + 1;
|
||||
v_l = MultiByteToWideChar(CP_UTF8, 0, var, var_l, NULL, 0);
|
||||
v = self->_calloc(sizeof(wchar_t), v_l + 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, var, var_l, v, v_l);
|
||||
return self->wutf8(self, _wgetenv(v));
|
||||
}
|
||||
|
||||
char * apportable_wugetenv (apportable a, const wchar_t * var)
|
||||
{
|
||||
apportable self;
|
||||
wchar_t * v;
|
||||
size_t var_l, v_l;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
var_l = wcslen(var) + 1;
|
||||
v_l = MultiByteToWideChar(CP_UTF8, 0, var, var_l, NULL, 0);
|
||||
v = self->_calloc(sizeof(wchar_t), v_l + 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, var, var_l, v, v_l);
|
||||
return self->wutf8(self, _wgetenv(v));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#else /*!_WIN32*/
|
||||
|
||||
char * apportable_wutf8 (apportable a, const wchar_t * s)
|
||||
{
|
||||
apportable self;
|
||||
char * ret;
|
||||
size_t s_l, ret_l;
|
||||
/* iconv expects in-out params, so allocate copies */
|
||||
wchar_t * iconv_s;
|
||||
char * iconv_ret;
|
||||
size_t iconv_s_l, iconv_ret_l;
|
||||
iconv_t iconv_obj;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
s_l = sizeof(wchar_t) * ( wcslen(s) + 1 );
|
||||
iconv_obj = iconv_open("UTF-8", self->_iconv_wchar_t);
|
||||
if (iconv_obj == (iconv_t) -1) {
|
||||
return NULL;
|
||||
}
|
||||
ret_l = sizeof(char) * ( s_l + 1 );
|
||||
ret = self->_calloc(1, ret_l);
|
||||
|
||||
iconv_s = (wchar_t *) s, iconv_ret = ret;
|
||||
iconv_s_l = s_l, iconv_ret_l = ret_l;
|
||||
iconv(iconv_obj, (char **)&iconv_s, &iconv_s_l, &iconv_ret, &iconv_ret_l);
|
||||
iconv_close(iconv_obj);
|
||||
|
||||
// char * b = malloc(1000);
|
||||
// sprintf(b, "%zu '%ls' '%s'", ret_l, s, ret);
|
||||
// printf("%s\n", b);
|
||||
// return b;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char * apportable_wutf8_free (apportable a, wchar_t * s)
|
||||
{
|
||||
apportable self;
|
||||
char * ret;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
ret = self->wutf8(self, s);
|
||||
self->_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
wchar_t * apportable_uwchar_t (apportable a, const char * s)
|
||||
{
|
||||
apportable self;
|
||||
wchar_t * ret;
|
||||
wchar_t * buffer;
|
||||
size_t s_l, buffer_l;
|
||||
/* iconv expects in-out params, so allocate copies */
|
||||
char * iconv_s;
|
||||
wchar_t * iconv_buffer;
|
||||
size_t iconv_s_l, iconv_buffer_l;
|
||||
iconv_t iconv_obj;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
s_l = sizeof(char) * ( strlen(s) + 1 );
|
||||
iconv_obj = iconv_open(self->_iconv_wchar_t, "UTF-8");
|
||||
if (iconv_obj == (iconv_t) -1) {
|
||||
return NULL;
|
||||
}
|
||||
buffer_l = sizeof(wchar_t) * ( s_l + 1 );
|
||||
buffer = self->_calloc(1, buffer_l);
|
||||
|
||||
iconv_s = (char *) s, iconv_buffer = buffer;
|
||||
iconv_s_l = s_l, iconv_buffer_l = buffer_l;
|
||||
iconv(iconv_obj, (char **)&iconv_s, &iconv_s_l, (char **) &iconv_buffer, &iconv_buffer_l);
|
||||
|
||||
// char * b2 = malloc(1000);
|
||||
// sprintf(b2, "%d->%d %d->%d <%ls> <%s>", s_l, iconv_s_l, buffer_l, iconv_buffer_l, s, buffer);
|
||||
// return b2;
|
||||
|
||||
iconv_close(iconv_obj);
|
||||
ret = self->_wcsndup(self, buffer, 0);
|
||||
self->_free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
wchar_t * apportable_uwchar_t_free (apportable a, char * s)
|
||||
{
|
||||
apportable self;
|
||||
wchar_t * ret;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
ret = self->uwchar_t(self, s);
|
||||
self->_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
char * apportable_ugetenv (apportable a, char * var)
|
||||
{
|
||||
apportable self;
|
||||
char * ret;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (!(ret = self->_strndup(self, getenv(var), 0)))
|
||||
ret = self->_strndup(self, "", 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char * apportable_wugetenv (apportable a, wchar_t * wvar)
|
||||
{
|
||||
apportable self;
|
||||
char * var, * ret;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
var = self->wutf8(self, wvar);
|
||||
if (!(ret = self->ugetenv(self, var)))
|
||||
ret = self->_strndup(self, "", 0);
|
||||
self->_free(var);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
/* for UTF-16 "wide" character */
|
||||
wchar_t * apportable_wprogfile (apportable a, const wchar_t * library_name) {
|
||||
apportable self;
|
||||
wchar_t * ret;
|
||||
HMODULE handle;
|
||||
wchar_t * wfile;
|
||||
wchar_t * file;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (!self->enabled)
|
||||
return self->_wcsndup(self, library_name, 0);
|
||||
|
||||
ret = NULL;
|
||||
handle = NULL;
|
||||
if (!GetModuleHandleExW(0, library_name, &handle)) {
|
||||
if (handle)
|
||||
FreeLibrary(handle);
|
||||
return NULL;
|
||||
}
|
||||
wfile = self->_calloc(sizeof(WCHAR), MAX_PATH);
|
||||
file = self->_calloc(sizeof(char), MAX_PATH);
|
||||
if (GetModuleFileNameW(handle, wfile, MAX_PATH))
|
||||
ret = file;
|
||||
self->_free(wfile);
|
||||
if (!ret)
|
||||
self->_free(file);
|
||||
FreeLibrary(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* for utf-8, will call into native UTF-16 */
|
||||
char * apportable_progfile (apportable a, const char * library_name) {
|
||||
apportable self;
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (!self->enabled)
|
||||
return self->_strndup(a, (const char *)library_name, 0);
|
||||
|
||||
char * ret = NULL;
|
||||
PWSTR wlibnam;
|
||||
size_t ln_l, wlibnam_l;
|
||||
if (library_name == NULL)
|
||||
return apportable_wprogfile (self, NULL);
|
||||
ln_l = strlen(library_name) + 1;
|
||||
wlibnam_l = MultiByteToWideChar (CP_UTF8, 0, library_name, ln_l, NULL, 0);
|
||||
wlibnam = self->_calloc (sizeof(char), wlibnam_l + 0);
|
||||
if (!MultiByteToWideChar (CP_UTF8, 0, library_name, ln_l, wlibnam, wlibnam_l))
|
||||
{
|
||||
self->_free(wlibnam);
|
||||
return NULL;
|
||||
}
|
||||
ret = apportable_wprogfile (a, wlibnam);
|
||||
self->_free(wlibnam);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#elif defined __APPLE__
|
||||
|
||||
char * apportable_progfile (apportable a, const char * library_name) {
|
||||
apportable self;
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (!self->enabled)
|
||||
return self->_strndup(self, library_name, 0);
|
||||
|
||||
const char* library_base_name = library_name ? strrchr(library_name, DIRSEP_C) : NULL;
|
||||
if (!library_base_name)
|
||||
library_base_name = library_name;
|
||||
else
|
||||
library_base_name += 1; // skip found '/'
|
||||
char* library_file = NULL;
|
||||
|
||||
const char * image_name = NULL;
|
||||
const char * library_name_sep = NULL;
|
||||
unsigned long image_count = _dyld_image_count();
|
||||
errno = 0;
|
||||
for (unsigned long i = 0; i < image_count; i++) {
|
||||
image_name = (const char *)_dyld_get_image_name(i); // dyld owns string
|
||||
if (!image_name || !image_name[0])
|
||||
continue;
|
||||
library_name_sep = strrchr(image_name, '/');
|
||||
if (!library_name_sep)
|
||||
library_name_sep = image_name;
|
||||
else
|
||||
library_name_sep += 1; // skip found '/'
|
||||
if (!library_name || !strcmp(library_name_sep, library_base_name)) {
|
||||
library_file = self->_strndup(a, image_name, PATH_MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return library_file;
|
||||
}
|
||||
|
||||
#elif defined __GNUC__
|
||||
|
||||
extern char *program_invocation_name;
|
||||
|
||||
wchar_t * apportable_progfile (apportable a, const wchar_t * library_name) {
|
||||
apportable self;
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (!self->enabled)
|
||||
return self->_strndup(a, library_name, 0);
|
||||
|
||||
const char* library_base_name = library_name ? strrchr(library_name, DIRSEP_C) : NULL;
|
||||
if (!library_base_name)
|
||||
library_base_name = library_name;
|
||||
else
|
||||
library_base_name += 1; // skip found '/'
|
||||
char * library_file = NULL;
|
||||
char * image_name;
|
||||
char * image_name_real;
|
||||
char * library_name_sep;
|
||||
FILE * cmdline;
|
||||
|
||||
void *handle = dlopen(NULL, RTLD_LAZY);
|
||||
if (!handle)
|
||||
return NULL;
|
||||
const struct link_map * link_map = 0;
|
||||
int ret = dlinfo(handle, RTLD_DI_LINKMAP, &link_map);
|
||||
if (ret || !link_map) {
|
||||
dlclose(handle);
|
||||
return NULL;
|
||||
}
|
||||
image_name_real = realpath(program_invocation_name, NULL);
|
||||
/* sometimes program_invocation_name gets wiped for reasons of beauty... */
|
||||
if (!image_name_real || !image_name_real[0]) {
|
||||
if ((cmdline = fopen("/proc/self/cmdline", "rb"))) {
|
||||
char *arg = 0;
|
||||
size_t size = 0;
|
||||
while (getdelim(&arg, &size, 0, cmdline) != -1) {
|
||||
image_name_real = arg;
|
||||
break;
|
||||
}
|
||||
fclose(cmdline);
|
||||
}
|
||||
}
|
||||
while (link_map->l_prev)
|
||||
link_map = link_map->l_prev;
|
||||
for(int i = 0; link_map->l_next != NULL; link_map = link_map->l_next, i++) {
|
||||
if (link_map->l_name && link_map->l_name[0])
|
||||
image_name = link_map->l_name; /* Absolute pathname according to dlinfo(3) */
|
||||
else {
|
||||
if (i == 0 && image_name_real != NULL)
|
||||
image_name = image_name_real;
|
||||
}
|
||||
if (!image_name || !image_name[0])
|
||||
continue;
|
||||
library_name_sep = strrchr(image_name, DIRSEP_C);
|
||||
if (!library_name_sep)
|
||||
library_name_sep = image_name;
|
||||
else
|
||||
library_name_sep += 1; // skip found '/'
|
||||
if (!library_name || !strcmp(library_name_sep, library_base_name)) {
|
||||
library_file = self->_strndup(a, image_name, PATH_MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (image_name_real)
|
||||
free(image_name_real);
|
||||
return library_file;
|
||||
}
|
||||
|
||||
|
||||
// #else
|
||||
|
||||
// char * apportable_progfile (apportable a, const wchar_t * library_name) {
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
// char * apportable_pathexp(apportable a, const wchar_t * template, const wchar_t * library_path) {
|
||||
// return self->_strndup(a, template, strlen(template));
|
||||
// }
|
||||
|
||||
#endif /*_WIN32, __APPLE__, ...*/
|
||||
|
||||
|
||||
char * apportable_whereis(apportable a, const char * searchpath, const char * bin, int execonly)
|
||||
{
|
||||
apportable self;
|
||||
char * sep;
|
||||
char * pathbuf;
|
||||
char * pathlim;
|
||||
char * path;
|
||||
size_t bin_l;
|
||||
char * cand;
|
||||
size_t cand_l;
|
||||
wchar_t * wcand;
|
||||
#if defined _WIN32
|
||||
size_t wcand_l;
|
||||
#endif
|
||||
int filetest;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (!self->enabled)
|
||||
return NULL;
|
||||
|
||||
pathbuf = (self->_strndup(self, searchpath, 0));
|
||||
pathlim = pathbuf + strlen(pathbuf);
|
||||
path = pathbuf;
|
||||
bin_l = strlen(bin);
|
||||
|
||||
while (path < pathlim) {
|
||||
sep = strchr(path, PATHSEP_C);
|
||||
if (!sep)
|
||||
sep = pathlim;
|
||||
*sep = 0;
|
||||
/* construct candidate binary path */
|
||||
cand_l = strlen(path) + strlen(DIRSEP_S) + bin_l + 1;
|
||||
if (!(cand = self->_calloc(1, cand_l)))
|
||||
{
|
||||
self->_free(pathbuf);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(cand, path);
|
||||
strcat(cand, DIRSEP_S);
|
||||
strcat(cand, bin);
|
||||
cand[cand_l - 1] = 0;
|
||||
#if !defined _WIN32
|
||||
wcand = NULL;
|
||||
filetest = execonly ? X_OK : F_OK;
|
||||
if (access(cand, filetest) == -1)
|
||||
#else
|
||||
wcand_l = MultiByteToWideChar(CP_UTF8, 0, cand, cand_l, NULL, 0);
|
||||
wcand = self->_calloc(sizeof(wchar_t), wcand_l + 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, cand, cand_l, wcand, wcand_l);
|
||||
if (_waccess_s(wcand, 04) != 0)
|
||||
#endif
|
||||
{
|
||||
if (wcand)
|
||||
self->_free(wcand);
|
||||
path = sep < pathlim ? sep + 1 : pathlim;
|
||||
self->_free(cand);
|
||||
continue;
|
||||
}
|
||||
/* found a candidate */
|
||||
self->_free(pathbuf);
|
||||
return cand;
|
||||
}
|
||||
free(pathbuf);
|
||||
return self->_strndup(self, bin, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
char * apportable_pathexp(apportable a, const char * template, const char * library_path)
|
||||
{
|
||||
apportable self;
|
||||
char * exec_path_sym;
|
||||
unsigned long exec_path_symlen;
|
||||
char * result;
|
||||
const char * library_name;
|
||||
char * executable_path_p;
|
||||
char * executable_path;
|
||||
unsigned long exec_path_len;
|
||||
unsigned long template_len;
|
||||
const char * sub_template;
|
||||
unsigned long sub_template_len;
|
||||
unsigned long result_len;
|
||||
|
||||
self = APPORTABLE_STATE(a);
|
||||
if (!self->enabled)
|
||||
return NULL;
|
||||
|
||||
if (!library_path || !template)
|
||||
return NULL;
|
||||
|
||||
exec_path_sym = "$ORIGIN";
|
||||
exec_path_symlen = strlen(exec_path_sym);
|
||||
result = NULL;
|
||||
|
||||
/* if not starting with $ORIGIN, return straight away a copy of the template, unaltered */
|
||||
if (strncmp(template, exec_path_sym, exec_path_symlen) != 0) {
|
||||
result = self->_strndup(a, template, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
library_name = strrchr(library_path, DIRSEP_C);
|
||||
library_name = library_name ? library_name + 1 : library_path;
|
||||
if (library_name - library_path != 0) {
|
||||
executable_path_p = self->_strndup(a, library_path, library_name - library_path);
|
||||
executable_path = executable_path_p;
|
||||
} else {
|
||||
executable_path_p = NULL;
|
||||
executable_path = "." DIRSEP_S;
|
||||
}
|
||||
|
||||
exec_path_len = strlen(executable_path);
|
||||
template_len = strlen(template);
|
||||
sub_template = &template[exec_path_symlen];
|
||||
while (sub_template[0] == DIRSEP_C && executable_path[0] == DIRSEP_C)
|
||||
sub_template += 1;
|
||||
sub_template_len = template_len - exec_path_symlen;
|
||||
result_len = template_len + exec_path_len - exec_path_symlen;
|
||||
|
||||
// concatenate
|
||||
result = calloc(1, result_len + 1);
|
||||
memcpy(result, executable_path, exec_path_len); // "@executable_path"
|
||||
memcpy(&result[exec_path_len], sub_template, sub_template_len); // "/../share/"
|
||||
result[result_len] = 0; // (again)
|
||||
|
||||
if (executable_path_p)
|
||||
free(executable_path_p);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /*APPORTABLE*/
|
||||
|
@ -0,0 +1,80 @@
|
||||
/* apportable.c - Self-awareness, cross-platform
|
||||
*
|
||||
* Copyright (C) 2018 Claudio Luck
|
||||
*
|
||||
* This file is part of apportable.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of either
|
||||
*
|
||||
* - the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or
|
||||
*
|
||||
* - the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or both in parallel, as here.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef APPORTABLE_H
|
||||
#define APPORTABLE_H
|
||||
|
||||
|
||||
typedef struct apportable_t
|
||||
{
|
||||
int initialized; /* keep this first, for initialization = {0} */
|
||||
int enabled;
|
||||
char * _iconv_wchar_t;
|
||||
void * (*_calloc) (size_t, size_t);
|
||||
void (*_free) (void *);
|
||||
|
||||
char * (*_strndup) (struct apportable_t *, const char *, size_t);
|
||||
wchar_t * (*_wcsndup) (struct apportable_t *, const wchar_t *, size_t);
|
||||
|
||||
char * (*wutf8) (struct apportable_t *, const wchar_t *);
|
||||
char * (*wutf8_free) (struct apportable_t *, wchar_t *);
|
||||
wchar_t * (*uwchar_t) (struct apportable_t *, const char *);
|
||||
wchar_t * (*uwchar_t_free) (struct apportable_t *, char *);
|
||||
char * (*ugetenv) (struct apportable_t *, char *);
|
||||
char * (*wugetenv) (struct apportable_t *, wchar_t *);
|
||||
|
||||
char * (*whereis) (struct apportable_t *, const char *, const char *, int);
|
||||
char * (*progfile) (struct apportable_t *, const char *);
|
||||
char * (*pathexp) (struct apportable_t *, const char *, const char *);
|
||||
}
|
||||
apportable_t, * apportable;
|
||||
|
||||
|
||||
void apportable_new (apportable a);
|
||||
void apportable_init (apportable a, int enabled);
|
||||
apportable apportable_getstate (apportable a);
|
||||
|
||||
char * apportable_strndup (apportable a, const char * str, size_t size);
|
||||
wchar_t * apportable_wcsndup (apportable a, const wchar_t * str, size_t syms);
|
||||
|
||||
char * apportable_wutf8 (apportable a, const wchar_t * s);
|
||||
char * apportable_wutf8_free (apportable a, wchar_t * s);
|
||||
wchar_t * apportable_uwchar_t (apportable a, const char * s);
|
||||
wchar_t * apportable_uwchar_t_free (apportable a, char * s);
|
||||
char * apportable_ugetenv (apportable a, char * var);
|
||||
char * apportable_wugetenv (apportable a, wchar_t * wvar);
|
||||
|
||||
char * apportable_whereis (apportable a, const char * searchpath, const char * bin, int execonly);
|
||||
char * apportable_progfile (apportable a, const char * library_name);
|
||||
char * apportable_pathexp (apportable a, const char * template, const char * library_path);
|
||||
|
||||
|
||||
#endif /*APPORTABLE_H*/
|
@ -0,0 +1,439 @@
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "Python.h"
|
||||
#include "bytesobject.h"
|
||||
|
||||
#include "apportable.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// #include <iconv.h>
|
||||
|
||||
|
||||
struct module_state {
|
||||
PyObject *error;
|
||||
apportable_t apportable;
|
||||
};
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
# define GETSTATE(self) ((struct module_state*)PyModule_GetState((self)))
|
||||
#else
|
||||
# define GETSTATE(self) (&_state)
|
||||
static struct module_state _state;
|
||||
#endif
|
||||
|
||||
|
||||
char * hexdump(void * p, size_t count)
|
||||
{
|
||||
unsigned char * m = (unsigned char *) p;
|
||||
char * s = calloc(3, count);
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
sprintf(s + 3*i, "%02x", ((m + i))[0]);
|
||||
s[3*i + 2] = ' ';
|
||||
}
|
||||
s[3*count - 1] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
char * hexstr(char * p)
|
||||
{
|
||||
size_t l;
|
||||
l = strlen(p);
|
||||
return hexdump(p, l + 1);
|
||||
}
|
||||
|
||||
|
||||
char * whexstr(wchar_t * p)
|
||||
{
|
||||
size_t l;
|
||||
l = wcslen(p);
|
||||
return hexdump(p, sizeof(wchar_t) * (l + 1));
|
||||
}
|
||||
|
||||
|
||||
/* Python Unicode string object -> wchar_t * */
|
||||
wchar_t * _appoext_pyobywstr (PyObject * self, PyObject * str)
|
||||
{
|
||||
wchar_t * wstr;
|
||||
Py_ssize_t str_l;
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03020000
|
||||
str_l = PyUnicode_GetLength(str);
|
||||
if (!(wstr = PyUnicode_AsWideCharString(str, NULL)))
|
||||
return NULL;
|
||||
#else
|
||||
Py_ssize_t wstr_l;
|
||||
|
||||
str_l = PyUnicode_GetSize(str); /* DEPRECATED in Python 3.2+ */
|
||||
wstr_l = sizeof(wchar_t) * (str_l + 1);
|
||||
if (!(wstr = PyMem_Malloc(wstr_l)))
|
||||
return NULL;
|
||||
memset(wstr, 0, wstr_l);
|
||||
if (-1 == (wstr_l = PyUnicode_AsWideChar((PyUnicodeObject *) str, wstr, wstr_l))) {
|
||||
PyMem_Free(wstr);
|
||||
return NULL;
|
||||
}
|
||||
wstr[wstr_l] = 0;
|
||||
#endif
|
||||
|
||||
return wstr; /* to PyMem_Free() */
|
||||
}
|
||||
|
||||
|
||||
/* take a Python Unicode string object, and get a UTF-8 copy */
|
||||
char * _appoext_pyobyutf8 (PyObject * self, PyObject * str)
|
||||
{
|
||||
PyObject * bytes;
|
||||
char * s, * ret;
|
||||
size_t s_l;
|
||||
|
||||
if (!(bytes = PyUnicode_AsUTF8String(str)))
|
||||
return NULL;
|
||||
if (!(s = PyBytes_AsString(bytes))) {
|
||||
return NULL;
|
||||
}
|
||||
s_l = strlen(s);
|
||||
if (!(ret = PyMem_Malloc(s_l + 1)))
|
||||
return NULL;
|
||||
memcpy(ret, s, s_l);
|
||||
ret[s_l] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _selftest (PyObject * self)
|
||||
{
|
||||
struct module_state *st;
|
||||
st = GETSTATE(self);
|
||||
apportable a = &(st->apportable);
|
||||
|
||||
// wchar_t * t = L"äβ©☃☂";
|
||||
// wchar_t * x = "❤"
|
||||
// "\xc3\xa4\xce\xb2\xc2\xa9\xe2\x98\x83\xe2\x98\x82"; /* UTF-16 */
|
||||
char * tt = "\xc3\xa4\xce\xb2\xe2\x9d\xa4\xc2\xa9\xe2\x98\x83\xe2\x98\x82";
|
||||
char * r;
|
||||
wchar_t * wr;
|
||||
char * r2;
|
||||
PyObject * o = PyUnicode_FromString(tt);
|
||||
|
||||
|
||||
r = _appoext_pyobyutf8(self, o);
|
||||
printf("utf-8 : %zu '%s'\n", strlen(r), r);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
|
||||
wr = _appoext_pyobywstr(self, o);
|
||||
printf("wchar_t: %zu %s\n", wcslen(wr), whexstr(wr));
|
||||
|
||||
r2 = a->wutf8(a, wr);
|
||||
printf("wutf8(): %zu %s\n", strlen(r2), hexstr(r2));
|
||||
|
||||
o = PyUnicode_FromString(r2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
appoext_selftest (PyObject * self, PyObject * args)
|
||||
{
|
||||
_selftest(self);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_strndup (PyObject * self, PyObject * args)
|
||||
{
|
||||
PyObject * str;
|
||||
Py_ssize_t size;
|
||||
struct module_state *st;
|
||||
char * s, * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "Un", &str, &size)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
s = _appoext_pyobyutf8(self, str);
|
||||
ret = st->apportable._strndup(&(st->apportable), s, size);
|
||||
|
||||
pyres = PyUnicode_FromString(ret);
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_wcsndup (PyObject * self, PyObject * args)
|
||||
{
|
||||
PyObject * str;
|
||||
Py_ssize_t size;
|
||||
wchar_t * wstr;
|
||||
struct module_state *st;
|
||||
char * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "Un", &str, &size)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
wstr = _appoext_pyobywstr(self, str);
|
||||
ret = (char *) st->apportable._wcsndup(&(st->apportable), wstr, size);
|
||||
PyMem_Free(wstr);
|
||||
|
||||
pyres = PyUnicode_FromWideChar((wchar_t *) ret, wcslen((wchar_t *) ret));
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_wutf8 (PyObject * self, PyObject * args)
|
||||
{
|
||||
PyObject * str;
|
||||
wchar_t * wstr;
|
||||
struct module_state *st;
|
||||
char * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "U", &str)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
wstr = _appoext_pyobywstr(self, str);
|
||||
ret = st->apportable.wutf8(&(st->apportable), wstr);
|
||||
|
||||
pyres = PyUnicode_FromString(ret);
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_uwchar_t (PyObject * self, PyObject * args)
|
||||
{
|
||||
PyObject * str;
|
||||
char * cstr;
|
||||
struct module_state *st;
|
||||
wchar_t * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "U", &str)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
cstr = _appoext_pyobyutf8(self, str);
|
||||
ret = st->apportable.uwchar_t(&(st->apportable), cstr);
|
||||
|
||||
pyres = PyUnicode_FromWideChar((wchar_t *) ret, wcslen((wchar_t *) ret));
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_whereis (PyObject * self, PyObject * args)
|
||||
{
|
||||
struct module_state *st;
|
||||
PyObject * osp, * obin;
|
||||
char * searchpath, * bin;
|
||||
int execonly;
|
||||
char * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "UUb", &osp, &obin, &execonly)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
searchpath = _appoext_pyobyutf8(self, osp);
|
||||
bin = _appoext_pyobyutf8(self, obin);
|
||||
|
||||
ret = st->apportable.whereis(&(st->apportable), searchpath, bin, execonly);
|
||||
if (!ret) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
pyres = PyUnicode_FromString(ret);
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_progfile (PyObject * self, PyObject * args)
|
||||
{
|
||||
const char * library_name;
|
||||
struct module_state *st;
|
||||
char * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "z", &library_name)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
|
||||
ret = st->apportable.progfile(&(st->apportable), library_name);
|
||||
// pyres = Py_BuildValue("s", ret);
|
||||
pyres = PyUnicode_FromString(ret);
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_pathexp (PyObject * self, PyObject * args)
|
||||
{
|
||||
PyObject * ot, * olb;
|
||||
const char * template, * library_path;
|
||||
struct module_state *st;
|
||||
char * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "UU", &ot, &olb)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
template = _appoext_pyobyutf8(self, ot);
|
||||
library_path = _appoext_pyobyutf8(self, olb);
|
||||
|
||||
ret = st->apportable.pathexp(&(st->apportable), template, library_path);
|
||||
pyres = PyUnicode_FromString(ret);
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_ugetenv (PyObject * self, PyObject * args)
|
||||
{
|
||||
struct module_state *st;
|
||||
PyObject * oenv;
|
||||
char * env;
|
||||
char * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "U", &oenv)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
env = _appoext_pyobyutf8(self, oenv);
|
||||
|
||||
ret = st->apportable.ugetenv(&(st->apportable), env);
|
||||
if (!ret) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
pyres = PyUnicode_FromString(ret);
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
appoext_wugetenv (PyObject * self, PyObject * args)
|
||||
{
|
||||
struct module_state *st;
|
||||
PyObject * oenv;
|
||||
wchar_t * env;
|
||||
char * ret;
|
||||
PyObject * pyres;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "U", &oenv)) {
|
||||
return NULL;
|
||||
}
|
||||
st = GETSTATE(self);
|
||||
env = _appoext_pyobywstr(self, oenv);
|
||||
|
||||
ret = st->apportable.wugetenv(&(st->apportable), env);
|
||||
if (!ret) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
pyres = PyUnicode_FromString(ret);
|
||||
free(ret);
|
||||
return pyres;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static PyMethodDef apportable_methods[] = {
|
||||
{"selftest", appoext_selftest, METH_VARARGS, NULL},
|
||||
{"strndup", appoext_strndup, METH_VARARGS, NULL},
|
||||
{"wcsndup", appoext_wcsndup, METH_VARARGS, NULL},
|
||||
{"wutf8", appoext_wutf8, METH_VARARGS, NULL},
|
||||
{"uwchar_t", appoext_uwchar_t, METH_VARARGS, NULL},
|
||||
{"progfile", appoext_progfile, METH_VARARGS, NULL},
|
||||
{"pathexp", appoext_pathexp, METH_VARARGS, NULL},
|
||||
{"whereis", appoext_whereis, METH_VARARGS, NULL},
|
||||
{"ugetenv", appoext_ugetenv, METH_VARARGS, NULL},
|
||||
{"wugetenv", appoext_wugetenv, METH_VARARGS, NULL},
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
static int apportable_traverse(PyObject *self, visitproc visit, void *arg) {
|
||||
Py_VISIT(GETSTATE(self)->error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apportable_clear(PyObject *self) {
|
||||
Py_CLEAR(GETSTATE(self)->error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT, "apportable", NULL, sizeof(struct module_state),
|
||||
apportable_methods, NULL, apportable_traverse, apportable_clear, NULL
|
||||
};
|
||||
|
||||
#define INITERROR return NULL
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_apportable(void)
|
||||
|
||||
#else /* PY_MAJOR_VERSION >= 3 */
|
||||
#define INITERROR return
|
||||
|
||||
void
|
||||
initapportable(void)
|
||||
#endif
|
||||
{
|
||||
struct module_state *st;
|
||||
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *module = PyModule_Create(&moduledef);
|
||||
#else
|
||||
PyObject *module = Py_InitModule("apportable", apportable_methods);
|
||||
#endif
|
||||
|
||||
if (module == NULL)
|
||||
INITERROR;
|
||||
st = GETSTATE(module);
|
||||
apportable_init(&(st->apportable), 1);
|
||||
|
||||
st->error = PyErr_NewException("apportable.Error", NULL, NULL);
|
||||
if (st->error == NULL) {
|
||||
Py_DECREF(module);
|
||||
INITERROR;
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return module;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Not for release */
|
||||
|
||||
// PyErr_Format(PyExc_ValueError, "X %zd", str_l);
|
||||
// return NULL;
|
||||
|
@ -0,0 +1,25 @@
|
||||
|
||||
import sys
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
if sys.platform != 'win32':
|
||||
ext_libs = ['iconv']
|
||||
else:
|
||||
ext_libs = []
|
||||
|
||||
|
||||
apportable = Extension(
|
||||
'apportable',
|
||||
define_macros = [('APPORTABLE', '1')],
|
||||
libraries = [] + ext_libs,
|
||||
sources = ['apportable.c', 'apportable_pyext.c']
|
||||
)
|
||||
|
||||
|
||||
setup(
|
||||
name = 'libapportable-test',
|
||||
version = '1.0',
|
||||
description = 'Apportable tests',
|
||||
ext_modules = [apportable],
|
||||
)
|
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
try:
|
||||
unicode
|
||||
except:
|
||||
unicode = str
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import apportable
|
||||
except ImportError:
|
||||
sys.path.append("build/lib")
|
||||
import apportable
|
||||
|
||||
|
||||
class TestClass(unittest.TestCase):
|
||||
|
||||
t1 = u"abcde"
|
||||
t2 = u"äβ©☃☂"
|
||||
|
||||
def test_strndup(self):
|
||||
a = apportable
|
||||
|
||||
for t in (self.t1, self.t2):
|
||||
self.assertEqual( a.strndup(t, 0), t )
|
||||
for x in range(1, len(t)+1):
|
||||
# self.assertEqual( repr(a.strndup(t, x)), repr(t[0:x]) )
|
||||
self.assertEqual( a.strndup(t, x), t[0:x] )
|
||||
|
||||
def test_wcsndup(self):
|
||||
a = apportable
|
||||
|
||||
for t in (self.t1, self.t2):
|
||||
self.assertEqual( a.wcsndup(t, 0), t )
|
||||
for x in range(1, len(t)+1):
|
||||
self.assertEqual( a.wcsndup(t, x), t[0:x] )
|
||||
|
||||
def test_wutf8(self):
|
||||
a = apportable
|
||||
|
||||
for t in (self.t1, self.t2):
|
||||
self.assertEqual( a.wutf8(t), t)
|
||||
|
||||
def test_uwchar_t(self):
|
||||
a = apportable
|
||||
|
||||
for t in (self.t1, self.t2):
|
||||
self.assertEqual( a.uwchar_t(t), t)
|
||||
|
||||
def test_whereis(self):
|
||||
a = apportable
|
||||
|
||||
pth = unicode(os.environ.get("PATH", "/opt:/bin"))
|
||||
wherepy = subprocess.check_output("whereis python", shell=True)
|
||||
wherepy = wherepy.decode('utf-8').split('\n')[0]
|
||||
self.assertEqual(a.whereis(pth, u"python", 1), wherepy)
|
||||
self.assertEqual(a.whereis(u"/opt:/etc", u"hosts", 1), "hosts")
|
||||
self.assertEqual(a.whereis(u"/opt:/etc", u"hosts", 0), "/etc/hosts")
|
||||
self.assertEqual(a.whereis(u"::::", u"hosts", 0), "hosts")
|
||||
self.assertEqual(a.whereis(u"" |