📄 apr-tutorial.sgml
字号:
When we handle files, we have to call apr_file_open() at first. Here is the prototype declaration./* excerpted from apr_file_io.h */<tscreen><verb>APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **newf, const char *fname, apr_int32_t flag, apr_fileperms_t perm, apr_pool_t *pool);</verb></tscreen>The first argument type is apr_file_t**, which is result argument. Namely, you can create an apr_file_t object by calling apr_file_open(). The second argument is file name path. The third argument is a bit-wised flag. The bit-flags are defined in apr_file_io.h. The fourth argument is file access permission, which has effects on a newly created file. The value is bit-wised flag. The bit-flags are defined in apr_file_info.h. For example, if you want to create a file that has an access permission 0600, i.e. read-write access only by the file owner, you have to specify APR_UREAD|APR_UWRITE. In usual cases, you can use APR_OS_DEFAULT as file permission. The fifth final argument is a memory pool to use. Needless to say, you need to create the memory pool by apr_pool_create().After we open file, we can handle file by other APIs. We can find them in apr_file_io.h. The basic APIs are apr_file_read() and apr_file_write(). As you expect, apr_file_read() allows us to read something from file, and apr_file_write() allows us to write something to file. Here is the prototype declarations,/* excerpted from apr_file_io.h */<tscreen><verb>APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes);APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes);</verb></tscreen>The third argument of both functions is value-result argument. It means, by which, we specify the input value's length on entry and get the output result's length on exit. In particular, apr_file_read() returns the length of read bytes and apr_file_write() returns the length of written bytes. Here is a sample code.<tscreen><verb>/* pseudo code about apr_file_write() */strcpy(outbuf, "123456789");apr_size_t outlen = strlen(outbuf);rv = apr_file_write(fp, outbuf, &outlen);printf("apr_file_write() rv = %d, nbytes = %d\n", rv, outlen);</verb></tscreen>In this case, before calling apr_file_write(), 'outlen' variable's value is 9. Passing in &outlen to apr_file_write() tells the API the writable length is 9. After returning from apr_file_write(), 'outlen' variable's value becomes actual written length. Usually, it is 9 if the file is a local file. Theoretically, it could become a smaller value (e.g. by disk full).We have to call apr_file_close() to close the file. Rather than that, we can implicitly close the file by destroying the memory pool that is passed to apr_file_open(). I prefer explicit closing to such an implicit way. It's just my opinion.<em>REMARK</em>: There are some source code compatibility issues among libapr versions. The third argument of apr_file_open() has APR_FOPEN_ prefix after libapr-1.1.0, althouth it formerly didn't. We should use APR_FOPEN_CREATE instead of APR_CREATE. Please see apr_file_io.h of your using version.Similarly, the fourth argument of apr_file_open() has APR_FPROT_ prefixes after libapr-1.1.0.<em>REMARK</em>: There is a portability issue about file path separator. Unix(POSIX) uses slash('/'), and MS-Windows uses backslash('\') as separator. If you write an application program for both Unix and MS-Windows, I recommend you to canonicalize file pathes to use slash('/') as separators, because MS-Windows accepts it.<em>REMARK</em>: Be careful about apr_file_gets() usage. Calling apr_file_gets() without APR_BUFFERED severely hits performance. This is because that apr_file_gets() internally calls apr_file_read() per one byte. Remember you must open file with APR_BUFFERED flag when you use apr_file_gets(). I recommend you to specify APR_BUFFERED flag except the following cases:<itemize><item>When you mmap the file (it causes an error to mmap APR_BUFFERED file)</item><item>No read/write (e.g. a file only for lock purpose)</item><item>You are sure that you reads/writes with a buffer big enough</item></itemize><em>REMARK</em>: While you open file with APR_BUFFERED flag and if you call apr_file_trunc() for the file, you must call apr_file_flush() before apr_file_trunc(). Otherwise, the file becomes broken.<em>REMARK</em>: When you open file with APR_BUFFERED flag and the file is shared by multiple threads, APR_XTHREAD flag is also required for the file. Unfortunately, APR_XTHREAD flag on Windows has a side effect. My experiences tell me not to use APR_XTHREAD flag on Windows.We can get file information such as size, timestamp, owner, access permission, and so on. Such the information are in apr_finfo_t structure, which we find in apr_file_info.h. There are two related APIs as follows:/* excerpted from apr_file_io.h */<tscreen><verb>APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted, apr_file_t *thefile);/* excerpted from apr_file_info.h */APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname, apr_int32_t wanted, apr_pool_t *pool);</verb></tscreen>apr_file_info_get() requires apr_file_t object, and apr_stat() requires file name. If we have already opened file and have apr_file_t object, it is better to use apr_file_info_get(). Otherwise, we have to call apr_stat(). Unlike other many types, apr_finfo_t is complete type. Rather than calling API to create object, we have to allocate the apr_finfo_t memory explicitly. Typically, it's allocated in local stack, because what we want to know might be some of the attributes such as file size or timestamp. Note that some memories, e.g. apr_finfo_t::fname, are allocated in the memory pool. This would cause a serious bug. Be careful.Please take a look at <htmlurl url="../sample/finfo-sample.c" name="finfo-sample.c"> about the usage. There are some file handling APIs that works based on file names. For example, apr_file_remove() and apr_file_copy(). You can find them in apr_file_io.h and apr_file_info.h.<em>REMARK</em>: Some APIs have 'wanted' argument that we specify bit-wised flag to get file attributes. The APIs are apr_dir_read(), apr_stat(), apr_lstat(), and apr_file_info_get(). Note that the value of 'wanted' argument could be beyond the OS file system support. and in such a case, the API returns APR_INCOMPLETE.<sect>file lock<p>When we intend to lock file among processes, we can use apr_file_lock(). Historically, there are several confusions about file lock APIs on Unix. Thus, it is very useful that libapr provides one simple API./* excerpted from apr_file_io.h */<tscreen><verb>APR_DECLARE(apr_status_t) apr_file_lock(apr_file_t *thefile, int type);APR_DECLARE(apr_status_t) apr_file_unlock(apr_file_t *thefile);</verb></tscreen>apr_file_lock() has two arguments. One is apr_file_t object. The other is flag, by which we specify the lock type. The lock type is either APR_FLOCK_SHARED or APR_FLOCK_EXCLUSIVE. We can use the former as a readable lock, and the latter as a writable lock. To unlock the file, we call apr_file_unlock(). Or, calling apr_file_close() implicitly does unlock. Take a look at <htmlurl url="../sample/flock-sample.c" name="flock-sample.c"> about the usage. Additionally, we can use a bitwised-or flag APR_FLOCK_NONBLOCK. Without APR_FLOCK_NONBLOCK flag, calling apr_file_lock() would block. With APR_FLOCK_NONBLOCK, if apr_file_lock() can't lock file, it returns an error code, APR_EAGAIN.We should compare apr_file_lock() return value with APR_SUCCESS. If the value is APR_SUCCESS, the file was successfully locked. Othewrise, we failed to lock the file.<sect>file-system directory handling<p>When we handle file-system directories, we have to call apr_dir_open() at first. By apr_dir_open(), we have an apr_dir_t object. All we can do with apr_dir_t is to scan the directory. The API is apr_dir_read(). Finally, we call apr_dir_close() to close the directory. The prototype declarations are as follows:/* excerpted from apr_file_info.h */<tscreen><verb>APR_DECLARE(apr_status_t) apr_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool);APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_dir_t *thedir);APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *thedir);</verb></tscreen>The first argument of apr_dir_open() is result argument. By which, we can get a newly created apr_dir_t object. The second argument is directory name. The third argument is memory pool to use.The first argument of apr_dir_read() is result argument. As mentioned earlier, apr_finfo_t is complete type. Thus, we have to allocate its memory explicitly. apr_dir_read() returns an entry of the directory by apr_finfo_t. The entry is either file or directory. The second argument is bit-wised flag. The flags are defined in apr_file_info.h with APR_FINFO_ prefix, such as APR_FINFO_SIZE, APR_FINFO_TYPE and APR_FINFO_NAME. The third argument is apr_dir_t object to scan.Here is a sample code: <tscreen><verb>/* pseudo code about apr_dir_read() *//* no error checks */apr_pool_t *mp;apr_pool_create(&mp, NULL);const char *dirpath = "/home";/* directory path to scan */apr_dir_t *dir;apr_dir_open(&dir, dirpath, mp);/* create the apr_dir_t object */apr_finfo_t dirent;apr_dir_read(&dirent, APR_FINFO_DIRENT, dir);/* the apr_finfo_t object filled *//* dirent is the first entry of the directory. * the entry is either file or directory. */apr_dir_close(dir);</verb></tscreen>In the sample code above we call apr_dir_read() only once, but we usually call apr_dir_read() multiple times to scan all files under the directory. To scan all files, we just keep calling apr_dir_read() while it returns APR_SUCCESS. Take a look at <htmlurl url="../sample/dir-sample.c" name="dir-sample.c"> about precise usage.<tscreen><verb>/* pseudo code about apr_dir_read() loop. error checks omitted *//* typical while loop of apr_dir_read() */apr_dir_open(&dir, dirpath, mp);while ((apr_dir_read(&dirent, APR_FINFO_NAME, dir)) == APR_SUCCESS) { printf("file name is %s\n", dirent.name);}apr_dir_close(dir);</verb></tscreen>As you can imagine, apr_dir_t object has the current position. Calling apr_dir_read() makes the position move forward. We can rewind the (internal)position by apr_dir_rewind(). All the operations we can do with the position are just these two, moving forward and rewinding.As you see in <htmlurl url="../sample/dir-sample.c" name="dir-sample.c">, if you scan directories recursively, you need to call apr_dir_open() recursively.<em>REMARK</em>: On Unix, apr_dir_read() returns apr_finfo_t object whose apr_file_t::fname is NULL<sect>character string handling<p>I assume you are familiar with C language's string APIs, such as strlen(3) and strcpy(3).libapr provides some string APIs. They are almost same as the common(ANSI C) APIs. Why does libapr provide a yet another string APIs? The benefit of libapr's string APIs is related to memory pool. In a common C string handling, we have to write much memory management code. The following code is an example.<tscreen><verb>/* ANSI C string example (a bit naive code) *//* we concatenate three strings, s1, s2, s3 */int len1 = strlen(s1);int len2 = strlen(s2);int len3 = strlen(s3);int total_len = len1 + len2 + len3;char *cat_str = malloc(total_len + 1);strcpy(cat_str, s1);strcat(cat_str, s2);strcat(cat_str, s3);/* later, we have to free the allocated memory */free(cat_str);</verb></tscreen>The same thing is written with libapr as follows:<tscreen><verb>/* pseudo code about libapr string APIs */apr_pool_t *mp;apr_pool_create(&mp, NULL);/* apr_pstrcat() takes care of both memory allocation and string concatenation. * If the concatenated string is read-only, we should use 'const char*' type. */const char *cat_str = apr_pstrcat(mp, s1, s2, s3, NULL);/* later, all we have to do is to destroy the memory pool to free all the memory */apr_pool_destroy(mp);</verb></tscreen>Like apr_pstrcat(), apr_psprintf() allows you to write much simpler code. You can find other string APIs in apr_strings.h.<sect>time handling<p>libapr's time APIs are almost based on POSIX scheme. The value of apr_time_t is the elapsed time since UNIX epoch time(1970/1/1). The big differences are that apr_time_t is 64bit(long long) number and it represents microseconds. The most useful API is apr_time_now(). As you can guess, it returns the current time. You can find the protype declaration in apr_time.h./* excerpted from apr_time.h */<tscreen><verb>APR_DECLARE(apr_time_t) apr_time_now(void);</verb></tscreen>In many cases, we need to convert apr_time_t value to the other formats. There are mainly two formats.- apr_time_exp_t (time structure)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -