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>
This commit is contained in:
René Scharfe 2022-04-20 22:26:09 +02:00 committed by Junio C Hamano
parent 6cd33dceed
commit 2c2db194bd
2 changed files with 76 additions and 0 deletions

View File

@ -56,6 +56,20 @@
static VOLATILE_LIST_HEAD(tempfile_list);
static void remove_template_directory(struct tempfile *tempfile,
int in_signal_handler)
{
if (tempfile->directorylen > 0 &&
tempfile->directorylen < tempfile->filename.len &&
tempfile->filename.buf[tempfile->directorylen] == '/') {
strbuf_setlen(&tempfile->filename, tempfile->directorylen);
if (in_signal_handler)
rmdir(tempfile->filename.buf);
else
rmdir_or_warn(tempfile->filename.buf);
}
}
static void remove_tempfiles(int in_signal_handler)
{
pid_t me = getpid();
@ -74,6 +88,7 @@ static void remove_tempfiles(int in_signal_handler)
unlink(p->filename.buf);
else
unlink_or_warn(p->filename.buf);
remove_template_directory(p, in_signal_handler);
p->active = 0;
}
@ -100,6 +115,7 @@ static struct tempfile *new_tempfile(void)
tempfile->owner = 0;
INIT_LIST_HEAD(&tempfile->list);
strbuf_init(&tempfile->filename, 0);
tempfile->directorylen = 0;
return tempfile;
}
@ -198,6 +214,52 @@ struct tempfile *mks_tempfile_tsm(const char *filename_template, int suffixlen,
return tempfile;
}
struct tempfile *mks_tempfile_dt(const char *directory_template,
const char *filename)
{
struct tempfile *tempfile;
const char *tmpdir;
struct strbuf sb = STRBUF_INIT;
int fd;
size_t directorylen;
if (!ends_with(directory_template, "XXXXXX")) {
errno = EINVAL;
return NULL;
}
tmpdir = getenv("TMPDIR");
if (!tmpdir)
tmpdir = "/tmp";
strbuf_addf(&sb, "%s/%s", tmpdir, directory_template);
directorylen = sb.len;
if (!mkdtemp(sb.buf)) {
int orig_errno = errno;
strbuf_release(&sb);
errno = orig_errno;
return NULL;
}
strbuf_addf(&sb, "/%s", filename);
fd = open(sb.buf, O_CREAT | O_EXCL | O_RDWR, 0600);
if (fd < 0) {
int orig_errno = errno;
strbuf_setlen(&sb, directorylen);
rmdir(sb.buf);
strbuf_release(&sb);
errno = orig_errno;
return NULL;
}
tempfile = new_tempfile();
strbuf_swap(&tempfile->filename, &sb);
tempfile->directorylen = directorylen;
tempfile->fd = fd;
activate_tempfile(tempfile);
return tempfile;
}
struct tempfile *xmks_tempfile_m(const char *filename_template, int mode)
{
struct tempfile *tempfile;
@ -316,6 +378,7 @@ void delete_tempfile(struct tempfile **tempfile_p)
close_tempfile_gently(tempfile);
unlink_or_warn(tempfile->filename.buf);
remove_template_directory(tempfile, 0);
deactivate_tempfile(tempfile);
*tempfile_p = NULL;
}

View File

@ -82,6 +82,7 @@ struct tempfile {
FILE *volatile fp;
volatile pid_t owner;
struct strbuf filename;
size_t directorylen;
};
/*
@ -198,6 +199,18 @@ static inline struct tempfile *xmks_tempfile(const char *filename_template)
return xmks_tempfile_m(filename_template, 0600);
}
/*
* Attempt to create a temporary directory in $TMPDIR and to create and
* open a file in that new directory. Derive the directory name from the
* template in the manner of mkdtemp(). Arrange for directory and file
* to be deleted if the program exits before they are deleted
* explicitly. On success return a tempfile whose "filename" member
* contains the full path of the file and its "fd" member is open for
* writing the file. On error return NULL and set errno appropriately.
*/
struct tempfile *mks_tempfile_dt(const char *directory_template,
const char *filename);
/*
* Associate a stdio stream with the temporary file (which must still
* be open). Return `NULL` (*without* deleting the file) on error. The