1 /* util.{cc,hh} -- various bits
2 *
3 * Copyright (c) 2003-2012 Eddie Kohler
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version. This program is distributed in the hope that it will be
9 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
12 */
13
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif
17 #include "util.hh"
18 #include <lcdf/error.hh>
19 #include <lcdf/straccum.hh>
20 #include <lcdf/vector.hh>
21 #include <ctype.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #ifdef HAVE_FCNTL_H
26 # include <fcntl.h>
27 #endif
28 #if defined(_MSDOS) || defined(_WIN32)
29 # include <io.h>
30 #endif
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34
35 String
read_file(String filename,ErrorHandler * errh,bool warning)36 read_file(String filename, ErrorHandler *errh, bool warning)
37 {
38 FILE *f;
39 if (!filename || filename == "-") {
40 filename = "<stdin>";
41 f = stdin;
42 #if defined(_MSDOS) || defined(_WIN32)
43 // Set the file mode to binary
44 _setmode(_fileno(f), _O_BINARY);
45 #endif
46 } else if (!(f = fopen(filename.c_str(), "rb"))) {
47 errh->xmessage((warning ? errh->e_warning : errh->e_error) + ErrorHandler::make_landmark_anno(filename), strerror(errno));
48 return String();
49 }
50
51 StringAccum sa;
52 int amt;
53 do {
54 if (char *x = sa.reserve(8192)) {
55 amt = fread(x, 1, 8192, f);
56 sa.adjust_length(amt);
57 } else
58 amt = 0;
59 } while (amt != 0);
60 if (!feof(f) || ferror(f))
61 errh->xmessage((warning ? errh->e_warning : errh->e_error) + ErrorHandler::make_landmark_anno(filename), strerror(errno));
62 if (f != stdin)
63 fclose(f);
64 return sa.take_string();
65 }
66
67 String
printable_filename(const String & s)68 printable_filename(const String &s)
69 {
70 if (!s || s == "-")
71 return String::make_stable("<stdin>");
72 else
73 return s;
74 }
75
76 String
pathname_filename(const String & path)77 pathname_filename(const String &path)
78 {
79 int slash = path.find_right('/');
80 if (slash >= 0 && slash != path.length() - 1)
81 return path.substring(slash + 1);
82 else
83 return path;
84 }
85
86 static String
simplify_filename(String x)87 simplify_filename(String x)
88 {
89 while (x.substring(0, 2) == "./")
90 x = x.substring(2);
91 int pos;
92 while ((pos = x.find_left("/./")) >= 0)
93 x = x.substring(0, pos) + x.substring(pos + 2);
94 return x;
95 }
96
97 bool
same_filename(const String & a,const String & b)98 same_filename(const String &a, const String &b)
99 {
100 return simplify_filename(a) == simplify_filename(b);
101 }
102
103 String
shell_quote(const String & str)104 shell_quote(const String &str)
105 {
106 if (!str)
107 return String::make_stable("\"\"");
108
109 const char *begin = str.begin();
110 const char *end = str.end();
111 StringAccum sa;
112
113 #if defined(_MSDOS) || defined(_WIN32)
114 sa.append('\"');
115
116 for (const char *s = begin; s < end; ++s)
117 if (isalnum((unsigned char) *s) || *s == '_' || *s == '-' || *s == '+' || *s == '\\' || *s == ':' || *s == '.')
118 /* do nothing */;
119 else if (*s == '\"') {
120 sa.append(begin, s);
121 sa.append("\"\"\"", 3);
122 begin = s + 1;
123 } else {
124 sa.append(begin, s + 1);
125 begin = s + 1;
126 }
127
128 if (sa.length() > 1) {
129 sa.append(begin, end);
130 sa.append('\"');
131 return sa.take_string();
132 }
133 #else
134 for (const char *s = begin; s < end; s++)
135 if (isalnum((unsigned char) *s) || *s == '_' || *s == '-' || *s == '+' || *s == '/' || *s == ':' || *s == '.')
136 /* do nothing */;
137 else {
138 sa.append(begin, s);
139 sa.append('\\');
140 begin = s;
141 }
142
143 if (sa.length()) {
144 sa.append(begin, end);
145 return sa.take_string();
146 }
147 #endif
148
149 return str;
150 }
151
152 int
mysystem(const char * command,ErrorHandler * errh)153 mysystem(const char *command, ErrorHandler *errh)
154 {
155 if (no_create) {
156 errh->message("would run %s", command);
157 return 0;
158 } else {
159 if (verbose)
160 errh->message("running %s", command);
161 return system(command);
162 }
163 }
164
165 #if !HAVE_MKSTEMP
166 static const char *
my_tmpnam()167 my_tmpnam()
168 {
169 # ifndef WIN32
170 return tmpnam(0);
171 # else
172 const char *dir = getenv("TEMP");
173 return _tempnam(dir ? dir : ".", "otftmp.");
174 # endif
175 }
176 #endif
177
178 int
temporary_file(String & filename,ErrorHandler * errh)179 temporary_file(String &filename, ErrorHandler *errh)
180 {
181 if (no_create)
182 return 0; // random number suffices
183
184 #if HAVE_MKSTEMP
185 const char *tmpdir = getenv("TMPDIR");
186 if (tmpdir)
187 filename = String(tmpdir) + "/otftotfm.XXXXXX";
188 else {
189 # ifdef P_tmpdir
190 filename = P_tmpdir "/otftotfm.XXXXXX";
191 # else
192 filename = "/tmp/otftotfm.XXXXXX";
193 # endif
194 }
195 int fd = mkstemp(filename.mutable_c_str());
196 if (fd < 0)
197 errh->error("temporary file %<%s%>: %s", filename.c_str(), strerror(errno));
198 return fd;
199 #else // !HAVE_MKSTEMP
200 for (int tries = 0; tries < 5; tries++) {
201 if (!(filename = my_tmpnam()))
202 return errh->error("cannot create temporary file");
203 # ifdef O_EXCL
204 int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0600);
205 # else
206 int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
207 # endif
208 if (fd >= 0)
209 return fd;
210 }
211 return errh->error("temporary file %<%s%>: %s", filename.c_str(), strerror(errno));
212 #endif
213 }
214
215 bool
parse_unicode_number(const char * begin,const char * end,int require_prefix,uint32_t & result)216 parse_unicode_number(const char* begin, const char* end, int require_prefix, uint32_t& result)
217 {
218 bool allow_lower = (require_prefix == 1);
219 if (require_prefix < 0)
220 /* do not look for prefix */;
221 else if (begin + 7 == end && begin[0] == 'u' && begin[1] == 'n' && begin[2] == 'i')
222 begin += 3;
223 else if (begin + 5 <= end && begin + 7 >= end && begin[0] == 'u')
224 begin++;
225 else if (begin + 3 <= end && begin + 8 >= end && begin[0] == 'U' && begin[1] == '+')
226 begin += 2, allow_lower = true;
227 else if (require_prefix > 1)
228 /* some prefix was required */
229 return false;
230
231 uint32_t value;
232 for (value = 0; begin < end; begin++)
233 if (*begin >= '0' && *begin <= '9')
234 value = (value << 4) | (*begin - '0');
235 else if (*begin >= 'A' && *begin <= 'F')
236 value = (value << 4) | (*begin - 'A' + 10);
237 else if (allow_lower && *begin >= 'a' && *begin <= 'f')
238 value = (value << 4) | (*begin - 'a' + 10);
239 else
240 return false;
241
242 if (value > 0
243 && (value <= 0xD7FF || (value >= 0xE000 && value <= 0x10FFFF))) {
244 result = value;
245 return true;
246 } else
247 return false;
248 }
249
250 #if 0
251 String
252 shell_command_output(String cmdline, const String &input, ErrorHandler *errh, bool strip_newlines)
253 {
254 FILE *f = tmpfile();
255 if (!f)
256 errh->fatal("cannot create temporary file: %s", strerror(errno));
257 ignore_result(fwrite(input.data(), 1, input.length(), f));
258 fflush(f);
259 rewind(f);
260
261 String new_cmdline = cmdline + " 0<&" + String(fileno(f));
262 FILE *p = popen(new_cmdline.c_str(), "r");
263 if (!p)
264 errh->fatal("%<%s%>: %s", cmdline.c_str(), strerror(errno));
265
266 StringAccum sa;
267 int amt;
268 do {
269 if (char *x = sa.reserve(2048)) {
270 amt = fread(x, 1, 2048, p);
271 sa.adjust_length(amt);
272 } else
273 amt = 0;
274 } while (amt != 0 && sa.length() < 200000);
275 if (amt != 0)
276 errh->warning("%<%s%> output too long, truncated", cmdline.c_str());
277 else if (!feof(p) || ferror(p))
278 errh->error("%<%s%>: %s", cmdline.c_str(), strerror(errno));
279
280 fclose(f);
281 pclose(p);
282 while (strip_newlines && sa && (sa.back() == '\n' || sa.back() == '\r'))
283 sa.pop_back();
284 return sa.take_string();
285 }
286 #endif
287