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