Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/mpg123/src/compat/compat.c
4392 views
1
/*
2
compat: Some compatibility functions (basic memory & string stuff in separate file)
3
4
The mpg123 code is determined to keep it's legacy. A legacy of old, old UNIX.
5
So anything possibly somewhat advanced should be considered to be put here, with proper #ifdef;-)
6
7
copyright 2007-2023 by the mpg123 project - free software under the terms of the LGPL 2.1
8
see COPYING and AUTHORS files in distribution or http://mpg123.org
9
initially written by Thomas Orgis, Windows Unicode stuff by JonY.
10
*/
11
12
#include "config.h"
13
/* This source file does need _POSIX_SOURCE to get some sigaction. */
14
#define _POSIX_SOURCE
15
#include "compat.h"
16
17
#ifdef _MSC_VER
18
#include <io.h>
19
20
#if(defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP))
21
#define WINDOWS_UWP
22
#endif
23
24
#endif
25
#ifdef HAVE_SYS_STAT_H
26
# include <sys/stat.h>
27
#endif
28
#ifdef HAVE_DIRENT_H
29
# include <dirent.h>
30
#endif
31
32
/* Win32 is only supported with unicode now. These headers also cover
33
module stuff. The WANT_WIN32_UNICODE macro is synonymous with
34
"want windows-specific API, and only the unicode variants of which". */
35
#ifdef WANT_WIN32_UNICODE
36
#include <wchar.h>
37
#include <windows.h>
38
#include <winnls.h>
39
#include <shlwapi.h>
40
#endif
41
42
#include "../common/debug.h"
43
44
#ifndef WINDOWS_UWP
45
46
char *INT123_compat_getenv(const char* name)
47
{
48
char *ret = NULL;
49
#ifdef WANT_WIN32_UNICODE
50
wchar_t *env;
51
wchar_t *wname = NULL;
52
if(INT123_win32_utf8_wide(name, &wname, NULL) > 0)
53
{
54
env = _wgetenv(wname);
55
free(wname);
56
if(env)
57
INT123_win32_wide_utf8(env, &ret, NULL);
58
}
59
#else
60
ret = getenv(name);
61
if(ret)
62
ret = INT123_compat_strdup(ret);
63
#endif
64
return ret;
65
}
66
67
#endif
68
69
#include "wpathconv.h"
70
71
/* Always add a default permission mask in case of flags|O_CREAT. */
72
int INT123_compat_open(const char *filename, int flags)
73
{
74
int ret;
75
#if defined (WANT_WIN32_UNICODE)
76
wchar_t *frag = NULL;
77
78
frag = u2wlongpath(filename);
79
/* Fallback to plain open when ucs-2 conversion fails */
80
if(!frag)
81
goto open_fallback;
82
83
/*Try _wopen */
84
ret = _wopen(frag, flags|_O_BINARY, _S_IREAD | _S_IWRITE);
85
if(ret != -1 )
86
goto open_ok; /* msdn says -1 means failure */
87
88
open_fallback:
89
#endif
90
91
#if defined(MPG123_COMPAT_MSVCRT_IO)
92
/* MSDN says POSIX function is deprecated beginning in Visual C++ 2005 */
93
/* Try plain old _open(), if it fails, do nothing */
94
ret = _open(filename, flags|_O_BINARY, _S_IREAD | _S_IWRITE);
95
#else
96
ret = open(filename, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
97
#endif
98
99
#if defined (WANT_WIN32_UNICODE)
100
open_ok:
101
free(frag);
102
#endif
103
104
return ret;
105
}
106
107
/* Moved over from wav.c, logic with fallbacks added from the
108
example of INT123_compat_open(). */
109
FILE* INT123_compat_fopen(const char *filename, const char *mode)
110
{
111
FILE* stream = NULL;
112
#ifdef WANT_WIN32_UNICODE
113
int cnt = 0;
114
wchar_t *wname = NULL;
115
wchar_t *wmode = NULL;
116
117
wname = u2wlongpath(filename);
118
if(!wname)
119
goto fopen_fallback;
120
cnt = INT123_win32_utf8_wide(mode, &wmode, NULL);
121
if( (wmode == NULL) || (cnt == 0))
122
goto fopen_fallback;
123
124
stream = _wfopen(wname, wmode);
125
if(stream) goto fopen_ok;
126
127
fopen_fallback:
128
#endif
129
stream = fopen(filename, mode);
130
#ifdef WANT_WIN32_UNICODE
131
132
fopen_ok:
133
free(wmode);
134
free(wname);
135
#endif
136
return stream;
137
}
138
139
FILE* INT123_compat_fdopen(int fd, const char *mode)
140
{
141
#if defined(MPG123_COMPAT_MSVCRT_IO)
142
return _fdopen(fd, mode);
143
#else
144
return fdopen(fd, mode);
145
#endif
146
}
147
148
int INT123_compat_close(int infd)
149
{
150
#if defined(MPG123_COMPAT_MSVCRT_IO)
151
return _close(infd);
152
#else
153
return close(infd);
154
#endif
155
}
156
157
int INT123_compat_fclose(FILE *stream)
158
{
159
return fclose(stream);
160
}
161
162
void INT123_compat_binmode(int fd, int enable)
163
{
164
#if defined(HAVE__SETMODE)
165
_setmode(fd, enable ? _O_BINARY : _O_TEXT);
166
#elif defined(HAVE_SETMODE)
167
setmode(fd, enable ? O_BINARY : O_TEXT);
168
#endif
169
}
170
171
#ifndef WINDOWS_UWP
172
173
/*
174
The Windows file and path stuff is an extract of jon_y's win32 loader
175
prototype from the loader_rework branch. It's been divided in to
176
reusable functons by ThOr in the hope to work out some generic-looking
177
loader code for both POSIX and Windows. The routines might be
178
helpful for consistent path work in other parts of mpg123, too.
179
180
This all is about getting some working code on a wide range of
181
systems while staying somewhat sane. If it does ridiculously inefficient
182
things with extraneous copies and grabbing of functions that made
183
it late to some official APIs, that's still fine with us.
184
*/
185
186
#ifdef WANT_WIN32_UNICODE
187
typedef HRESULT (__stdcall *PCA_ptr)( const wchar_t *, const wchar_t*, unsigned long, wchar_t **);
188
#endif
189
190
char* INT123_compat_catpath(const char *prefix, const char* path)
191
{
192
char *ret = NULL;
193
#ifdef WANT_WIN32_UNICODE
194
wchar_t *wprefix = NULL; /* Wide windows versions of */
195
wchar_t *wpath = NULL; /* input arguments. */
196
wchar_t *locwret = NULL; /* Tmp return value from LocalAlloc */
197
/*
198
This variation of combinepath can work with long and UNC paths, but
199
is not officially exposed in any DLLs, It also allocates all its buffers
200
internally via LocalAlloc, avoiding buffer overflow problems.
201
ThOr: I presume this hack is for supporting pre-8 Windows, as
202
from Windows 8 on, this is documented in the API.
203
*/
204
PCA_ptr mypac = NULL;
205
HMODULE pathcch = NULL;
206
207
if(!prefix && !path)
208
goto catpath_end;
209
wprefix = u2wpath(prefix);
210
wpath = u2wpath(path);
211
if((prefix && !wprefix) || (path && !wpath))
212
goto catpath_end;
213
214
/* Again: I presume this whole fun is to get at PathAllocCombine
215
even when pathcch.h is not available (like in MinGW32). */
216
if( (pathcch = GetModuleHandleA("kernelbase")) )
217
mypac = (PCA_ptr) GetProcAddress(pathcch, "PathAllocCombine");
218
if(mypac) /* PATHCCH_ALLOW_LONG_PATH = 1 per API docs */
219
{
220
debug("Actually calling PathAllocCombine!");
221
mypac(wprefix, wpath, 1, &locwret);
222
}
223
else
224
{
225
/* Playing safe, if we'd care much about performance, this would be on
226
the stack. */
227
locwret = LocalAlloc(LPTR, sizeof(wchar_t)*MAX_PATH);
228
if(locwret)
229
PathCombineW(locwret, wprefix, wpath);
230
}
231
ret = w2upath(locwret);
232
233
catpath_end:
234
LocalFree(locwret);
235
free(wprefix);
236
free(wpath);
237
#else
238
size_t len, prelen, patlen;
239
240
if(path && path[0] == '/')
241
prefix = NULL; /* Absolute path stays as it is. */
242
prelen = prefix ? strlen(prefix) : 0;
243
patlen = path ? strlen(path) : 0;
244
/* Concatenate the two, put a / in between if both present. */
245
len = ((prefix && path) ? 1 : 0) + prelen + patlen;
246
ret = malloc(len+1);
247
if(ret)
248
{
249
size_t off=0;
250
memcpy(ret, prefix, prelen);
251
if(prefix && path)
252
ret[prelen+(off++)] = '/';
253
memcpy(ret+prelen+off, path, patlen);
254
ret[len] = 0;
255
}
256
#endif
257
return ret;
258
}
259
260
int INT123_compat_isdir(const char *path)
261
{
262
int ret = 0;
263
#ifdef WANT_WIN32_UNICODE
264
wchar_t *wpath;
265
wpath = u2wlongpath(path);
266
if(wpath)
267
{
268
DWORD attr = GetFileAttributesW(wpath);
269
if(attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY)
270
ret=1;
271
free(wpath);
272
}
273
#else
274
struct stat sb;
275
if(path && !stat(path, &sb))
276
{
277
if(S_ISDIR(sb.st_mode))
278
ret=1;
279
}
280
#endif
281
return ret;
282
}
283
284
struct compat_dir
285
{
286
char *path;
287
#ifdef WANT_WIN32_UNICODE
288
int gotone; /* Got a result stored from FindFirstFileW. */
289
WIN32_FIND_DATAW d;
290
HANDLE ffn;
291
#else
292
DIR* dir;
293
#endif
294
};
295
296
struct compat_dir* INT123_compat_diropen(char *path)
297
{
298
struct compat_dir *cd;
299
if(!path)
300
return NULL;
301
cd = malloc(sizeof(*cd));
302
if(!cd)
303
return NULL;
304
#ifdef WANT_WIN32_UNICODE
305
cd->gotone = 0;
306
{
307
char *pattern;
308
wchar_t *wpattern;
309
pattern = INT123_compat_catpath(path, "*");
310
wpattern = u2wlongpath(pattern);
311
if(wpattern)
312
{
313
cd->ffn = FindFirstFileW(wpattern, &(cd->d));
314
if(cd->ffn == INVALID_HANDLE_VALUE)
315
{
316
/* FindClose() only needed after successful first find, right? */
317
free(cd);
318
cd = NULL;
319
}
320
else
321
cd->gotone = 1;
322
}
323
free(wpattern);
324
free(pattern);
325
}
326
#else
327
cd->dir = opendir(path);
328
if(!cd->dir)
329
{
330
free(cd);
331
cd = NULL;
332
}
333
#endif
334
if(cd)
335
{
336
cd->path = INT123_compat_strdup(path);
337
if(!cd->path)
338
{
339
INT123_compat_dirclose(cd);
340
cd = NULL;
341
}
342
}
343
return cd;
344
}
345
346
void INT123_compat_dirclose(struct compat_dir *cd)
347
{
348
if(cd)
349
{
350
free(cd->path);
351
#ifdef WANT_WIN32_UNICODE
352
FindClose(cd->ffn);
353
#else
354
closedir(cd->dir);
355
#endif
356
free(cd);
357
}
358
}
359
360
char* INT123_compat_nextfile(struct compat_dir *cd)
361
{
362
if(!cd)
363
return NULL;
364
#ifdef WANT_WIN32_UNICODE
365
while(cd->gotone || FindNextFileW(cd->ffn, &(cd->d)))
366
{
367
cd->gotone = 0;
368
if(!(cd->d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
369
{
370
char *ret;
371
INT123_win32_wide_utf8(cd->d.cFileName, &ret, NULL);
372
return ret;
373
}
374
}
375
#else
376
{
377
struct dirent *dp;
378
while((dp = readdir(cd->dir)))
379
{
380
struct stat fst;
381
char *fullpath = INT123_compat_catpath(cd->path, dp->d_name);
382
if(fullpath && !stat(fullpath, &fst) && S_ISREG(fst.st_mode))
383
{
384
free(fullpath);
385
return INT123_compat_strdup(dp->d_name);
386
}
387
free(fullpath);
388
}
389
}
390
#endif
391
return NULL;
392
}
393
394
char* INT123_compat_nextdir(struct compat_dir *cd)
395
{
396
if(!cd)
397
return NULL;
398
#ifdef WANT_WIN32_UNICODE
399
while(cd->gotone || FindNextFileW(cd->ffn, &(cd->d)))
400
{
401
cd->gotone = 0;
402
if(cd->d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
403
{
404
char *ret;
405
INT123_win32_wide_utf8(cd->d.cFileName, &ret, NULL);
406
return ret;
407
}
408
}
409
#else
410
{
411
struct dirent *dp;
412
while((dp = readdir(cd->dir)))
413
{
414
struct stat fst;
415
char *fullpath = INT123_compat_catpath(cd->path, dp->d_name);
416
if(fullpath && !stat(fullpath, &fst) && S_ISDIR(fst.st_mode))
417
{
418
free(fullpath);
419
return INT123_compat_strdup(dp->d_name);
420
}
421
free(fullpath);
422
}
423
}
424
#endif
425
return NULL;
426
}
427
428
#endif
429
430
// Revisit logic of write():
431
// Return -1 if interrupted before any data was written,
432
// set errno to EINTR. Any other error value is serious
433
// for blocking I/O, which we assume here. EAGAIN should be
434
// handed through.
435
// Reaction to zero-sized write attempts could also be funky, so avoid that.
436
// May return short count for various reasons. I assume that
437
// any serious condition will show itself as return value -1
438
// eventually.
439
440
// These uninterruptible write/read functions shall persist as long as
441
// possible to finish the desired operation. A short byte count is short
442
// because of a serious reason (maybe EOF, maybe out of disk space). You
443
// can inspect errno.
444
445
size_t INT123_unintr_write(int fd, void const *buffer, size_t bytes)
446
{
447
size_t written = 0;
448
errno = 0;
449
while(bytes)
450
{
451
errno = 0;
452
ptrdiff_t part = write(fd, (char*)buffer+written, bytes);
453
// Just on short writes, we do not abort. Only when
454
// there was no successful operation (even zero write) at all.
455
// Any other error than EINTR ends things here.
456
if(part >= 0)
457
{
458
bytes -= part;
459
written += part;
460
} else if(errno != EINTR && errno != EAGAIN
461
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
462
// Not all platforms define it (or only in more modern POSIX modes).
463
// Standard says it is supposed to be a macro, so simple check here.
464
&& errno != EWOULDBLOCK
465
#endif
466
)
467
break;
468
}
469
return written;
470
}
471
472
/* Same for reading the data. */
473
size_t INT123_unintr_read(int fd, void *buffer, size_t bytes)
474
{
475
size_t got = 0;
476
errno = 0;
477
while(bytes)
478
{
479
errno = 0;
480
ptrdiff_t part = read(fd, (char*)buffer+got, bytes);
481
if(part > 0) // == 0 is end of file
482
{
483
bytes -= part;
484
got += part;
485
} else if(errno != EINTR && errno != EAGAIN
486
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
487
&& errno != EWOULDBLOCK
488
#endif
489
)
490
break;
491
}
492
return got;
493
}
494
495
// and again for streams
496
size_t INT123_unintr_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
497
{
498
size_t written = 0;
499
errno = 0;
500
while(size && nmemb)
501
{
502
errno = 0;
503
size_t part = fwrite((char*)ptr+written*size, size, nmemb, stream);
504
if(part > 0)
505
{
506
nmemb -= part;
507
written += part;
508
} else if(errno != EINTR)
509
break;
510
}
511
return written;
512
}
513
514
#ifndef NO_CATCHSIGNAL
515
#if (!defined(WIN32) || defined (__CYGWIN__)) && !defined(__PSP__) && defined(HAVE_SIGNAL_H)
516
void (*INT123_catchsignal(int signum, void(*handler)(int)))(int)
517
{
518
struct sigaction new_sa;
519
struct sigaction old_sa;
520
521
#ifdef DONT_CATCH_SIGNALS
522
fprintf (stderr, "Not catching any signals.\n");
523
return ((void (*)()) -1);
524
#endif
525
526
new_sa.sa_handler = handler;
527
sigemptyset(&new_sa.sa_mask);
528
new_sa.sa_flags = 0;
529
if(sigaction(signum, &new_sa, &old_sa) == -1)
530
return ((void (*)(int)) -1); // Not rather NULL?
531
return (old_sa.sa_handler);
532
}
533
#endif
534
#endif
535
536