该版本可以使用共享内存来存储zengl脚本的编译缓存。在config.zl配置文件中增加了和共享内存相关的配置,当前版本还新增了magick模块,可以进行图像相关的操作...
def TRUE 1; def FALSE 0; def KBYTE 1024; .................................................... zengl_cache_enable = TRUE; // 是否开启zengl脚本的编译缓存,默认为FALSE即不开启,设置为TRUE可以开启编译缓存 shm_enable = TRUE; // 是否将zengl脚本的编译缓存放入共享内存 shm_min_size = 300 * KBYTE; // 需要放进共享内存的缓存的最小大小,只有超过这个大小的缓存才放入共享内存中,以字节为单位 |
.................................................... #define SHM_MIN_SIZE (300 * 1024) // 如果配置文件中没有设置shm_min_size时,就使用该宏的值作为需要放进共享内存的缓存的最小大小(以字节为单位) .................................................... static long config_shm_enable; // 是否将zengl脚本的编译缓存放入共享内存 static long config_shm_min_size; // 需要放进共享内存的缓存的最小大小,只有超过这个大小的缓存才放入共享内存中,以字节为单位 .................................................... /** * zenglServer启动时会执行的入口函数 */ int main(int argc, char * argv[]) { // 获取配置文件中设置的shm_enable即是否开启共享内存来存储编译缓存 if(zenglApi_GetValueAsInt(VM,"shm_enable", &config_shm_enable) < 0) config_shm_enable = ZL_EXP_FALSE; // 获取配置文件中设置的shm_min_size的值,也就是开启共享内存的情况下,需要放进共享内存的缓存的最小大小,只有超过这个大小的缓存才放入共享内存中,以字节为单位 if(zenglApi_GetValueAsInt(VM,"shm_min_size", &config_shm_min_size) < 0) config_shm_min_size = SHM_MIN_SIZE; .................................................... // 将远程调试相关的配置,以及是否开启zengl脚本的编译缓存的配置,记录到日志中 write_to_server_log_pipe(WRITE_TO_LOG, "remote_debug_enable: %s remote_debugger_ip: %s remote_debugger_port: %ld" " zengl_cache_enable: %s shm_enable: %s shm_min_size: %ld\n", config_remote_debug_enable ? "True" : "False", config_remote_debugger_ip, config_remote_debugger_port, config_zengl_cache_enable ? "True" : "False", config_shm_enable ? "True" : "False", config_shm_min_size); .................................................... } |
/** * 尝试重利用full_path脚本文件对应的缓存数据,cache_path表示缓存数据所在的文件路径 * 如果缓存文件不存在,则会重新生成缓存文件,如果full_path脚本文件内容发生了改变或者其加载的脚本文件内容发生了改变,也会重新生成缓存 * 外部调用者通过is_reuse_cache变量的值来判断是否需要生成缓存文件,如果is_reuse_cache为ZL_EXP_FALSE,就表示没有重利用缓存,则需要生成缓存文件 * 如果is_reuse_cache为ZL_EXP_TRUE,则说明重利用了缓存,不需要再生成缓存文件了 */ static void main_try_to_reuse_zengl_cache(ZL_EXP_VOID * VM, char * cache_path, char * full_path, ZL_EXP_BOOL * is_reuse_cache) { FILE * ptr_fp; ZL_EXP_VOID * cachePoint = NULL; ZENGL_EXPORT_API_CACHE_TYPE * api_cache; ZL_EXP_LONG offset, cache_mtime, file_mtime; ZL_EXP_BYTE * mempoolPtr; ZL_EXP_CHAR ** filenames, * filename; ZL_EXP_INT cacheSize, i; struct stat stat_result; (* is_reuse_cache) = ZL_EXP_FALSE; if(stat(cache_path, &stat_result)==0) { // 获取缓存文件的修改时间 cache_mtime = (ZL_EXP_LONG)stat_result.st_mtime; } else { // 获取文件的状态信息失败,可能缓存文件不存在,需要重新编译生成缓存,直接返回 write_to_server_log_pipe(WRITE_TO_PIPE, "can not stat cache file: \"%s\", maybe no such cache file [recompile]\n", cache_path); return ; } if(stat(full_path, &stat_result)==0) { // 获取主执行脚本的修改时间 file_mtime = (ZL_EXP_LONG)stat_result.st_mtime; if(file_mtime >= cache_mtime) { // 如果主执行脚本的修改时间大于等于缓存数据的修改时间,则说明主执行脚本的内容发生了改变,需要重新编译生成新的缓存 write_to_server_log_pipe(WRITE_TO_PIPE, "\"%s\" mtime:%ld [changed] [recompile]\n", full_path, file_mtime); return; } } else { // 主执行脚本不存在,直接返回 write_to_server_log_pipe(WRITE_TO_PIPE, "warning stat script file: \"%s\" failed, maybe no such file! [recompile]\n", full_path); return ; } // 打开缓存文件 if((ptr_fp = fopen(cache_path, "rb")) == NULL) { write_to_server_log_pipe(WRITE_TO_PIPE, "no cache file: \"%s\" [recompile]\n", cache_path); return ; } flock(ptr_fp->_fileno, LOCK_SH); // 加文件共享锁,如果有进程在修改缓存内容的话,所有读缓存的进程都会等待写入完成,再执行读操作 fstat(ptr_fp->_fileno, &stat_result); cacheSize = stat_result.st_size; ZL_EXP_BYTE is_create = ZL_EXP_FALSE; // 判断是否是新创建的共享内存,如果是新建的,则需要将缓存数据读取到共享内存中,如果共享内存已经存在,则无需再读取 // is_use_shm用于判断是否使用共享内存,如果配置中启用了共享内存,同时编译缓存的大小超过了配置文件中shm_min_size的值,则表示当前编译缓存需要放到共享内存中 ZL_EXP_BYTE is_use_shm = config_shm_enable ? ((cacheSize > config_shm_min_size) ? ZL_EXP_TRUE : ZL_EXP_FALSE) : ZL_EXP_FALSE; int shm_id = -1; key_t share_mem_key; if(is_use_shm) { // 如果使用共享内存,则先将缓存路径转为共享内存key,再通过该key来获取已存在的共享内存,如果共享内存不存在时,则新建一个共享内存 share_mem_key = ftok(cache_path, 1); shm_id = shmget(share_mem_key, cacheSize, 0666); if(shm_id == -1) { if(errno == ENOENT) { // 不存在,则新建一个共享内存 shm_id = shmget(share_mem_key, cacheSize, IPC_CREAT | 0666); is_create = ZL_EXP_TRUE; } else { // 获取共享内存失败,则将is_use_shm设为FALSE,表示使用普通的文件缓存方式 write_to_server_log_pipe(WRITE_TO_PIPE, "shmget <key: 0x%x size: %d cache_path: %s> failed [%d] %s [read from cache file]\n", share_mem_key, cacheSize, cache_path, errno, strerror(errno)); is_use_shm = ZL_EXP_FALSE; } } } if(is_use_shm) { // 如果使用共享内存,则通过shmat库函数,将共享内存映射到当前进程的线性地址空间,从而得到当前进程可以访问的内存地址 cachePoint = shmat(shm_id, NULL, 0); if(cachePoint == ((ZL_EXP_VOID *)-1)) { // 映射失败,记录错误,并使用原始的文件缓存方式 write_to_server_log_pipe(WRITE_TO_PIPE, "shmat <id: %d cache_path: %s> failed [%d] %s [read from cache file]\n", shm_id, cache_path, errno, strerror(errno)); is_use_shm = ZL_EXP_FALSE; } } if(!is_use_shm || is_create) { if(!is_use_shm) { // 如果不使用共享内存,则在根据缓存大小,新建一个堆内存空间,编译缓存会读取到该堆内存中 cachePoint = malloc(cacheSize); } if(fread(cachePoint, cacheSize, 1, ptr_fp) != 1) { // 读取编译缓存数据到堆内存(普通文件缓存方式),或者读取到新创建的共享内存中 write_to_server_log_pipe(WRITE_TO_PIPE, "read cache file \"%s\" failed [recompile]\n", cache_path); goto end; } } api_cache = (ZENGL_EXPORT_API_CACHE_TYPE *)cachePoint; if(api_cache->signer != ZL_EXP_API_CACHE_SIGNER) { // 根据缓存签名判断是否是有效的缓存数据 write_to_server_log_pipe(WRITE_TO_PIPE, "invalid cache file \"%s\" [recompile]\n", cache_path); goto end; } mempoolPtr = ((ZL_EXP_BYTE *)cachePoint + api_cache->mempoolOffset); offset = (ZL_EXP_LONG)api_cache->filenames; filenames = (ZL_EXP_CHAR **)(mempoolPtr + offset - 1); if(api_cache->filenames_count > 0) { // 循环判断加载的脚本文件的内容是否发生了改变,如果改变了,则需要重新编译生成新的缓存 for(i=0; i < api_cache->filenames_count; i++) { offset = (ZL_EXP_LONG)(filenames[i]); filename = (ZL_EXP_CHAR *)(mempoolPtr + offset - 1); if(stat(filename, &stat_result)==0) { file_mtime = (ZL_EXP_LONG)stat_result.st_mtime; if(file_mtime >= cache_mtime){ write_to_server_log_pipe(WRITE_TO_PIPE, "\"%s\" mtime:%ld [changed] [recompile]\n", filename, file_mtime); goto end; } } else { write_to_server_log_pipe(WRITE_TO_PIPE, " stat \"%s\" failed [recompile]\n", filename); goto end; } } } // 通过zenglApi_ReUseCacheMemData接口函数,将编译好的缓存数据加载到编译器和解释器中,这样就可以跳过编译过程,直接运行 if(zenglApi_ReUseCacheMemData(VM, cachePoint, cacheSize) == -1) { if(is_use_shm) write_to_server_log_pipe(WRITE_TO_PIPE, "[shm:0x%x] reuse cache file \"%s\" failed: %s [recompile]\n", share_mem_key, cache_path, zenglApi_GetErrorString(VM)); else write_to_server_log_pipe(WRITE_TO_PIPE, "reuse cache file \"%s\" failed: %s [recompile]\n", cache_path, zenglApi_GetErrorString(VM)); goto end; } (* is_reuse_cache) = ZL_EXP_TRUE; if(is_use_shm) write_to_server_log_pipe(WRITE_TO_PIPE, "[shm:0x%x] reuse cache file: \"%s\" mtime:%ld\n", share_mem_key, cache_path, cache_mtime); else write_to_server_log_pipe(WRITE_TO_PIPE, "reuse cache file: \"%s\" mtime:%ld\n", cache_path, cache_mtime); end: fclose(ptr_fp); flock(ptr_fp->_fileno, LOCK_UN); // 解锁 if(cachePoint != NULL) { if(is_use_shm) // 如果使用了共享内存,则通过shmdt来解除映射 shmdt(cachePoint); else // 如果是普通的文件缓存方式,则将之前创建的堆内存释放掉 free(cachePoint); } } |
/** * 在编译执行结束后,生成缓存数据并写入缓存文件 */ static void main_write_zengl_cache_to_file(ZL_EXP_VOID * VM, char * cache_path) { FILE * ptr_fp; ZL_EXP_VOID * cachePoint; ZL_EXP_INT cacheSize; // 通过zenglApi_CacheMemData接口函数,将编译器和解释器中的主要的内存数据缓存到cachePoint对应的内存中 if(zenglApi_CacheMemData(VM, &cachePoint, &cacheSize) == -1) { write_to_server_log_pipe(WRITE_TO_PIPE, "write zengl cache to file \"%s\" failed: %s\n", cache_path,zenglApi_GetErrorString(VM)); return; } // 打开cache_path对应的缓存文件 if((ptr_fp = fopen(cache_path, "wb")) == NULL) { write_to_server_log_pipe(WRITE_TO_PIPE, "write zengl cache to file \"%s\" failed: open failed\n", cache_path); return; } flock(ptr_fp->_fileno, LOCK_EX); // 写入缓存数据之前,先加入互斥锁,让所有读进程等待写入完成 struct stat stat_result; fstat(ptr_fp->_fileno, &stat_result); ZL_EXP_INT cachefileSize = stat_result.st_size; key_t share_mem_key = ftok(cache_path, 1); int shm_id = shmget(share_mem_key, cachefileSize, 0666); if(shm_id != -1) { // 由于生成了新的编译缓存数据,因此,如果存在对应的共享内存,则将共享内存移除掉,下次读缓存时,就会创建一个新的共享内存,并将新的缓存数据写入共享内存 shmctl(shm_id, IPC_RMID, NULL); write_to_server_log_pipe(WRITE_TO_PIPE, "remove shm key: 0x%x, shm id: %d, ", share_mem_key, shm_id); } // 将缓存数据写入缓存文件 if( fwrite(cachePoint, cacheSize, 1, ptr_fp) != 1) write_to_server_log_pipe(WRITE_TO_PIPE, "write zengl cache to file \"%s\" failed: write failed\n", cache_path); else write_to_server_log_pipe(WRITE_TO_PIPE, "write zengl cache to file \"%s\" success \n", cache_path); fclose(ptr_fp); flock(ptr_fp->_fileno, LOCK_UN); // 解锁 } |
/** * 当主进程接收到SIGINT或者SIGTERM终止信号时,会触发的信号处理函数 */ void sig_terminate_master_callback() { .......................................................... // 删除共享内存 DIR * dp; struct dirent * ep; char * path = "zengl/caches"; char filename[SESSION_FILEPATH_MAX_LEN]; int path_dir_len = strlen(path); int ep_name_len, left_len, del_shm_num = 0; key_t share_mem_key; struct stat ep_stat; strncpy(filename, path, path_dir_len); filename[path_dir_len] = '/'; left_len = SESSION_FILEPATH_MAX_LEN - path_dir_len - 2; dp = opendir(path); if (dp != NULL) // 循环根据编译缓存的文件名,得到相应的共享内存key,并根据key将已存在的共享内存移除掉 { int cpy_len; while((ep = readdir(dp))) { ep_name_len = strlen(ep->d_name); if(ep_name_len > 20) { cpy_len = (ep_name_len <= left_len) ? ep_name_len : left_len; strncpy(filename + path_dir_len + 1, ep->d_name, cpy_len); filename[path_dir_len + 1 + cpy_len] = '\0'; if(stat(filename, &ep_stat) == 0) { if(ep_stat.st_size > config_shm_min_size) { share_mem_key = ftok(filename, 1); int shm_id = shmget(share_mem_key, ep_stat.st_size, 0666); if(shm_id != -1) { shmctl(shm_id, IPC_RMID, NULL); write_to_server_log_pipe(WRITE_TO_LOG, "************ remove shm key: 0x%x [cache_file: %s]\n", share_mem_key, ep->d_name); del_shm_num++; } else if(errno != ENOENT) { write_to_server_log_pipe(WRITE_TO_LOG, "!!!******!!! remove shm key: 0x%x failed [%d] %s\n", share_mem_key, errno, strerror(errno)); } } } else write_to_server_log_pipe(WRITE_TO_LOG, "!!!******!!! remove shm cache_path: \"%s\" failed [%d] %s\n", filename, errno, strerror(errno)); } } closedir(dp); } else { write_to_server_log_pipe(WRITE_TO_LOG, "!!!******!!! opendir \"%s\" failed [%d] %s\n", path, errno, strerror(errno)); } write_to_server_log_pipe(WRITE_TO_LOG, "------------ remove shm number: %d\n", del_shm_num); // 如果所有子进程都退出了,就释放相关资源,并退出主进程,子进程和主进程都退出后,整个程序也就退出了 sem_unlink("accept_sem"); sem_close(my_thread_lock.accept_sem); write_to_server_log_pipe(WRITE_TO_LOG, "closed accept_sem\n"); shutdown(server_socket_fd, SHUT_RDWR); write_to_server_log_pipe(WRITE_TO_LOG, "shutdowned server socket\n"); close(server_socket_fd); write_to_server_log_pipe(WRITE_TO_LOG, "closed server socket\n===================================\n\n"); free(server_log_pipe_string.str); exit(0); } |
use builtin; data['title'] = 'mustache模板测试'; data["val"] = "my world!"; data["zl"] = "<b>welcome to zengl!<span style="color:green">大家好</span></b>"; ................................................ print bltMustacheFileRender("test.tpl",data); |
{{> header.tpl}} <b>hello {{val}}!</b> <br/> <h3>{{ zl }}</h3> <h3>{{{ zl }}}</h3> <h3>{{& zl }}</h3> .................................................. |
zengl@zengl-ubuntu:~/zenglServer$ make USE_MYSQL=yes USE_MAGICK=6 cd zengl/linux && make libzengl.a make[1]: Entering directory `/home/zengl/zenglServer/zengl/linux' gcc -D ZL_LANG_EN_WITH_CH -g3 -ggdb -O0 -std=c99 -fvisibility=hidden -fPIC -c zengl_main.c zengl_parser.c zengl_symbol.c zengl_locals.c zengl_assemble.c zengl_ld.c zenglrun_main.c zenglrun_func.c zenglrun_hash_array.c zenglApi.c zenglApi_BltModFuns.c zenglDebug.c ar rc libzengl.a zengl_main.o zengl_parser.o zengl_symbol.o zengl_locals.o zengl_assemble.o zengl_ld.o zenglrun_main.o zenglrun_func.o zenglrun_hash_array.o zenglApi.o zenglApi_BltModFuns.o zenglDebug.o make[1]: Leaving directory `/home/zengl/zenglServer/zengl/linux' cd crustache && make libcrustache.a make[1]: Entering directory `/home/zengl/zenglServer/crustache' gcc -g3 -ggdb -O0 -std=c99 -fvisibility=hidden -fPIC -c buffer.c crustache.c houdini_html.c stack.c ar rc libcrustache.a buffer.o crustache.o houdini_html.o stack.o make[1]: Leaving directory `/home/zengl/zenglServer/crustache' gcc -g3 -ggdb -O0 -std=c99 main.c http_parser.c module_request.c module_builtin.c module_session.c dynamic_string.c multipart_parser.c resources.c client_socket_list.c json.c randutils.c md5.c debug.c main.h http_parser.h common_header.h module_request.h module_builtin.h module_session.h dynamic_string.h multipart_parser.h resources.h client_socket_list.h json.h randutils.h md5.h debug.h module_mysql.c module_mysql.h module_magick.c module_magick.h zengl/linux/zengl_exportfuns.h -o zenglServer zengl/linux/libzengl.a crustache/libcrustache.a -lpthread -lm -DUSE_MYSQL `mysql_config --cflags --libs` -D USE_MAGICK=6 `pkg-config --cflags --libs Wand` mysql module is enabled!!! magick module is enabled!!! zengl@zengl-ubuntu:~/zenglServer$ |
/* * module_magick.c * * Created on: May 27, 2018 * Author: zengl */ #include "main.h" #include "module_magick.h" #include <string.h> /** * zenglServer使用ImageMagick来操作jpg,png,gif之类的图像 * ImageMagick的官方网站:www.imagemagick.org * 并使用MagickWand封装的API来操作ImageMagick * MagickWand对应的网站地址:https://www.imagemagick.org/script/magick-wand.php * 目前只支持imagemagick 6的版本,暂不支持imagemagick 7的版本 */ #include <wand/MagickWand.h> /** * 根据当前执行脚本的目录路径,加上filename文件名,来生成可以被fopen等C库函数使用的路径,定义在module_builtin.c文件中 */ void builtin_make_fullpath(char * full_path, char * filename, MAIN_DATA * my_data); static __thread ZL_EXP_BOOL st_is_magick_genesis = ZL_EXP_FALSE; /** * 通过MagickWandGenesis初始化MagickWand环境 * 在调用MagickWand其他接口之前,需要先使用MagickWandGenesis来初始化环境 * zenglServer会根据需要自动初始化该环境,因此,脚本中无需手动执行初始化操作 */ static ZL_EXP_BOOL st_magick_wand_genesis() { if(st_is_magick_genesis == ZL_EXP_FALSE) { MagickWandGenesis(); st_is_magick_genesis = ZL_EXP_TRUE; write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] MagickWandGenesis \n"); // debug return ZL_EXP_TRUE; } else return ZL_EXP_FALSE; } /** * 这是一个和MagickWand相关的资源释放回调函数,由于MagickWand操作图像时,会先通过NewMagickWand得到一个MagickWand实例, * 再由该实例去执行各种图像操作,例如,加载图像,调整图像大小等,该实例在创建和使用过程中,会分配内存资源 * 如果脚本中没有通过magickDestroyWand模块函数手动释放掉这些实例资源的话,zenglServer会在脚本执行结束时,自动通过下面这个回调函数, * 以及DestroyMagickWand接口来释放掉NewMagickWand所分配的实例,以防止内存泄露 * 每个NewMagickWand创建的实例的指针都会存储到zenglServer的资源列表中,这样就可以在脚本执行结束时,检测是否有没有释放掉的实例 */ static void st_magick_destroy_wand_callback(ZL_EXP_VOID * VM_ARG, void * ptr) { MagickWand * magick_wand = (MagickWand *)ptr; DestroyMagickWand(magick_wand); write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] DestroyMagickWand: %x\n", magick_wand); // debug } /** * 在使用某个MagickWand实例指针执行相关图像操作前,会先通过下面这个函数来检测指针是否是一个有效的实例指针 * 由于每个新建的实例的指针都会存储到资源列表中,因此,如果在资源列表中找得到该指针,则说明是一个有效的实例指针 */ static ZL_EXP_BOOL st_is_valid_magick_wand(RESOURCE_LIST * resource_list, void * magick_wand) { int ret = resource_list_get_ptr_idx(resource_list, magick_wand, st_magick_destroy_wand_callback); if(ret >= 0) return ZL_EXP_TRUE; else return ZL_EXP_FALSE; } /** * 模块函数会通过下面这个函数来检查提供的指针参数是否是有效的实例指针,如果不是有效的实例指针,则抛出错误 * 该函数又会通过st_is_valid_magick_wand来进行基础的检测,如果st_is_valid_magick_wand返回0,则抛出错误 */ static MAIN_DATA * st_assert_magick_wand(ZL_EXP_VOID * VM_ARG, void * magick_wand, const char * module_fun_name) { MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data"); if(!st_is_valid_magick_wand(&(my_data->resource_list), magick_wand)) { zenglApi_Exit(VM_ARG,"%s runtime error: invalid magick_wand", module_fun_name); } return my_data; } /** * 如果使用了MagickWandGenesis初始化MagickWand环境 * 则在结束时,需要使用MagickWandTerminus来终止MagickWand环境 * zenglServer会在脚本执行结束时,自动调用下面这个函数来执行终止环境的操作 */ void export_magick_terminus() { if(st_is_magick_genesis == ZL_EXP_TRUE) { MagickWandTerminus(); st_is_magick_genesis = ZL_EXP_FALSE; write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] MagickWandTerminus \n"); // debug } } /** * magickWandGenesis模块函数,初始化MagickWand环境 * 在zengl脚本中无需手动调用该模块函数来执行初始化操作,因为,zenglServer会根据需要自动执行初始化操作 * 当然也可以手动通过该模块函数来执行初始化,如果手动执行过,则zenglServer就不会再重复执行初始化操作了 * 因为一旦初始化后,会设置st_is_magick_genesis静态全局变量,如果该变量的值被设置了,就说明已经初始化过了,可以防止重复的初始化操作 */ ZL_EXP_VOID module_magick_wand_genesis(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { int retval = (int)st_magick_wand_genesis(); zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, retval, 0); } /** * magickNewWand模块函数,新建一个MagickWand实例,并将该实例的指针返回 * 在执行具体的图像操作之前,需要先新建一个MagickWand实例,因为,大部分图像操作接口都需要接受一个实例指针作为参数 * 该模块函数在创建了一个实例指针后,还会将该指针存储到资源列表中,这样,其他的图像操作函数在接受到一个实例指针时, * 就可以根据该指针是否存在于资源列表中来判断是否是一个有效的实例指针了,并且如果脚本中没有手动释放掉这些实例指针的话, * zenglServer还可以从资源列表中将未释放掉的实例指针给自动释放掉 * * magickNewWand模块函数在调用时,不需要提供任何参数,它会将新建好的实例指针以整数的形式作为结果返回 */ ZL_EXP_VOID module_magick_new_wand(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { st_magick_wand_genesis(); MagickWand * magick_wand = NewMagickWand(); write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] NewMagickWand: %x\n", magick_wand); // debug zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)magick_wand, 0); MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data"); int ret_code = resource_list_set_member(&(my_data->resource_list), magick_wand, st_magick_destroy_wand_callback); if(ret_code != 0) { zenglApi_Exit(VM_ARG, "magickNewWand add resource to resource_list failed, resource_list_set_member error code:%d", ret_code); } } /** * magickReadImage模块函数,将指定的图像文件加载到MagickWand实例 * 该模块函数的第一个参数magick_wand需要是一个有效的MagickWand实例指针,第二个参数filename表示需要加载的图像的文件名,该文件名是相对于当前执行脚本的相对路径 * 例如: * use builtin, magick; * fun exit(err) * print err; * bltExit(); * endfun * wand = magickNewWand(); * if(!magickReadImage(wand, 'king.png')) * exit('read king.png failed'); * endif * 上面脚本先通过magickNewWand新建了一个wand实例,接着通过magickReadImage将king.png图像加载到wand实例 * 接着就可以通过wand实例来操作图像了 * magickReadImage模块函数在执行成功后会返回1,执行失败会返回0,通常执行失败的原因可能是图像文件不存在,或者加载的文件内容不是一个有效的图像格式,执行失败的具体原因会记录在日志中 */ ZL_EXP_VOID module_magick_read_image(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 2) zenglApi_Exit(VM_ARG,"usage: magickReadImage(magick_wand, filename): integer"); MagickBooleanType status; zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [magick_wand] of magickReadImage must be integer"); } MagickWand * magick_wand = (MagickWand *)arg.val.integer; MAIN_DATA * my_data = st_assert_magick_wand(VM_ARG, magick_wand, "magickReadImage"); zenglApi_GetFunArg(VM_ARG,2,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the second argument [filename] of magickReadImage must be string"); } char full_path[FULL_PATH_SIZE]; char * filename = arg.val.str; builtin_make_fullpath(full_path, filename, my_data); status = MagickReadImage(magick_wand, full_path); if(status == MagickFalse) { ExceptionType severity; char * description=MagickGetException(magick_wand, &severity); write_to_server_log_pipe(WRITE_TO_PIPE, "MagickReadImage failed: %s\n", description); description=(char *) MagickRelinquishMemory(description); zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0); } else zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0); } /** * magickGetImageFormat模块函数,返回MagickWand实例所加载的图像的格式 * 该模块函数的第一个参数magick_wand需要是一个有效的MagickWand实例指针 * 例如: * use builtin, magick; * fun exit(err) * print err; * bltExit(); * endfun * wand = magickNewWand(); * if(!magickReadImage(wand, 'king.png')) * exit('read king.png failed'); * endif * print 'format: ' + magickGetImageFormat(wand) + '<br/>'; * 上面脚本的执行结果如下: * format: PNG * 模块函数会将图像的格式以字符串的形式返回,上面png图像就返回了PNG,如果是jpg图像会返回JPEG等 */ ZL_EXP_VOID module_magick_get_image_format(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: magickGetImageFormat(magick_wand): integer or string"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [magick_wand] of magickGetImageFormat must be integer"); } MagickWand * magick_wand = (MagickWand *)arg.val.integer; st_assert_magick_wand(VM_ARG, magick_wand, "magickGetImageFormat"); char * format = MagickGetImageFormat(magick_wand); if(format == NULL) zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0); else { zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, format, 0, 0); MagickRelinquishMemory(format); } } /** * magickGetImageWidth模块函数,返回图像的宽度 * 该模块函数的第一个参数magick_wand需要是一个有效的MagickWand实例指针 * 例如: * use builtin, magick; * fun exit(err) * print err; * bltExit(); * endfun * wand = magickNewWand(); * if(!magickReadImage(wand, 'king.png')) * exit('read king.png failed'); * endif * print 'width: ' + magickGetImageWidth(wand) + '<br/>'; * 上面脚本的执行结果如下: * width: 450 * 执行结果说明king.png图像的宽度是450像素 */ ZL_EXP_VOID module_magick_get_image_width(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: magickGetImageWidth(magick_wand): integer"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [magick_wand] of magickGetImageWidth must be integer"); } MagickWand * magick_wand = (MagickWand *)arg.val.integer; st_assert_magick_wand(VM_ARG, magick_wand, "magickGetImageWidth"); ZL_EXP_LONG width = MagickGetImageWidth(magick_wand); zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, width, 0); } /** * magickGetImageHeight模块函数,返回图像的高度 * 该模块函数的第一个参数magick_wand需要是一个有效的MagickWand实例指针 * 例如: * use builtin, magick; * fun exit(err) * print err; * bltExit(); * endfun * wand = magickNewWand(); * if(!magickReadImage(wand, 'king.png')) * exit('read king.png failed'); * endif * print 'height: ' + magickGetImageHeight(wand) + '<br/>'; * 上面脚本的执行结果如下: * height: 332 * 执行结果说明king.png图像的高度是332像素 */ ZL_EXP_VOID module_magick_get_image_height(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: magickGetImageHeight(magick_wand): integer"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [magick_wand] of magickGetImageHeight must be integer"); } MagickWand * magick_wand = (MagickWand *)arg.val.integer; st_assert_magick_wand(VM_ARG, magick_wand, "magickGetImageHeight"); ZL_EXP_LONG height = MagickGetImageHeight(magick_wand); zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, height, 0); } /** * magickResizeImage模块函数,将图像缩放到所需的尺寸 * 该模块函数的第一个参数magick_wand需要是一个有效的MagickWand实例指针,第二个参数width表示需要缩放的宽度,第三个参数height表示需要缩放的高度, * 第四个参数filter_type表示缩放操作时,需要使用的滤镜类型,不同的滤镜生成的图像质量和图像体积大小会有所区别 * 例如: * use builtin, magick; * fun exit(err) * print err; * bltExit(); * endfun * wand = magickNewWand(); * if(!magickReadImage(wand, 'king.png')) * exit('read king.png failed'); * endif * if(!magickResizeImage(wand, 200, 150, "LanczosFilter")) * exit('resize king.png failed'); * endif * 上面脚本在执行时,会使用LanczosFilter滤镜将wand加载的图像缩放到200x150尺寸 * 第四个参数可以是字符串形式的滤镜类型,底层会将字符串映射为同名的enum类型的滤镜值,也可以直接传enum对应的整数值,但是,不同的6.x版本中的enum类型对应的整数值是不同的 * 传错了整数值,可能会发生段错误,因此,传字符串要保险点。字符串目前只支持通用滤镜类型,所谓通用滤镜类型,是指从6.2的低版本到6.9的高版本中都存在的滤镜类型,高版本中 * 新增了很多低版本中没有的滤镜类型,要使用这些新增的非通用的滤镜类型,只能传整数值过来 * 模块函数执行成功会返回整数1,失败则返回整数0,失败的原因会记录到日志中 */ ZL_EXP_VOID module_magick_resize_image(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 4) zenglApi_Exit(VM_ARG,"usage: magickResizeImage(magick_wand, width, height, filter_type): integer"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [magick_wand] of magickResizeImage must be integer"); } MagickWand * magick_wand = (MagickWand *)arg.val.integer; st_assert_magick_wand(VM_ARG, magick_wand, "magickResizeImage"); zenglApi_GetFunArg(VM_ARG,2,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the second argument [width] of magickResizeImage must be integer"); } ZL_EXP_LONG width = arg.val.integer; zenglApi_GetFunArg(VM_ARG,3,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the third argument [height] of magickResizeImage must be integer"); } ZL_EXP_LONG height = arg.val.integer; char * filter_types_str[] = { "UndefinedFilter", "PointFilter", "BoxFilter", "TriangleFilter", "HermiteFilter", "HanningFilter", "HammingFilter", "BlackmanFilter", "GaussianFilter", "QuadraticFilter", "CubicFilter", "CatromFilter", "MitchellFilter", "LanczosFilter", "BesselFilter", "SincFilter" }; int filter_types_str_len = sizeof(filter_types_str)/sizeof(filter_types_str[0]); FilterTypes filter_types_enum[] = { UndefinedFilter, PointFilter, BoxFilter, TriangleFilter, HermiteFilter, HanningFilter, HammingFilter, BlackmanFilter, GaussianFilter, QuadraticFilter, CubicFilter, CatromFilter, MitchellFilter, LanczosFilter, BesselFilter, SincFilter }; FilterTypes filter_type = LanczosFilter; // 默认值 zenglApi_GetFunArg(VM_ARG,4,&arg); if(arg.type == ZL_EXP_FAT_STR) { for(int i=0; i < filter_types_str_len; i++) { if(filter_types_str[i][0] == arg.val.str[0] && strlen(filter_types_str[i]) == strlen(arg.val.str) && strcmp(filter_types_str[i], arg.val.str) == 0) { filter_type = (FilterTypes)filter_types_enum[i]; break; } } } else if(arg.type == ZL_EXP_FAT_INT) { filter_type = (FilterTypes)arg.val.integer; } write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] filter type: %d\n", filter_type); // debug MagickBooleanType status = MagickResizeImage(magick_wand, width, height,filter_type,1.0); if(status == MagickFalse) { ExceptionType severity; char * description=MagickGetException(magick_wand, &severity); write_to_server_log_pipe(WRITE_TO_PIPE, "MagickResizeImage failed: %s\n", description); description=(char *) MagickRelinquishMemory(description); zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0); } else zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0); } /** * magickWriteImage模块函数,将MagickWand实例中存储的图像写入到指定的文件中 * 该模块函数的第一个参数magick_wand需要是一个有效的MagickWand实例指针,第二个参数filename表示需要写入的文件名,该文件名是相对于当前执行脚本的路径 * 例如: * use builtin, magick; * fun exit(err) * print err; * bltExit(); * endfun * wand = magickNewWand(); * if(!magickReadImage(wand, 'king.png')) * exit('read king.png failed'); * endif * if(!magickResizeImage(wand, 200, 150, "LanczosFilter")) * exit('resize king.png failed'); * endif * if(!magickWriteImage(wand, 'thumb.jpg')) * exit('write to thumb.jpg failed'); * endif * 上面在执行完缩放操作后,将wand中缩放以后的图像写入到thumb.jpg文件中,从而生成了king.png的缩略图 * 该模块函数执行成功会返回整数1,执行失败返回整数0,并将失败的原因记录到日志中 */ ZL_EXP_VOID module_magick_write_image(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 2) zenglApi_Exit(VM_ARG,"usage: magickWriteImage(magick_wand, filename): integer"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [magick_wand] of magickWriteImage must be integer"); } MagickWand * magick_wand = (MagickWand *)arg.val.integer; MAIN_DATA * my_data = st_assert_magick_wand(VM_ARG, magick_wand, "magickWriteImage"); zenglApi_GetFunArg(VM_ARG,2,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the second argument [filename] of magickWriteImage must be string"); } char full_path[FULL_PATH_SIZE]; char * filename = arg.val.str; builtin_make_fullpath(full_path, filename, my_data); MagickBooleanType status = MagickWriteImages(magick_wand, full_path, MagickTrue); if(status == MagickFalse) { ExceptionType severity; char * description=MagickGetException(magick_wand, &severity); write_to_server_log_pipe(WRITE_TO_PIPE, "magickWriteImage failed: %s\n", description); description=(char *) MagickRelinquishMemory(description); zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0); } else zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0); } /** * magickDestroyWand模块函数,注销掉不再使用的MagickWand实例,并释放该实例所占用的内存资源 * 该模块函数的第一个参数magick_wand需要是一个有效的MagickWand实例指针 * 当某个实例指针不再需要使用时,可以手动将其释放掉,如果忘了手动调用该模块函数执行释放操作的话,则在脚本结束时,zenglServer会自动根据资源列表将没释放掉的实例指针给释放掉。 */ ZL_EXP_VOID module_magick_destroy_wand(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: magickDestroyWand(magick_wand): integer"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [magick_wand] of magickDestroyWand must be integer"); } MagickWand * magick_wand = (MagickWand *)arg.val.integer; MAIN_DATA * my_data = st_assert_magick_wand(VM_ARG, magick_wand, "magickDestroyWand"); MagickWand * retval = DestroyMagickWand(magick_wand); write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] DestroyMagickWand: %x\n", magick_wand); // debug zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)retval, 0); int ret_code = resource_list_remove_member(&(my_data->resource_list), magick_wand); // 将释放掉的实例指针从资源列表中移除 if(ret_code != 0) { zenglApi_Exit(VM_ARG, "magickDestroyWand remove resource from resource_list failed, resource_list_remove_member error code:%d", ret_code); } } /** * magickWandTerminus模块函数,使用MagickWandTerminus接口来终止MagickWand环境 * 如果初始化过MagickWand环境,那么在结束时,都需要使用MagickWandTerminus接口来终止环境, * 如果在脚本中忘了手动调用该模块函数的话,zenglServer会在脚本执行结束时,自动通过上面的export_magick_terminus()函数来执行终止操作 * 如果模块函数执行了终止操作,则返回1,如果没有执行终止操作,则返回0。没执行终止操作的可能原因是没有初始化过,不需要终止,或者是资源列表中还有没有释放掉的实例指针 */ ZL_EXP_VOID module_magick_wand_terminus(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data"); int res_count = resource_list_get_count_by_callback(&(my_data->resource_list), st_magick_destroy_wand_callback); if(st_is_magick_genesis == ZL_EXP_TRUE && res_count == 0) { MagickWandTerminus(); st_is_magick_genesis = ZL_EXP_FALSE; write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] MagickWandTerminus \n"); // debug zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 1, 0); return; } if(res_count > 0) { write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] have %d wand resource in resource_list, skip MagickWandTerminus \n", res_count); // debug } else if(!st_is_magick_genesis) { write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] MagickWand not init, skip MagickWandTerminus \n", res_count); // debug } zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0); } /** * magick模块的初始化函数,里面设置了与该模块相关的各个模块函数及其相关的处理句柄 */ ZL_EXP_VOID module_magick_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT moduleID) { zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickWandGenesis",module_magick_wand_genesis); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickNewWand",module_magick_new_wand); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickReadImage",module_magick_read_image); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickGetImageFormat",module_magick_get_image_format); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickGetImageWidth",module_magick_get_image_width); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickGetImageHeight",module_magick_get_image_height); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickResizeImage",module_magick_resize_image); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickWriteImage",module_magick_write_image); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickDestroyWand",module_magick_destroy_wand); zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickWandTerminus",module_magick_wand_terminus); } |
use builtin, magick; fun exit(err) print err; print '</body></html>'; bltExit(); endfun print '<!Doctype html> <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>测试magick模块</title> </head> <body>'; // magickWandGenesis(); wand = magickNewWand(); if(!magickReadImage(wand, 'king.png')) exit('read king.png failed'); endif print '<img src="king.png"><br/>'; print 'format: ' + magickGetImageFormat(wand) + '<br/>'; print 'width: ' + magickGetImageWidth(wand) + '<br/>'; print 'height: ' + magickGetImageHeight(wand) + '<br/>'; if(!magickResizeImage(wand, 200, 150, "LanczosFilter")) exit('resize king.png failed'); endif if(!magickWriteImage(wand, 'thumb.jpg')) exit('write to thumb.jpg failed'); endif wand = magickNewWand(); if(!magickReadImage(wand, 'thumb.jpg')) exit('read thumb.jpg failed'); endif print '===============<br/>the thumb.jpg:<br/><img src="thumb.jpg"><br/>'; print 'format: ' + magickGetImageFormat(wand) + '<br/>'; print 'width: ' + magickGetImageWidth(wand) + '<br/>'; print 'height: ' + magickGetImageHeight(wand) + '<br/>'; print 'end'; // magickDestroyWand(wand); // magickWandTerminus(); print '</body></html>'; |
/** * bltDate模块函数,将时间戳转为字符串格式返回 * 第一个参数format表示需要生成的字符串格式,第二个可选参数timestamp表示需要进行转换的时间戳,如果没有提供第二个参数,则默认使用当前时间对应的时间戳 * 例如: * print bltDate('%Y-%m-%d %H:%M:%S') + '<br/>'; * print bltDate('%Y-%m-%d %H:%M:%S', 574210255)+ '<br/>'; * 执行结果如下: * 2018-06-18 10:53:28 * 1988-03-13 06:50:55 * 上面第一个语句中,没有提供第二个参数,则使用当前时间戳。第二个语句中第二个参数574210255时间戳对应的日期时间是:1988-03-13 06:50:55 * 由于该模块函数底层是使用strftime来生成格式化字符串的,因此,具体的格式是由strftime来决定的,例如:%Y表示年,%m表示月等 * 可以使用man strftime来查看具体有哪些格式 */ ZL_EXP_VOID module_builtin_date(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: bltDate(format[, timestamp])"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the first argument [format] of bltDate must be string"); } char * format = arg.val.str; time_t rawtime; if(argcount > 1) { zenglApi_GetFunArg(VM_ARG,2,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the second argument [timestamp] of bltDate must be integer"); } rawtime = (time_t)arg.val.integer; } else { time(&rawtime); } struct tm * timeinfo; char buffer[128]; timeinfo = localtime(&rawtime); size_t ret = strftime (buffer,sizeof(buffer), format,timeinfo); if(ret == 0) { zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_STR, "", 0, 0); } else { zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_STR, buffer, 0, 0); } } /** * bltMkdir模块函数,根据指定的路径,创建目录 * 第一个参数path表示相对于当前执行脚本的路径,该模块函数将根据该路径来创建目录,第二个可选参数file_mode表示创建目录的读写执行权限 * 例如: * use builtin; * def TRUE 1; * def FALSE 0; * path = 'tmpdir'; * if(bltMkdir(path, 0e777) == TRUE) * print 'mkdir ' + path + ' success!' + '<br/>'; * else * print 'the ' + path + ' exists, no need real mkdir' + '<br/>'; * endif * 上面这段脚本在执行后,将在当前执行脚本的目录中创建一个名为tmpdir的子目录,如果tmpdir已经存在,则bltMkdir模块函数会返回0 * 上面脚本中bltMkdir的第二个参数0e777是一个八进制值,表示需要创建的目录的读写执行权限是rwxrwxrwx,也就是所有用户都可以操作该目录 */ ZL_EXP_VOID module_builtin_mkdir(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: bltMkdir(path[, file_mode])"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the first argument [path] of bltMkdir must be string"); } char full_path[FULL_PATH_SIZE]; char * filename = arg.val.str; mode_t file_mode; if(argcount > 1) { zenglApi_GetFunArg(VM_ARG,2,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the second argument [file_mode] of bltMkdir must be integer"); } file_mode = (mode_t)arg.val.integer; } else { file_mode = 0755; } MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data"); builtin_make_fullpath(full_path, filename, my_data); struct stat st = {0}; if (stat(full_path, &st) == -1) { if(mkdir(full_path, file_mode) != 0) { zenglApi_Exit(VM_ARG,"bltMkdir <%s> failed [%d] %s", full_path, errno, strerror(errno)); } zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0); } else zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0); } /** * bltUnlink模块函数,删除指定路径对应的文件 * 该模块函数的第一个参数path表示相对于当前执行脚本的路径,如果path对应的文件存在,且具有权限,则模块函数会将该文件给删除掉 * 例如: * bltUnlink('thumb.jpg'); * 上面脚本中,如果thumb.jpg存在,则将其删除,如果文件不存在则直接返回0 * 该模块函数只能用于删除常规文件,不可以删除目录 */ ZL_EXP_VOID module_builtin_unlink(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: bltUnlink(path)"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the first argument [path] of bltUnlink must be string"); } char full_path[FULL_PATH_SIZE]; char * filename = arg.val.str; MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data"); builtin_make_fullpath(full_path, filename, my_data); struct stat st = {0}; if (stat(full_path, &st) == -1) zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0); else { if(unlink(full_path) != 0) { zenglApi_Exit(VM_ARG,"bltUnlink <%s> failed [%d] %s", full_path, errno, strerror(errno)); } zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0); } } /** * bltFileExists模块函数,检测指定路径的文件是否存在 * 该模块函数的第一个参数path表示相对于当前执行脚本的路径,如果path对应的文件存在,则返回1,否则返回0 * 例如: * file = 'thumb.jpg'; * if(bltFileExists(file)) * bltUnlink(file); * print 'unlink ' + file + ' success!' + '<br/>'; * else * print file + ' not exists, no need real unlink' + '<br/>'; * endif * 上面脚本在执行时,如果bltFileExists模块函数检测到thumb.jpg文件存在,则会将该文件删除,并提示unlink thumb.jpg success! * 如果文件不存在,则bltFileExists模块函数会返回0,并提示thumb.jpg not exists, no need real unlink * bltFileExists模块函数还可以检测目录是否存在 */ ZL_EXP_VOID module_builtin_file_exists(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; if(argcount < 1) zenglApi_Exit(VM_ARG,"usage: bltFileExists(path)"); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the first argument [path] of bltFileExists must be string"); } char full_path[FULL_PATH_SIZE]; char * filename = arg.val.str; MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data"); builtin_make_fullpath(full_path, filename, my_data); struct stat st = {0}; int retval = stat(full_path, &st); if (retval == -1) { if(errno == ENOENT) zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0); else zenglApi_Exit(VM_ARG,"bltFileExists <%s> failed [%d] %s", full_path, errno, strerror(errno)); } else if(retval == 0) zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0); else zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0); } |
use builtin; def TRUE 1; def FALSE 0; print '<!Doctype html> <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>测试bltDate, bltMkdir, bltFileExists, bltUnlink模块函数</title> </head> <body>'; print bltDate('%Y-%m-%d %H:%M:%S') + '<br/>'; print bltDate('%Y-%m-%d %H:%M:%S', 574210255)+ '<br/>'; path = 'tmpdir'; if(bltFileExists(path)) // bltFileExists还可以检测目录是否存在 print path + ' dir exists<br/>'; else print path + ' dir not exists<br/>'; endif if(bltMkdir(path, 0e777) == TRUE) print 'mkdir ' + path + ' success!' + '<br/>'; else print 'the ' + path + ' exists, no need real mkdir' + '<br/>'; endif file = 'thumb.jpg'; if(bltFileExists(file)) bltUnlink(file); print 'unlink ' + file + ' success!' + '<br/>'; else print file + ' not exists, no need real unlink' + '<br/>'; endif print '</body></html>'; |