summaryrefslogtreecommitdiff
path: root/src/paths.h
blob: 8be56f8cbd02b50bea7bdd8b8ff61253be85bcf7 (plain)
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/**
 * \file paths.h
 * XDG Directory Specification implementation
 */

#ifndef PATHS_H
#define PATHS_H

/**
 * Opaque type for paths
 */
typedef struct paths_t paths_t;

/**
 * Opaque type for a currently open file
 */
typedef struct paths_file_t paths_file_t;

/**
 * The different sources or directories if you will that are in the
 * specification.
 */
typedef enum paths_source_t
{
    /** directory for data files */
    PATHS_DATA,
    /** directory for config files */
    PATHS_CONFIG,
    /** directory for cache files */
    PATHS_CACHE,
    /** directory for runtime files */
    PATHS_RUNTIME,
} paths_source_t;

/** Do not block write calls */
#define PATHS_NONBLOCK  (0x01)
/** Append on each write */
#define PATHS_APPEND    (0x02)
/** Create file if needed */
#define PATHS_CREATE    (0x04)
/** Truncate file when opening */
#define PATHS_TRUNCATE  (0x08)
/** If PATHS_CREATE is set, give error if file already exists */
#define PATHS_EXCLUSIVE (0x10)

/**
 * Create a new paths.
 * Initializes the list of directories from enviroment variables.
 * Returned object has a reference count of one.
 * Threadsafe.
 * Returns NULL in case of allocation errors.
 * @return a new paths
 */
MALLOC paths_t* paths_new(void);

/**
 * Increase the reference count for paths.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 */
NONULL void paths_ref(paths_t* paths);

/**
 * Decrease the reference count for paths and free if it reached zero.
 * Threadsafe.
 * @param paths paths object, may be NULL
 */
void paths_unref(paths_t* paths);

/**
 * Get single directory where user-specific files should be written.
 * May only return NULL for PATHS_RUNTIME all other sources are guaranteed to
 * return a non-NULL string. They may however return non-existant paths.
 * PATHS_RUNTIME will return NULL if no such path was found as it has no
 * default.
 * If you wish paths to be created if missing, please use paths_write().
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @param source requested directory
 * @return absolute path to directory or NULL
 */
NONULL const char* paths_user_dir(paths_t* paths, paths_source_t source);

/**
 * Get preference ordered list of directories to search for files.
 * All paths are absolute and NULL terminated. The list itself is NULL
 * terminated and may never be empty. Path returned by paths_user_dir() for
 * the same source is not included.
 * Will return NULL for PATHS_CACHE and PATHS_RUNTIME as they are only
 * user-specific writable directories.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @param source requested directory
 * @return NULL terminated list of absolute paths to search or NULL
 */
NONULL const char** paths_search_dirs(paths_t* paths, paths_source_t source);

/**
 * Callback called with the result(s) from paths_find(). May be called from
 * any thread. Return false to cancel the search, true to continue until all
 * has been found. filename will be NULL when no more matches was found.
 * The filename pointer is only valid until the callback has returned.
 * @param userdata userdata given to paths_find()
 * @param filenamme found file or NULL if no more results
 * @return true to continue the search, false to not */
typedef bool (* paths_find_callback_t)(void* userdata, const char* filename);

/**
 * Asynchronous search for matching files in directories for source.
 * Returned in preference order, first paths_user_dir() and then
 * paths_search_dirs().
 * The callback is always called at least once.
 * Match may use *, ?, [] and {} glob matches. It may also contain / characters
 * to search in subdirectories
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @param source requested directory
 * @param match glob pattern to search with. may not be NULL
 * @param find_callback callback to call when finding results, may not be NULL
 * @param userdata argument to callback, may be NULL
 */
NONULL_ARGS(1, 3, 4)
void paths_find(paths_t* paths, paths_source_t source, const char* match,
                paths_find_callback_t find_callback, void* userdata);

