1/*
2 * fontconfig/src/fccompat.c
3 *
4 * Copyright © 2012 Red Hat, Inc.
5 *
6 * Author(s):
7 *  Akira TAGOH
8 *
9 * Permission to use, copy, modify, distribute, and sell this software and its
10 * documentation for any purpose is hereby granted without fee, provided that
11 * the above copyright notice appear in all copies and that both that
12 * copyright notice and this permission notice appear in supporting
13 * documentation, and that the name of the author(s) not be used in
14 * advertising or publicity pertaining to distribution of the software without
15 * specific, written prior permission.  The authors make no
16 * representations about the suitability of this software for any purpose.  It
17 * is provided "as is" without express or implied warranty.
18 *
19 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
20 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
21 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
22 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
23 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
24 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
25 * PERFORMANCE OF THIS SOFTWARE.
26 */
27
28#include "fcint.h"
29
30#include <errno.h>
31#if HAVE_SYS_TYPES_H
32#include <sys/types.h>
33#endif
34#if HAVE_SYS_STAT_H
35#include <sys/stat.h>
36#endif
37#if HAVE_FCNTL_H
38#include <fcntl.h>
39#endif
40#if HAVE_UNISTD_H
41#include <unistd.h>
42#endif
43#include <stdarg.h>
44#include <stdlib.h>
45#include <string.h>
46#include <time.h>
47
48#ifdef O_CLOEXEC
49#define FC_O_CLOEXEC O_CLOEXEC
50#else
51#define FC_O_CLOEXEC 0
52#endif
53#ifdef O_LARGEFILE
54#define FC_O_LARGEFILE O_LARGEFILE
55#else
56#define FC_O_LARGEFILE 0
57#endif
58#ifdef O_BINARY
59#define FC_O_BINARY O_BINARY
60#else
61#define FC_O_BINARY 0
62#endif
63#ifdef O_TEMPORARY
64#define FC_O_TEMPORARY O_TEMPORARY
65#else
66#define FC_O_TEMPORARY 0
67#endif
68#ifdef O_NOINHERIT
69#define FC_O_NOINHERIT O_NOINHERIT
70#else
71#define FC_O_NOINHERIT 0
72#endif
73
74#ifndef HAVE_UNISTD_H
75/* Values for the second argument to access. These may be OR'd together. */
76#ifndef R_OK
77#define R_OK    4       /* Test for read permission.  */
78#endif
79#ifndef W_OK
80#define W_OK    2       /* Test for write permission.  */
81#endif
82#ifndef F_OK
83#define F_OK    0       /* Test for existence.  */
84#endif
85
86typedef int mode_t;
87#endif /* !HAVE_UNISTD_H */
88
89#if !defined (HAVE_MKOSTEMP) && !defined(HAVE_MKSTEMP) && !defined(HAVE__MKTEMP_S)
90static int
91mkstemp (char *template)
92{
93    static const char s[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
94    int fd, i;
95    size_t l;
96
97    if (template == NULL)
98    {
99	errno = EINVAL;
100	return -1;
101    }
102    l = strlen (template);
103    if (l < 6 || strcmp (&template[l - 6], "XXXXXX") != 0)
104    {
105	errno = EINVAL;
106	return -1;
107    }
108    do
109    {
110	errno = 0;
111	for (i = l - 6; i < l; i++)
112	{
113	    int r = FcRandom ();
114	    template[i] = s[r % 62];
115	}
116	fd = FcOpen (template, FC_O_BINARY | O_CREAT | O_EXCL | FC_O_TEMPORARY | FC_O_NOINHERIT | O_RDWR, 0600);
117    } while (fd < 0 && errno == EEXIST);
118    if (fd >= 0)
119	errno = 0;
120
121    return fd;
122}
123#define HAVE_MKSTEMP 1
124#endif
125
126int
127FcOpen(const char *pathname, int flags, ...)
128{
129    int fd = -1;
130
131    if (flags & O_CREAT)
132    {
133	va_list ap;
134	mode_t mode;
135
136	va_start(ap, flags);
137	mode = (mode_t) va_arg(ap, int);
138	va_end(ap);
139
140	fd = open(pathname, flags | FC_O_CLOEXEC | FC_O_LARGEFILE, mode);
141    }
142    else
143    {
144	fd = open(pathname, flags | FC_O_CLOEXEC | FC_O_LARGEFILE);
145    }
146
147    return fd;
148}
149
150int
151FcMakeTempfile (char *template)
152{
153    int fd = -1;
154
155#if HAVE_MKOSTEMP
156    fd = mkostemp (template, FC_O_CLOEXEC);
157#elif HAVE_MKSTEMP
158    fd = mkstemp (template);
159#  ifdef F_DUPFD_CLOEXEC
160    if (fd != -1)
161    {
162	int newfd = fcntl(fd, F_DUPFD_CLOEXEC, STDIN_FILENO);
163
164	close(fd);
165	fd = newfd;
166    }
167#  elif defined(FD_CLOEXEC)
168    if (fd != -1)
169    {
170	fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
171    }
172#  endif
173#elif HAVE__MKTEMP_S
174   if (_mktemp_s(template, strlen(template) + 1) != 0)
175       return -1;
176   fd = FcOpen(template, O_RDWR | O_EXCL | O_CREAT, 0600);
177#endif
178
179    return fd;
180}
181
182int32_t
183FcRandom(void)
184{
185    int32_t result;
186
187#if HAVE_RANDOM_R
188    static struct random_data fcrandbuf;
189    static char statebuf[256];
190    static FcBool initialized = FcFalse;
191#ifdef _AIX
192    static char *retval;
193    long res;
194#endif
195
196    if (initialized != FcTrue)
197    {
198#ifdef _AIX
199	initstate_r (time (NULL), statebuf, 256, &retval, &fcrandbuf);
200#else
201	initstate_r (time (NULL), statebuf, 256, &fcrandbuf);
202#endif
203	initialized = FcTrue;
204    }
205
206#ifdef _AIX
207    random_r (&res, &fcrandbuf);
208    result = (int32_t)res;
209#else
210    random_r (&fcrandbuf, &result);
211#endif
212#elif HAVE_RANDOM
213    static char statebuf[256];
214    char *state;
215    static FcBool initialized = FcFalse;
216
217    if (initialized != FcTrue)
218    {
219	state = initstate (time (NULL), statebuf, 256);
220	initialized = FcTrue;
221    }
222    else
223	state = setstate (statebuf);
224
225    result = random ();
226
227    setstate (state);
228#elif HAVE_LRAND48
229    result = lrand48 ();
230#elif HAVE_RAND_R
231    static unsigned int seed;
232
233    seed = time (NULL);
234    result = rand_r (&seed);
235#elif HAVE_RAND
236    static FcBool initialized = FcFalse;
237
238    if (initialized != FcTrue)
239    {
240	srand (time (NULL));
241	initialized = FcTrue;
242    }
243    result = rand ();
244#else
245# error no random number generator function available.
246#endif
247
248    return result;
249}
250
251#ifdef _WIN32
252#include <direct.h>
253#define mkdir(path,mode) _mkdir(path)
254#endif
255
256FcBool
257FcMakeDirectory (const FcChar8 *dir)
258{
259    FcChar8 *parent;
260    FcBool  ret;
261
262    if (strlen ((char *) dir) == 0)
263	return FcFalse;
264
265    parent = FcStrDirname (dir);
266    if (!parent)
267	return FcFalse;
268    if (access ((char *) parent, F_OK) == 0)
269	ret = mkdir ((char *) dir, 0755) == 0 && chmod ((char *) dir, 0755) == 0;
270    else if (access ((char *) parent, F_OK) == -1)
271	ret = FcMakeDirectory (parent) && (mkdir ((char *) dir, 0755) == 0) && chmod ((char *) dir, 0755) == 0;
272    else
273	ret = FcFalse;
274    FcStrFree (parent);
275    return ret;
276}
277
278ssize_t
279FcReadLink (const FcChar8 *pathname,
280	    FcChar8       *buf,
281	    size_t         bufsiz)
282{
283#ifdef HAVE_READLINK
284    return readlink ((const char *) pathname, (char *)buf, bufsiz);
285#else
286    /* XXX: this function is only used for FcConfigRealFilename() so far
287     * and returning -1 as an error still just works.
288     */
289    errno = ENOSYS;
290    return -1;
291#endif
292}
293
294/* On Windows MingW provides dirent.h / openddir(), but MSVC does not */
295#ifndef HAVE_DIRENT_H
296
297struct DIR {
298    struct dirent d_ent;
299    HANDLE handle;
300    WIN32_FIND_DATA fdata;
301    FcBool valid;
302};
303
304FcPrivate DIR *
305FcCompatOpendirWin32 (const char *dirname)
306{
307    size_t len;
308    char *name;
309    DIR *dir;
310
311    dir = calloc (1, sizeof (struct DIR));
312    if (dir == NULL)
313        return NULL;
314
315    len = strlen (dirname);
316    name = malloc (len + 3);
317    if (name == NULL)
318    {
319      free (dir);
320      return NULL;
321    }
322    memcpy (name, dirname, len);
323    name[len++] = FC_DIR_SEPARATOR;
324    name[len++] = '*';
325    name[len] = '\0';
326
327    dir->handle = FindFirstFileEx (name, FindExInfoBasic, &dir->fdata, FindExSearchNameMatch, NULL, 0);
328
329    free (name);
330
331    if (!dir->handle)
332    {
333        free (dir);
334        dir = NULL;
335
336        if (GetLastError () == ERROR_FILE_NOT_FOUND)
337            errno = ENOENT;
338        else
339            errno = EACCES;
340    }
341
342    dir->valid = FcTrue;
343    return dir;
344}
345
346FcPrivate struct dirent *
347FcCompatReaddirWin32 (DIR *dir)
348{
349    if (dir->valid != FcTrue)
350        return NULL;
351
352    dir->d_ent.d_name = dir->fdata.cFileName;
353
354    if ((dir->fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
355        dir->d_ent.d_type = DT_DIR;
356    else if (dir->fdata.dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
357        dir->d_ent.d_type = DT_REG;
358    else
359        dir->d_ent.d_type = DT_UNKNOWN;
360
361    if (!FindNextFile (dir->handle, &dir->fdata))
362        dir->valid = FcFalse;
363
364    return &dir->d_ent;
365}
366
367FcPrivate int
368FcCompatClosedirWin32 (DIR *dir)
369{
370    if (dir != NULL && dir->handle != NULL)
371    {
372        FindClose (dir->handle);
373        free (dir);
374    }
375    return 0;
376}
377#endif /* HAVE_DIRENT_H */
378
379#define __fccompat__
380#include "fcaliastail.h"
381#undef __fccompat__
382