summaryrefslogtreecommitdiffstats
path: root/mbbsd/file.c
blob: 3b657acc1b8dbe6d647cea06748132501f17bee6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* $Id$ */

#include "bbs.h"

/**
 * file.c 是針對以"行"為單位的檔案所定義的一些 operation。
 */

/**
 * 傳回 file 檔的行數
 * @param file
 */
int file_count_line(const char *file)
{
    FILE           *fp;
    int             count = 0;
    char            buf[200];

    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[STRLEN], *ptr;
    int i = 0;

    if ((fp = fopen(file, "r")) == NULL)
    return 0;

    while (fgets(buf, STRLEN, 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[PATHLEN];
    char buf[STRLEN + 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;
}