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