123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- mktemp.c
- Abstract:
- This module implements the mktemp (temporary file and directory creation)
- utility.
- Author:
- Evan Green 11-Oct-2013
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <libgen.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define MKTEMP_VERSION_MAJOR 1
- #define MKTEMP_VERSION_MINOR 0
- #define MKTEMP_USAGE \
- "usage: mktemp [-duq] [--suffix=SUFFIX] [--tmpdir[=DIR]] [-p DIR] [-t] " \
- "template\n " \
- "The mktemp utility creates a temporary file or directory safely and \n" \
- "prints its name. If no template is supplied, tmp.XXXXXXXXXX is used, \n" \
- "and --tmpdir is implied. Valid options are:\n" \
- " -d, --directory -- Create a directory, not a file.\n" \
- " -u, --dry-run -- Do not create anything, just print a name.\n" \
- " -q, --quiet -- Suppress messages about file/directory creation.\n" \
- " --suffix=SUFFIX -- Append the given suffix to the template.\n" \
- " --tmpdir=DIR -- Prepend the given directory to the template. If not \n" \
- " specified, prepend the value of the TMPDIR environment " \
- "variable.\n" \
- " -p DIR -- Use the given directory as a prefix.\n" \
- " -t -- Interpret the template relative to a directory: TMPDIR if set, \n"\
- " or the directory specified by -p, or /tmp.\n" \
- " --help -- Display this help text and exit.\n" \
- " --version -- Display the application version and exit.\n"
- #define MKTEMP_OPTIONS_STRING "duqp:t"
- //
- // Define the name of the environment variable mktemp looks at to get the
- // temporary directory prefix in some scenarios.
- //
- #define MKTEMP_DIRECTORY_VARIABLE "TMPDIR"
- //
- // Define the default template to use if none is provided.
- //
- #define MKTEMP_DEFAULT_TEMPLATE "tmp.XXXXXXXXXX"
- //
- // Define the permissions on temporary files.
- //
- #define TEMPORARY_FILE_PERMISSIONS (S_IRUSR | S_IWUSR)
- //
- // Define the permissions on a temporary directory.
- //
- #define TEMPORARY_DIRECTORY_PERMISSIONS (S_IRUSR | S_IWUSR | S_IXUSR)
- //
- // Define the minimum number of trailing X characters to enforce.
- //
- #define MKTEMP_MINIMUM_REPLACE_COUNT 3
- //
- // Define the number of times mktemp will try before giving up.
- //
- #define MKTEMP_TRY_COUNT 1000000
- //
- // Define mktemp option flags.
- //
- //
- // Set this flag to create a directory instead of a file.
- //
- #define MKTEMP_OPTION_DIRECTORY 0x00000001
- //
- // Set this flag to only perform a dry run, not actually create a
- // file/directory.
- //
- #define MKTEMP_OPTION_DRY_RUN 0x00000002
- //
- // Set this option to not print stuff.
- //
- #define MKTEMP_OPTION_QUIET 0x00000004
- //
- // Set this option to use the old directory order (the -t option).
- //
- #define MKTEMP_OPTION_OLD_DIRECTORY_ORDER 0x00000008
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- MktempReplaceTemplate (
- PSTR String,
- UINTN ReplaceCount
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option MktempLongOptions[] = {
- {"directory", no_argument, 0, 'd'},
- {"dry-run", no_argument, 0, 'u'},
- {"quiet", no_argument, 0, 'q'},
- {"suffix", required_argument, 0, 's'},
- {"tmpdir", optional_argument, 0, 'T'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- MktempMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the mktemp utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PSTR AppendedPath;
- BOOL AppendedPathAllocated;
- ULONG AppendedPathSize;
- ULONG ArgumentIndex;
- INT Descriptor;
- size_t Length;
- INT Option;
- ULONG Options;
- PSTR Prefix;
- PSTR PrefixedPath;
- BOOL PrefixedPathAllocated;
- ULONG PrefixedPathSize;
- ULONG PrefixSize;
- size_t ReplaceCount;
- struct stat Stat;
- int Status;
- PSTR Suffix;
- ULONG SuffixSize;
- PSTR Template;
- PSTR TemplateCopy;
- ULONG TemplateSize;
- ULONG Try;
- PSTR Variable;
- AppendedPathAllocated = FALSE;
- Prefix = NULL;
- PrefixedPath = NULL;
- PrefixedPathAllocated = FALSE;
- Options = 0;
- Suffix = NULL;
- Template = NULL;
- TemplateCopy = NULL;
- srand(time(NULL) ^ getpid());
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- MKTEMP_OPTIONS_STRING,
- MktempLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'd':
- Options |= MKTEMP_OPTION_DIRECTORY;
- break;
- case 'u':
- Options |= MKTEMP_OPTION_DRY_RUN;
- break;
- case 'q':
- Options |= MKTEMP_OPTION_QUIET;
- break;
- case 'p':
- Prefix = optarg;
- break;
- case 't':
- Options |= MKTEMP_OPTION_OLD_DIRECTORY_ORDER;
- break;
- case 's':
- Suffix = optarg;
- break;
- case 'T':
- Options &= ~MKTEMP_OPTION_OLD_DIRECTORY_ORDER;
- Prefix = optarg;
- if (Prefix == NULL) {
- Prefix = getenv(MKTEMP_DIRECTORY_VARIABLE);
- }
- break;
- case 'V':
- SwPrintVersion(MKTEMP_VERSION_MAJOR, MKTEMP_VERSION_MINOR);
- return 1;
- case 'h':
- printf(MKTEMP_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- ArgumentIndex = optind;
- if (ArgumentIndex > ArgumentCount) {
- ArgumentIndex = ArgumentCount;
- }
- Template = NULL;
- if (ArgumentCount - ArgumentIndex != 0) {
- Template = Arguments[ArgumentIndex];
- ArgumentIndex += 1;
- //
- // If there was no template, use a default one, and get the prefix from the
- // environment if one is not already set.
- //
- } else {
- Template = MKTEMP_DEFAULT_TEMPLATE;
- Options |= MKTEMP_OPTION_OLD_DIRECTORY_ORDER;
- }
- //
- // If the old style directory order is in effect, then prefer the contents
- // of the variable TMPDIR, followed by the argument from -p, followed by
- // /tmp.
- //
- if ((Options & MKTEMP_OPTION_OLD_DIRECTORY_ORDER) != 0) {
- Variable = getenv(MKTEMP_DIRECTORY_VARIABLE);
- if (Variable != NULL) {
- Prefix = Variable;
- }
- if (Prefix == NULL) {
- Prefix = "/tmp";
- }
- }
- if (ArgumentCount - ArgumentIndex > 1) {
- SwPrintError(0, NULL, "Expected no more than one operand");
- Status = 1;
- goto MainEnd;
- }
- //
- // Ensure there are enough X characters to replace.
- //
- Length = strlen(Template);
- ReplaceCount = 0;
- while ((Length - ReplaceCount != 0) &&
- (Template[Length - 1 - ReplaceCount] == 'X')) {
- ReplaceCount += 1;
- }
- if (ReplaceCount < MKTEMP_MINIMUM_REPLACE_COUNT) {
- SwPrintError(0, Template, "Too few Xs in template");
- Status = 1;
- goto MainEnd;
- }
- TemplateSize = Length + 1;
- PrefixSize = 0;
- if (Prefix != NULL) {
- PrefixSize = strlen(Prefix) + 1;
- }
- SuffixSize = 0;
- if (Suffix != NULL) {
- SuffixSize = strlen(Suffix) + 1;
- }
- Status = -1;
- TemplateCopy = strdup(Template);
- if (TemplateCopy == NULL) {
- Status = ENOMEM;
- goto MainEnd;
- }
- for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
- MktempReplaceTemplate(TemplateCopy + Length - ReplaceCount,
- ReplaceCount);
- //
- // Append the prefix if there is one.
- //
- if (Prefix != NULL) {
- Status = SwAppendPath(Prefix,
- PrefixSize,
- TemplateCopy,
- TemplateSize,
- &PrefixedPath,
- &PrefixedPathSize);
- if (Status == FALSE) {
- Status = ENOMEM;
- SwPrintError(Status, NULL, "Failed to create string");
- goto MainEnd;
- }
- PrefixedPathAllocated = TRUE;
- } else {
- PrefixedPath = TemplateCopy;
- PrefixedPathSize = TemplateSize;
- }
- //
- // Append the suffix if there is one.
- //
- if (Suffix != NULL) {
- Status = SwAppendPath(PrefixedPath,
- PrefixedPathSize,
- Suffix,
- SuffixSize,
- &AppendedPath,
- &AppendedPathSize);
- if (Status == FALSE) {
- Status = ENOMEM;
- SwPrintError(Status, NULL, "Failed to create string");
- goto MainEnd;
- }
- PrefixedPathAllocated = TRUE;
- } else {
- AppendedPath = PrefixedPath;
- AppendedPathSize = PrefixedPathSize;
- }
- //
- // Whew, now that the path is created, try to create it.
- //
- if ((Options & MKTEMP_OPTION_DRY_RUN) != 0) {
- Status = SwStat(AppendedPath, FALSE, &Stat);
- if ((Status != 0) && (Status == ENOENT)) {
- Status = 0;
- break;
- }
- if (Status != 0) {
- if ((Options & MKTEMP_OPTION_QUIET) == 0) {
- SwPrintError(Status, AppendedPath, "Unable to stat");
- }
- goto MainEnd;
- }
- } else if ((Options & MKTEMP_OPTION_DIRECTORY) != 0) {
- Status = SwMakeDirectory(AppendedPath,
- TEMPORARY_DIRECTORY_PERMISSIONS);
- if (Status == 0) {
- break;
- }
- if (errno != EEXIST) {
- if ((Options & MKTEMP_OPTION_QUIET) == 0) {
- SwPrintError(errno,
- AppendedPath,
- "Unable to create directory");
- }
- goto MainEnd;
- }
- } else {
- Descriptor = open(AppendedPath,
- O_RDWR | O_CREAT | O_EXCL,
- TEMPORARY_FILE_PERMISSIONS);
- if (Descriptor >= 0) {
- close(Descriptor);
- Status = 0;
- break;
- }
- if ((errno != EEXIST) && (errno != EISDIR)) {
- if ((Options & MKTEMP_OPTION_QUIET) == 0) {
- SwPrintError(errno, AppendedPath, "Unable to create file");
- }
- goto MainEnd;
- }
- }
- //
- // Clean up.
- //
- if (PrefixedPathAllocated != FALSE) {
- free(PrefixedPath);
- PrefixedPath = NULL;
- PrefixedPathAllocated = FALSE;
- }
- if (AppendedPathAllocated != FALSE) {
- free(AppendedPath);
- AppendedPath = NULL;
- AppendedPathAllocated = FALSE;
- }
- assert(TemplateCopy != NULL);
- free(TemplateCopy);
- TemplateCopy = NULL;
- }
- if (Try == MKTEMP_TRY_COUNT) {
- SwPrintError(0, NULL, "Tried %d times and failed", MKTEMP_TRY_COUNT);
- goto MainEnd;
- }
- //
- // Print out what was found!
- //
- assert(AppendedPath != NULL);
- if ((Options & MKTEMP_OPTION_QUIET) == 0) {
- printf("%s\n", AppendedPath);
- }
- Status = 0;
- MainEnd:
- if (PrefixedPathAllocated != FALSE) {
- free(PrefixedPath);
- PrefixedPath = NULL;
- PrefixedPathAllocated = FALSE;
- }
- if (AppendedPathAllocated != FALSE) {
- free(AppendedPath);
- AppendedPath = NULL;
- AppendedPathAllocated = FALSE;
- }
- free(TemplateCopy);
- TemplateCopy = NULL;
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- MktempReplaceTemplate (
- PSTR String,
- UINTN ReplaceCount
- )
- /*++
- Routine Description:
- This routine creates random ASCII characters in the range of 0-9 and A-Z.
- Arguments:
- String - Supplies a pointer to the template.
- ReplaceCount - Supplies the number of characters to replace.
- Return Value:
- None.
- --*/
- {
- ULONG RandomIndex;
- INT Value;
- for (RandomIndex = 0; RandomIndex < ReplaceCount; RandomIndex += 1) {
- //
- // Create a random value using letters and numbers. Avoid relying
- // on case sensitivity just in case. For reference,
- // 36^5 is 60.4 million.
- //
- Value = rand() % 36;
- if (Value >= 10) {
- Value += 'A' - 10;
- } else {
- Value += '0';
- }
- String[RandomIndex] = Value;
- }
- return;
- }
|