linphone-LpConfigImpl文件对应的JNI层文件分析

来源:互联网 发布:php小项目源码 编辑:程序博客网 时间:2024/06/05 01:19

说明

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

// LpConfigextern "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;    }}#elseint 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;    }}#elseint 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;}
0 0