1 /** 2 Create temporary files and folders. These are wrappers around the 3 corresponding functions in `core.sys.posix.stdlib`. 4 5 Copyright: © 2019 Arne Ludwig <arne.ludwig@posteo.de> 6 License: Subject to the terms of the MIT license, as written in the 7 included LICENSE file. 8 Authors: Arne Ludwig <arne.ludwig@posteo.de> 9 */ 10 module dalicious.tempfile; 11 12 import std.stdio; 13 14 version (Posix) 15 { 16 import std.algorithm : endsWith; 17 import std.conv : to; 18 import std.exception : errnoEnforce; 19 import std.stdio : File; 20 import std..string : fromStringz; 21 import std.typecons : Tuple, tuple; 22 23 /** 24 Generates a uniquely named temporary directory from template. 25 26 The last six characters of template must be XXXXXX and these are 27 replaced with a string that makes the directory name unique. The 28 directory is then created with permissions 0700. 29 30 Returns: The generated directory name. 31 */ 32 string mkdtemp(in string templateString) @trusted 33 { 34 import core.sys.posix.stdlib : c_mkdtemp = mkdtemp; 35 36 char[255] dirnameBuffer; 37 auto len = templateString.length; 38 assert(len < dirnameBuffer.length); 39 assert(templateString.endsWith("XXXXXX")); 40 41 dirnameBuffer[0 .. len] = templateString[]; 42 dirnameBuffer[len] = 0; 43 44 errnoEnforce(null != c_mkdtemp(dirnameBuffer.ptr), "cannot create temporary directory"); 45 46 return to!string(fromStringz(dirnameBuffer.ptr)); 47 } 48 49 /// 50 unittest 51 { 52 import std.algorithm : startsWith; 53 import std.file : isDir, rmdir; 54 55 string tempDir = mkdtemp(".unittest-XXXXXX"); 56 57 try 58 { 59 assert(isDir(tempDir)); 60 assert(tempDir.startsWith(".unittest-")); 61 } 62 finally 63 { 64 rmdir(tempDir); 65 } 66 } 67 68 /** 69 Generate a unique temporary filename from templateString, creates and 70 opens the file, and returns the open file and generated name. 71 72 The last six characters of template must be "XXXXXX" and these are 73 replaced with a string that makes the filename unique. 74 75 The optional templateSuffix will be appended to the file name. 76 77 Returns: The open file and generated name. 78 */ 79 Tuple!(File, "file", string, "name") mkstemp(in string templateString) @trusted 80 { 81 import core.sys.posix.stdlib : mkstemp; 82 83 char[255] dirnameBuffer; 84 auto len = templateString.length; 85 assert(len < dirnameBuffer.length); 86 assert(templateString.endsWith("XXXXXX")); 87 88 dirnameBuffer[0 .. len] = templateString[]; 89 dirnameBuffer[len] = 0; 90 91 auto fd = mkstemp(dirnameBuffer.ptr); 92 93 errnoEnforce(fd != -1, "cannot create temporary file"); 94 95 File tempFile; 96 tempFile.fdopen(fd, "r+"); 97 98 return tuple!("file", "name")(tempFile, dirnameBuffer.ptr.fromStringz.to!string); 99 } 100 101 /// 102 unittest 103 { 104 import std.algorithm : startsWith; 105 import std.file : remove; 106 107 auto tempFile = mkstemp(".unittest-XXXXXX"); 108 scope (exit) 109 remove(tempFile.name); 110 111 assert(tempFile.name.startsWith(".unittest-")); 112 assert(tempFile.file.isOpen); 113 assert(!tempFile.file.error); 114 tempFile.file.writeln("foobar"); 115 tempFile.file.flush(); 116 tempFile.file.rewind(); 117 assert(tempFile.file.readln() == "foobar\n"); 118 } 119 120 private extern (C) int mkstemps(char*, int); 121 122 /// ditto 123 Tuple!(File, "file", string, "name") mkstemp(in string templateString, in string templateSuffix) @trusted 124 { 125 char[255] dirnameBuffer; 126 auto len = templateString.length + templateSuffix.length; 127 assert(len < dirnameBuffer.length); 128 assert(templateString.endsWith("XXXXXX")); 129 130 dirnameBuffer[0 .. len] = templateString[] ~ templateSuffix[]; 131 dirnameBuffer[len] = 0; 132 133 auto fd = mkstemps(dirnameBuffer.ptr, templateSuffix.length.to!int); 134 135 errnoEnforce(fd != -1, "cannot create temporary file"); 136 137 File tempFile; 138 tempFile.fdopen(fd, "r+"); 139 140 return tuple!("file", "name")(tempFile, dirnameBuffer.ptr.fromStringz.to!string); 141 } 142 143 /// 144 unittest 145 { 146 import std.algorithm : endsWith, startsWith; 147 import std.file : remove; 148 149 auto tempFile = mkstemp(".unittest-XXXXXX", ".ext"); 150 scope (exit) 151 remove(tempFile.name); 152 153 assert(tempFile.name.startsWith(".unittest-")); 154 assert(tempFile.name.endsWith(".ext")); 155 assert(tempFile.file.isOpen); 156 assert(!tempFile.file.error); 157 tempFile.file.writeln("foobar"); 158 tempFile.file.flush(); 159 tempFile.file.rewind(); 160 assert(tempFile.file.readln() == "foobar\n"); 161 } 162 } 163 else 164 { 165 static assert(0, "Only intended for use on POSIX compliant OS."); 166 }