1 /*
2 * util.cpp - miscellaneous support functions.
3 *
4 * Copyright (c) 2004 by Alastair M. Robinson
5 * Distributed under the terms of the GNU General Public License -
6 * see the file named "COPYING" for more details.
7 *
8 */
9
10 #include <iostream>
11 #include <fstream>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <cstring>
15 #include <unistd.h>
16 #include <libgen.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <errno.h>
20
21 #ifdef WIN32
22 #include <windows.h>
23 #endif
24
25 #include "debug.h"
26 #include "searchpath.h"
27 #include "dirtreewalker.h"
28 #include "pathsupport.h"
29
30
31 using namespace std;
32
33
34 // Open a file from a utf-8-encoded filename.
35 // On Unix, this is a straight passthrough to fopen().
36 // On Win32 the filename is will converted to wchar_t, then opened with _wfopen
37 // If either translation or opening the translated filename fails, falls back to using
38 // the untranslated filename. (Should take care of code-page command-line arguments...)
39
FOpenUTF8(const char * name,const char * mode)40 FILE *FOpenUTF8(const char *name,const char *mode)
41 {
42 #ifdef WIN32
43 FILE *result=NULL;
44
45 size_t fnlen=MultiByteToWideChar(CP_UTF8,0,name,-1,NULL,0);
46 size_t modelen=MultiByteToWideChar(CP_UTF8,0,mode,-1,NULL,0);
47
48 if(fnlen && modelen)
49 {
50 wchar_t *fnbuf=(wchar_t *)malloc(fnlen*sizeof(wchar_t));
51 wchar_t *modebuf=(wchar_t *)malloc(modelen*sizeof(wchar_t));
52 if(MultiByteToWideChar(CP_UTF8,0,name,-1,fnbuf,fnlen))
53 {
54 MultiByteToWideChar(CP_UTF8,0,mode,-1,modebuf,modelen);
55
56 result=_wfopen(fnbuf,modebuf);
57
58 }
59 free(modebuf);
60 free(fnbuf);
61 }
62 // If we've not succeeded in opening the file, fall back to regular fopen() -
63 // maybe it's a code-page encoded filename supplied on the command line...
64 if(result)
65 return(result);
66 #endif
67 return(fopen(name,mode));
68 }
69
70
71 // This function is now recursive, so you can provide a pathname of the form
72 // /home/user/obscure1/obscure2/obscure3 - and all three of the last components will
73 // be created if they don't exist already.
74
CreateDirIfNeeded(const char * path)75 bool CreateDirIfNeeded(const char *path)
76 {
77 struct stat s;
78 if(stat(path,&s)==0)
79 return(true);
80
81 if(errno==ENOENT)
82 {
83 char *s2=strdup(path);
84 char *parent=dirname(s2);
85 if(strcmp(parent,".")!=0)
86 CreateDirIfNeeded(parent);
87 free(s2);
88
89 #ifdef WIN32
90 return(mkdir(path)==0);
91 #else
92 return(mkdir(path,0755)==0);
93 #endif
94 }
95 return(false);
96 }
97
98
CheckFileExists(const char * file)99 bool CheckFileExists(const char *file)
100 {
101 struct stat s;
102 return(stat(file,&s)==0);
103 }
104
105
CheckSettingsDir(const char * dirname)106 bool CheckSettingsDir(const char *dirname)
107 {
108 const char *homedir=get_homedir();
109 if(homedir)
110 {
111 char *path=(char *)malloc(strlen(homedir)+strlen(dirname)+2);
112 sprintf(path,"%s%c%s",homedir,SEARCHPATH_SEPARATOR,dirname);
113
114 Debug[TRACE] << "Settings directory: " << path << endl;
115 CreateDirIfNeeded(path);
116
117 free(path);
118 return(true);
119 }
120 else
121 return(false);
122 }
123
124
BuildAbsoluteFilename(const char * fname)125 char *BuildAbsoluteFilename(const char *fname)
126 {
127 char *result=NULL;
128 char cwdbuf[1024];
129 int l;
130
131 if(!(getcwd(cwdbuf,1023)))
132 throw "Can't get curent working directory";
133
134 l=strlen(fname)+strlen(cwdbuf)+3;
135 result=(char *)malloc(l);
136
137 sprintf(result,"%s%c%s",cwdbuf,SEARCHPATH_SEPARATOR,fname);
138 return(result);
139 }
140
141
BuildFilename(const char * root,const char * suffix,const char * fileext)142 char *BuildFilename(const char *root,const char *suffix,const char *fileext)
143 {
144 // Build a filename like <imagename><channel>.<extension>
145 // Must take care not to treat any . characters before the last slash as
146 // a file-extension point!
147
148 char *extension;
149
150 if(!suffix)
151 suffix="";
152
153 char *filename=NULL;
154 if(fileext)
155 filename=(char *)malloc(strlen(root)+strlen(suffix)+strlen(fileext)+3);
156 else
157 filename=(char *)malloc(strlen(root)+strlen(suffix)+3);
158
159 char *root2=strdup(root);
160 extension = root2 + strlen (root2) - 1;
161 while (extension >= root2)
162 {
163 if(*extension == '/' || *extension == '\\')
164 {
165 extension=root2 + strlen(root2);
166 break;
167 }
168 if (*extension == '.')
169 break;
170 extension--;
171 }
172 if (extension >= root2 && fileext && strlen(fileext))
173 {
174 *(extension++) = '\0';
175 sprintf(filename,"%s%s.%s", root2, suffix, fileext);
176 }
177 else
178 {
179 if(extension>=root2)
180 *(extension++) = '\0';
181 sprintf(filename,"%s%s", root2, suffix);
182 }
183 free(root2);
184
185 return(filename);
186 }
187
188
SerialiseFilename(const char * fname,int serialno,int max)189 char *SerialiseFilename(const char *fname,int serialno,int max)
190 {
191 int digits=0;
192 while(max)
193 {
194 ++digits;
195 max/=10;
196 }
197 char *ftmp=strdup(fname);
198 const char *extension="";
199 int idx=strlen(ftmp)-1;
200 while(idx>0)
201 {
202 if(ftmp[idx]=='.')
203 break;
204 --idx;
205 }
206 if(idx)
207 {
208 extension=ftmp+idx+1;
209 ftmp[idx]=0;
210 }
211
212 char *result=(char *)malloc(strlen(ftmp)+strlen(extension)+digits+4);
213 if(digits)
214 sprintf(result,"%s_%0*d.%s",ftmp,digits,serialno,extension);
215 else
216 sprintf(result,"%s_%d.%s",ftmp,serialno,extension);
217 free(ftmp);
218 return(result);
219 }
220
221
222
TestNumeric(char * str)223 int TestNumeric(char *str)
224 {
225 int result=1;
226 int c;
227 while((c=*str++))
228 {
229 if((c<'0')||(c>'9'))
230 result=0;
231 }
232 return(result);
233 }
234
235
TestHostName(char * str,char ** hostname,int * port)236 bool TestHostName(char *str,char **hostname,int *port)
237 {
238 int c;
239 char *src=str;
240 while((c=*src++))
241 {
242 if(c==':')
243 {
244 if(TestNumeric(src))
245 {
246 int hnl=src-str;
247 *port=atoi(src);
248 *hostname=(char *)malloc(hnl+1);
249 strncpy(*hostname,str,hnl);
250 (*hostname)[hnl-1]=0;
251 return(true);
252 }
253 }
254
255 }
256 return(false);
257 }
258
259
CompareFiles(const char * fn1,const char * fn2)260 bool CompareFiles(const char *fn1,const char *fn2)
261 {
262 bool result=true;
263 int l1,l2;
264 char *buf1,*buf2;
265 ifstream i1,i2;
266 i1.open(fn1,ios::binary);
267 i2.open(fn2,ios::binary);
268
269 i1.seekg(0, ios::end);
270 l1= i1.tellg();
271 i1.seekg (0, ios::beg);
272
273 i2.seekg(0, ios::end);
274 l2= i2.tellg();
275 i2.seekg (0, ios::beg);
276
277 if(l1==l2)
278 {
279 buf1 = new char [l1];
280 buf2 = new char [l2];
281
282 i1.read (buf1,l1);
283 i2.read (buf2,l2);
284
285 for(int i=0;i<l1;++i)
286 {
287 if(buf1[i]!=buf2[i])
288 {
289 result=false;
290 i=l1;
291 }
292 }
293 delete[] buf1;
294 delete[] buf2;
295 }
296 else
297 result=false;
298 i1.close();
299 i2.close();
300
301 return(result);
302 }
303
304
305
306 // Given a top-level directory, scans recursively looking for an executable,
307 // and returns its path, minus the executable.
FindParent(std::string initialpath,std::string program)308 std::string FindParent(std::string initialpath, std::string program)
309 {
310 DirTreeWalker dtw(initialpath.c_str());
311 const char *fn;
312 while((fn=dtw.NextFile()))
313 {
314 Debug[TRACE] << "Checking " << fn << std::endl;
315 if(MatchBaseName(program.c_str(),fn)==0)
316 return(dtw);
317 Debug[TRACE] << "Getting next file..." << std::endl;
318 }
319 DirTreeWalker *w;
320 while((w=dtw.NextDirectory()))
321 {
322 Debug[TRACE] << "Recursively scanning: " << *w << std::endl;
323 std::string result=FindParent(*w,program);
324 if(result.size())
325 return(result);
326 Debug[TRACE] << "Getting next dir..." << std::endl;
327 }
328 return("");
329 }
330
331
332
333 // A "safe" version of strdup which returns a valid pointer to an empty string
334 // rather than NULL if src is NULL
335
SafeStrdup(const char * src)336 char *SafeStrdup(const char *src)
337 {
338 if(src)
339 return(strdup(src));
340 else
341 return(strdup(""));
342 }
343
344
345 // A "safe" version of strcat which returns a valid pointer to an empty string
346 // if both parameters are null, a copy of the non-null parameter if the other is
347 // null, or a newly-allocated string containing the concatenated parameters if
348 // both are valid.
349 // In all cases the result is in newly-allocated storage and may (and must!) be
350 // free()ed when no longer needed.
351
SafeStrcat(const char * str1,const char * str2)352 char *SafeStrcat(const char *str1,const char *str2)
353 {
354 if(str1 && str2)
355 {
356 int l=strlen(str1)+strlen(str2)+1;
357 char *result=(char *)malloc(l);
358 sprintf(result,"%s%s",str1,str2);
359 return(result);
360 }
361 if(str1)
362 return(strdup(str1));
363 if(str2)
364 return(strdup(str2));
365 return(strdup(""));
366 }
367
368
369 // Utility function to compare two strings, but ignoring differences
370 // due to spaces.
StrcasecmpIgnoreSpaces(const char * str1,const char * str2)371 int StrcasecmpIgnoreSpaces(const char *str1,const char *str2)
372 {
373 while((*str1!=0) && (*str2!=0))
374 {
375 while(*str1==32)
376 ++str1;
377 while(*str2==32)
378 ++str2;
379 int s1=(*str1++) & ~32;
380 int s2=(*str2++) & ~32;
381 if(s1!=s2)
382 {
383 if(s1<s2)
384 return(-1);
385 else
386 return(1);
387 }
388 }
389 // If we reach here, the strings must be equal.
390 return(0);
391 }
392
393
ShellQuote(std::string & in)394 std::string ShellQuote(std::string &in)
395 {
396 string out("'");
397
398 for(int i=0;i<in.size();++i)
399 {
400 if(in[i]=='\'')
401 out+="'\\''";
402 else
403 out+=in[i];
404 }
405 out+="'";
406 return(out);
407 }
408
ShellQuote(const char * in)409 std::string ShellQuote(const char *in)
410 {
411 string tmp(in);
412 return(ShellQuote(tmp));
413 }
414
415
416 // Dummy class for seeding the random number generator. Declaring a global instance
417 // of this class ensures that the seed is set once and only once for the entire program.
418
419 class RandomSeededClass
420 {
421 public:
RandomSeededClass()422 RandomSeededClass()
423 {
424 srand(time(NULL));
425 }
Random(int max)426 inline int Random(int max)
427 {
428 return(rand() % max);
429 }
430 };
431
432 RandomSeededClass globalrandomseeded;
433
434 // By deferring the call to the class's function we ensure that the global
435 // is instantiated, and thus the seed is set.
436
RandomSeeded(int max)437 int RandomSeeded(int max)
438 {
439 return(globalrandomseeded.Random(max));
440 }
441
442