shared: add file_create_dated() helper
[platform/upstream/weston.git] / shared / file-util.c
1 /*
2  * Copyright © 2015 Collabora, Ltd.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <time.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 #include "file-util.h"
33
34 static int
35 current_time_str(char *str, size_t len, const char *fmt)
36 {
37         time_t t;
38         struct tm *t_local;
39         int ret;
40
41         t = time(NULL);
42         t_local = localtime(&t);
43         if (!t_local) {
44                 errno = ETIME;
45                 return -1;
46         }
47
48         ret = strftime(str, len, fmt, t_local);
49         if (ret == 0) {
50                 errno = ETIME;
51                 return -1;
52         }
53
54         return ret;
55 }
56
57 static int
58 create_file_excl(const char *fname)
59 {
60         return open(fname, O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, 00666);
61 }
62
63 /** Create a unique file with date and time in the name
64  *
65  * \param path_prefix Path and file name prefix.
66  * \param suffix File name suffix.
67  * \param name_out[out] Buffer for the resulting file name.
68  * \param name_len Number of bytes usable in name_out.
69  * \return stdio FILE pointer, or NULL on failure.
70  *
71  * Create and open a new file with the name concatenated from
72  * path_prefix, date and time, and suffix. If a file with this name
73  * already exists, an counter number is added to the end of the
74  * date and time sub-string. The counter is increased until a free file
75  * name is found.
76  *
77  * Once creating the file succeeds, the name of the file is in name_out.
78  * On failure, the contents of name_out are undefined and errno is set.
79  */
80 FILE *
81 file_create_dated(const char *path_prefix, const char *suffix,
82                   char *name_out, size_t name_len)
83 {
84         char timestr[128];
85         int ret;
86         int fd;
87         int cnt = 0;
88
89         if (current_time_str(timestr, sizeof(timestr), "%F_%H-%M-%S") < 0)
90                 return NULL;
91
92         ret = snprintf(name_out, name_len, "%s%s%s",
93                        path_prefix, timestr, suffix);
94         if (ret < 0 || (size_t)ret >= name_len) {
95                 errno = ENOBUFS;
96                 return NULL;
97         }
98
99         fd = create_file_excl(name_out);
100
101         while (fd == -1 && errno == EEXIST) {
102                 cnt++;
103
104                 ret = snprintf(name_out, name_len, "%s%s-%d%s",
105                                path_prefix, timestr, cnt, suffix);
106                 if (ret < 0 || (size_t)ret >= name_len) {
107                         errno = ENOBUFS;
108                         return NULL;
109                 }
110
111                 fd = create_file_excl(name_out);
112         }
113
114         if (fd == -1)
115                 return NULL;
116
117         return fdopen(fd, "w");
118 }