1 /* util.{cc,hh} -- various bits
2  *
3  * Copyright (c) 2003-2019 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 FILE*
mypopen(const char * command,const char * type,ErrorHandler * errh)166 mypopen(const char* command, const char* type, ErrorHandler* errh)
167 {
168     if (no_create) {
169         errh->message("would run %s", command);
170         return popen("true", type);
171     } else {
172         if (verbose)
173             errh->message("running %s", command);
174         return popen(command, type);
175     }
176 }
177 
178 #if !HAVE_MKSTEMP
179 static const char *
my_tmpnam()180 my_tmpnam()
181 {
182 # ifndef WIN32
183     return tmpnam(0);
184 # else
185     const char *dir = getenv("TEMP");
186     return _tempnam(dir ? dir : ".", "otftmp.");
187 # endif
188 }
189 #endif
190 
191 int
temporary_file(String & filename,ErrorHandler * errh)192 temporary_file(String &filename, ErrorHandler *errh)
193 {
194     if (no_create)
195         return 0;               // random number suffices
196 
197 #if HAVE_MKSTEMP
198     const char *tmpdir = getenv("TMPDIR");
199     if (tmpdir)
200         filename = String(tmpdir) + "/otftotfm.XXXXXX";
201     else {
202 # ifdef P_tmpdir
203         filename = P_tmpdir "/otftotfm.XXXXXX";
204 # else
205         filename = "/tmp/otftotfm.XXXXXX";
206 # endif
207     }
208     int fd = mkstemp(filename.mutable_c_str());
209     if (fd < 0)
210         errh->error("temporary file %<%s%>: %s", filename.c_str(), strerror(errno));
211     return fd;
212 #else  // !HAVE_MKSTEMP
213     for (int tries = 0; tries < 5; tries++) {
214         if (!(filename = my_tmpnam()))
215             return errh->error("cannot create temporary file");
216 # ifdef O_EXCL
217         int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0600);
218 # else
219         int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
220 # endif
221         if (fd >= 0)
222             return fd;
223     }
224     return errh->error("temporary file %<%s%>: %s", filename.c_str(), strerror(errno));
225 #endif
226 }
227 
228 bool
parse_unicode_number(const char * begin,const char * end,int require_prefix,uint32_t & result)229 parse_unicode_number(const char* begin, const char* end, int require_prefix, uint32_t& result)
230 {
231     bool allow_lower = (require_prefix == 1);
232     if (require_prefix < 0)
233         /* do not look for prefix */;
234     else if (begin + 7 == end && begin[0] == 'u' && begin[1] == 'n' && begin[2] == 'i')
235         begin += 3;
236     else if (begin + 5 <= end && begin + 7 >= end && begin[0] == 'u')
237         begin++;
238     else if (begin + 3 <= end && begin + 8 >= end && begin[0] == 'U' && begin[1] == '+')
239         begin += 2, allow_lower = true;
240     else if (require_prefix > 1)
241         /* some prefix was required */
242         return false;
243 
244     uint32_t value;
245     for (value = 0; begin < end; begin++)
246         if (*begin >= '0' && *begin <= '9')
247             value = (value << 4) | (*begin - '0');
248         else if (*begin >= 'A' && *begin <= 'F')
249             value = (value << 4) | (*begin - 'A' + 10);
250         else if (allow_lower && *begin >= 'a' && *begin <= 'f')
251             value = (value << 4) | (*begin - 'a' + 10);
252         else
253             return false;
254 
255     if (value > 0
256         && (value <= 0xD7FF || (value >= 0xE000 && value <= 0x10FFFF))) {
257         result = value;
258         return true;
259     } else
260         return false;
261 }
262 
263 #if 0
264 String
265 shell_command_output(String cmdline, const String &input, ErrorHandler *errh, bool strip_newlines)
266 {
267     FILE *f = tmpfile();
268     if (!f)
269         errh->fatal("cannot create temporary file: %s", strerror(errno));
270     ignore_result(fwrite(input.data(), 1, input.length(), f));
271     fflush(f);
272     rewind(f);
273 
274     String new_cmdline = cmdline + " 0<&" + String(fileno(f));
275     FILE *p = popen(new_cmdline.c_str(), "r");
276     if (!p)
277         errh->fatal("%<%s%>: %s", cmdline.c_str(), strerror(errno));
278 
279     StringAccum sa;
280     int amt;
281     do {
282         if (char *x = sa.reserve(2048)) {
283             amt = fread(x, 1, 2048, p);
284             sa.adjust_length(amt);
285         } else
286             amt = 0;
287     } while (amt != 0 && sa.length() < 200000);
288     if (amt != 0)
289         errh->warning("%<%s%> output too long, truncated", cmdline.c_str());
290     else if (!feof(p) || ferror(p))
291         errh->error("%<%s%>: %s", cmdline.c_str(), strerror(errno));
292 
293     fclose(f);
294     pclose(p);
295     while (strip_newlines && sa && (sa.back() == '\n' || sa.back() == '\r'))
296         sa.pop_back();
297     return sa.take_string();
298 }
299 #endif
300