Minor changes
[matilda.git] / src / file_io.c
blobf41ff563d2b3b1fed0dcffd6a52302d23f7cc183
1 /*
2 Functions for file input/output.
3 */
5 #include "config.h"
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <dirent.h>
14 #include <time.h> /* localtime */
15 #include <errno.h>
17 #include "alloc.h"
18 #include "engine.h"
19 #include "flog.h"
20 #include "types.h"
23 Create a new file and open it for writing; creating new filenames if the file
24 already exists.
25 RETURNS file descriptor
27 int create_and_open_file(
28 char * restrict filename,
29 u32 filename_size,
30 const char * restrict prefix,
31 const char * restrict extension
32 ) {
33 u32 attempt = 1;
34 time_t t = time(NULL);
35 struct tm tm = *localtime(&t);
37 while (1) {
38 if (attempt == 1) {
39 snprintf(filename, filename_size, "%s%s_%04u%02u%02u%02u%02u.%s", data_folder(), prefix,
40 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
41 } else {
42 snprintf(filename, filename_size, "%s%s_%04u%02u%02u%02u%02u_%u.%s", data_folder(), prefix,
43 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, attempt, extension);
46 int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
48 /* File created */
49 if (fd != -1) {
50 return fd;
53 /* We will never be able to create the file */
54 if (errno != EEXIST) {
55 return -1;
58 ++attempt;
63 RETURNS the number of bytes read or -1 if failed to open/be read
65 d32 read_binary_file(
66 void * dst_buf,
67 u32 buf_len,
68 const char * filename
69 ) {
70 FILE * h = fopen(filename, "rb");
71 if (h == NULL) {
72 return -1;
75 u32 total_read = 0;
77 char * dst_buf2 = (char *)dst_buf;
78 bool file_unfinished;
80 do {
81 size_t rd = fread(dst_buf2 + total_read, 1, buf_len - total_read, h);
83 if (ferror(h) != 0) {
84 fclose(h);
85 return -1;
88 total_read += rd;
89 } while (buf_len > total_read && (file_unfinished = (feof(h) == 0)));
91 char tmp[2];
92 fread(tmp, 1, 1, h);
93 file_unfinished = (feof(h) == 0);
95 fclose(h);
97 if (file_unfinished) {
98 char * s = alloc();
99 snprintf(s, MAX_PAGE_SIZ, "file %s longer than buffer available for reading\n", filename);
100 flog_crit("file", s);
101 release(s);
104 return total_read;
108 RETURNS the number of ASCII characters read or -1 if failed to open/be read
110 d32 read_ascii_file(
111 char * restrict dst_buf,
112 u32 buf_len,
113 const char * restrict filename
115 FILE * h = fopen(filename, "r"); /* text file, hopefully ASCII */
116 if (h == NULL) {
117 return -1;
120 u32 total_read = 0;
121 bool file_unfinished;
123 do {
124 size_t rd = fread(dst_buf + total_read, 1, buf_len - total_read, h);
126 if (ferror(h) != 0) {
127 char * s = alloc();
128 snprintf(s, MAX_PAGE_SIZ, "%s: %s", filename, strerror(errno));
129 flog_warn("file", s);
130 release(s);
131 fclose(h);
132 return -1;
135 total_read += rd;
137 } while (buf_len > total_read && (file_unfinished = (feof(h) == 0)));
139 char tmp[2];
140 fread(tmp, 1, 1, h);
141 file_unfinished = (feof(h) == 0);
143 fclose(h);
145 if (file_unfinished) {
146 char * s = alloc();
147 snprintf(s, MAX_PAGE_SIZ, "%s: larger than buffer space", filename);
148 flog_crit("file", s);
149 release(s);
150 return -1;
153 dst_buf[total_read] = 0;
154 return total_read;
157 static bool ends_in(
158 const char * restrict a,
159 const char * restrict b
161 size_t len_a = strlen(a);
162 size_t len_b = strlen(b);
164 if (len_a <= len_b) {
165 return false;
168 size_t offset = len_a - len_b;
170 for (u16 i = 0; i < len_b; ++i) {
171 if (a[offset + i] != b[i]) {
172 return false;
176 return true;
180 static u32 allocated;
181 static u32 filenames_found;
182 static u32 _max_files;
184 static void _recurse_find_files(
185 const char * restrict root,
186 const char * restrict extension,
187 char ** filenames
189 DIR * dir;
190 struct dirent * entry;
192 if (!(dir = opendir(root))) {
193 return;
196 while (filenames_found <= _max_files && (entry = readdir(dir)) != NULL) {
197 if (entry->d_name[0] == '.') { /* ignore special and hidden files */
198 continue;
201 u32 strl = strlen(root) + strlen(entry->d_name) + 2;
202 if (strl >= MAX_PATH_SIZ) {
203 flog_crit("file", "path too long");
206 if (!ends_in(entry->d_name, extension)) { /* try following as if folder */
207 char * path = malloc(strl);
209 if (path == NULL) {
210 flog_crit("file", "find files: system out of memory");
213 snprintf(path, strl, "%s%s/", root, entry->d_name);
214 _recurse_find_files(path, extension, filenames);
215 free(path);
216 } else {
217 allocated += strl;
218 filenames[filenames_found] = malloc(strl);
220 if (filenames[filenames_found] == NULL) {
221 flog_crit("file", "find files: system out of memory");
224 snprintf(filenames[filenames_found], strl, "%s%s", root, entry->d_name);
225 filenames_found++;
227 if (filenames_found > _max_files) {
228 char * s = alloc();
229 snprintf(s, MAX_PAGE_SIZ, "maximum number of files (%u) reached", _max_files);
230 flog_crit("file", s);
231 release(s);
235 closedir(dir);
239 Searches for and allocates the space needed for the relative path to the files
240 found ending with the text present in extension.
241 At most fills max_files file names.
242 RETURN number of file names saved
244 u32 recurse_find_files(
245 const char * restrict root,
246 const char * restrict extension,
247 char ** filenames,
248 u32 max_files
250 filenames_found = 0;
251 _max_files = max_files;
252 _recurse_find_files(root, extension, filenames);
253 return filenames_found;