tempfile: add mks_tempfile_dt()

Add a function to create a temporary file with a certain name in a temporary directory created using mkdtemp(3). Its result is more sightly than the paths created by mks_tempfile_ts(), which include a random prefix. That's useful for files passed to a program that displays their name, e.g. an external diff tool. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Apr 20, 2022 at 22:26 UTC 2c2db194bd9e2f2dda7f1a47d26a1f86b3dd635a
2 files changed +76
tempfile.c
+63
@@ -56,6 +56,20 @@
56
57 static VOLATILE_LIST_HEAD(tempfile_list);
58
59 +static void remove_template_directory(struct tempfile *tempfile,
60 + int in_signal_handler)
61 +{
62 + if (tempfile->directorylen > 0 &&
63 + tempfile->directorylen < tempfile->filename.len &&
64 + tempfile->filename.buf[tempfile->directorylen] == '/') {
65 + strbuf_setlen(&tempfile->filename, tempfile->directorylen);
66 + if (in_signal_handler)
67 + rmdir(tempfile->filename.buf);
68 + else
69 + rmdir_or_warn(tempfile->filename.buf);
70 + }
71 +}
72 +
73 static void remove_tempfiles(int in_signal_handler)
74 {
75 pid_t me = getpid();
@@ -74,6 +88,7 @@ static void remove_tempfiles(int in_signal_handler)
88 unlink(p->filename.buf);
89 else
90 unlink_or_warn(p->filename.buf);
91 + remove_template_directory(p, in_signal_handler);
92
93 p->active = 0;
94 }
@@ -100,6 +115,7 @@ static struct tempfile *new_tempfile(void)
115 tempfile->owner = 0;
116 INIT_LIST_HEAD(&tempfile->list);
117 strbuf_init(&tempfile->filename, 0);
118 + tempfile->directorylen = 0;
119 return tempfile;
120 }
121
@@ -198,6 +214,52 @@ struct tempfile *mks_tempfile_tsm(const char *filename_template, int suffixlen,
214 return tempfile;
215 }
216
217 +struct tempfile *mks_tempfile_dt(const char *directory_template,
218 + const char *filename)
219 +{
220 + struct tempfile *tempfile;
221 + const char *tmpdir;
222 + struct strbuf sb = STRBUF_INIT;
223 + int fd;
224 + size_t directorylen;
225 +
226 + if (!ends_with(directory_template, "XXXXXX")) {
227 + errno = EINVAL;
228 + return NULL;
229 + }
230 +
231 + tmpdir = getenv("TMPDIR");
232 + if (!tmpdir)
233 + tmpdir = "/tmp";
234 +
235 + strbuf_addf(&sb, "%s/%s", tmpdir, directory_template);
236 + directorylen = sb.len;
237 + if (!mkdtemp(sb.buf)) {
238 + int orig_errno = errno;
239 + strbuf_release(&sb);
240 + errno = orig_errno;
241 + return NULL;
242 + }
243 +
244 + strbuf_addf(&sb, "/%s", filename);
245 + fd = open(sb.buf, O_CREAT | O_EXCL | O_RDWR, 0600);
246 + if (fd < 0) {
247 + int orig_errno = errno;
248 + strbuf_setlen(&sb, directorylen);
249 + rmdir(sb.buf);
250 + strbuf_release(&sb);
251 + errno = orig_errno;
252 + return NULL;
253 + }
254 +
255 + tempfile = new_tempfile();
256 + strbuf_swap(&tempfile->filename, &sb);
257 + tempfile->directorylen = directorylen;
258 + tempfile->fd = fd;
259 + activate_tempfile(tempfile);
260 + return tempfile;
261 +}
262 +
263 struct tempfile *xmks_tempfile_m(const char *filename_template, int mode)
264 {
265 struct tempfile *tempfile;
@@ -316,6 +378,7 @@ void delete_tempfile(struct tempfile **tempfile_p)
378
379 close_tempfile_gently(tempfile);
380 unlink_or_warn(tempfile->filename.buf);
381 + remove_template_directory(tempfile, 0);
382 deactivate_tempfile(tempfile);
383 *tempfile_p = NULL;
384 }
tempfile.h
+13
@@ -82,6 +82,7 @@ struct tempfile {
82 FILE *volatile fp;
83 volatile pid_t owner;
84 struct strbuf filename;
85 + size_t directorylen;
86 };
87
88 /*
@@ -198,6 +199,18 @@ static inline struct tempfile *xmks_tempfile(const char *filename_template)
199 return xmks_tempfile_m(filename_template, 0600);
200 }
201
202 +/*
203 + * Attempt to create a temporary directory in $TMPDIR and to create and
204 + * open a file in that new directory. Derive the directory name from the
205 + * template in the manner of mkdtemp(). Arrange for directory and file
206 + * to be deleted if the program exits before they are deleted
207 + * explicitly. On success return a tempfile whose "filename" member
208 + * contains the full path of the file and its "fd" member is open for
209 + * writing the file. On error return NULL and set errno appropriately.
210 + */
211 +struct tempfile *mks_tempfile_dt(const char *directory_template,
212 + const char *filename);
213 +
214 /*
215 * Associate a stdio stream with the temporary file (which must still
216 * be open). Return `NULL` (*without* deleting the file) on error. The