diff options
Diffstat (limited to 'src/libbbsutil/file.c')
-rw-r--r-- | src/libbbsutil/file.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/src/libbbsutil/file.c b/src/libbbsutil/file.c index 21313283..5b891598 100644 --- a/src/libbbsutil/file.c +++ b/src/libbbsutil/file.c @@ -1,4 +1,6 @@ #include <stdio.h> +#include <stdlib.h> // random +#include <sys/file.h> // flock #include <unistd.h> #include <fcntl.h> #include <sys/types.h> @@ -97,6 +99,10 @@ dashd(const char *fname) return (stat(fname, &st) == 0 && S_ISDIR(st.st_mode)); } +/* ----------------------------------------------------- */ +/* 檔案操作函數:複製、搬移、附加 */ +/* ----------------------------------------------------- */ + #define BUFFER_SIZE 8192 int copy_file_to_file(const char *src, const char *dst) { @@ -295,3 +301,189 @@ Link(const char *src, const char *dst) return Copy(src, dst); } +/* ----------------------------------------------------- */ +/* 檔案內容處理函數:以「行」為單位 */ +/* ----------------------------------------------------- */ + +#define LINEBUFSZ (PATH_MAX) +#define STR_SPACE " \t\n\r" + + +/** + * 傳回 file 檔的行數 + * @param file + */ +int file_count_line(const char *file) +{ + FILE *fp; + int count = 0; + char buf[LINEBUFSZ]; + + if ((fp = fopen(file, "r"))) { + while (fgets(buf, sizeof(buf), fp)) { + if (strchr(buf, '\n') == NULL) + continue; + count++; + } + fclose(fp); + } + return count; +} + +/** + * 將 string append 到檔案 file 後端 (不加換行) + * @param file 要被 append 的檔 + * @param string + * @return 成功傳回 0,失敗傳回 -1。 + */ +int file_append_line(const char *file, const char *string) +{ + FILE *fp; + if ((fp = fopen(file, "a")) == NULL) + return -1; + flock(fileno(fp), LOCK_EX); + fputs(string, fp); + flock(fileno(fp), LOCK_UN); + fclose(fp); + return 0; +} + +/** + * 將 "$key\n" append 到檔案 file 後端 + * @param file 要被 append 的檔 + * @param key 沒有換行的字串 + * @return 成功傳回 0,失敗傳回 -1。 + */ +int file_append_record(const char *file, const char *key) +{ + FILE *fp; + if (!key || !*key) return -1; + if ((fp = fopen(file, "a")) == NULL) + return -1; + flock(fileno(fp), LOCK_EX); + fputs(key, fp); + fputs("\n", fp); + flock(fileno(fp), LOCK_UN); + fclose(fp); + return 0; +} + +/** + * 傳回檔案 file 中 key 所在行數 + */ +int file_find_record(const char *file, const char *key) +{ + FILE *fp; + char buf[LINEBUFSZ], *ptr; + int i = 0; + + if ((fp = fopen(file, "r")) == NULL) + return 0; + + while (fgets(buf, LINEBUFSZ, fp)) { + char *strtok_pos; + i++; + if ((ptr = strtok_r(buf, STR_SPACE, &strtok_pos)) && !strcasecmp(ptr, key)) { + fclose(fp); + return i; + } + } + fclose(fp); + return 0; +} + +/** + * 傳回檔案 file 中是否有 key + */ +int file_exist_record(const char *file, const char *key) +{ + return file_find_record(file, key) > 0 ? 1 : 0; +} + +/** + * 刪除檔案 file 中以 string 開頭的行 + * @param file 要處理的檔案 + * @param string 尋找的 key name + * @param case_sensitive 是否要處理大小寫 + * @return 成功傳回 0,失敗傳回 -1。 + */ +int +file_delete_record(const char *file, const char *string, int case_sensitive) +{ + // TODO nfp 用 tmpfile() 比較好? 不過 Rename 會變慢... + FILE *fp = NULL, *nfp = NULL; + char fnew[PATH_MAX]; + char buf[LINEBUFSZ + 1]; + int ret = -1, i = 0; + const size_t toklen = strlen(string); + + if (!toklen) + return 0; + + do { + snprintf(fnew, sizeof(fnew), "%s.%3.3X", file, (unsigned int)(random() & 0xFFF)); + if (access(fnew, 0) != 0) + break; + } while (i++ < 10); // max tries = 10 + + if (access(fnew, 0) == 0) return -1; // cannot create temp file. + + i = 0; + if ((fp = fopen(file, "r")) && (nfp = fopen(fnew, "w"))) { + while (fgets(buf, sizeof(buf), fp)) + { + size_t klen = strcspn(buf, STR_SPACE); + if (toklen == klen) + { + if (((case_sensitive && strncmp(buf, string, toklen) == 0) || + (!case_sensitive && strncasecmp(buf, string, toklen) == 0))) + { + // found line. skip it. + i++; + continue; + } + } + // other wise, keep the line. + fputs(buf, nfp); + } + fclose(nfp); nfp = NULL; + if (i > 0) + { + if(Rename(fnew, file) < 0) + ret = -1; + else + ret = 0; + } else { + unlink(fnew); + ret = 0; + } + } + if(fp) + fclose(fp); + if(nfp) + fclose(nfp); + return ret; +} + +/** + * 對每一筆 record 做 func 這件事。 + * @param file + * @param func 處理每筆 record 的 handler,為一 function pointer。 + * 第一個參數是檔案中的一行,第二個參數為 info。 + * @param info 一個額外的參數。 + */ +int file_foreach_entry(const char *file, int (*func)(char *, int), int info) +{ + char line[80]; + FILE *fp; + + if ((fp = fopen(file, "r")) == NULL) + return -1; + + while (fgets(line, sizeof(line), fp)) { + (*func)(line, info); + } + + fclose(fp); + return 0; +} |