说明
native 函数
private native long newLpConfigImpl(String file);
private native void delete(long ptr);
private native void sync(long ptr);
private native void setInt(long ptr, String section, String key, int value);
private native void setFloat(long ptr, String section, String key, float value);
private native void setBool(long ptr, String section, String key, boolean value);
private native void setString(long ptr, String section, String key, String value);
private native void setIntRange(long ptr, String section, String key, int min, int max);
private native int getInt(long ptr, String section, String key, int defaultValue);
private native float getFloat(long ptr, String section, String key, float defaultValue);
private native boolean getBool(long ptr, String section, String key, boolean defaultValue);
private native String getString(long ptr, String section, String key, String defaultValue);
private native int[] getIntRange(long ptr, String section, String key, int defaultMin, int defaultMax);
具体的函数分析
newLpConfigImpl
// LpConfig
extern "C" jlong Java_org_linphone_core_LpConfigImpl_newLpConfigImpl(JNIEnv *env, jobject thiz, jstring file) {
const char *cfile = env->GetStringUTFChars(file, NULL);
LpConfig *lp = lp_config_new(cfile);
env->ReleaseStringUTFChars(file, cfile);
return (jlong) lp;
}
lp_config_new
submodules/linphone/coreapi/lpconfig.c:LpConfig * lp_config_new(const char *filename){
submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC LpConfig * lp_config_new(const char *filename);
根据以往的经验, 本篇写文章就是根据lpconfig.h和lpconfig.c文件展开的。 这充分说明了, linphone是按模块化来区分的 。 这一点在以后的分析中,和自己写程序的时候, 会有很大的帮助。
LpConfig * lp_config_new(const char *filename){
return lp_config_new_with_factory(filename, NULL);
}
lp_config_new_with_factory
LpConfig *lp_config_new_with_factory(const char *config_filename, const char *factory_config_filename) {
LpConfig *lpconfig=lp_new0(LpConfig,1);
lpconfig->refcnt=1;
if (config_filename!=NULL){
if(ortp_file_exist(config_filename) == 0) {
lpconfig->filename=lp_realpath(config_filename, NULL);
if(lpconfig->filename == NULL) {
ms_error("Could not find the real path of %s: %s", config_filename, strerror(errno));
goto fail;
}
} else {
lpconfig->filename = ms_strdup(config_filename);
}
lpconfig->tmpfilename=ortp_strdup_printf("%s.tmp",lpconfig->filename);
ms_message("Using (r/w) config information from %s", lpconfig->filename);
#if !defined(_WIN32)
{
struct stat fileStat;
if ((stat(lpconfig->filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) {
/* make existing configuration files non-group/world-accessible */
if (chmod(lpconfig->filename, S_IRUSR | S_IWUSR) == -1) {
ms_warning("unable to correct permissions on "
"configuration file: %s", strerror(errno));
}
}
}
#endif /*_WIN32*/
/*open with r+ to check if we can write on it later*/
lpconfig->file=fopen(lpconfig->filename,"r+");
#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH
if (lpconfig->file==NULL){
lpconfig->file=fopen(lpconfig->tmpfilename,"r+");
if (lpconfig->file){
ms_warning("Could not open %s but %s works, app may have crashed during last sync.",lpconfig->filename,lpconfig->tmpfilename);
}
}
#endif
if (lpconfig->file!=NULL){
lp_config_parse(lpconfig,lpconfig->file);
fclose(lpconfig->file);
lpconfig->file=NULL;
lpconfig->modified=0;
}
}
if (factory_config_filename != NULL) {
lp_config_read_file(lpconfig, factory_config_filename);
}
return lpconfig;
fail:
ms_free(lpconfig);
return NULL;
}
LpConfig
struct _LpConfig{
int refcnt;
FILE *file;
char *filename;
char *tmpfilename;
MSList *sections;
int modified;
int readonly;
};
这个下划线和不错, 将结构做成一个统一的格式, 使用的又是一个没有“_”格式, 这样又方便查找, 又利于其他人的理解。这里用的太帅了。
这里有一个FILE类型的声明, 我突然想到, 是不是那个作为配置文件的文件, 是不是通过这个类存储的呢, 有可能, 也有可能这就是一个结构,存储在另一个地方。我感觉还是前者, 毕竟是FILE类型么。好, 继续看看。
lp_new0
submodules/linphone/coreapi/lpconfig.c:#define lp_new0(type,n) (type*)calloc(sizeof(type),n)
原来是给再分配内存重新分配了一个名字。为啥! 就是为了便于阅读。也许吧。
lp_realpath
submodules/linphone/coreapi/lpconfig.c:char* lp_realpath(const char* file, char* name) {
char* lp_realpath(const char* file, char* name) {
#if defined(_WIN32) || defined(__QNX__) || defined(ANDROID)
return ms_strdup(file);
#else
char * output = realpath(file, name);
char * msoutput = ms_strdup(output);
free(output);
return msoutput;
#endif
}
就是分配内存之类的, 这是个通用的格式。不予理会, 以后可以详细看。
ortp_file_exist
submodules/linphone/oRTP/include/ortp/port.h:ORTP_PUBLIC int ortp_file_exist(const char *pathname);
submodules/linphone/oRTP/src/port.c:int ortp_file_exist(const char *pathname) {
submodules/linphone/oRTP/src/port.c:int ortp_file_exist(const char *pathname) {
submodules/linphone/mediastreamer2/src/ortp-deps/port.c:int ortp_file_exist(const char *pathname) {
submodules/linphone/mediastreamer2/src/ortp-deps/port.c:int ortp_file_exist(const char *pathname) {
submodules/linphone/mediastreamer2/src/ortp-deps/ortp/port.h:ORTP_PUBLIC int ortp_file_exist(const char *pathname);
这里有两个显现, 分别在oRTP和ortp-deps中。让我先看看, 在下结论这里为什么有两个, 让我带着怀疑‘两个实现不一样吗?’看看。
- 第一个
#if defined (_WIN32_WCE) || defined(_MSC_VER)
int ortp_file_exist(const char *pathname) {
FILE* fd;
if (pathname==NULL) return -1;
fd=fopen(pathname,"r");
if (fd==NULL) {
return -1;
} else {
fclose(fd);
return 0;
}
}
#else
int ortp_file_exist(const char *pathname) {
return access(pathname,F_OK);
}
#endif /*_WIN32_WCE*/
这里不得不赞叹, 这个预编译也太神奇了, 不同的代码可以存在一个地方, 我曾经在写java的时候被老板说过可不可以像c语言一样预编译,但是并没有什么在意, 没想到现在遇到了,真感觉c语言不错。
- 第二个
#if defined (_WIN32_WCE) || defined(_MSC_VER)
int ortp_file_exist(const char *pathname) {
FILE* fd;
if (pathname==NULL) return -1;
fd=fopen(pathname,"r");
if (fd==NULL) {
return -1;
} else {
fclose(fd);
return 0;
}
}
#else
int ortp_file_exist(const char *pathname) {
return access(pathname,F_OK);
}
#endif /*_WIN32_WCE*/
第一个和第二个代码一样啊, 为什么会这个样子呢, 估计是第二个是第一个的改进版, 所以才出奇的相似吧。肯定是这个样子的。
ortp_strdup_printf
submodules/linphone/mediastreamer2/src/ortp-deps/logging.c:char *ortp_strdup_printf(const char *fmt,...){
submodules/linphone/oRTP/src/logging.c:char *ortp_strdup_printf(const char *fmt,...){
这一一看就是打印log日志的地方。详细看一看,毕竟我们公司的项目里面用到了,这个, 这个号对比的学习一番。
char *ortp_strdup_printf(const char *fmt,...){
char *ret;
va_list args;
va_start (args, fmt);
ret=ortp_strdup_vprintf(fmt, args);
va_end (args);
return ret;
}
这里还是归结到了其他地方, 看下面。
ortp_strdup_vprintf
submodules/linphone/oRTP/include/ortp/port.h:ORTP_PUBLIC char *ortp_strdup_vprintf(const char *fmt, va_list ap);
submodules/linphone/oRTP/src/logging.c:char * ortp_strdup_vprintf(const char *fmt, va_list ap)
哇塞!
char * ortp_strdup_vprintf(const char *fmt, va_list ap)
{
/* Guess we need no more than 100 bytes. */
int n, size = 200;
char *p,*np;
#ifndef _WIN32
va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
#endif
if ((p = (char *) ortp_malloc (size)) == NULL)
return NULL;
while (1){
/* Try to print in the allocated space. */
#ifndef _WIN32
va_copy(cap,ap);
n = vsnprintf (p, size, fmt, cap);
va_end(cap);
#else
/*this works on 32 bits, luckily*/
n = vsnprintf (p, size, fmt, ap);
#endif
/* If that worked, return the string. */
if (n > -1 && n < size)
return p;
//printf("Reallocing space.\n");
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n + 1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
if ((np = (char *) ortp_realloc (p, size)) == NULL)
{
free(p);
return NULL;
} else {
p = np;
}
}
}
这么多的预编译。真应该好好看看, 但是我估计我也不明白。往下看吧。
delete
extern "C" void Java_org_linphone_core_LpConfigImpl_delete(JNIEnv *env, jobject thiz, jlong lpc) {
LpConfig *lp = (LpConfig *)lpc;
lp_config_destroy(lp);
}
lp_config_destroy
static void _lp_config_destroy(LpConfig *lpconfig){
if (lpconfig->filename!=NULL) ortp_free(lpconfig->filename);
if (lpconfig->tmpfilename) ortp_free(lpconfig->tmpfilename);
ms_list_for_each(lpconfig->sections,(void (*)(void*))lp_section_destroy);
ms_list_free(lpconfig->sections);
free(lpconfig);
}
sync
extern "C" void Java_org_linphone_core_LpConfigImpl_sync(JNIEnv *env, jobject thiz, jlong lpc) {
LpConfig *lp = (LpConfig *)lpc;
lp_config_sync(lp);
}
lp_config_sync
int lp_config_sync(LpConfig *lpconfig){
FILE *file;
if (lpconfig->filename==NULL) return -1;
if (lpconfig->readonly) return 0;
#ifndef _WIN32
/* don't create group/world-accessible files */
(void) umask(S_IRWXG | S_IRWXO);
#endif
file=fopen(lpconfig->tmpfilename,"w");
if (file==NULL){
ms_warning("Could not write %s ! Maybe it is read-only. Configuration will not be saved.",lpconfig->filename);
lpconfig->readonly=1;
return -1;
}
ms_list_for_each2(lpconfig->sections,(void (*)(void *,void*))lp_section_write,(void *)file);
fclose(file);
#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH
/* On windows, rename() does not accept that the newpath is an existing file, while it is accepted on Unix.
* As a result, we are forced to first delete the linphonerc file, and then rename.*/
if (remove(lpconfig->filename)!=0){
ms_error("Cannot remove %s: %s",lpconfig->filename, strerror(errno));
}
#endif
if (rename(lpconfig->tmpfilename,lpconfig->filename)!=0){
ms_error("Cannot rename %s into %s: %s",lpconfig->tmpfilename,lpconfig->filename,strerror(errno));
}
lpconfig->modified=0;
return 0;
}
啊哈, 我就说了么, 这个就是写入到文件中的, 不知道是哪个文件。 让我去找找, 应该LinphoneCoreFactory.java中, 我看看是不是。
呜呜呜, 没找到, 这个无耻的搜索linphonerc 这个文件了, 想知道这个是哪里知道的么, 是在linphone工程目录下, 是真的运行目录下。/data/data/org.sainthigh.linphone/files
让我们碰碰运气。
原来是在linphoneManager中。
mLPConfigXsd = basePath + "/lpconfig.xsd";
mLinphoneFactoryConfigFile = basePath + "/linphonerc";
mLinphoneConfigFile = basePath + "/.linphonerc";
mLinphoneRootCaFile = basePath + "/rootca.pem";
mRingSoundFile = basePath + "/oldphone_mono.wav";
mRingbackSoundFile = basePath + "/ringback.wav";
mPauseSoundFile = basePath + "/hold.mkv";
mChatDatabaseFile = basePath + "/linphone-history.db";
mCallLogDatabaseFile = basePath + "/linphone-log-history.db";
mErrorToneFile = basePath + "/error.wav";
mConfigFile = basePath + "/configrc";
mUserCertificatePath = basePath;
setInt
extern "C" void Java_org_linphone_core_LpConfigImpl_setInt(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jint value) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
lp_config_set_int((LpConfig *)lpc, csection, ckey, (int) value);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
}
这个linphonecore_jni可有意思了, 把所有的有关与java和c对应的东西,都放在这里。
lp_config_set_int
submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value);
submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value);
submodules/linphone/coreapi/lpconfig.h:LINPHONE_PUBLIC void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value);
submodules/linphone/coreapi/lpconfig.c:void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value){
submodules/linphone/coreapi/lpconfig.c:void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value){
submodules/linphone/coreapi/lpconfig.c:void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value){
void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value){
char tmp[30];
snprintf(tmp,sizeof(tmp),"%i",value);
lp_config_set_string(lpconfig,section,key,tmp);
}
void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value){
char tmp[30];
snprintf(tmp,sizeof(tmp),"0x%x",value);
lp_config_set_string(lpconfig,section,key,tmp);
}
void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value){
char tmp[30];
snprintf(tmp,sizeof(tmp),"%lli",(long long)value);
lp_config_set_string(lpconfig,section,key,tmp);
}
lp_config_set_string
这个应该是写入文件的操作了。为什么会这么想呢, 前面已经遇到了这样的函数了啊。没什么可激动的。
void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value){
LpItem *item;
LpSection *sec=lp_config_find_section(lpconfig,section);
if (sec!=NULL){
item=lp_section_find_item(sec,key);
if (item!=NULL){
if (value!=NULL && value[0] != '\0')
lp_item_set_value(item,value);
else lp_section_remove_item(sec,item);
}else{
if (value!=NULL && value[0] != '\0')
lp_section_add_item(sec,lp_item_new(key,value));
}
}else if (value!=NULL && value[0] != '\0'){
sec=lp_section_new(section);
lp_config_add_section(lpconfig,sec);
lp_section_add_item(sec,lp_item_new(key,value));
}
lpconfig->modified++;
}
不错不错, 原来差不多所有的有关操作都在这个类里面的,这是结构化, 不错不错。
LpItem
typedef struct _LpItem{
char *key;
char *value;
int is_comment;
bool_t overwrite; // If set to true, will add overwrite=true when converted to xml
} LpItem;
lp_section_find_item
LpItem *lp_section_find_item(const LpSection *sec, const char *name){
MSList *elem;
LpItem *item;
/*printf("Looking for item %s\n",name);*/
for (elem=sec->items;elem!=NULL;elem=ms_list_next(elem)){
item=(LpItem*)elem->data;
if (!item->is_comment && strcmp(item->key,name)==0) {
/*printf("Item %s found\n",name);*/
return item;
}
}
return NULL;
}
LpSection
typedef struct _LpSection{
char *name;
MSList *items;
MSList *params;
bool_t overwrite; // If set to true, will add overwrite=true to all items of this section when converted to xml
} LpSection;
lp_config_find_section
LpSection *lp_config_find_section(const LpConfig *lpconfig, const char *name){
LpSection *sec;
MSList *elem;
/*printf("Looking for section %s\n",name);*/
for (elem=lpconfig->sections;elem!=NULL;elem=ms_list_next(elem)){
sec=(LpSection*)elem->data;
if (strcmp(sec->name,name)==0){
/*printf("Section %s found\n",name);*/
return sec;
}
}
return NULL;
}
lp_item_set_value
void lp_item_set_value(LpItem *item, const char *value){
char *prev_value=item->value;
item->value=ortp_strdup(value);
ortp_free(prev_value);
}
其实我现在还是没有搞明白ortp_strfup 到底是干什么用的, 好尴尬, 其实我已经看到过了。
lp_section_remove_item
void lp_section_remove_item(LpSection *sec, LpItem *item){
sec->items=ms_list_remove(sec->items,(void *)item);
lp_item_destroy(item);
}
lp_item_destroy
void lp_item_destroy(void *pitem){
LpItem item=(LpItem)pitem;
if (item->key) ortp_free(item->key);
ortp_free(item->value);
free(item);
}
就是将整个结构都删除掉, 让其不在保存。
lp_section_add_item
void lp_section_add_item(LpSection *sec,LpItem *item){
sec->items=ms_list_append(sec->items,(void *)item);
}
将新的数据添加到列表中。
lp_section_new
LpSection *lp_section_new(const char *name){
LpSection *sec=lp_new0(LpSection,1);
sec->name=ortp_strdup(name);
return sec;
}
lp_config_add_section
void lp_config_add_section(LpConfig *lpconfig, LpSection *section){
lpconfig->sections=ms_list_append(lpconfig->sections,(void *)section);
}
setFloat
extern "C" void Java_org_linphone_core_LpConfigImpl_setFloat(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jfloat value) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
lp_config_set_float((LpConfig *)lpc, csection, ckey, (float) value);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
}
lp_config_set_float
void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value){
char tmp[30];
snprintf(tmp,sizeof(tmp),"%f",value);
lp_config_set_string(lpconfig,section,key,tmp);
}
lp_config_set_string
void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value){
LpItem *item;
LpSection *sec=lp_config_find_section(lpconfig,section);
if (sec!=NULL){
item=lp_section_find_item(sec,key);
if (item!=NULL){
if (value!=NULL && value[0] != '\0')
lp_item_set_value(item,value);
else lp_section_remove_item(sec,item);
}else{
if (value!=NULL && value[0] != '\0')
lp_section_add_item(sec,lp_item_new(key,value));
}
}else if (value!=NULL && value[0] != '\0'){
sec=lp_section_new(section);
lp_config_add_section(lpconfig,sec);
lp_section_add_item(sec,lp_item_new(key,value));
}
lpconfig->modified++;
}
lp_item_set_value
void lp_item_set_value(LpItem *item, const char *value){
char *prev_value=item->value;
item->value=ortp_strdup(value);
ortp_free(prev_value);
}
setBool
extern "C" void Java_org_linphone_core_LpConfigImpl_setBool(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jboolean value) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
lp_config_set_int((LpConfig *)lpc, csection, ckey, value ? 1 : 0);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
}
setString
extern "C" void Java_org_linphone_core_LpConfigImpl_setString(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jstring value) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
const char *cvalue = value ? env->GetStringUTFChars(value, NULL) : NULL;
lp_config_set_string((LpConfig *)lpc, csection, ckey, cvalue);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
if (value) env->ReleaseStringUTFChars(value, cvalue);
}
setIntRange
extern "C" void Java_org_linphone_core_LpConfigImpl_setIntRange(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jint min, jint max) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
lp_config_set_range((LpConfig *)lpc, csection, ckey, min, max);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
}
lp_config_set_range
void lp_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value) {
char tmp[30];
snprintf(tmp, sizeof(tmp), "%i-%i", min_value, max_value);
lp_config_set_string(lpconfig, section, key, tmp);
}
getInt
extern "C" jint Java_org_linphone_core_LpConfigImpl_getInt(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jint defaultValue) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
int returnValue = lp_config_get_int((LpConfig *)lpc, csection, ckey, (int) defaultValue);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
return (jint) returnValue;
}
getFloat
extern "C" jfloat Java_org_linphone_core_LpConfigImpl_getFloat(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jfloat defaultValue) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
float returnValue = lp_config_get_float((LpConfig *)lpc, csection, ckey, (float) defaultValue);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
return (jfloat) returnValue;
}
getBool
extern "C" jboolean Java_org_linphone_core_LpConfigImpl_getBool(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jboolean defaultValue) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
int returnValue = lp_config_get_int((LpConfig *)lpc, csection, ckey, defaultValue ? 1 : 0);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
return (jboolean) returnValue == 1;
}
getString
extern "C" jstring Java_org_linphone_core_LpConfigImpl_getString(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jstring defaultValue) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
const char *cvalue = defaultValue ? env->GetStringUTFChars(defaultValue, NULL) : NULL;
const char *returnValue = lp_config_get_string((LpConfig *)lpc, csection, ckey, cvalue);
jstring jreturnValue = NULL;
if (returnValue)
jreturnValue = env->NewStringUTF(returnValue);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
if (cvalue)
env->ReleaseStringUTFChars(defaultValue, cvalue);
return jreturnValue;
}
getIntRange
extern "C" jintArray Java_org_linphone_core_LpConfigImpl_getIntRange(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jint defaultmin, jint defaultmax) {
const char *csection = env->GetStringUTFChars(section, NULL);
const char *ckey = env->GetStringUTFChars(key, NULL);
int *values = (int*)calloc(2, sizeof(int));
lp_config_get_range((LpConfig *)lpc, csection, ckey, &values[0], &values[1], defaultmin, defaultmax);
jintArray returnValues = env->NewIntArray(2);
env->SetIntArrayRegion(returnValues, 0, 2, values);
ms_free(values);
env->ReleaseStringUTFChars(section, csection);
env->ReleaseStringUTFChars(key, ckey);
return returnValues;
}