/**
 * Synchronous setup to create file in directory.
 * Returns NULL in case of error, see errno for details.
 * Depending on source and convention, the actual file may not be created with
 * the given name until paths_file_close().
 * The file will eventually always end up in paths_user_dir() for directory.
 * If any part of the target path is missing it will be created if possible.
 * If flags contains PATHS_CREATE then the 5th argument mode_t mode must be
 * given, just as for open().
 * Threadsafe but what happens if two threads at the same time opens the same
 * target file is up to the platforms locking mechanism.
 * The returned paths_file_t is not threadsafe.
 * @param paths paths object, may not be NULL
 * @param source target directoru
 * @param filename relative path of target file, may not be NULL
 * @param flags flags
 * @param ... mode if PATHS_CREATE is included in flags
 * @return a file reference or NULL
 */
NONULL paths_file_t* paths_write(paths_t* paths, paths_source_t source,
                                 const char* filename, unsigned int flags,
                                 ...);

/**
 * Write data to file.
 * Works just as libc write().
 * Not threadsafe,
 * the paths_file_t must only be accessed by one thread at a time.
 * @param file target file, may not be NULL
 * @param data data to write, may be NULL if size is zero
 * @param size the maximum number of bytes to read from data
 * @return number of bytes written to target
 */
NONULL_ARGS(1)
ssize_t paths_file_write(paths_file_t* file, const void* data, size_t size);

/**
 * Close and finish saving file.
 * If the file was saved successfully, the paths_file_t object is freed.
 * If the save failed in any way, the object is not freed so you must call
 * paths_file_abort() to finish up (or call paths_file_close() again and
 * hope for better luck this time).
 * Not threadsafe,
 * the paths_file_t must only be accessed by one thread at a time.
 * @param file target file, may not be NULL
 * @return zero on success, non-zero in case of error
 */
NONULL int paths_file_close(paths_file_t* file);

/**
 * Abort saving the file. The target file is removed unless the original version
 * before paths_write() could be restored.
 * Not threadsafe,
 * the paths_file_t must only be accessed by one thread at a time.
 * @param file target file, may be NULL
 */
void paths_file_abort(paths_file_t* file);

/**
 * Get absolute path to directory where user-specific data files should be
 * written. Returned directory may not exist.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @return absolute path to data directory
 */
static inline NONULL const char* paths_user_data(paths_t* paths)
{
    return paths_user_dir(paths, PATHS_DATA);
}

/**
 * Get absolute path to directory where user-specific config files should be
 * written. Returned directory may not exist.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @return absolute path to config directory
 */
static inline NONULL const char* paths_user_config(paths_t* paths)
{
    return paths_user_dir(paths, PATHS_CONFIG);
}

/**
 * Get absolute path to directory where user-specific cache files should be
 * written. Returned directory may not exist.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @return absolute path to cache directory
 */
static inline NONULL const char* paths_user_cache(paths_t* paths)
{
    return paths_user_dir(paths, PATHS_CACHE);
}

/**
 * Get absolute path to directory where user-specific runtime files should be
 * written.
 * If not defined in enviroment or not valid NULL is returned.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @return absolute path to runtime directory or NULL
 */
static inline NONULL const char* paths_user_runtime(paths_t* paths)
{
    return paths_user_dir(paths, PATHS_RUNTIME);
}

/**
 * Get preference ordered list of directories to search for data files.
 * All paths are absolute and NULL terminated. The list itself is NULL
 * terminated and may never be empty. Path returned by paths_user_data()
 * is not included.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @return NULL terminated list of absolute paths to search
 */
static inline NONULL const char** paths_data(paths_t* paths)
{
    return paths_search_dirs(paths, PATHS_DATA);
}

/**
 * Get preference ordered list of directories to search for config files.
 * All paths are absolute and NULL terminated. The list itself is NULL
 * terminated and may never be empty. Path returned by paths_user_config()
 * is not included.
 * Threadsafe.
 * @param paths paths object, may not be NULL
 * @return NULL terminated list of absolute paths to search
 */
static inline NONULL const char** paths_config(paths_t* paths)
{
    return paths_search_dirs(paths, PATHS_CONFIG);
}

#endif /* PATHS_H */