When we handle files, we have to call apr_file_open() at first. Here is the prototype declaration.
/* excerpted from apr_file_io.h */
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);
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 */
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);
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.
/* 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);
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.
REMARK: 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.
REMARK: 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.
REMARK: 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:
REMARK: 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.
REMARK: 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 */
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);
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 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.
REMARK: 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.