mktemp.c (4087B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- // getentropy got in POSIX.1-2024 but defining _POSIX_C_SOURCE causes too much side-effects
- #define _DEFAULT_SOURCE // getentropy
- #include <errno.h>
- #include <limits.h> // PATH_MAX
- #include <stdbool.h>
- #include <stdio.h> // fprintf
- #include <stdlib.h> // getenv
- #include <string.h> // strerror
- #include <unistd.h> // getopt, getentropy
- #ifdef HAS_GETOPT_LONG
- #include <getopt.h>
- #endif
- const char *argv0 = "mktemp";
- static int
- unsafe_mktemp(char *template)
- {
- size_t l = strlen(template);
- size_t len = 0;
- size_t off = l - 1;
- while(off > 0 && template[off] == 'X')
- off--, len++;
- if(len < 6)
- {
- fprintf(stderr,
- "%s: error: Template '%s' is invalid (need >= 6 final X, got %zd)\n",
- argv0,
- template,
- len);
- return 1;
- }
- for(int retries = 100; 0 < retries; retries--)
- {
- unsigned long r = 0;
- if(getentropy(&r, sizeof(r)) != 0)
- {
- fprintf(stderr,
- "%s: error: Failed to get entropy for random filename: %s\n",
- argv0,
- strerror(errno));
- return 1;
- }
- for(int i = len; i > 0; i--, r >>= 5)
- template[off + i] = 'A' + (r & 15) + (r & 16) * 2;
- if(access(template, F_OK) != 0 && errno == ENOENT)
- {
- puts(template);
- return 0;
- }
- }
- fprintf(stderr, "%s: error: Exhausted all 100 attempts at generating a filename\n", argv0);
- return 1;
- }
- int
- main(int argc, char *argv[])
- {
- bool o_create_dir = false, o_quiet = false, o_unsafe = false;
- char template[PATH_MAX] = "tmp.XXXXXXXXXX";
- const char *tmpdir = NULL;
- int c = -1;
- #ifdef HAS_GETOPT_LONG
- // Strictly for GNUisms compatibility so no long-only options
- // clang-format off
- static struct option opts[] = {
- {"directory", no_argument, 0, 'd'},
- {"dry-run", required_argument, 0, 'u'},
- {"quiet", no_argument, 0, 'q'},
- {"tmpdir", required_argument, 0, 'p'},
- {0, 0, 0, 0},
- };
- // clang-format on
- // Need + as first character to get POSIX-style option parsing
- while((c = getopt_long(argc, argv, "+:dqp:tu", opts, NULL)) != -1)
- #else
- while((c = getopt(argc, argv, ":dqp:tu")) != -1)
- #endif
- {
- switch(c)
- {
- case 'd':
- o_create_dir = true;
- break;
- case 'q':
- o_quiet = true;
- break;
- case 'p':
- tmpdir = optarg;
- break;
- case 't':
- if(tmpdir == NULL) tmpdir = getenv("TMPDIR");
- if(tmpdir == NULL) tmpdir = "/tmp";
- break;
- case 'u':
- o_unsafe = true;
- break;
- case '?':
- fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
- return 1;
- default:
- fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- if(argc == 1)
- {
- strncpy(template, *argv, PATH_MAX);
- }
- else if(argc > 1)
- {
- fprintf(stderr, "%s: error: Only one template argument is supported, got %d\n", argv0, argc);
- return 1;
- }
- if(tmpdir)
- {
- if(chdir(tmpdir) < 0)
- {
- fprintf(stderr,
- "%s: error: Failed changing directory into tmpdir '%s': %s\n",
- argv0,
- tmpdir,
- strerror(errno));
- return 1;
- }
- printf("%s/", tmpdir);
- }
- char template_copy[PATH_MAX] = "";
- memcpy(template_copy, template, PATH_MAX);
- if(o_unsafe) return unsafe_mktemp(template);
- if(o_create_dir)
- {
- char *dir = mkdtemp(template);
- if(dir == NULL)
- {
- if(!o_quiet)
- fprintf(stderr,
- "%s: error: Failed creating random directory with template '%s': %s\n",
- argv0,
- template_copy,
- strerror(errno));
- return 1;
- }
- puts(dir);
- return 0;
- }
- int fd = mkstemp(template);
- if(fd < 0)
- {
- if(!o_quiet)
- fprintf(stderr,
- "%s: error: Failed creating random file with template '%s': %s\n",
- argv0,
- template_copy,
- strerror(errno));
- return 1;
- }
- puts(template);
- if(close(fd) < 0)
- {
- if(!o_quiet) fprintf(stderr, "%s: error: Failed closing file: %s\n", argv0, strerror(errno));
- return 1;
- }
- return 0;
- }