1 //------------------------------------------------------------------------------
2 // emStd2.cpp
3 //
4 // Copyright (C) 2004-2012,2014-2020 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20
21 #if defined(_WIN32)
22 # include <ctype.h>
23 # include <direct.h>
24 # include <io.h>
25 # include <windows.h>
26 # ifndef F_OK
27 # define F_OK 0
28 # endif
29 # ifndef W_OK
30 # define W_OK 2
31 # endif
32 # ifndef R_OK
33 # define R_OK 4
34 # endif
35 #else
36 # include <dirent.h>
37 # include <fcntl.h>
38 # include <pwd.h>
39 # include <sched.h>
40 # include <signal.h>
41 # include <sys/times.h>
42 # include <sys/wait.h>
43 # include <unistd.h>
44 # include <dlfcn.h>
45 #endif
46 #if defined(_MSC_VER)
47 # include <isa_availability.h>
48 extern "C" int __isa_available;
49 #endif
50 #include <emCore/emStd2.h>
51 #include <emCore/emInstallInfo.h>
52 #include <emCore/emThread.h>
53
54
55 //==============================================================================
56 //================================ emException =================================
57 //==============================================================================
58
emException(const char * format,...)59 emException::emException(const char * format, ...)
60 {
61 va_list args;
62
63 va_start(args,format);
64 Text=emString::VFormat(format,args);
65 va_end(args);
66 }
67
68
~emException()69 emException::~emException()
70 {
71 }
72
73
74 //==============================================================================
75 //=========================== Host, user, process id ===========================
76 //==============================================================================
77
emGetHostName()78 emString emGetHostName()
79 {
80 #if defined(_WIN32)
81 char tmp[512];
82 DWORD sz;
83
84 sz=sizeof(tmp)-1;
85 if (!GetComputerName(tmp,&sz)) {
86 emFatalError(
87 "emGetHostName: GetComputerName failed: %s",
88 emGetErrorText(GetLastError()).Get()
89 );
90 }
91 tmp[sizeof(tmp)-1]=0;
92 return emString(tmp);
93 #else
94 char tmp[512];
95
96 if (gethostname(tmp,sizeof(tmp))!=0) {
97 emFatalError(
98 "emGetHostName: gethostname failed: %s",
99 emGetErrorText(errno).Get()
100 );
101 }
102 tmp[sizeof(tmp)-1]=0;
103 return emString(tmp);
104 #endif
105 }
106
107
emGetUserName()108 emString emGetUserName()
109 {
110 #if defined(_WIN32)
111 char tmp[512];
112 DWORD sz;
113
114 sz=sizeof(tmp)-1;
115 if (!GetUserName(tmp,&sz)) {
116 emFatalError(
117 "emGetUserName: GetUserName failed: %s",
118 emGetErrorText(GetLastError()).Get()
119 );
120 }
121 tmp[sizeof(tmp)-1]=0;
122 return emString(tmp);
123 #elif defined(ANDROID)
124 struct passwd * pw;
125 int i;
126
127 errno=0;
128 pw=getpwuid(getuid());
129 if (!pw || !pw->pw_name) {
130 emFatalError(
131 "emGetUserName: getpwuid failed: %s",
132 emGetErrorText(errno).Get()
133 );
134 }
135 return emString(pw->pw_name);
136 #else
137 char tmp[1024];
138 struct passwd pwbuf;
139 struct passwd * pw;
140 int i;
141
142 errno=0;
143 i=getpwuid_r(getuid(),&pwbuf,tmp,sizeof(tmp),&pw);
144 if (i!=0 || !pw || !pw->pw_name) {
145 emFatalError(
146 "emGetUserName: getpwuid_r failed: %s",
147 emGetErrorText(errno).Get()
148 );
149 }
150 return emString(pw->pw_name);
151 #endif
152 }
153
154
emGetProcessId()155 int emGetProcessId()
156 {
157 #if defined(_WIN32)
158 return GetCurrentProcessId();
159 #else
160 return getpid();
161 #endif
162 }
163
164
165 //==============================================================================
166 //================================ Error Texts =================================
167 //==============================================================================
168
169 #if !defined(_WIN32)
emGetErrorText_strerror_r_helper(int res,const char * buf)170 const char * emGetErrorText_strerror_r_helper(int res, const char * buf)
171 {
172 return res==0 ? buf : NULL;
173 }
emGetErrorText_strerror_r_helper(const char * res,const char * buf)174 const char * emGetErrorText_strerror_r_helper(const char * res, const char * buf)
175 {
176 return res;
177 }
178 #endif
179
180
emGetErrorText(int errorNumber)181 emString emGetErrorText(int errorNumber)
182 {
183 #if defined(_WIN32)
184 char tmp[512];
185
186 if (!FormatMessage(
187 FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
188 NULL,
189 (DWORD)errorNumber,
190 0,
191 tmp,
192 sizeof(tmp)-1,
193 NULL
194 )) {
195 sprintf(tmp,"error #%d",errorNumber);
196 }
197 return emString(tmp);
198 #else
199 char tmp[512];
200 const char * p;
201
202 memset(tmp,0,sizeof(tmp));
203 p=emGetErrorText_strerror_r_helper(strerror_r(errorNumber,tmp,sizeof(tmp)),tmp);
204 tmp[sizeof(tmp)-1]=0;
205 if (!p) {
206 sprintf(tmp,"error #%d",errorNumber);
207 p=tmp;
208 }
209 return emString(p);
210 #endif
211 }
212
213
214 //==============================================================================
215 //================================ SIMD support ================================
216 //==============================================================================
217
emCanCpuDoAvx2()218 bool emCanCpuDoAvx2()
219 {
220 static const struct Detector {
221
222 bool canCpuDoAvx2;
223
224 Detector()
225 {
226 # if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
227 # if !defined(__clang__)
228 __builtin_cpu_init();
229 # endif
230 canCpuDoAvx2=
231 __builtin_cpu_supports("avx2") &&
232 __builtin_cpu_supports("avx") &&
233 __builtin_cpu_supports("sse4.1") &&
234 # if !defined(__clang__)
235 __builtin_cpu_supports("ssse3") &&
236 # endif
237 __builtin_cpu_supports("sse3") &&
238 __builtin_cpu_supports("sse2") &&
239 __builtin_cpu_supports("sse") &&
240 __builtin_cpu_supports("mmx")
241 ;
242 # elif defined(_MSC_VER)
243 canCpuDoAvx2=(__isa_available >= __ISA_AVAILABLE_AVX2);
244 # else
245 canCpuDoAvx2=false;
246 # endif
247 }
248
249 } detector;
250
251 return detector.canCpuDoAvx2;
252 }
253
254
255 //==============================================================================
256 //==================================== Time ====================================
257 //==============================================================================
258
emSleepMS(int millisecs)259 void emSleepMS(int millisecs)
260 {
261 #if defined(_WIN32)
262 if (millisecs<0) millisecs=0;
263 Sleep((DWORD)millisecs);
264 #else
265 // usleep(0) is unlike sched_yield() on some systems.
266 if (millisecs<=0) sched_yield();
267 else if ((unsigned long)millisecs>ULONG_MAX/1000) sleep(millisecs/1000);
268 else usleep(((unsigned long)millisecs)*1000);
269 #endif
270 }
271
272
emGetClockMS()273 emUInt64 emGetClockMS()
274 {
275 #if defined(_WIN32)
276 static emThreadMiniMutex mutex;
277 static emUInt64 ms64=0;
278 static DWORD tcks=0;
279 DWORD t;
280 emUInt64 res;
281
282 mutex.Lock();
283 t=GetTickCount();
284 ms64+=(DWORD)(t-tcks);
285 tcks=t;
286 res=ms64;
287 mutex.Unlock();
288 return res;
289 #else
290 static emThreadMiniMutex mutex;
291 static clock_t tcks=0;
292 static unsigned long tps=0;
293 static unsigned long rem=0;
294 static emUInt64 ms64=0;
295 emUInt64 t,res;
296 clock_t d;
297 tms tb;
298
299 mutex.Lock();
300 d=times(&tb)-tcks;
301 if (d) {
302 tcks+=d;
303 if (tps<=0) {
304 tps=(unsigned long)sysconf(_SC_CLK_TCK);
305 if (((long)tps)<=0) {
306 emFatalError("sysconf(_SC_CLK_TCK) failed");
307 }
308 }
309 t=((emUInt64)d)*1000+rem;
310 rem=(unsigned long)(t%tps);
311 ms64+=(emUInt64)(t/tps);
312 }
313 res=ms64;
314 mutex.Unlock();
315 return res;
316 #endif
317 }
318
319
emGetCPUTSC()320 emUInt64 emGetCPUTSC()
321 {
322 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
323 static const struct Detector {
324
325 bool haveRDTSC;
326
327 Detector()
328 {
329 emUInt32 d;
330 asm volatile (
331 # if defined(__x86_64__)
332 "push %%rbx\n"
333 "pushfq\n"
334 "movl (%%rsp),%%eax\n"
335 "movl %%eax,%%ebx\n"
336 "xorl $0x00200000,%%eax\n"
337 "movl %%eax,(%%rsp)\n"
338 "popfq\n"
339 "pushfq\n"
340 "movl (%%rsp),%%eax\n"
341 "xorl %%ebx,%%eax\n"
342 "movl %%ebx,(%%rsp)\n"
343 "popfq\n"
344 # else
345 "push %%ebx\n"
346 "pushfl\n"
347 "popl %%eax\n"
348 "movl %%eax,%%ebx\n"
349 "xorl $0x00200000,%%eax\n"
350 "pushl %%eax\n"
351 "popfl\n"
352 "pushfl\n"
353 "popl %%eax\n"
354 "xorl %%ebx,%%eax\n"
355 "pushl %%ebx\n"
356 "popfl\n"
357 # endif
358 "testl $0x00200000,%%eax\n"
359 "je 3f\n"
360 "xorl %%eax,%%eax\n"
361 "cpuid\n"
362 "cmpl $0x756e6547,%%ebx\n"
363 "jne 1f\n"
364 "cmpl $0x49656e69,%%edx\n"
365 "jne 1f\n"
366 "cmpl $0x6c65746e,%%ecx\n"
367 "je 2f\n"
368 "1:\n"
369 "cmpl $0x68747541,%%ebx\n"
370 "jne 3f\n"
371 "cmpl $0x69746e65,%%edx\n"
372 "jne 3f\n"
373 "cmpl $0x444d4163,%%ecx\n"
374 "jne 3f\n"
375 "2:\n"
376 "movl $1,%%eax\n"
377 "cpuid\n"
378 "jmp 4f\n"
379 "3:\n"
380 "xorl %%edx,%%edx\n"
381 "4:\n"
382 # if defined(__x86_64__)
383 "pop %%rbx\n"
384 # else
385 "pop %%ebx\n"
386 # endif
387 : "=d"(d) : : "eax","ecx","cc"
388 );
389 haveRDTSC=((d>>4)&1)!=0;
390 }
391
392 } detector;
393
394 emUInt32 a,d;
395
396 if (detector.haveRDTSC) {
397 asm volatile (
398 "rdtsc\n"
399 : "=a"(a),"=d"(d) : : "cc"
400 );
401 return (((emUInt64)d)<<32)|a;
402 }
403 #endif
404 return 0;
405 }
406
407
408 //==============================================================================
409 //============================ Files & Directories =============================
410 //==============================================================================
411
emGetParentPath(const char * path)412 emString emGetParentPath(const char * path)
413 {
414 int i;
415
416 i=strlen(path);
417 #if defined(_WIN32)
418 while (i>0 && (path[i-1]=='\\' || path[i-1]=='/' || path[i-1]==':')) i--;
419 while (i>0 && path[i-1]!='\\' && path[i-1]!='/' && path[i-1]!=':') i--;
420 while (i>0 && (path[i-1]=='\\' || path[i-1]=='/' || path[i-1]==':')) i--;
421 if (i<=1) {
422 if (path[0]=='\\') {
423 if (path[1]=='\\') i=2; else i=1;
424 }
425 else if (path[0] && path[1]==':') {
426 if (path[2]=='\\' || path[2]=='/') i=3; else i=2;
427 }
428 }
429 #else
430 while (i>0 && path[i-1]=='/') i--;
431 while (i>0 && path[i-1]!='/') i--;
432 while (i>0 && path[i-1]=='/') i--;
433 if (i==0 && path[0]=='/') i++;
434 #endif
435 return emString(path,i);
436 }
437
438
emGetChildPath(const char * path,const char * name)439 emString emGetChildPath(const char * path, const char * name)
440 {
441 emString subPath;
442 char * subPathPtr;
443 int pathLen,nameLen;
444
445 pathLen=strlen(path);
446 #if defined(_WIN32)
447 if (pathLen>0 && (path[pathLen-1]=='\\' || path[pathLen-1]=='/')) pathLen--;
448 if (name[0]=='\\' || name[0]=='/') name++;
449 if (pathLen==0 && name[0]!=0 && name[1]==':') return emString(name);
450 #else
451 if (pathLen>0 && path[pathLen-1]=='/') pathLen--;
452 if (name[0]=='/') name++;
453 #endif
454 nameLen=strlen(name);
455 subPathPtr=subPath.SetLenGetWritable(pathLen+1+nameLen);
456 memcpy(subPathPtr,path,pathLen);
457 #if defined(_WIN32)
458 subPathPtr[pathLen]='\\';
459 #else
460 subPathPtr[pathLen]='/';
461 #endif
462 memcpy(subPathPtr+pathLen+1,name,nameLen);
463 return subPath;
464 }
465
466
emGetAbsolutePath(const emString & path,const char * cwd)467 emString emGetAbsolutePath(const emString & path, const char * cwd)
468 {
469 emString absPath;
470 const char * p;
471 int i, j;
472 bool cool;
473
474 p=path;
475 #if defined(_WIN32)
476 if (p[0] && p[1]==':') {
477 if (p[2]=='\\' || p[2]=='/') {
478 absPath=path;
479 cool=true;
480 i=3;
481 }
482 else {
483 if (cwd) absPath=cwd;
484 else absPath=emGetCurrentDirectory();
485 if (tolower(absPath[0])!=tolower(p[0]) || absPath[1]!=':') {
486 emFatalError("emGetAbsolutePath impossible for: %s", path.Get());
487 }
488 cool=false;
489 i=2;
490 }
491 }
492 else if (p[0]=='\\' || p[0]=='/') {
493 if (p[1]=='\\' || p[1]=='/') {
494 absPath=path;
495 cool=true;
496 i=2;
497 }
498 else {
499 if (cwd) {
500 if (cwd[0] && cwd[1]==':') {
501 absPath=emString(cwd,2);
502 absPath+="\\";
503 }
504 else {
505 emFatalError("emGetAbsolutePath for %s impossible with cwd %s", path.Get(), cwd);
506 }
507 }
508 else {
509 absPath=emGetCurrentDirectory().GetSubString(0,3);
510 }
511 cool=false;
512 i=1;
513 }
514 }
515 #else
516 if (p[0]=='/') {
517 absPath=path;
518 cool=true;
519 i=1;
520 }
521 #endif
522 else {
523 if (cwd) absPath=cwd;
524 else absPath=emGetCurrentDirectory();
525 cool=false;
526 i=0;
527 }
528 while (p[i]) {
529 #if defined(_WIN32)
530 for (j=i; p[j]!=0 && p[j]!='\\' && p[j]!='/'; j++);
531 #else
532 for (j=i; p[j]!=0 && p[j]!='/'; j++);
533 #endif
534 if (j==i || (j==i+1 && p[i]=='.')) {
535 if (cool) {
536 absPath=emString(p,i);
537 cool=false;
538 }
539 }
540 else if (j==i+2 && p[i]=='.' && p[i+1]=='.') {
541 if (cool) {
542 absPath=emString(p,i);
543 cool=false;
544 }
545 absPath=emGetParentPath(absPath);
546 }
547 else if (!cool) {
548 absPath=emGetChildPath(absPath,emString(p+i,j-i));
549 }
550 if (!p[j]) break;
551 i=j+1;
552 }
553
554 return absPath;
555 }
556
557
emGetNameInPath(const char * path)558 const char * emGetNameInPath(const char * path)
559 {
560 int i;
561
562 i=strlen(path);
563 #if defined(_WIN32)
564 while (i>0 && (path[i-1]=='\\' || path[i-1]=='/' || path[i-1]==':')) i--;
565 while (i>0 && path[i-1]!='\\' && path[i-1]!='/' && path[i-1]!=':') i--;
566 #else
567 while (i>0 && path[i-1]=='/') i--;
568 while (i>0 && path[i-1]!='/') i--;
569 #endif
570 return path+i;
571 }
572
573
emGetExtensionInPath(const char * path)574 const char * emGetExtensionInPath(const char * path)
575 {
576 const char * name, * end, * p;
577
578 name=emGetNameInPath(path);
579 end=name+strlen(name);
580 p=end;
581 while (p>name && *p!='.') p--;
582 if (*p!='.') p=end;
583 return p;
584 }
585
586
emIsExistingPath(const char * path)587 bool emIsExistingPath(const char * path)
588 {
589 return access(path,F_OK)==0;
590 }
591
592
emIsReadablePath(const char * path)593 bool emIsReadablePath(const char * path)
594 {
595 return access(path,R_OK)==0;
596 }
597
598
emIsWritablePath(const char * path)599 bool emIsWritablePath(const char * path)
600 {
601 return access(path,W_OK)==0;
602 }
603
604
emIsDirectory(const char * path)605 bool emIsDirectory(const char * path)
606 {
607 struct em_stat st;
608
609 #if defined(_WIN32)
610 int i=strlen(path);
611 while (i>0 && (path[i-1]=='\\' || path[i-1]=='/')) i--;
612 if (
613 i==2 && path[1]==':' && (
614 (path[0]>='A' && path[0]<='Z') ||
615 (path[0]>='a' && path[0]<='z')
616 )
617 ) return true;
618 #endif
619
620 if (em_stat(path,&st)!=0) return false;
621 if ((st.st_mode&S_IFMT)!=S_IFDIR) return false;
622 return true;
623 }
624
625
emIsRegularFile(const char * path)626 bool emIsRegularFile(const char * path)
627 {
628 struct em_stat st;
629
630 if (em_stat(path,&st)!=0) return false;
631 if ((st.st_mode&S_IFMT)!=S_IFREG) return false;
632 return true;
633 }
634
635
emIsSymLinkPath(const char * path)636 bool emIsSymLinkPath(const char * path)
637 {
638 #if defined(_WIN32)
639 return false;
640 #else
641 struct em_stat st;
642
643 if (em_lstat(path,&st)!=0) return false;
644 if ((st.st_mode&S_IFMT)!=S_IFLNK) return false;
645 return true;
646 #endif
647 }
648
649
emIsHiddenPath(const char * path)650 bool emIsHiddenPath(const char * path)
651 {
652 #if defined(_WIN32)
653 DWORD d;
654
655 d=GetFileAttributes(path);
656 return d!=0xFFFFFFFF && (d&FILE_ATTRIBUTE_HIDDEN)!=0;
657 #else
658 return emGetNameInPath(path)[0] == '.';
659 #endif
660 }
661
662
emTryGetFileSize(const char * path)663 emUInt64 emTryGetFileSize(const char * path)
664 {
665 struct em_stat st;
666
667 if (em_stat(path,&st)!=0) {
668 throw emException(
669 "Failed to get size of \"%s\": %s",
670 path,
671 emGetErrorText(errno).Get()
672 );
673 }
674 return st.st_size;
675 }
676
677
emTryGetFileTime(const char * path)678 time_t emTryGetFileTime(const char * path)
679 {
680 struct em_stat st;
681
682 if (em_stat(path,&st)!=0) {
683 throw emException(
684 "Failed to get modification time of \"%s\": %s",
685 path,
686 emGetErrorText(errno).Get()
687 );
688 }
689 return st.st_mtime;
690 }
691
692
emGetCurrentDirectory()693 emString emGetCurrentDirectory()
694 {
695 char tmp[1024];
696
697 if (getcwd(tmp,sizeof(tmp))==NULL) {
698 emFatalError("getcwd failed: %s",emGetErrorText(errno).Get());
699 }
700 return emString(tmp);
701 }
702
703
704 #if defined(_WIN32)
705 struct emDirHandleContent {
706 HANDLE handle;
707 WIN32_FIND_DATA data;
708 bool first;
709 };
710 #endif
711
712
emTryOpenDir(const char * path)713 emDirHandle emTryOpenDir(const char * path)
714 {
715 #if defined(_WIN32)
716 emDirHandleContent * hc;
717 DWORD d;
718
719 hc=new emDirHandleContent;
720 hc->handle=FindFirstFile(
721 emGetChildPath(path,"*.*"),
722 &hc->data
723 );
724 if (hc->handle==INVALID_HANDLE_VALUE) {
725 d=GetLastError();
726 if (d!=ERROR_NO_MORE_FILES) {
727 delete hc;
728 throw emException(
729 "Failed to read directory \"%s\": %s",
730 path,
731 emGetErrorText(d).Get()
732 );
733 }
734 }
735 hc->first=true;
736 return hc;
737 #else
738 DIR * dir;
739
740 dir=opendir(path);
741 if (!dir) {
742 throw emException(
743 "Failed to read directory \"%s\": %s",
744 path,
745 emGetErrorText(errno).Get()
746 );
747 }
748
749 return (emDirHandle)dir;
750 #endif
751 }
752
753
emTryReadDir(emDirHandle dirHandle)754 emString emTryReadDir(emDirHandle dirHandle)
755 {
756 #if defined(_WIN32)
757 emDirHandleContent * hc;
758
759 hc=(emDirHandleContent*)dirHandle;
760 for (;;) {
761 if (hc->handle==INVALID_HANDLE_VALUE) return emString();
762 if (hc->first) {
763 hc->first=false;
764 }
765 else {
766 if (!FindNextFile(hc->handle,&hc->data)) {
767 if (GetLastError()!=ERROR_NO_MORE_FILES) {
768 throw emException(
769 "Failed to read directory: %s",
770 emGetErrorText(GetLastError()).Get()
771 );
772 }
773 FindClose(hc->handle);
774 hc->handle=INVALID_HANDLE_VALUE;
775 return emString();
776 }
777 }
778 if (
779 hc->data.cFileName[0] &&
780 strcmp(hc->data.cFileName,".")!=0 &&
781 strcmp(hc->data.cFileName,"..")!=0
782 ) return emString(hc->data.cFileName);
783 }
784 #else
785 struct dirent * de;
786 emString res;
787
788 for (;;) {
789 errno=0;
790 de=readdir((DIR*)dirHandle);
791 if (!de) {
792 if (errno) {
793 throw emException(
794 "Failed to read directory: %s",
795 emGetErrorText(errno).Get()
796 );
797 }
798 break;
799 }
800 if (
801 de->d_name[0] &&
802 strcmp(de->d_name,".")!=0 &&
803 strcmp(de->d_name,"..")!=0
804 ) {
805 res=emString(de->d_name);
806 break;
807 }
808 }
809 return res;
810 #endif
811 }
812
813
emCloseDir(emDirHandle dirHandle)814 void emCloseDir(emDirHandle dirHandle)
815 {
816 #if defined(_WIN32)
817 emDirHandleContent * hc;
818
819 hc=(emDirHandleContent*)dirHandle;
820 if (hc->handle!=INVALID_HANDLE_VALUE) {
821 FindClose(hc->handle);
822 }
823 delete hc;
824 #else
825 closedir((DIR*)dirHandle);
826 #endif
827 }
828
829
emTryLoadDir(const char * path)830 emArray<emString> emTryLoadDir(const char * path)
831 {
832 emDirHandle dirHandle;
833 emArray<emString> names;
834 emString name;
835
836 names.SetTuningLevel(1);
837 dirHandle=emTryOpenDir(path);
838 for (;;) {
839 try {
840 name=emTryReadDir(dirHandle);
841 }
842 catch (const emException & exception) {
843 emCloseDir(dirHandle);
844 throw exception;
845 }
846 if (name.IsEmpty()) break;
847 names+=name;
848 }
849 emCloseDir(dirHandle);
850 names.Compact();
851 return names;
852 }
853
854
emTryLoadFile(const char * path)855 emArray<char> emTryLoadFile(const char * path)
856 {
857 emArray<char> buf;
858 FILE * f;
859 emInt64 l;
860 int i,j;
861
862 buf.SetTuningLevel(4);
863 f=fopen(path,"rb");
864 if (!f) goto L_Err;
865 if (fseek(f,0,SEEK_END)!=0) goto L_Err;
866 l=ftell(f);
867 if (l<0) goto L_Err;
868 if (l>INT_MAX) { errno=EFBIG; goto L_Err; }
869 buf.SetCount((int)l,true);
870 if (fseek(f,0,SEEK_SET)!=0) goto L_Err;
871 for (i=0; i<buf.GetCount(); i+=j) {
872 j=fread(buf.GetWritable()+i,1,buf.GetCount()-i,f);
873 if (j<=0) goto L_Err;
874 }
875 fclose(f);
876 return buf;
877 L_Err:
878 if (f) fclose(f);
879 throw emException(
880 "Failed to read file \"%s\": %s",
881 path,
882 emGetErrorText(errno).Get()
883 );
884 }
885
886
emTrySaveFile(const char * path,const char * data,int len)887 void emTrySaveFile(const char * path, const char * data, int len)
888 {
889 FILE * f;
890 int i;
891
892 f=fopen(path,"wb");
893 if (!f) goto L_Err;
894 while (len>0) {
895 i=fwrite(data,1,len,f);
896 if (i<=0) goto L_Err;
897 data+=i;
898 len-=i;
899 }
900 fclose(f);
901 return;
902 L_Err:
903 if (f) fclose(f);
904 throw emException(
905 "Failed to write file \"%s\": %s",
906 path,
907 emGetErrorText(errno).Get()
908 );
909 }
910
911
emTrySaveFile(const char * path,const emArray<char> & data)912 void emTrySaveFile(const char * path, const emArray<char> & data)
913 {
914 emTrySaveFile(path,data,data.GetCount());
915 }
916
917
emTryMakeDirectories(const char * path,int mode)918 void emTryMakeDirectories(const char * path, int mode)
919 {
920 emString parentPath;
921
922 if (*path && access(path,F_OK)!=0) {
923 parentPath=emGetParentPath(path);
924 if (parentPath!=path) emTryMakeDirectories(parentPath,mode);
925 #if defined(_WIN32)
926 if (mkdir(path)!=0) {
927 #else
928 if (mkdir(path,mode)!=0) {
929 #endif
930 throw emException(
931 "Failed to create directory \"%s\": %s",
932 path,
933 emGetErrorText(errno).Get()
934 );
935 }
936 }
937 }
938
939
940 void emTryRemoveFile(const char * path)
941 {
942 if (!*path) {
943 throw emException("Cannot to remove file: empty path");
944 }
945 if (unlink(path)!=0) {
946 throw emException(
947 "Failed to remove \"%s\": %s",
948 path,
949 emGetErrorText(errno).Get()
950 );
951 }
952 }
953
954
955 void emTryRemoveDirectory(const char * path)
956 {
957 if (!*path) {
958 throw emException("Cannot to remove directory: empty path");
959 }
960 if (rmdir(path)!=0) {
961 throw emException(
962 "Failed to remove directory \"%s\": %s",
963 path,
964 emGetErrorText(errno).Get()
965 );
966 }
967 }
968
969
970 #ifdef _WIN32
971 static emString emW2A(const wchar_t * w)
972 {
973 emString str;
974 char * p;
975 int i,l;
976
977 l=WideCharToMultiByte(CP_ACP,0,w,-1,NULL,0,NULL,NULL);
978 if (l>0) {
979 p=str.SetLenGetWritable(l);
980 l=WideCharToMultiByte(CP_ACP,0,w,-1,p,l,NULL,NULL);
981 }
982 if (l<=0) {
983 str.Clear();
984 for (i=0; w[i]; i++) str.Add(w[i]>=32 && w[i]<=126 ? (char)w[i] : '?');
985 }
986 return str;
987 }
988 #endif
989
990
991 #ifdef _WIN32
992 static void emTryRemoveFileOrTreeW(const wchar_t * path, bool force)
993 {
994 emArray<wchar_t> subPath;
995 WIN32_FIND_DATAW data;
996 HANDLE h;
997 DWORD d;
998
999 if (!*path) {
1000 throw emException("Cannot remove file or directory: empty path");
1001 }
1002
1003 d=GetFileAttributesW(path);
1004 if (d==INVALID_FILE_ATTRIBUTES) {
1005 // Yes, even do not simply return successful on ERROR_FILE_NOT_FOUND,
1006 // because we don't want to miss path conversion errors.
1007 throw emException(
1008 "Failed to get file attributes of \"%s\": %s",
1009 emW2A(path).Get(),
1010 emGetErrorText(GetLastError()).Get()
1011 );
1012 }
1013 if (force && (d&FILE_ATTRIBUTE_READONLY)!=0) {
1014 SetFileAttributesW(path,d&~FILE_ATTRIBUTE_READONLY);
1015 }
1016 if (d&FILE_ATTRIBUTE_DIRECTORY) {
1017 subPath=emArray<wchar_t>(path,lstrlenW(path));
1018 subPath.Add(L"\\*.*\0",5);
1019 h=FindFirstFileW(subPath.Get(),&data);
1020 if (h==INVALID_HANDLE_VALUE) {
1021 d=GetLastError();
1022 if (d!=ERROR_NO_MORE_FILES) {
1023 throw emException(
1024 "Failed to read directory \"%s\": %s",
1025 emW2A(path).Get(),
1026 emGetErrorText(d).Get()
1027 );
1028 }
1029 }
1030 else {
1031 do {
1032 if (
1033 data.cFileName[0] &&
1034 lstrcmpW(data.cFileName,L".")!=0 &&
1035 lstrcmpW(data.cFileName,L"..")!=0
1036 ) {
1037 subPath=emArray<wchar_t>(path,lstrlenW(path));
1038 subPath.Add(L"\\",1);
1039 subPath.Add(data.cFileName,lstrlenW(data.cFileName)+1);
1040 emTryRemoveFileOrTreeW(subPath,force);
1041 }
1042 } while(FindNextFileW(h,&data));
1043 if (GetLastError()!=ERROR_NO_MORE_FILES) {
1044 FindClose(h);
1045 throw emException(
1046 "Failed to read directory: %s",
1047 emGetErrorText(GetLastError()).Get()
1048 );
1049 }
1050 FindClose(h);
1051 }
1052 if (!RemoveDirectoryW(path)) {
1053 throw emException(
1054 "Failed to remove directory \"%s\": %s",
1055 emW2A(path).Get(),
1056 emGetErrorText(GetLastError()).Get()
1057 );
1058 }
1059 }
1060 else {
1061 if (!DeleteFileW(path)) {
1062 throw emException(
1063 "Failed to remove \"%s\": %s",
1064 emW2A(path).Get(),
1065 emGetErrorText(GetLastError()).Get()
1066 );
1067 }
1068 }
1069 }
1070 #endif
1071
1072
1073 void emTryRemoveFileOrTree(const char * path, bool force)
1074 {
1075 #ifdef _WIN32
1076 emArray<wchar_t> wPath;
1077 emString absPath;
1078 int i,l;
1079
1080 // For the (indirect) use by emTmpConv, removal of trees must be quite
1081 // reliable. But on Windows, "normal" removal fails if:
1082 // - A path is longer than 260 bytes.
1083 // - A path contains Unicode characters not in the active code page.
1084 // - A file name equals a device name (aux, con, com1, com2, ...).
1085 // All these problems can be solved by using wide char functions and
1086 // prefixing the absolute path with "\\?\". But care must be taken
1087 // because "\\?\" brings some certain behavior:
1088 // - Slashes are not implicitly converted to backslashes.
1089 // - "." and ".." are losing their meaning.
1090 // - File names can contain characters like "<", ">" and "|"
1091 // (Hint: There is the alternative prefix "\\.\", but that one does not
1092 // help with long paths.)
1093
1094 static const struct VersionTester {
1095 bool result;
1096 VersionTester()
1097 {
1098 // From which Windows version on is the \\?\ path prefix
1099 // supported? I could not find it out, but I could test
1100 // XP (5.1).
1101 static const unsigned long minWinVer[2] = { 5, 1 };
1102 OSVERSIONINFO osvi;
1103 memset(&osvi,0,sizeof(osvi));
1104 osvi.dwOSVersionInfoSize=sizeof(osvi);
1105 GetVersionEx(&osvi);
1106 result=
1107 osvi.dwMajorVersion > minWinVer[0] || (
1108 osvi.dwMajorVersion == minWinVer[0] &&
1109 osvi.dwMinorVersion >= minWinVer[1]
1110 )
1111 ;
1112 }
1113 } versionTester;
1114
1115 absPath=emGetAbsolutePath(path);
1116
1117 if (versionTester.result) {
1118 if (absPath[0] && absPath[1]==':' && (absPath[2]=='\\' || absPath[2]=='/')) {
1119 for (i=0; absPath[i]; i++) {
1120 if (absPath[i]=='/') absPath.Replace(i,1,"\\");
1121 }
1122 absPath.Insert(0,"\\\\?\\");
1123 }
1124 }
1125
1126 SetLastError(ERROR_SUCCESS);
1127 l=MultiByteToWideChar(CP_ACP,0,absPath.Get(),-1,NULL,0);
1128 if (l>0) {
1129 wPath.SetCount(l+1);
1130 wPath.Set(l,0);
1131 l=MultiByteToWideChar(CP_ACP,0,absPath.Get(),-1,wPath.GetWritable(),l);
1132 }
1133 if (l<=0 || GetLastError()!=ERROR_SUCCESS) {
1134 throw emException(
1135 "Failed to convert path \"%s\" to wide char: %s",
1136 absPath.Get(),emGetErrorText(GetLastError()).Get()
1137 );
1138 }
1139
1140 emTryRemoveFileOrTreeW(wPath,force);
1141
1142 #else
1143
1144 emArray<emString> list;
1145 struct em_stat st;
1146 int i;
1147
1148 if (!*path) {
1149 throw emException("Cannot remove file or directory: empty path");
1150 }
1151
1152 if (em_lstat(path,&st)!=0) {
1153 throw emException(
1154 "Failed to get file information of \"%s\": %s",
1155 path,
1156 emGetErrorText(errno).Get()
1157 );
1158 }
1159
1160 if ((st.st_mode&S_IFMT)==S_IFDIR) {
1161 if (force && (st.st_mode&0700)!=0700) {
1162 chmod(path,(st.st_mode&07777)|0700);
1163 }
1164 list=emTryLoadDir(path);
1165 for (i=0; i<list.GetCount(); i++) {
1166 emTryRemoveFileOrTree(emGetChildPath(path,list[i]),force);
1167 }
1168 if (rmdir(path)!=0) {
1169 throw emException(
1170 "Failed to remove directory \"%s\": %s",
1171 path,
1172 emGetErrorText(errno).Get()
1173 );
1174 }
1175 }
1176 else {
1177 if (unlink(path)!=0) {
1178 throw emException(
1179 "Failed to remove \"%s\": %s",
1180 path,
1181 emGetErrorText(errno).Get()
1182 );
1183 }
1184 }
1185 #endif
1186 }
1187
1188
1189 void emTryCopyFileOrTree(const char * targetPath, const char * sourcePath)
1190 {
1191 emDirHandle dh;
1192 emString nm;
1193 FILE * sf, * tf;
1194 char buf[8192];
1195 int bufFill,len;
1196
1197 if (emIsDirectory(sourcePath)) {
1198 emTryMakeDirectories(targetPath);
1199 dh=emTryOpenDir(sourcePath);
1200 try {
1201 for (;;) {
1202 nm=emTryReadDir(dh);
1203 if (nm.IsEmpty()) break;
1204 emTryCopyFileOrTree(
1205 emGetChildPath(targetPath,nm),
1206 emGetChildPath(sourcePath,nm)
1207 );
1208 }
1209 }
1210 catch (const emException & exception) {
1211 emCloseDir(dh);
1212 throw exception;
1213 }
1214 emCloseDir(dh);
1215 }
1216 else if (emIsExistingPath(targetPath)) {
1217 errno=EEXIST;
1218 goto L_Throw_per_errno;
1219 }
1220 else {
1221 sf=fopen(sourcePath,"rb");
1222 if (!sf) {
1223 goto L_Throw_per_errno;
1224 }
1225 tf=fopen(targetPath,"wb");
1226 if (!tf) {
1227 fclose(sf);
1228 goto L_Throw_per_errno;
1229 }
1230 bufFill=0;
1231 do {
1232 if (sf) {
1233 len=sizeof(buf)-bufFill;
1234 if (len>0) {
1235 len=fread(buf+bufFill,1,len,sf);
1236 if (ferror(sf)) {
1237 fclose(sf);
1238 fclose(tf);
1239 goto L_Throw_per_errno;
1240 }
1241 if (feof(sf)) {
1242 fclose(sf);
1243 sf=NULL;
1244 }
1245 bufFill+=len;
1246 }
1247 }
1248 if (bufFill>0) {
1249 len=fwrite(buf,1,bufFill,tf);
1250 if (ferror(tf)) {
1251 if (sf) fclose(sf);
1252 fclose(tf);
1253 goto L_Throw_per_errno;
1254 }
1255 bufFill-=len;
1256 if (bufFill>0) memmove(buf,buf+len,bufFill);
1257 }
1258 } while (sf || bufFill>0);
1259 fclose(tf);
1260 }
1261 return;
1262
1263 L_Throw_per_errno:
1264 throw emException(
1265 "Failed to copy \"%s\" to \"%s\": %s",
1266 sourcePath,
1267 targetPath,
1268 emGetErrorText(errno).Get()
1269 );
1270 }
1271
1272
1273 //==============================================================================
1274 //======================== Accessing Dynamic Libraries =========================
1275 //==============================================================================
1276
1277 struct emLibTableEntry {
1278 emString Filename;
1279 emUInt64 RefCount; // Zero means infinite.
1280 #if defined(_WIN32)
1281 HMODULE HModule;
1282 #else
1283 void * DLHandle;
1284 #endif
1285 };
1286
1287 static emThreadMiniMutex emLibTableMutex;
1288 static emArray<emLibTableEntry*> emLibTable;
1289
1290
1291 static int emCompareLibEntryFilename(
1292 emLibTableEntry * const * entry, void * filename, void * context
1293 )
1294 {
1295 return strcmp((*entry)->Filename.Get(),(const char*)filename);
1296 }
1297
1298
1299 emLibHandle emTryOpenLib(const char * libName, bool isFilename)
1300 {
1301 emLibTableEntry * e;
1302 emString filename;
1303 int idx;
1304 #if defined(_WIN32)
1305 HMODULE hModule;
1306 #else
1307 void * dlHandle;
1308 #endif
1309
1310 if (isFilename) {
1311 filename=libName;
1312 }
1313 else {
1314 #if defined(_WIN32) || defined(__CYGWIN__)
1315 filename=emString::Format("%s.dll",libName);
1316 #elif defined(__APPLE__)
1317 filename=emString::Format("lib%s.dylib",libName);
1318 #else
1319 filename=emString::Format("lib%s.so",libName);
1320 #endif
1321 }
1322
1323 emLibTableMutex.Lock();
1324
1325 idx=emLibTable.BinarySearchByKey(
1326 (void*)filename.Get(),
1327 emCompareLibEntryFilename
1328 );
1329 if (idx>=0) {
1330 e=emLibTable[idx];
1331 if (e->RefCount) e->RefCount++;
1332 emLibTableMutex.Unlock();
1333 return e;
1334 }
1335 #if defined(_WIN32)
1336 hModule=LoadLibrary(filename.Get());
1337 if (!hModule) {
1338 emLibTableMutex.Unlock();
1339 throw emException(
1340 "Failed to load library \"%s\": %s",
1341 filename.Get(),
1342 emGetErrorText(GetLastError()).Get()
1343 );
1344 }
1345 #else
1346 dlHandle=dlopen(filename,RTLD_NOW|RTLD_GLOBAL);
1347 if (!dlHandle) {
1348 emLibTableMutex.Unlock();
1349 # if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)
1350 throw emException("%s",dlerror());
1351 # else
1352 throw emException(
1353 "Failed to load library \"%s\": %s",
1354 filename.Get(),
1355 dlerror()
1356 );
1357 # endif
1358 }
1359 #endif
1360 e=new emLibTableEntry;
1361 e->Filename=filename;
1362 e->RefCount=1;
1363 #if defined(_WIN32)
1364 e->HModule=hModule;
1365 #else
1366 e->DLHandle=dlHandle;
1367 #endif
1368 emLibTable.Insert(~idx,e);
1369
1370 filename.Clear();
1371 e->Filename.MakeNonShared();
1372
1373 emLibTableMutex.Unlock();
1374 return e;
1375 }
1376
1377
1378 void * emTryResolveSymbolFromLib(emLibHandle handle, const char * symbol)
1379 {
1380 emLibTableEntry * e;
1381 void * r;
1382 #if defined(_WIN32)
1383 #else
1384 const char * err;
1385 #endif
1386
1387 e=(emLibTableEntry*)handle;
1388 #if defined(_WIN32)
1389 r=(void*)GetProcAddress(e->HModule,symbol);
1390 if (!r) {
1391 throw emException(
1392 "Failed to get address of \"%s\" in \"%s\": %s",
1393 symbol,
1394 e->Filename.Get(),
1395 emGetErrorText(GetLastError()).Get()
1396 );
1397 }
1398 #else
1399 dlerror();
1400 r=dlsym(e->DLHandle,symbol);
1401 err=dlerror();
1402 if (err) {
1403 # if defined(ANDROID)
1404 throw emException(
1405 "Failed to get address of \"%s\" in \"%s\".",
1406 symbol,
1407 e->Filename.Get()
1408 );
1409 # elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)
1410 throw emException("%s",err);
1411 # else
1412 throw emException(
1413 "Failed to get address of \"%s\" in \"%s\": %s",
1414 symbol,
1415 e->Filename.Get(),
1416 err
1417 );
1418 # endif
1419 }
1420 #endif
1421 return r;
1422 }
1423
1424
1425 void emCloseLib(emLibHandle handle)
1426 {
1427 emLibTableEntry * e;
1428
1429 emLibTableMutex.Lock();
1430 e=(emLibTableEntry*)handle;
1431 if (e->RefCount && !e->RefCount--) {
1432 #if defined(_WIN32)
1433 FreeLibrary(e->HModule);
1434 e->HModule=NULL;
1435 #else
1436 dlclose(e->DLHandle);
1437 e->DLHandle=NULL;
1438 #endif
1439 emLibTable.BinaryRemoveByKey(
1440 (void*)e->Filename.Get(),
1441 emCompareLibEntryFilename
1442 );
1443 delete e;
1444 }
1445 emLibTableMutex.Unlock();
1446 }
1447
1448
1449 void * emTryResolveSymbol(
1450 const char * libName, bool isFilename, const char * symbol
1451 )
1452 {
1453 emLibTableEntry * e;
1454 void * r;
1455
1456 e=(emLibTableEntry*)emTryOpenLib(libName,isFilename);
1457 r=emTryResolveSymbolFromLib(e,symbol);
1458 emLibTableMutex.Lock();
1459 e->RefCount=0;
1460 emLibTableMutex.Unlock();
1461 return r;
1462 }
1463
1464
1465 //==============================================================================
1466 //=========================== Pseudo Random Numbers ============================
1467 //==============================================================================
1468
1469 int emGetIntRandom(int minimum, int maximum)
1470 {
1471 return (int)emGetInt64Random(minimum,maximum);
1472 }
1473
1474
1475 unsigned int emGetUIntRandom(unsigned int minimum, unsigned int maximum)
1476 {
1477 return (unsigned int)emGetUInt64Random(minimum,maximum);
1478 }
1479
1480
1481 emInt64 emGetInt64Random(emInt64 minimum, emInt64 maximum)
1482 {
1483 return (emInt64)(emGetUInt64Random(
1484 ((emUInt64)minimum)^(((emUInt64)1)<<63),
1485 ((emUInt64)maximum)^(((emUInt64)1)<<63)
1486 )^(((emUInt64)1)<<63));
1487 }
1488
1489
1490 emUInt64 emGetUInt64Random(emUInt64 minimum, emUInt64 maximum)
1491 {
1492 static emUInt32 seedLo=0x302D9934U;
1493 static emUInt32 seedHi=0xD5441C6EU;
1494 static emUInt32 count=0;
1495 emUInt32 a,b,c;
1496 emUInt64 r;
1497
1498 if (!count) {
1499 a=(emUInt32)time(NULL);
1500 b=(emUInt32)emGetClockMS();
1501 c=(emUInt32)emGetProcessId();
1502 seedLo^=(a+b*1321+c*1231277)*0x106F68F6U+0x0723BF76U;
1503 seedHi^=(a*9601769+b*5099+c)*0xA0ECFAC5U+0x1840E54BU;
1504 }
1505 count++;
1506 seedLo=seedLo*0xC78D632DU+0xBDFAAE3BU;
1507 seedHi=seedHi*0xAC7D7A21U+0x2FF59947U;
1508 r=maximum-minimum+1;
1509 if (r>(emUInt64)0xffffffff) r=((((emUInt64)seedHi)<<32)|seedLo)%r;
1510 else if (r) r=((seedLo>>16)^seedHi)%((emUInt32)r);
1511 else r=(((emUInt64)seedHi)<<32)|seedLo;
1512 return r+minimum;
1513 }
1514
1515
1516 double emGetDblRandom(double minimum, double maximum)
1517 {
1518 return emGetUInt64Random(
1519 0,(emUInt64)(emInt64)-1
1520 )/((double)(emUInt64)(emInt64)-1)*(maximum-minimum)+minimum;
1521 }
1522
1523
1524 //==============================================================================
1525 //========================== Checksums and hash codes ==========================
1526 //==============================================================================
1527
1528 emUInt32 emCalcAdler32(const char * src, int srcLen, emUInt32 start)
1529 {
1530 const char * brk, * end;
1531 emUInt32 lo, hi;
1532
1533 lo=start&0xffff;
1534 hi=start>>16;
1535 end=src+srcLen;
1536 while (src<end) {
1537 if (end-src<=5552) brk=end;
1538 else brk=src+5552;
1539 do {
1540 lo+=(emUInt8)*src++;
1541 hi+=lo;
1542 } while(src<brk);
1543 lo%=65521;
1544 hi%=65521;
1545 }
1546 return (hi<<16)|lo;
1547 }
1548
1549
1550 emUInt32 emCalcCRC32(const char * src, int srcLen, emUInt32 start)
1551 {
1552 static const struct CRC32Table {
1553
1554 emUInt32 tab[256];
1555
1556 CRC32Table()
1557 {
1558 emUInt32 r;
1559 int i,j;
1560 for (i=0; i<256; i++) {
1561 for (r=i, j=0; j<8; j++) {
1562 if ((r&1)!=0) r=(r>>1)^0xEDB88320; else r>>=1;
1563 }
1564 tab[i]=r;
1565 }
1566 }
1567
1568 } crc32Table;
1569
1570 const char * end;
1571 emUInt32 r;
1572
1573 r=start;
1574 if (srcLen>0) {
1575 r=~r;
1576 end=src+srcLen;
1577 do {
1578 r=crc32Table.tab[((emUInt8)*src++)^((emUInt8)r)]^(r>>8);
1579 } while (src<end);
1580 r=~r;
1581 }
1582 return r;
1583 }
1584
1585
1586 emUInt64 emCalcCRC64(const char * src, int srcLen, emUInt64 start)
1587 {
1588 static const struct CRC64Table {
1589
1590 emUInt64 tab[256];
1591
1592 CRC64Table()
1593 {
1594 emUInt64 r;
1595 int i,j;
1596 for (i=0; i<256; i++) {
1597 for (r=i, j=0; j<8; j++) {
1598 if ((r&1)!=0) r=(r>>1)^(((emUInt64)0xD8000000)<<32);
1599 else r>>=1;
1600 }
1601 tab[i]=r;
1602 }
1603 }
1604
1605 } crc64Table;
1606
1607 const char * end;
1608 emUInt64 r;
1609
1610 r=start;
1611 if (srcLen>0) {
1612 r=~r;
1613 end=src+srcLen;
1614 do {
1615 r=crc64Table.tab[((emUInt8)*src++)^((emUInt8)r)]^(r>>8);
1616 } while (src<end);
1617 r=~r;
1618 }
1619 return r;
1620 }
1621
1622
1623 int emCalcHashCode(const char * str, int start)
1624 {
1625 unsigned int r,c;
1626
1627 r=(unsigned int)start;
1628 c=(unsigned char)*str++;
1629 if (c) {
1630 do {
1631 r=r*335171+c;
1632 c=(unsigned char)*str++;
1633 } while (c);
1634 }
1635 return (int)r;
1636 }
1637
1638
1639 emString emCalcHashName(const char * src, int srcLen, int hashLen)
1640 {
1641 emString hash;
1642 char * hashPtr;
1643 unsigned int a;
1644 emUInt64 b;
1645 int i,j,k;
1646
1647 // Part 1: Prepare the hash name consisting of digits and non-capital
1648 // letters only.
1649 //
1650 // For this algorithm, it has been proved with a test program, that the
1651 // prime number 6795413 produces the theoretical minimum possible number
1652 // of collisions for all possible sources with all combinations of
1653 // srcLen = 1 to 3 (256 combinations per source byte) and hashLen = 1 to
1654 // 5 (36 combinations per hash byte). About 50 different prime numbers
1655 // have been tested, three of them resulted best, 6795413 is one of
1656 // those three. But it is still not proved that the results are even an
1657 // optimum for greater srcLen and hashLen.
1658 hashPtr=hash.SetLenGetWritable(hashLen);
1659 memset(hashPtr,0,hashLen);
1660 for (i=0; i<srcLen; i++) {
1661 for (j=0; j<hashLen; j++) {
1662 a=(unsigned char)hashPtr[j];
1663 if (j==hashLen-1) a+=(unsigned char)src[i];
1664 a*=6795413;
1665 hashPtr[j]=(char)(a%36);
1666 a/=36;
1667 for (k=j-1; k>=0 && a!=0; k--) {
1668 a+=hashPtr[k];
1669 hashPtr[k]=(char)(a%36);
1670 a/=36;
1671 }
1672 }
1673 }
1674 for (i=0; i<hashLen; i++) {
1675 a=(unsigned char)hashPtr[i];
1676 if (a<10) a+='0';
1677 else a+='a'-10;
1678 hashPtr[i]=(char)a;
1679 }
1680
1681 // Part 2: Improve the hash name a little bit through capitalization.
1682 // This is an extra algorithm because it could happen that the user
1683 // performs comparisons ignoring the capitalization, for example when
1684 // using the name as a DOS file name.
1685 for (i=0, j=0; i<hashLen; i++) {
1686 if (hashPtr[i]>='a' && hashPtr[i]<='z') j++;
1687 }
1688 if (j<=32) b=emCalcCRC32(src,srcLen);
1689 else b=emCalcCRC64(src,srcLen);
1690 if (j<=16) b^=b>>16;
1691 if (j<=8) b^=b>>8;
1692 if (j<=4) b^=b>>4;
1693 if (j<=2) b^=b>>2;
1694 if (j<=1) b^=b>>1;
1695 for (i=0; i<hashLen; i++) {
1696 if (hashPtr[i]>='a' && hashPtr[i]<='z') {
1697 if ((b&1)!=0) hashPtr[i]+=(char)('A'-'a');
1698 b>>=1;
1699 }
1700 }
1701
1702 return hash;
1703 }
1704