1 /****************************************************************************
2 * *
3 * GNAT COMPILER COMPONENTS *
4 * *
5 * A D A I N T *
6 * *
7 * C Implementation File *
8 * *
9 * Copyright (C) 1992-2019, Free Software Foundation, Inc. *
10 * *
11 * GNAT is free software; you can redistribute it and/or modify it under *
12 * terms of the GNU General Public License as published by the Free Soft- *
13 * ware Foundation; either version 3, or (at your option) any later ver- *
14 * sion. GNAT is distributed in the hope that it will be useful, but WITH- *
15 * OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
16 * or FITNESS FOR A PARTICULAR PURPOSE. *
17 * *
18 * As a special exception under Section 7 of GPL version 3, you are granted *
19 * additional permissions described in the GCC Runtime Library Exception, *
20 * version 3.1, as published by the Free Software Foundation. *
21 * *
22 * You should have received a copy of the GNU General Public License and *
23 * a copy of the GCC Runtime Library Exception along with this program; *
24 * see the files COPYING3 and COPYING.RUNTIME respectively. If not, see *
25 * <http://www.gnu.org/licenses/>. *
26 * *
27 * GNAT was originally developed by the GNAT team at New York University. *
28 * Extensive contributions were provided by Ada Core Technologies Inc. *
29 * *
30 ****************************************************************************/
31
32 /* This file contains those routines named by Import pragmas in
33 packages in the GNAT hierarchy (especially GNAT.OS_Lib) and in
34 package Osint. Many of the subprograms in OS_Lib import standard
35 library calls directly. This file contains all other routines. */
36
37 /* Ensure access to errno is thread safe. */
38
39 #ifndef _REENTRANT
40 #define _REENTRANT
41 #endif
42
43 #ifndef _THREAD_SAFE
44 #define _THREAD_SAFE
45 #endif
46
47 /* Use 64 bit Large File API */
48 #if defined (__QNX__)
49 #define _LARGEFILE64_SOURCE 1
50 #elif !defined(_LARGEFILE_SOURCE)
51 #define _LARGEFILE_SOURCE
52 #endif
53 #define _FILE_OFFSET_BITS 64
54
55 #ifdef __vxworks
56
57 /* No need to redefine exit here. */
58 #undef exit
59
60 /* We want to use the POSIX variants of include files. */
61 #define POSIX
62 #include "vxWorks.h"
63
64 #if defined (__mips_vxworks)
65 #include "cacheLib.h"
66 #endif /* __mips_vxworks */
67
68 /* If SMP, access vxCpuConfiguredGet */
69 #ifdef _WRS_CONFIG_SMP
70 #include <vxCpuLib.h>
71 #endif /* _WRS_CONFIG_SMP */
72
73 /* We need to know the VxWorks version because some file operations
74 (such as chmod) are only available on VxWorks 6. */
75 #include "version.h"
76
77 /* vwModNum.h and dosFsLib.h are needed for the VxWorks 6 rename workaround.
78 See below. */
79 #if (_WRS_VXWORKS_MAJOR == 6)
80 #include <vwModNum.h>
81 #include <dosFsLib.h>
82 #endif /* 6.x */
83 #endif /* VxWorks */
84
85 #if defined (__APPLE__)
86 #include <unistd.h>
87 #endif
88
89 #if defined (__hpux__)
90 #include <sys/param.h>
91 #include <sys/pstat.h>
92 #endif
93
94 #ifdef __PikeOS__
95 #define __BSD_VISIBLE 1
96 #endif
97
98 #ifdef __QNX__
99 #include <sys/syspage.h>
100 #endif
101
102 #ifdef IN_RTS
103
104 #ifdef STANDALONE
105 #include <errno.h>
106 #include <sys/types.h>
107 #include <sys/stat.h>
108 #include <unistd.h>
109 #include <stdlib.h>
110 #include <string.h>
111
112 /* for CPU_SET/CPU_ZERO */
113 #define _GNU_SOURCE
114 #define __USE_GNU
115
116 #include "runtime.h"
117
118 #else
119 #include "tconfig.h"
120 #include "tsystem.h"
121 #endif
122
123 #include <sys/stat.h>
124 #include <fcntl.h>
125 #include <time.h>
126
127 #if defined (__vxworks) || defined (__ANDROID__)
128 /* S_IREAD and S_IWRITE are not defined in VxWorks or Android */
129 #ifndef S_IREAD
130 #define S_IREAD (S_IRUSR | S_IRGRP | S_IROTH)
131 #endif
132
133 #ifndef S_IWRITE
134 #define S_IWRITE (S_IWUSR)
135 #endif
136 #endif
137
138 /* We don't have libiberty, so use malloc. */
139 #define xmalloc(S) malloc (S)
140 #define xrealloc(V,S) realloc (V,S)
141 #else
142 #include "config.h"
143 #include "system.h"
144 #include "version.h"
145 #endif
146
147 #ifdef __cplusplus
148 extern "C" {
149 #endif
150
151 #if defined (__DJGPP__)
152
153 /* For isalpha-like tests in the compiler, we're expected to resort to
154 safe-ctype.h/ISALPHA. This isn't available for the runtime library
155 build, so we fallback on ctype.h/isalpha there. */
156
157 #ifdef IN_RTS
158 #include <ctype.h>
159 #define ISALPHA isalpha
160 #endif
161
162 #elif defined (__MINGW32__) || defined (__CYGWIN__)
163
164 #include "mingw32.h"
165
166 /* Current code page and CCS encoding to use, set in initialize.c. */
167 UINT __gnat_current_codepage;
168 UINT __gnat_current_ccs_encoding;
169
170 #include <sys/utime.h>
171
172 /* For isalpha-like tests in the compiler, we're expected to resort to
173 safe-ctype.h/ISALPHA. This isn't available for the runtime library
174 build, so we fallback on ctype.h/isalpha there. */
175
176 #ifdef IN_RTS
177 #include <ctype.h>
178 #define ISALPHA isalpha
179 #endif
180
181 #elif defined (__Lynx__)
182
183 /* Lynx utime.h only defines the entities of interest to us if
184 defined (VMOS_DEV), so ... */
185 #define VMOS_DEV
186 #include <utime.h>
187 #undef VMOS_DEV
188
189 #else
190 #include <utime.h>
191 #endif
192
193 /* wait.h processing */
194 #ifdef __MINGW32__
195 # if OLD_MINGW
196 # include <sys/wait.h>
197 # endif
198 #elif defined (__vxworks) && defined (__RTP__)
199 # include <wait.h>
200 #elif defined (__Lynx__)
201 /* ??? We really need wait.h and it includes resource.h on Lynx. GCC
202 has a resource.h header as well, included instead of the lynx
203 version in our setup, causing lots of errors. We don't really need
204 the lynx contents of this file, so just workaround the issue by
205 preventing the inclusion of the GCC header from doing anything. */
206 # define GCC_RESOURCE_H
207 # include <sys/wait.h>
208 #elif defined (__PikeOS__)
209 /* No wait() or waitpid() calls available. */
210 #else
211 /* Default case. */
212 #include <sys/wait.h>
213 #endif
214
215 #if defined (__DJGPP__)
216 #include <process.h>
217 #include <signal.h>
218 #include <dir.h>
219 #include <utime.h>
220 #undef DIR_SEPARATOR
221 #define DIR_SEPARATOR '\\'
222
223 #elif defined (_WIN32)
224
225 #include <windows.h>
226 #include <accctrl.h>
227 #include <aclapi.h>
228 #include <tlhelp32.h>
229 #include <signal.h>
230 #undef DIR_SEPARATOR
231 #define DIR_SEPARATOR '\\'
232
233 #else
234 #include <utime.h>
235 #endif
236
237 #include "adaint.h"
238
239 /* Define symbols O_BINARY and O_TEXT as harmless zeroes if they are not
240 defined in the current system. On DOS-like systems these flags control
241 whether the file is opened/created in text-translation mode (CR/LF in
242 external file mapped to LF in internal file), but in Unix-like systems,
243 no text translation is required, so these flags have no effect. */
244
245 #ifndef O_BINARY
246 #define O_BINARY 0
247 #endif
248
249 #ifndef O_TEXT
250 #define O_TEXT 0
251 #endif
252
253 #ifndef HOST_EXECUTABLE_SUFFIX
254 #define HOST_EXECUTABLE_SUFFIX ""
255 #endif
256
257 #ifndef HOST_OBJECT_SUFFIX
258 #define HOST_OBJECT_SUFFIX ".o"
259 #endif
260
261 #ifndef PATH_SEPARATOR
262 #define PATH_SEPARATOR ':'
263 #endif
264
265 #ifndef DIR_SEPARATOR
266 #define DIR_SEPARATOR '/'
267 #endif
268
269 /* Check for cross-compilation. */
270 #if defined (CROSS_COMPILE) || defined (CROSS_DIRECTORY_STRUCTURE)
271 #define IS_CROSS 1
272 int __gnat_is_cross_compiler = 1;
273 #else
274 #undef IS_CROSS
275 int __gnat_is_cross_compiler = 0;
276 #endif
277
278 char __gnat_dir_separator = DIR_SEPARATOR;
279
280 char __gnat_path_separator = PATH_SEPARATOR;
281
282 /* The GNAT_LIBRARY_TEMPLATE contains a list of expressions that define
283 the base filenames that libraries specified with -lsomelib options
284 may have. This is used by GNATMAKE to check whether an executable
285 is up-to-date or not. The syntax is
286
287 library_template ::= { pattern ; } pattern NUL
288 pattern ::= [ prefix ] * [ postfix ]
289
290 These should only specify names of static libraries as it makes
291 no sense to determine at link time if dynamic-link libraries are
292 up to date or not. Any libraries that are not found are supposed
293 to be up-to-date:
294
295 * if they are needed but not present, the link
296 will fail,
297
298 * otherwise they are libraries in the system paths and so
299 they are considered part of the system and not checked
300 for that reason.
301
302 ??? This should be part of a GNAT host-specific compiler
303 file instead of being included in all user applications
304 as well. This is only a temporary work-around for 3.11b. */
305
306 #ifndef GNAT_LIBRARY_TEMPLATE
307 #define GNAT_LIBRARY_TEMPLATE "lib*.a"
308 #endif
309
310 const char *__gnat_library_template = GNAT_LIBRARY_TEMPLATE;
311
312 #if defined (__vxworks)
313 #define GNAT_MAX_PATH_LEN PATH_MAX
314
315 #else
316
317 #if defined (__MINGW32__)
318 #include "mingw32.h"
319
320 #if OLD_MINGW
321 #include <sys/param.h>
322 #endif
323
324 #else
325 #include <sys/param.h>
326 #endif
327
328 #ifdef MAXPATHLEN
329 #define GNAT_MAX_PATH_LEN MAXPATHLEN
330 #else
331 #define GNAT_MAX_PATH_LEN 256
332 #endif
333
334 #endif
335
336 /* Used for runtime check that Ada constant File_Attributes_Size is no
337 less than the actual size of struct file_attributes (see Osint
338 initialization). */
339 int __gnat_size_of_file_attributes = sizeof (struct file_attributes);
340
341 void __gnat_stat_to_attr (int fd, char* name, struct file_attributes* attr);
342
343 /* The __gnat_max_path_len variable is used to export the maximum
344 length of a path name to Ada code. max_path_len is also provided
345 for compatibility with older GNAT versions, please do not use
346 it. */
347
348 int __gnat_max_path_len = GNAT_MAX_PATH_LEN;
349 int max_path_len = GNAT_MAX_PATH_LEN;
350
351 /* Control whether we can use ACL on Windows. */
352
353 int __gnat_use_acl = 1;
354
355 /* The following macro HAVE_READDIR_R should be defined if the
356 system provides the routine readdir_r.
357 ... but we never define it anywhere??? */
358 #undef HAVE_READDIR_R
359
360 #define MAYBE_TO_PTR32(argv) argv
361
362 static const char ATTR_UNSET = 127;
363
364 /* Reset the file attributes as if no system call had been performed */
365
366 void
__gnat_reset_attributes(struct file_attributes * attr)367 __gnat_reset_attributes (struct file_attributes* attr)
368 {
369 attr->exists = ATTR_UNSET;
370 attr->error = EINVAL;
371
372 attr->writable = ATTR_UNSET;
373 attr->readable = ATTR_UNSET;
374 attr->executable = ATTR_UNSET;
375
376 attr->regular = ATTR_UNSET;
377 attr->symbolic_link = ATTR_UNSET;
378 attr->directory = ATTR_UNSET;
379
380 attr->timestamp = (OS_Time)-2;
381 attr->file_length = -1;
382 }
383
384 int
__gnat_error_attributes(struct file_attributes * attr)385 __gnat_error_attributes (struct file_attributes *attr) {
386 return attr->error;
387 }
388
389 OS_Time
__gnat_current_time(void)390 __gnat_current_time (void)
391 {
392 time_t res = time (NULL);
393 return (OS_Time) res;
394 }
395
396 /* Return the current local time as a string in the ISO 8601 format of
397 "YYYY-MM-DD HH:MM:SS.SS". The returned string is 22 + 1 (NULL) characters
398 long. */
399
400 void
__gnat_current_time_string(char * result)401 __gnat_current_time_string (char *result)
402 {
403 const char *format = "%Y-%m-%d %H:%M:%S";
404 /* Format string necessary to describe the ISO 8601 format */
405
406 const time_t t_val = time (NULL);
407
408 strftime (result, 22, format, localtime (&t_val));
409 /* Convert the local time into a string following the ISO format, copying
410 at most 22 characters into the result string. */
411
412 result [19] = '.';
413 result [20] = '0';
414 result [21] = '0';
415 /* The sub-seconds are manually set to zero since type time_t lacks the
416 precision necessary for nanoseconds. */
417 }
418
419 void
__gnat_to_gm_time(OS_Time * p_time,int * p_year,int * p_month,int * p_day,int * p_hours,int * p_mins,int * p_secs)420 __gnat_to_gm_time (OS_Time *p_time, int *p_year, int *p_month, int *p_day,
421 int *p_hours, int *p_mins, int *p_secs)
422 {
423 struct tm *res;
424 time_t time = (time_t) *p_time;
425
426 res = gmtime (&time);
427 if (res)
428 {
429 *p_year = res->tm_year;
430 *p_month = res->tm_mon;
431 *p_day = res->tm_mday;
432 *p_hours = res->tm_hour;
433 *p_mins = res->tm_min;
434 *p_secs = res->tm_sec;
435 }
436 else
437 *p_year = *p_month = *p_day = *p_hours = *p_mins = *p_secs = 0;
438 }
439
440 void
__gnat_to_os_time(OS_Time * p_time,int year,int month,int day,int hours,int mins,int secs)441 __gnat_to_os_time (OS_Time *p_time, int year, int month, int day,
442 int hours, int mins, int secs)
443 {
444 struct tm v;
445
446 v.tm_year = year;
447 v.tm_mon = month;
448 v.tm_mday = day;
449 v.tm_hour = hours;
450 v.tm_min = mins;
451 v.tm_sec = secs;
452 v.tm_isdst = -1;
453
454 /* returns -1 of failing, this is s-os_lib Invalid_Time */
455
456 *p_time = (OS_Time) mktime (&v);
457 }
458
459 /* Place the contents of the symbolic link named PATH in the buffer BUF,
460 which has size BUFSIZ. If PATH is a symbolic link, then return the number
461 of characters of its content in BUF. Otherwise, return -1.
462 For systems not supporting symbolic links, always return -1. */
463
464 int
__gnat_readlink(char * path ATTRIBUTE_UNUSED,char * buf ATTRIBUTE_UNUSED,size_t bufsiz ATTRIBUTE_UNUSED)465 __gnat_readlink (char *path ATTRIBUTE_UNUSED,
466 char *buf ATTRIBUTE_UNUSED,
467 size_t bufsiz ATTRIBUTE_UNUSED)
468 {
469 #if defined (_WIN32) \
470 || defined(__vxworks) || defined (__PikeOS__)
471 return -1;
472 #else
473 return readlink (path, buf, bufsiz);
474 #endif
475 }
476
477 /* Creates a symbolic link named NEWPATH which contains the string OLDPATH.
478 If NEWPATH exists it will NOT be overwritten.
479 For systems not supporting symbolic links, always return -1. */
480
481 int
__gnat_symlink(char * oldpath ATTRIBUTE_UNUSED,char * newpath ATTRIBUTE_UNUSED)482 __gnat_symlink (char *oldpath ATTRIBUTE_UNUSED,
483 char *newpath ATTRIBUTE_UNUSED)
484 {
485 #if defined (_WIN32) \
486 || defined(__vxworks) || defined (__PikeOS__)
487 return -1;
488 #else
489 return symlink (oldpath, newpath);
490 #endif
491 }
492
493 /* Try to lock a file, return 1 if success. */
494
495 #if defined (__vxworks) \
496 || defined (_WIN32) || defined (__PikeOS__)
497
498 /* Version that does not use link. */
499
500 int
__gnat_try_lock(char * dir,char * file)501 __gnat_try_lock (char *dir, char *file)
502 {
503 int fd;
504 #ifdef __MINGW32__
505 TCHAR wfull_path[GNAT_MAX_PATH_LEN];
506 TCHAR wfile[GNAT_MAX_PATH_LEN];
507 TCHAR wdir[GNAT_MAX_PATH_LEN];
508
509 S2WSC (wdir, dir, GNAT_MAX_PATH_LEN);
510 S2WSC (wfile, file, GNAT_MAX_PATH_LEN);
511
512 /* ??? the code below crash on MingW64 for obscure reasons, a ticket
513 has been opened here:
514
515 https://sourceforge.net/p/mingw-w64/bugs/414/
516
517 As a workaround an equivalent set of code has been put in place below.
518
519 _stprintf (wfull_path, _T("%s%c%s"), wdir, _T(DIR_SEPARATOR), wfile);
520 */
521
522 _tcscpy (wfull_path, wdir);
523 _tcscat (wfull_path, L"\\");
524 _tcscat (wfull_path, wfile);
525
526 fd = _topen (wfull_path, O_CREAT | O_EXCL, 0600);
527 #else
528 char full_path[256];
529
530 sprintf (full_path, "%s%c%s", dir, DIR_SEPARATOR, file);
531 fd = open (full_path, O_CREAT | O_EXCL, 0600);
532 #endif
533
534 if (fd < 0)
535 return 0;
536
537 close (fd);
538 return 1;
539 }
540
541 #else
542
543 /* Version using link(), more secure over NFS. */
544 /* See TN 6913-016 for discussion ??? */
545
546 int
__gnat_try_lock(char * dir,char * file)547 __gnat_try_lock (char *dir, char *file)
548 {
549 char full_path[256];
550 char temp_file[256];
551 GNAT_STRUCT_STAT stat_result;
552 int fd;
553
554 sprintf (full_path, "%s%c%s", dir, DIR_SEPARATOR, file);
555 sprintf (temp_file, "%s%cTMP-%ld-%ld",
556 dir, DIR_SEPARATOR, (long)getpid(), (long)getppid ());
557
558 /* Create the temporary file and write the process number. */
559 fd = open (temp_file, O_CREAT | O_WRONLY, 0600);
560 if (fd < 0)
561 return 0;
562
563 close (fd);
564
565 /* Link it with the new file. */
566 link (temp_file, full_path);
567
568 /* Count the references on the old one. If we have a count of two, then
569 the link did succeed. Remove the temporary file before returning. */
570 __gnat_stat (temp_file, &stat_result);
571 unlink (temp_file);
572 return stat_result.st_nlink == 2;
573 }
574 #endif
575
576 /* Return the maximum file name length. */
577
578 int
__gnat_get_maximum_file_name_length(void)579 __gnat_get_maximum_file_name_length (void)
580 {
581 return -1;
582 }
583
584 /* Return nonzero if file names are case sensitive. */
585
586 static int file_names_case_sensitive_cache = -1;
587
588 int
__gnat_get_file_names_case_sensitive(void)589 __gnat_get_file_names_case_sensitive (void)
590 {
591 if (file_names_case_sensitive_cache == -1)
592 {
593 const char *sensitive = getenv ("GNAT_FILE_NAME_CASE_SENSITIVE");
594
595 if (sensitive != NULL
596 && (sensitive[0] == '0' || sensitive[0] == '1')
597 && sensitive[1] == '\0')
598 file_names_case_sensitive_cache = sensitive[0] - '0';
599 else
600 {
601 /* By default, we suppose filesystems aren't case sensitive on
602 Windows and Darwin (but they are on arm-darwin). */
603 #if defined (WINNT) || defined (__DJGPP__) \
604 || (defined (__APPLE__) && !(defined (__arm__) || defined (__arm64__)))
605 file_names_case_sensitive_cache = 0;
606 #else
607 file_names_case_sensitive_cache = 1;
608 #endif
609 }
610 }
611 return file_names_case_sensitive_cache;
612 }
613
614 /* Return nonzero if environment variables are case sensitive. */
615
616 int
__gnat_get_env_vars_case_sensitive(void)617 __gnat_get_env_vars_case_sensitive (void)
618 {
619 #if defined (WINNT) || defined (__DJGPP__)
620 return 0;
621 #else
622 return 1;
623 #endif
624 }
625
626 char
__gnat_get_default_identifier_character_set(void)627 __gnat_get_default_identifier_character_set (void)
628 {
629 return '1';
630 }
631
632 /* Return the current working directory. */
633
634 void
__gnat_get_current_dir(char * dir,int * length)635 __gnat_get_current_dir (char *dir, int *length)
636 {
637 #if defined (__MINGW32__)
638 TCHAR wdir[GNAT_MAX_PATH_LEN];
639
640 _tgetcwd (wdir, *length);
641
642 WS2SC (dir, wdir, GNAT_MAX_PATH_LEN);
643
644 #else
645 char* result = getcwd (dir, *length);
646 /* If the current directory does not exist, set length = 0
647 to indicate error. That can't happen on windows, where
648 you can't delete a directory if it is the current
649 directory of some process. */
650 if (!result)
651 {
652 *length = 0;
653 return;
654 }
655 #endif
656
657 *length = strlen (dir);
658
659 if (dir [*length - 1] != DIR_SEPARATOR)
660 {
661 dir [*length] = DIR_SEPARATOR;
662 ++(*length);
663 }
664 dir[*length] = '\0';
665 }
666
667 /* Return the suffix for object files. */
668
669 void
__gnat_get_object_suffix_ptr(int * len,const char ** value)670 __gnat_get_object_suffix_ptr (int *len, const char **value)
671 {
672 *value = HOST_OBJECT_SUFFIX;
673
674 if (*value == 0)
675 *len = 0;
676 else
677 *len = strlen (*value);
678
679 return;
680 }
681
682 /* Return the suffix for executable files. */
683
684 void
__gnat_get_executable_suffix_ptr(int * len,const char ** value)685 __gnat_get_executable_suffix_ptr (int *len, const char **value)
686 {
687 *value = HOST_EXECUTABLE_SUFFIX;
688
689 if (!*value)
690 *len = 0;
691 else
692 *len = strlen (*value);
693
694 return;
695 }
696
697 /* Return the suffix for debuggable files. Usually this is the same as the
698 executable extension. */
699
700 void
__gnat_get_debuggable_suffix_ptr(int * len,const char ** value)701 __gnat_get_debuggable_suffix_ptr (int *len, const char **value)
702 {
703 *value = HOST_EXECUTABLE_SUFFIX;
704
705 if (*value == 0)
706 *len = 0;
707 else
708 *len = strlen (*value);
709
710 return;
711 }
712
713 /* Returns the OS filename and corresponding encoding. */
714
715 void
__gnat_os_filename(char * filename ATTRIBUTE_UNUSED,char * w_filename ATTRIBUTE_UNUSED,char * os_name,int * o_length,char * encoding ATTRIBUTE_UNUSED,int * e_length)716 __gnat_os_filename (char *filename ATTRIBUTE_UNUSED,
717 char *w_filename ATTRIBUTE_UNUSED,
718 char *os_name, int *o_length,
719 char *encoding ATTRIBUTE_UNUSED, int *e_length)
720 {
721 #if defined (_WIN32) && ! defined (__vxworks) && ! defined (IS_CROSS)
722 WS2SC (os_name, (TCHAR *)w_filename, (DWORD)*o_length);
723 *o_length = strlen (os_name);
724 strcpy (encoding, "encoding=utf8");
725 *e_length = strlen (encoding);
726 #else
727 strcpy (os_name, filename);
728 *o_length = strlen (filename);
729 *e_length = 0;
730 #endif
731 }
732
733 /* Delete a file. */
734
735 int
__gnat_unlink(char * path)736 __gnat_unlink (char *path)
737 {
738 #if defined (__MINGW32__) && ! defined (__vxworks) && ! defined (IS_CROSS)
739 {
740 TCHAR wpath[GNAT_MAX_PATH_LEN];
741
742 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
743 return _tunlink (wpath);
744 }
745 #else
746 return unlink (path);
747 #endif
748 }
749
750 /* Rename a file. */
751
752 int
__gnat_rename(char * from,char * to)753 __gnat_rename (char *from, char *to)
754 {
755 #if defined (__MINGW32__) && ! defined (__vxworks) && ! defined (IS_CROSS)
756 {
757 TCHAR wfrom[GNAT_MAX_PATH_LEN], wto[GNAT_MAX_PATH_LEN];
758
759 S2WSC (wfrom, from, GNAT_MAX_PATH_LEN);
760 S2WSC (wto, to, GNAT_MAX_PATH_LEN);
761 return _trename (wfrom, wto);
762 }
763 #elif defined (__vxworks) && (_WRS_VXWORKS_MAJOR == 6)
764 {
765 /* When used on a dos filesystem under VxWorks 6.9 rename will trigger a
766 S_dosFsLib_FILE_NOT_FOUND errno when the file is not found. Let's map
767 that to ENOENT so Ada.Directory.Rename can detect that and raise the
768 Name_Error exception. */
769 int ret = rename (from, to);
770
771 if (ret && (errno == S_dosFsLib_FILE_NOT_FOUND))
772 {
773 errno = ENOENT;
774 }
775 return ret;
776 }
777 #else
778 return rename (from, to);
779 #endif
780 }
781
782 /* Changing directory. */
783
784 int
__gnat_chdir(char * path)785 __gnat_chdir (char *path)
786 {
787 #if defined (__MINGW32__) && ! defined (__vxworks) && ! defined (IS_CROSS)
788 {
789 TCHAR wpath[GNAT_MAX_PATH_LEN];
790
791 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
792 return _tchdir (wpath);
793 }
794 #else
795 return chdir (path);
796 #endif
797 }
798
799 /* Removing a directory. */
800
801 int
__gnat_rmdir(char * path)802 __gnat_rmdir (char *path)
803 {
804 #if defined (__MINGW32__) && ! defined (__vxworks) && ! defined (IS_CROSS)
805 {
806 TCHAR wpath[GNAT_MAX_PATH_LEN];
807
808 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
809 return _trmdir (wpath);
810 }
811 #elif defined (VTHREADS)
812 /* rmdir not available */
813 return -1;
814 #else
815 return rmdir (path);
816 #endif
817 }
818
819 #if defined (_WIN32) || defined (__linux__) || defined (__sun__) \
820 || defined (__FreeBSD__) || defined(__DragonFly__) || defined (__QNX__)
821 #define HAS_TARGET_WCHAR_T
822 #endif
823
824 #ifdef HAS_TARGET_WCHAR_T
825 #include <wchar.h>
826 #endif
827
828 int
__gnat_fputwc(int c,FILE * stream)829 __gnat_fputwc(int c, FILE *stream)
830 {
831 #ifdef HAS_TARGET_WCHAR_T
832 return fputwc ((wchar_t)c, stream);
833 #else
834 return fputc (c, stream);
835 #endif
836 }
837
838 FILE *
__gnat_fopen(char * path,char * mode,int encoding ATTRIBUTE_UNUSED)839 __gnat_fopen (char *path, char *mode, int encoding ATTRIBUTE_UNUSED)
840 {
841 #if defined (_WIN32) && ! defined (__vxworks) && ! defined (IS_CROSS)
842 TCHAR wpath[GNAT_MAX_PATH_LEN];
843 TCHAR wmode[10];
844
845 S2WS (wmode, mode, 10);
846
847 if (encoding == Encoding_Unspecified)
848 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
849 else if (encoding == Encoding_UTF8)
850 S2WSU (wpath, path, GNAT_MAX_PATH_LEN);
851 else
852 S2WS (wpath, path, GNAT_MAX_PATH_LEN);
853
854 return _tfopen (wpath, wmode);
855
856 #else
857 return GNAT_FOPEN (path, mode);
858 #endif
859 }
860
861 FILE *
__gnat_freopen(char * path,char * mode,FILE * stream,int encoding ATTRIBUTE_UNUSED)862 __gnat_freopen (char *path,
863 char *mode,
864 FILE *stream,
865 int encoding ATTRIBUTE_UNUSED)
866 {
867 #if defined (_WIN32) && ! defined (__vxworks) && ! defined (IS_CROSS)
868 TCHAR wpath[GNAT_MAX_PATH_LEN];
869 TCHAR wmode[10];
870
871 S2WS (wmode, mode, 10);
872
873 if (encoding == Encoding_Unspecified)
874 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
875 else if (encoding == Encoding_UTF8)
876 S2WSU (wpath, path, GNAT_MAX_PATH_LEN);
877 else
878 S2WS (wpath, path, GNAT_MAX_PATH_LEN);
879
880 return _tfreopen (wpath, wmode, stream);
881 #else
882 return freopen (path, mode, stream);
883 #endif
884 }
885
886 int
__gnat_open_read(char * path,int fmode)887 __gnat_open_read (char *path, int fmode)
888 {
889 int fd;
890 int o_fmode = O_BINARY;
891
892 if (fmode)
893 o_fmode = O_TEXT;
894
895 #if defined (__vxworks)
896 fd = open (path, O_RDONLY | o_fmode, 0444);
897 #elif defined (__MINGW32__)
898 {
899 TCHAR wpath[GNAT_MAX_PATH_LEN];
900
901 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
902 fd = _topen (wpath, O_RDONLY | o_fmode, 0444);
903 }
904 #else
905 fd = GNAT_OPEN (path, O_RDONLY | o_fmode);
906 #endif
907
908 return fd < 0 ? -1 : fd;
909 }
910
911 #if defined (__MINGW32__)
912 #define PERM (S_IREAD | S_IWRITE)
913 #else
914 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
915 #endif
916
917 int
__gnat_open_rw(char * path,int fmode)918 __gnat_open_rw (char *path, int fmode)
919 {
920 int fd;
921 int o_fmode = O_BINARY;
922
923 if (fmode)
924 o_fmode = O_TEXT;
925
926 #if defined (__MINGW32__)
927 {
928 TCHAR wpath[GNAT_MAX_PATH_LEN];
929
930 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
931 fd = _topen (wpath, O_RDWR | o_fmode, PERM);
932 }
933 #else
934 fd = GNAT_OPEN (path, O_RDWR | o_fmode, PERM);
935 #endif
936
937 return fd < 0 ? -1 : fd;
938 }
939
940 int
__gnat_open_create(char * path,int fmode)941 __gnat_open_create (char *path, int fmode)
942 {
943 int fd;
944 int o_fmode = O_BINARY;
945
946 if (fmode)
947 o_fmode = O_TEXT;
948
949 #if defined (__MINGW32__)
950 {
951 TCHAR wpath[GNAT_MAX_PATH_LEN];
952
953 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
954 fd = _topen (wpath, O_WRONLY | O_CREAT | O_TRUNC | o_fmode, PERM);
955 }
956 #else
957 fd = GNAT_OPEN (path, O_WRONLY | O_CREAT | O_TRUNC | o_fmode, PERM);
958 #endif
959
960 return fd < 0 ? -1 : fd;
961 }
962
963 int
__gnat_create_output_file(char * path)964 __gnat_create_output_file (char *path)
965 {
966 int fd;
967 #if defined (__MINGW32__)
968 {
969 TCHAR wpath[GNAT_MAX_PATH_LEN];
970
971 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
972 fd = _topen (wpath, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, PERM);
973 }
974 #else
975 fd = GNAT_OPEN (path, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, PERM);
976 #endif
977
978 return fd < 0 ? -1 : fd;
979 }
980
981 int
__gnat_create_output_file_new(char * path)982 __gnat_create_output_file_new (char *path)
983 {
984 int fd;
985 #if defined (__MINGW32__)
986 {
987 TCHAR wpath[GNAT_MAX_PATH_LEN];
988
989 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
990 fd = _topen (wpath, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT | O_EXCL, PERM);
991 }
992 #else
993 fd = GNAT_OPEN (path, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT | O_EXCL, PERM);
994 #endif
995
996 return fd < 0 ? -1 : fd;
997 }
998
999 int
__gnat_open_append(char * path,int fmode)1000 __gnat_open_append (char *path, int fmode)
1001 {
1002 int fd;
1003 int o_fmode = O_BINARY;
1004
1005 if (fmode)
1006 o_fmode = O_TEXT;
1007
1008 #if defined (__MINGW32__)
1009 {
1010 TCHAR wpath[GNAT_MAX_PATH_LEN];
1011
1012 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
1013 fd = _topen (wpath, O_WRONLY | O_CREAT | O_APPEND | o_fmode, PERM);
1014 }
1015 #else
1016 fd = GNAT_OPEN (path, O_WRONLY | O_CREAT | O_APPEND | o_fmode, PERM);
1017 #endif
1018
1019 return fd < 0 ? -1 : fd;
1020 }
1021
1022 /* Open a new file. Return error (-1) if the file already exists. */
1023
1024 int
__gnat_open_new(char * path,int fmode)1025 __gnat_open_new (char *path, int fmode)
1026 {
1027 int fd;
1028 int o_fmode = O_BINARY;
1029
1030 if (fmode)
1031 o_fmode = O_TEXT;
1032
1033 #if defined (__MINGW32__)
1034 {
1035 TCHAR wpath[GNAT_MAX_PATH_LEN];
1036
1037 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
1038 fd = _topen (wpath, O_WRONLY | O_CREAT | O_EXCL | o_fmode, PERM);
1039 }
1040 #else
1041 fd = GNAT_OPEN (path, O_WRONLY | O_CREAT | O_EXCL | o_fmode, PERM);
1042 #endif
1043
1044 return fd < 0 ? -1 : fd;
1045 }
1046
1047 /* Open a new temp file. Return error (-1) if the file already exists. */
1048
1049 int
__gnat_open_new_temp(char * path,int fmode)1050 __gnat_open_new_temp (char *path, int fmode)
1051 {
1052 int fd;
1053 int o_fmode = O_BINARY;
1054
1055 strcpy (path, "GNAT-XXXXXX");
1056
1057 #if (defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) \
1058 || defined (__linux__) || defined (__GLIBC__) || defined (__ANDROID__) \
1059 || defined (__DragonFly__) || defined (__QNX__)) && !defined (__vxworks)
1060 return mkstemp (path);
1061 #elif defined (__Lynx__)
1062 mktemp (path);
1063 #else
1064 if (mktemp (path) == NULL)
1065 return -1;
1066 #endif
1067
1068 if (fmode)
1069 o_fmode = O_TEXT;
1070
1071 fd = GNAT_OPEN (path, O_WRONLY | O_CREAT | O_EXCL | o_fmode, PERM);
1072 return fd < 0 ? -1 : fd;
1073 }
1074
1075 int
__gnat_open(char * path,int fmode)1076 __gnat_open (char *path, int fmode)
1077 {
1078 int fd;
1079
1080 #if defined (__MINGW32__)
1081 {
1082 TCHAR wpath[GNAT_MAX_PATH_LEN];
1083
1084 S2WSC (wpath, path, GNAT_MAX_PATH_LEN);
1085 fd = _topen (wpath, fmode, PERM);
1086 }
1087 #else
1088 fd = GNAT_OPEN (path, fmode, PERM);
1089 #endif
1090
1091 return fd < 0 ? -1 : fd;
1092 }
1093
1094 /****************************************************************
1095 ** Perform a call to GNAT_STAT or GNAT_FSTAT, and extract as much information
1096 ** as possible from it, storing the result in a cache for later reuse
1097 ****************************************************************/
1098
1099 void
__gnat_stat_to_attr(int fd,char * name,struct file_attributes * attr)1100 __gnat_stat_to_attr (int fd, char* name, struct file_attributes* attr)
1101 {
1102 GNAT_STRUCT_STAT statbuf;
1103 int ret, error;
1104
1105 if (fd != -1) {
1106 /* GNAT_FSTAT returns -1 and sets errno for failure */
1107 ret = GNAT_FSTAT (fd, &statbuf);
1108 error = ret ? errno : 0;
1109
1110 } else {
1111 /* __gnat_stat returns errno value directly */
1112 error = __gnat_stat (name, &statbuf);
1113 ret = error ? -1 : 0;
1114 }
1115
1116 /*
1117 * A missing file is reported as an attr structure with error == 0 and
1118 * exists == 0.
1119 */
1120
1121 if (error == 0 || error == ENOENT)
1122 attr->error = 0;
1123 else
1124 attr->error = error;
1125
1126 attr->regular = (!ret && S_ISREG (statbuf.st_mode));
1127 attr->directory = (!ret && S_ISDIR (statbuf.st_mode));
1128
1129 if (!attr->regular)
1130 attr->file_length = 0;
1131 else
1132 /* st_size may be 32 bits, or 64 bits which is converted to long. We
1133 don't return a useful value for files larger than 2 gigabytes in
1134 either case. */
1135 attr->file_length = statbuf.st_size; /* all systems */
1136
1137 attr->exists = !ret;
1138
1139 #if !defined (_WIN32)
1140 /* on Windows requires extra system call, see __gnat_is_readable_file_attr */
1141 attr->readable = (!ret && (statbuf.st_mode & S_IRUSR));
1142 attr->writable = (!ret && (statbuf.st_mode & S_IWUSR));
1143 attr->executable = (!ret && (statbuf.st_mode & S_IXUSR));
1144 #endif
1145
1146 if (ret != 0) {
1147 attr->timestamp = (OS_Time)-1;
1148 } else {
1149 attr->timestamp = (OS_Time)statbuf.st_mtime;
1150 }
1151 }
1152
1153 /****************************************************************
1154 ** Return the number of bytes in the specified file
1155 ****************************************************************/
1156
1157 __int64
__gnat_file_length_attr(int fd,char * name,struct file_attributes * attr)1158 __gnat_file_length_attr (int fd, char* name, struct file_attributes* attr)
1159 {
1160 if (attr->file_length == -1) {
1161 __gnat_stat_to_attr (fd, name, attr);
1162 }
1163
1164 return attr->file_length;
1165 }
1166
1167 __int64
__gnat_file_length(int fd)1168 __gnat_file_length (int fd)
1169 {
1170 struct file_attributes attr;
1171 __gnat_reset_attributes (&attr);
1172 return __gnat_file_length_attr (fd, NULL, &attr);
1173 }
1174
1175 long
__gnat_file_length_long(int fd)1176 __gnat_file_length_long (int fd)
1177 {
1178 struct file_attributes attr;
1179 __gnat_reset_attributes (&attr);
1180 return (long)__gnat_file_length_attr (fd, NULL, &attr);
1181 }
1182
1183 __int64
__gnat_named_file_length(char * name)1184 __gnat_named_file_length (char *name)
1185 {
1186 struct file_attributes attr;
1187 __gnat_reset_attributes (&attr);
1188 return __gnat_file_length_attr (-1, name, &attr);
1189 }
1190
1191 /* Create a temporary filename and put it in string pointed to by
1192 TMP_FILENAME. */
1193
1194 void
__gnat_tmp_name(char * tmp_filename)1195 __gnat_tmp_name (char *tmp_filename)
1196 {
1197 #if defined (__MINGW32__)
1198 {
1199 char *pname;
1200 char prefix[25];
1201
1202 /* tempnam tries to create a temporary file in directory pointed to by
1203 TMP environment variable, in c:\temp if TMP is not set, and in
1204 directory specified by P_tmpdir in stdio.h if c:\temp does not
1205 exist. The filename will be created with the prefix "gnat-". */
1206
1207 sprintf (prefix, "gnat-%d-", (int)getpid());
1208 pname = (char *) _tempnam ("c:\\temp", prefix);
1209
1210 /* if pname is NULL, the file was not created properly, the disk is full
1211 or there is no more free temporary files */
1212
1213 if (pname == NULL)
1214 *tmp_filename = '\0';
1215
1216 /* If pname start with a back slash and not path information it means that
1217 the filename is valid for the current working directory. */
1218
1219 else if (pname[0] == '\\')
1220 {
1221 strcpy (tmp_filename, ".\\");
1222 strcat (tmp_filename, pname+1);
1223 }
1224 else
1225 strcpy (tmp_filename, pname);
1226
1227 free (pname);
1228 }
1229
1230 #elif defined (__linux__) || defined (__FreeBSD__) || defined (__NetBSD__) \
1231 || defined (__OpenBSD__) || defined (__GLIBC__) || defined (__ANDROID__) \
1232 || defined (__DragonFly__) || defined (__QNX__)
1233 #define MAX_SAFE_PATH 1000
1234 char *tmpdir = getenv ("TMPDIR");
1235
1236 /* If tmpdir is longer than MAX_SAFE_PATH, revert to default value to avoid
1237 a buffer overflow. */
1238 if (tmpdir == NULL || strlen (tmpdir) > MAX_SAFE_PATH)
1239 #ifdef __ANDROID__
1240 strcpy (tmp_filename, "/cache/gnat-XXXXXX");
1241 #else
1242 strcpy (tmp_filename, "/tmp/gnat-XXXXXX");
1243 #endif
1244 else
1245 sprintf (tmp_filename, "%s/gnat-XXXXXX", tmpdir);
1246
1247 close (mkstemp(tmp_filename));
1248 #elif defined (__vxworks) && !defined (VTHREADS)
1249 int index;
1250 char *pos;
1251 char *savepos;
1252 static ushort_t seed = 0; /* used to generate unique name */
1253
1254 /* Generate a unique name. */
1255 strcpy (tmp_filename, "tmp");
1256
1257 index = 5;
1258 savepos = pos = tmp_filename + strlen (tmp_filename) + index;
1259 *pos = '\0';
1260
1261 while (1)
1262 {
1263 FILE *f;
1264 ushort_t t;
1265
1266 /* Fill up the name buffer from the last position. */
1267 seed++;
1268 for (t = seed; --index >= 0; t >>= 3)
1269 *--pos = '0' + (t & 07);
1270
1271 /* Check to see if its unique, if not bump the seed and try again. */
1272 f = fopen (tmp_filename, "r");
1273 if (f == NULL)
1274 break;
1275 fclose (f);
1276 pos = savepos;
1277 index = 5;
1278 }
1279 #else
1280 tmpnam (tmp_filename);
1281 #endif
1282 }
1283
1284 /* Open directory and returns a DIR pointer. */
1285
__gnat_opendir(char * name)1286 DIR* __gnat_opendir (char *name)
1287 {
1288 #if defined (__MINGW32__)
1289 TCHAR wname[GNAT_MAX_PATH_LEN];
1290
1291 S2WSC (wname, name, GNAT_MAX_PATH_LEN);
1292 return (DIR*)_topendir (wname);
1293
1294 #else
1295 return opendir (name);
1296 #endif
1297 }
1298
1299 /* Read the next entry in a directory. The returned string points somewhere
1300 in the buffer. */
1301
1302 #if defined (__sun__)
1303 /* For Solaris, be sure to use the 64-bit version, otherwise NFS reads may
1304 fail with EOVERFLOW if the server uses 64-bit cookies. */
1305 #define dirent dirent64
1306 #define readdir readdir64
1307 #endif
1308
1309 char *
__gnat_readdir(DIR * dirp,char * buffer,int * len)1310 __gnat_readdir (DIR *dirp, char *buffer, int *len)
1311 {
1312 #if defined (__MINGW32__)
1313 struct _tdirent *dirent = _treaddir ((_TDIR*)dirp);
1314
1315 if (dirent != NULL)
1316 {
1317 WS2SC (buffer, dirent->d_name, GNAT_MAX_PATH_LEN);
1318 *len = strlen (buffer);
1319
1320 return buffer;
1321 }
1322 else
1323 return NULL;
1324
1325 #elif defined (HAVE_READDIR_R)
1326 /* If possible, try to use the thread-safe version. */
1327 if (readdir_r (dirp, buffer) != NULL)
1328 {
1329 *len = strlen (((struct dirent*) buffer)->d_name);
1330 return ((struct dirent*) buffer)->d_name;
1331 }
1332 else
1333 return NULL;
1334
1335 #else
1336 struct dirent *dirent = (struct dirent *) readdir (dirp);
1337
1338 if (dirent != NULL)
1339 {
1340 strcpy (buffer, dirent->d_name);
1341 *len = strlen (buffer);
1342 return buffer;
1343 }
1344 else
1345 return NULL;
1346
1347 #endif
1348 }
1349
1350 /* Close a directory entry. */
1351
__gnat_closedir(DIR * dirp)1352 int __gnat_closedir (DIR *dirp)
1353 {
1354 #if defined (__MINGW32__)
1355 return _tclosedir ((_TDIR*)dirp);
1356
1357 #else
1358 return closedir (dirp);
1359 #endif
1360 }
1361
1362 /* Returns 1 if readdir is thread safe, 0 otherwise. */
1363
1364 int
__gnat_readdir_is_thread_safe(void)1365 __gnat_readdir_is_thread_safe (void)
1366 {
1367 #ifdef HAVE_READDIR_R
1368 return 1;
1369 #else
1370 return 0;
1371 #endif
1372 }
1373
1374 #if defined (_WIN32)
1375 /* Number of seconds between <Jan 1st 1601> and <Jan 1st 1970>. */
1376 static const unsigned long long w32_epoch_offset = 11644473600ULL;
1377
1378 /* Returns the file modification timestamp using Win32 routines which are
1379 immune against daylight saving time change. It is in fact not possible to
1380 use fstat for this purpose as the DST modify the st_mtime field of the
1381 stat structure. */
1382
1383 static time_t
win32_filetime(HANDLE h)1384 win32_filetime (HANDLE h)
1385 {
1386 union
1387 {
1388 FILETIME ft_time;
1389 unsigned long long ull_time;
1390 } t_write;
1391
1392 /* GetFileTime returns FILETIME data which are the number of 100 nanosecs
1393 since <Jan 1st 1601>. This function must return the number of seconds
1394 since <Jan 1st 1970>. */
1395
1396 if (GetFileTime (h, NULL, NULL, &t_write.ft_time))
1397 return (time_t) (t_write.ull_time / 10000000ULL - w32_epoch_offset);
1398 return (time_t) 0;
1399 }
1400
1401 /* As above but starting from a FILETIME. */
1402 static void
f2t(const FILETIME * ft,__time64_t * t)1403 f2t (const FILETIME *ft, __time64_t *t)
1404 {
1405 union
1406 {
1407 FILETIME ft_time;
1408 unsigned long long ull_time;
1409 } t_write;
1410
1411 t_write.ft_time = *ft;
1412 *t = (__time64_t) (t_write.ull_time / 10000000ULL - w32_epoch_offset);
1413 }
1414 #endif
1415
1416 /* Return a GNAT time stamp given a file name. */
1417
1418 OS_Time
__gnat_file_time_name_attr(char * name,struct file_attributes * attr)1419 __gnat_file_time_name_attr (char* name, struct file_attributes* attr)
1420 {
1421 if (attr->timestamp == (OS_Time)-2) {
1422 #if defined (_WIN32)
1423 BOOL res;
1424 WIN32_FILE_ATTRIBUTE_DATA fad;
1425 __time64_t ret = -1;
1426 TCHAR wname[GNAT_MAX_PATH_LEN];
1427 S2WSC (wname, name, GNAT_MAX_PATH_LEN);
1428
1429 if ((res = GetFileAttributesEx (wname, GetFileExInfoStandard, &fad)))
1430 f2t (&fad.ftLastWriteTime, &ret);
1431 attr->timestamp = (OS_Time) ret;
1432 #else
1433 __gnat_stat_to_attr (-1, name, attr);
1434 #endif
1435 }
1436 return attr->timestamp;
1437 }
1438
1439 OS_Time
__gnat_file_time_name(char * name)1440 __gnat_file_time_name (char *name)
1441 {
1442 struct file_attributes attr;
1443 __gnat_reset_attributes (&attr);
1444 return __gnat_file_time_name_attr (name, &attr);
1445 }
1446
1447 /* Return a GNAT time stamp given a file descriptor. */
1448
1449 OS_Time
__gnat_file_time_fd_attr(int fd,struct file_attributes * attr)1450 __gnat_file_time_fd_attr (int fd, struct file_attributes* attr)
1451 {
1452 if (attr->timestamp == (OS_Time)-2) {
1453 #if defined (_WIN32)
1454 HANDLE h = (HANDLE) _get_osfhandle (fd);
1455 time_t ret = win32_filetime (h);
1456 attr->timestamp = (OS_Time) ret;
1457
1458 #else
1459 __gnat_stat_to_attr (fd, NULL, attr);
1460 #endif
1461 }
1462
1463 return attr->timestamp;
1464 }
1465
1466 OS_Time
__gnat_file_time_fd(int fd)1467 __gnat_file_time_fd (int fd)
1468 {
1469 struct file_attributes attr;
1470 __gnat_reset_attributes (&attr);
1471 return __gnat_file_time_fd_attr (fd, &attr);
1472 }
1473
1474 /* Set the file time stamp. */
1475
1476 void
__gnat_set_file_time_name(char * name,time_t time_stamp)1477 __gnat_set_file_time_name (char *name, time_t time_stamp)
1478 {
1479 #if defined (__vxworks)
1480
1481 /* Code to implement __gnat_set_file_time_name for these systems. */
1482
1483 #elif defined (_WIN32)
1484 union
1485 {
1486 FILETIME ft_time;
1487 unsigned long long ull_time;
1488 } t_write;
1489 TCHAR wname[GNAT_MAX_PATH_LEN];
1490
1491 S2WSC (wname, name, GNAT_MAX_PATH_LEN);
1492
1493 HANDLE h = CreateFile
1494 (wname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1495 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
1496 NULL);
1497 if (h == INVALID_HANDLE_VALUE)
1498 return;
1499 /* Add number of seconds between <Jan 1st 1601> and <Jan 1st 1970> */
1500 t_write.ull_time = ((unsigned long long)time_stamp + w32_epoch_offset);
1501 /* Convert to 100 nanosecond units */
1502 t_write.ull_time *= 10000000ULL;
1503
1504 SetFileTime(h, NULL, NULL, &t_write.ft_time);
1505 CloseHandle (h);
1506 return;
1507
1508 #else
1509 struct utimbuf utimbuf;
1510 time_t t;
1511
1512 /* Set modification time to requested time. */
1513 utimbuf.modtime = time_stamp;
1514
1515 /* Set access time to now in local time. */
1516 t = time (NULL);
1517 utimbuf.actime = mktime (localtime (&t));
1518
1519 utime (name, &utimbuf);
1520 #endif
1521 }
1522
1523 /* Get the list of installed standard libraries from the
1524 HKEY_LOCAL_MACHINE\SOFTWARE\Ada Core Technologies\GNAT\Standard Libraries
1525 key. */
1526
1527 char *
__gnat_get_libraries_from_registry(void)1528 __gnat_get_libraries_from_registry (void)
1529 {
1530 char *result = (char *) xmalloc (1);
1531
1532 result[0] = '\0';
1533
1534 #if defined (_WIN32) && ! defined (__vxworks) && ! defined (IS_CROSS)
1535
1536 HKEY reg_key;
1537 DWORD name_size, value_size;
1538 char name[256];
1539 char value[256];
1540 DWORD type;
1541 DWORD index;
1542 LONG res;
1543
1544 /* First open the key. */
1545 res = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE", 0, KEY_READ, ®_key);
1546
1547 if (res == ERROR_SUCCESS)
1548 res = RegOpenKeyExA (reg_key, "Ada Core Technologies", 0,
1549 KEY_READ, ®_key);
1550
1551 if (res == ERROR_SUCCESS)
1552 res = RegOpenKeyExA (reg_key, "GNAT", 0, KEY_READ, ®_key);
1553
1554 if (res == ERROR_SUCCESS)
1555 res = RegOpenKeyExA (reg_key, "Standard Libraries", 0, KEY_READ, ®_key);
1556
1557 /* If the key exists, read out all the values in it and concatenate them
1558 into a path. */
1559 for (index = 0; res == ERROR_SUCCESS; index++)
1560 {
1561 value_size = name_size = 256;
1562 res = RegEnumValueA (reg_key, index, name, &name_size, 0,
1563 &type, (LPBYTE)value, &value_size);
1564
1565 if (res == ERROR_SUCCESS && type == REG_SZ)
1566 {
1567 char *old_result = result;
1568
1569 result = (char *) xmalloc (strlen (old_result) + value_size + 2);
1570 strcpy (result, old_result);
1571 strcat (result, value);
1572 strcat (result, ";");
1573 free (old_result);
1574 }
1575 }
1576
1577 /* Remove the trailing ";". */
1578 if (result[0] != 0)
1579 result[strlen (result) - 1] = 0;
1580
1581 #endif
1582 return result;
1583 }
1584
1585 /* Query information for the given file NAME and return it in STATBUF.
1586 * Returns 0 for success, or errno value for failure.
1587 */
1588 int
__gnat_stat(char * name,GNAT_STRUCT_STAT * statbuf)1589 __gnat_stat (char *name, GNAT_STRUCT_STAT *statbuf)
1590 {
1591 #ifdef __MINGW32__
1592 WIN32_FILE_ATTRIBUTE_DATA fad;
1593 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
1594 int name_len;
1595 BOOL res;
1596 DWORD error;
1597
1598 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
1599 name_len = _tcslen (wname);
1600
1601 if (name_len > GNAT_MAX_PATH_LEN)
1602 return EINVAL;
1603
1604 ZeroMemory (statbuf, sizeof(GNAT_STRUCT_STAT));
1605
1606 res = GetFileAttributesEx (wname, GetFileExInfoStandard, &fad);
1607
1608 if (res == FALSE) {
1609 error = GetLastError();
1610
1611 /* Check file existence using GetFileAttributes() which does not fail on
1612 special Windows files like con:, aux:, nul: etc... */
1613
1614 if (GetFileAttributes(wname) != INVALID_FILE_ATTRIBUTES) {
1615 /* Just pretend that it is a regular and readable file */
1616 statbuf->st_mode = S_IFREG | S_IREAD | S_IWRITE;
1617 return 0;
1618 }
1619
1620 switch (error) {
1621 case ERROR_ACCESS_DENIED:
1622 case ERROR_SHARING_VIOLATION:
1623 case ERROR_LOCK_VIOLATION:
1624 case ERROR_SHARING_BUFFER_EXCEEDED:
1625 return EACCES;
1626 case ERROR_BUFFER_OVERFLOW:
1627 return ENAMETOOLONG;
1628 case ERROR_NOT_ENOUGH_MEMORY:
1629 return ENOMEM;
1630 default:
1631 return ENOENT;
1632 }
1633 }
1634
1635 f2t (&fad.ftCreationTime, &statbuf->st_ctime);
1636 f2t (&fad.ftLastWriteTime, &statbuf->st_mtime);
1637 f2t (&fad.ftLastAccessTime, &statbuf->st_atime);
1638
1639 statbuf->st_size =
1640 (__int64)fad.nFileSizeLow | (__int64)fad.nFileSizeHigh << 32;
1641
1642 /* We do not have the S_IEXEC attribute, but this is not used on GNAT. */
1643 statbuf->st_mode = S_IREAD;
1644
1645 if (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1646 statbuf->st_mode |= S_IFDIR;
1647 else
1648 statbuf->st_mode |= S_IFREG;
1649
1650 if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
1651 statbuf->st_mode |= S_IWRITE;
1652
1653 return 0;
1654
1655 #else
1656 return GNAT_STAT (name, statbuf) == 0 ? 0 : errno;
1657 #endif
1658 }
1659
1660 /*************************************************************************
1661 ** Check whether a file exists
1662 *************************************************************************/
1663
1664 int
__gnat_file_exists_attr(char * name,struct file_attributes * attr)1665 __gnat_file_exists_attr (char* name, struct file_attributes* attr)
1666 {
1667 if (attr->exists == ATTR_UNSET)
1668 __gnat_stat_to_attr (-1, name, attr);
1669
1670 return attr->exists;
1671 }
1672
1673 int
__gnat_file_exists(char * name)1674 __gnat_file_exists (char *name)
1675 {
1676 struct file_attributes attr;
1677 __gnat_reset_attributes (&attr);
1678 return __gnat_file_exists_attr (name, &attr);
1679 }
1680
1681 /**********************************************************************
1682 ** Whether name is an absolute path
1683 **********************************************************************/
1684
1685 int
__gnat_is_absolute_path(char * name,int length)1686 __gnat_is_absolute_path (char *name, int length)
1687 {
1688 #ifdef __vxworks
1689 /* On VxWorks systems, an absolute path can be represented (depending on
1690 the host platform) as either /dir/file, or device:/dir/file, or
1691 device:drive_letter:/dir/file. */
1692
1693 int index;
1694
1695 if (name[0] == '/')
1696 return 1;
1697
1698 for (index = 0; index < length; index++)
1699 {
1700 if (name[index] == ':' &&
1701 ((name[index + 1] == '/') ||
1702 (isalpha (name[index + 1]) && index + 2 <= length &&
1703 name[index + 2] == '/')))
1704 return 1;
1705
1706 else if (name[index] == '/')
1707 return 0;
1708 }
1709 return 0;
1710 #else
1711 return (length != 0) &&
1712 (*name == '/' || *name == DIR_SEPARATOR
1713 #if defined (WINNT) || defined(__DJGPP__)
1714 || (length > 1 && ISALPHA (name[0]) && name[1] == ':')
1715 #endif
1716 );
1717 #endif
1718 }
1719
1720 int
__gnat_is_regular_file_attr(char * name,struct file_attributes * attr)1721 __gnat_is_regular_file_attr (char* name, struct file_attributes* attr)
1722 {
1723 if (attr->regular == ATTR_UNSET)
1724 __gnat_stat_to_attr (-1, name, attr);
1725
1726 return attr->regular;
1727 }
1728
1729 int
__gnat_is_regular_file(char * name)1730 __gnat_is_regular_file (char *name)
1731 {
1732 struct file_attributes attr;
1733
1734 __gnat_reset_attributes (&attr);
1735 return __gnat_is_regular_file_attr (name, &attr);
1736 }
1737
1738 int
__gnat_is_regular_file_fd(int fd)1739 __gnat_is_regular_file_fd (int fd)
1740 {
1741 int ret;
1742 GNAT_STRUCT_STAT statbuf;
1743
1744 ret = GNAT_FSTAT (fd, &statbuf);
1745 return (!ret && S_ISREG (statbuf.st_mode));
1746 }
1747
1748 int
__gnat_is_directory_attr(char * name,struct file_attributes * attr)1749 __gnat_is_directory_attr (char* name, struct file_attributes* attr)
1750 {
1751 if (attr->directory == ATTR_UNSET)
1752 __gnat_stat_to_attr (-1, name, attr);
1753
1754 return attr->directory;
1755 }
1756
1757 int
__gnat_is_directory(char * name)1758 __gnat_is_directory (char *name)
1759 {
1760 struct file_attributes attr;
1761
1762 __gnat_reset_attributes (&attr);
1763 return __gnat_is_directory_attr (name, &attr);
1764 }
1765
1766 #if defined (_WIN32)
1767
1768 /* Returns the same constant as GetDriveType but takes a pathname as
1769 argument. */
1770
1771 static UINT
GetDriveTypeFromPath(TCHAR * wfullpath)1772 GetDriveTypeFromPath (TCHAR *wfullpath)
1773 {
1774 TCHAR wdrv[MAX_PATH];
1775 TCHAR wpath[MAX_PATH];
1776 TCHAR wfilename[MAX_PATH];
1777 TCHAR wext[MAX_PATH];
1778
1779 _tsplitpath (wfullpath, wdrv, wpath, wfilename, wext);
1780
1781 if (_tcslen (wdrv) != 0)
1782 {
1783 /* we have a drive specified. */
1784 _tcscat (wdrv, _T("\\"));
1785 return GetDriveType (wdrv);
1786 }
1787 else
1788 {
1789 /* No drive specified. */
1790
1791 /* Is this a relative path, if so get current drive type. */
1792 if (wpath[0] != _T('\\') ||
1793 (_tcslen (wpath) > 2 && wpath[0] == _T('\\')
1794 && wpath[1] != _T('\\')))
1795 return GetDriveType (NULL);
1796
1797 UINT result = GetDriveType (wpath);
1798
1799 /* Cannot guess the drive type, is this \\.\ ? */
1800
1801 if (result == DRIVE_NO_ROOT_DIR &&
1802 _tcslen (wpath) >= 4 && wpath[0] == _T('\\') && wpath[1] == _T('\\')
1803 && wpath[2] == _T('.') && wpath[3] == _T('\\'))
1804 {
1805 if (_tcslen (wpath) == 4)
1806 _tcscat (wpath, wfilename);
1807
1808 LPTSTR p = &wpath[4];
1809 LPTSTR b = _tcschr (p, _T('\\'));
1810
1811 if (b != NULL)
1812 {
1813 /* logical drive \\.\c\dir\file */
1814 *b++ = _T(':');
1815 *b++ = _T('\\');
1816 *b = _T('\0');
1817 }
1818 else
1819 _tcscat (p, _T(":\\"));
1820
1821 return GetDriveType (p);
1822 }
1823
1824 return result;
1825 }
1826 }
1827
1828 /* This MingW section contains code to work with ACL. */
1829 static int
__gnat_check_OWNER_ACL(TCHAR * wname,DWORD CheckAccessDesired,GENERIC_MAPPING CheckGenericMapping)1830 __gnat_check_OWNER_ACL (TCHAR *wname,
1831 DWORD CheckAccessDesired,
1832 GENERIC_MAPPING CheckGenericMapping)
1833 {
1834 DWORD dwAccessDesired, dwAccessAllowed;
1835 PRIVILEGE_SET PrivilegeSet;
1836 DWORD dwPrivSetSize = sizeof (PRIVILEGE_SET);
1837 BOOL fAccessGranted = FALSE;
1838 HANDLE hToken = NULL;
1839 DWORD nLength = 0;
1840 PSECURITY_DESCRIPTOR pSD = NULL;
1841
1842 GetFileSecurity
1843 (wname, OWNER_SECURITY_INFORMATION |
1844 GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
1845 NULL, 0, &nLength);
1846
1847 if ((pSD = (SECURITY_DESCRIPTOR *) HeapAlloc
1848 (GetProcessHeap (), HEAP_ZERO_MEMORY, nLength)) == NULL)
1849 return 0;
1850
1851 /* Obtain the security descriptor. */
1852
1853 if (!GetFileSecurity
1854 (wname, OWNER_SECURITY_INFORMATION |
1855 GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
1856 pSD, nLength, &nLength))
1857 goto error;
1858
1859 if (!ImpersonateSelf (SecurityImpersonation))
1860 goto error;
1861
1862 if (!OpenThreadToken
1863 (GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken))
1864 goto error;
1865
1866 /* Undoes the effect of ImpersonateSelf. */
1867
1868 RevertToSelf ();
1869
1870 /* We want to test for write permissions. */
1871
1872 dwAccessDesired = CheckAccessDesired;
1873
1874 MapGenericMask (&dwAccessDesired, &CheckGenericMapping);
1875
1876 if (!AccessCheck
1877 (pSD , /* security descriptor to check */
1878 hToken, /* impersonation token */
1879 dwAccessDesired, /* requested access rights */
1880 &CheckGenericMapping, /* pointer to GENERIC_MAPPING */
1881 &PrivilegeSet, /* receives privileges used in check */
1882 &dwPrivSetSize, /* size of PrivilegeSet buffer */
1883 &dwAccessAllowed, /* receives mask of allowed access rights */
1884 &fAccessGranted))
1885 goto error;
1886
1887 CloseHandle (hToken);
1888 HeapFree (GetProcessHeap (), 0, pSD);
1889 return fAccessGranted;
1890
1891 error:
1892 if (hToken)
1893 CloseHandle (hToken);
1894 HeapFree (GetProcessHeap (), 0, pSD);
1895 return 0;
1896 }
1897
1898 static void
__gnat_set_OWNER_ACL(TCHAR * wname,ACCESS_MODE AccessMode,DWORD AccessPermissions)1899 __gnat_set_OWNER_ACL (TCHAR *wname,
1900 ACCESS_MODE AccessMode,
1901 DWORD AccessPermissions)
1902 {
1903 PACL pOldDACL = NULL;
1904 PACL pNewDACL = NULL;
1905 PSECURITY_DESCRIPTOR pSD = NULL;
1906 EXPLICIT_ACCESS ea;
1907 TCHAR username [100];
1908 DWORD unsize = 100;
1909
1910 /* Get current user, he will act as the owner */
1911
1912 if (!GetUserName (username, &unsize))
1913 return;
1914
1915 if (GetNamedSecurityInfo
1916 (wname,
1917 SE_FILE_OBJECT,
1918 DACL_SECURITY_INFORMATION,
1919 NULL, NULL, &pOldDACL, NULL, &pSD) != ERROR_SUCCESS)
1920 return;
1921
1922 BuildExplicitAccessWithName
1923 (&ea, username, AccessPermissions, (ACCESS_MODE) AccessMode, NO_INHERITANCE);
1924
1925 if (AccessMode == SET_ACCESS)
1926 {
1927 /* SET_ACCESS, we want to set an explicte set of permissions, do not
1928 merge with current DACL. */
1929 if (SetEntriesInAcl (1, &ea, NULL, &pNewDACL) != ERROR_SUCCESS)
1930 return;
1931 }
1932 else
1933 if (SetEntriesInAcl (1, &ea, pOldDACL, &pNewDACL) != ERROR_SUCCESS)
1934 return;
1935
1936 if (SetNamedSecurityInfo
1937 (wname, SE_FILE_OBJECT,
1938 DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL) != ERROR_SUCCESS)
1939 return;
1940
1941 LocalFree (pSD);
1942 LocalFree (pNewDACL);
1943 }
1944
1945 /* Check if it is possible to use ACL for wname, the file must not be on a
1946 network drive. */
1947
1948 static int
__gnat_can_use_acl(TCHAR * wname)1949 __gnat_can_use_acl (TCHAR *wname)
1950 {
1951 return __gnat_use_acl && GetDriveTypeFromPath (wname) != DRIVE_REMOTE;
1952 }
1953
1954 #endif /* defined (_WIN32) */
1955
1956 int
__gnat_is_readable_file_attr(char * name,struct file_attributes * attr)1957 __gnat_is_readable_file_attr (char* name, struct file_attributes* attr)
1958 {
1959 if (attr->readable == ATTR_UNSET)
1960 {
1961 #if defined (_WIN32)
1962 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
1963 GENERIC_MAPPING GenericMapping;
1964
1965 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
1966
1967 if (__gnat_can_use_acl (wname))
1968 {
1969 ZeroMemory (&GenericMapping, sizeof (GENERIC_MAPPING));
1970 GenericMapping.GenericRead = GENERIC_READ;
1971 attr->readable =
1972 __gnat_check_OWNER_ACL (wname, FILE_READ_DATA, GenericMapping);
1973 }
1974 else
1975 attr->readable = GetFileAttributes (wname) != INVALID_FILE_ATTRIBUTES;
1976 #else
1977 __gnat_stat_to_attr (-1, name, attr);
1978 #endif
1979 }
1980
1981 return attr->readable;
1982 }
1983
1984 int
__gnat_is_read_accessible_file(char * name)1985 __gnat_is_read_accessible_file (char *name)
1986 {
1987 #if defined (_WIN32)
1988 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
1989
1990 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
1991
1992 return !_waccess (wname, 4);
1993
1994 #elif defined (__vxworks)
1995 int fd;
1996
1997 if ((fd = open (name, O_RDONLY, 0)) < 0)
1998 return 0;
1999 close (fd);
2000 return 1;
2001
2002 #else
2003 return !access (name, R_OK);
2004 #endif
2005 }
2006
2007 int
__gnat_is_readable_file(char * name)2008 __gnat_is_readable_file (char *name)
2009 {
2010 struct file_attributes attr;
2011
2012 __gnat_reset_attributes (&attr);
2013 return __gnat_is_readable_file_attr (name, &attr);
2014 }
2015
2016 int
__gnat_is_writable_file_attr(char * name,struct file_attributes * attr)2017 __gnat_is_writable_file_attr (char* name, struct file_attributes* attr)
2018 {
2019 if (attr->writable == ATTR_UNSET)
2020 {
2021 #if defined (_WIN32)
2022 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2023 GENERIC_MAPPING GenericMapping;
2024
2025 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2026
2027 if (__gnat_can_use_acl (wname))
2028 {
2029 ZeroMemory (&GenericMapping, sizeof (GENERIC_MAPPING));
2030 GenericMapping.GenericWrite = GENERIC_WRITE;
2031
2032 attr->writable = __gnat_check_OWNER_ACL
2033 (wname, FILE_WRITE_DATA | FILE_APPEND_DATA, GenericMapping)
2034 && !(GetFileAttributes (wname) & FILE_ATTRIBUTE_READONLY);
2035 }
2036 else
2037 attr->writable =
2038 !(GetFileAttributes (wname) & FILE_ATTRIBUTE_READONLY);
2039
2040 #else
2041 __gnat_stat_to_attr (-1, name, attr);
2042 #endif
2043 }
2044
2045 return attr->writable;
2046 }
2047
2048 int
__gnat_is_writable_file(char * name)2049 __gnat_is_writable_file (char *name)
2050 {
2051 struct file_attributes attr;
2052
2053 __gnat_reset_attributes (&attr);
2054 return __gnat_is_writable_file_attr (name, &attr);
2055 }
2056
2057 int
__gnat_is_write_accessible_file(char * name)2058 __gnat_is_write_accessible_file (char *name)
2059 {
2060 #if defined (_WIN32)
2061 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2062
2063 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2064
2065 return !_waccess (wname, 2);
2066
2067 #elif defined (__vxworks)
2068 int fd;
2069
2070 if ((fd = open (name, O_WRONLY, 0)) < 0)
2071 return 0;
2072 close (fd);
2073 return 1;
2074
2075 #else
2076 return !access (name, W_OK);
2077 #endif
2078 }
2079
2080 int
__gnat_is_executable_file_attr(char * name,struct file_attributes * attr)2081 __gnat_is_executable_file_attr (char* name, struct file_attributes* attr)
2082 {
2083 if (attr->executable == ATTR_UNSET)
2084 {
2085 #if defined (_WIN32)
2086 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2087 GENERIC_MAPPING GenericMapping;
2088
2089 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2090
2091 if (__gnat_can_use_acl (wname))
2092 {
2093 ZeroMemory (&GenericMapping, sizeof (GENERIC_MAPPING));
2094 GenericMapping.GenericExecute = GENERIC_EXECUTE;
2095
2096 attr->executable =
2097 __gnat_check_OWNER_ACL (wname, FILE_EXECUTE, GenericMapping);
2098 }
2099 else
2100 {
2101 TCHAR *l, *last = _tcsstr(wname, _T(".exe"));
2102
2103 /* look for last .exe */
2104 if (last)
2105 while ((l = _tcsstr(last+1, _T(".exe"))))
2106 last = l;
2107
2108 attr->executable =
2109 GetFileAttributes (wname) != INVALID_FILE_ATTRIBUTES
2110 && (last - wname) == (int) (_tcslen (wname) - 4);
2111 }
2112 #else
2113 __gnat_stat_to_attr (-1, name, attr);
2114 #endif
2115 }
2116
2117 return attr->regular && attr->executable;
2118 }
2119
2120 int
__gnat_is_executable_file(char * name)2121 __gnat_is_executable_file (char *name)
2122 {
2123 struct file_attributes attr;
2124
2125 __gnat_reset_attributes (&attr);
2126 return __gnat_is_executable_file_attr (name, &attr);
2127 }
2128
2129 void
__gnat_set_writable(char * name)2130 __gnat_set_writable (char *name)
2131 {
2132 #if defined (_WIN32)
2133 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2134
2135 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2136
2137 if (__gnat_can_use_acl (wname))
2138 __gnat_set_OWNER_ACL (wname, GRANT_ACCESS, FILE_GENERIC_WRITE);
2139
2140 SetFileAttributes
2141 (wname, GetFileAttributes (wname) & ~FILE_ATTRIBUTE_READONLY);
2142 #elif ! (defined (__vxworks) && _WRS_VXWORKS_MAJOR < 6)
2143 GNAT_STRUCT_STAT statbuf;
2144
2145 if (GNAT_STAT (name, &statbuf) == 0)
2146 {
2147 statbuf.st_mode = statbuf.st_mode | S_IWUSR;
2148 chmod (name, statbuf.st_mode);
2149 }
2150 #endif
2151 }
2152
2153 /* must match definition in s-os_lib.ads */
2154 #define S_OWNER 1
2155 #define S_GROUP 2
2156 #define S_OTHERS 4
2157
2158 void
__gnat_set_executable(char * name,int mode ATTRIBUTE_UNUSED)2159 __gnat_set_executable (char *name, int mode ATTRIBUTE_UNUSED)
2160 {
2161 #if defined (_WIN32)
2162 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2163
2164 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2165
2166 if (__gnat_can_use_acl (wname))
2167 __gnat_set_OWNER_ACL (wname, GRANT_ACCESS, FILE_GENERIC_EXECUTE);
2168
2169 #elif ! (defined (__vxworks) && _WRS_VXWORKS_MAJOR < 6)
2170 GNAT_STRUCT_STAT statbuf;
2171
2172 if (GNAT_STAT (name, &statbuf) == 0)
2173 {
2174 if (mode & S_OWNER)
2175 statbuf.st_mode = statbuf.st_mode | S_IXUSR;
2176 if (mode & S_GROUP)
2177 statbuf.st_mode = statbuf.st_mode | S_IXGRP;
2178 if (mode & S_OTHERS)
2179 statbuf.st_mode = statbuf.st_mode | S_IXOTH;
2180 chmod (name, statbuf.st_mode);
2181 }
2182 #endif
2183 }
2184
2185 void
__gnat_set_non_writable(char * name)2186 __gnat_set_non_writable (char *name)
2187 {
2188 #if defined (_WIN32)
2189 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2190
2191 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2192
2193 if (__gnat_can_use_acl (wname))
2194 __gnat_set_OWNER_ACL
2195 (wname, DENY_ACCESS,
2196 FILE_WRITE_DATA | FILE_APPEND_DATA |
2197 FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES);
2198
2199 SetFileAttributes
2200 (wname, GetFileAttributes (wname) | FILE_ATTRIBUTE_READONLY);
2201 #elif ! (defined (__vxworks) && _WRS_VXWORKS_MAJOR < 6)
2202 GNAT_STRUCT_STAT statbuf;
2203
2204 if (GNAT_STAT (name, &statbuf) == 0)
2205 {
2206 statbuf.st_mode = statbuf.st_mode & 07577;
2207 chmod (name, statbuf.st_mode);
2208 }
2209 #endif
2210 }
2211
2212 void
__gnat_set_readable(char * name)2213 __gnat_set_readable (char *name)
2214 {
2215 #if defined (_WIN32)
2216 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2217
2218 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2219
2220 if (__gnat_can_use_acl (wname))
2221 __gnat_set_OWNER_ACL (wname, GRANT_ACCESS, FILE_GENERIC_READ);
2222
2223 #elif ! (defined (__vxworks) && _WRS_VXWORKS_MAJOR < 6)
2224 GNAT_STRUCT_STAT statbuf;
2225
2226 if (GNAT_STAT (name, &statbuf) == 0)
2227 {
2228 chmod (name, statbuf.st_mode | S_IREAD);
2229 }
2230 #endif
2231 }
2232
2233 void
__gnat_set_non_readable(char * name)2234 __gnat_set_non_readable (char *name)
2235 {
2236 #if defined (_WIN32)
2237 TCHAR wname [GNAT_MAX_PATH_LEN + 2];
2238
2239 S2WSC (wname, name, GNAT_MAX_PATH_LEN + 2);
2240
2241 if (__gnat_can_use_acl (wname))
2242 __gnat_set_OWNER_ACL (wname, DENY_ACCESS, FILE_GENERIC_READ);
2243
2244 #elif ! (defined (__vxworks) && _WRS_VXWORKS_MAJOR < 6)
2245 GNAT_STRUCT_STAT statbuf;
2246
2247 if (GNAT_STAT (name, &statbuf) == 0)
2248 {
2249 chmod (name, statbuf.st_mode & (~S_IREAD));
2250 }
2251 #endif
2252 }
2253
2254 int
__gnat_is_symbolic_link_attr(char * name ATTRIBUTE_UNUSED,struct file_attributes * attr)2255 __gnat_is_symbolic_link_attr (char* name ATTRIBUTE_UNUSED,
2256 struct file_attributes* attr)
2257 {
2258 if (attr->symbolic_link == ATTR_UNSET)
2259 {
2260 #if defined (__vxworks)
2261 attr->symbolic_link = 0;
2262
2263 #elif defined (_AIX) || defined (__APPLE__) || defined (__unix__)
2264 int ret;
2265 GNAT_STRUCT_STAT statbuf;
2266 ret = GNAT_LSTAT (name, &statbuf);
2267 attr->symbolic_link = (!ret && S_ISLNK (statbuf.st_mode));
2268 #else
2269 attr->symbolic_link = 0;
2270 #endif
2271 }
2272 return attr->symbolic_link;
2273 }
2274
2275 int
__gnat_is_symbolic_link(char * name ATTRIBUTE_UNUSED)2276 __gnat_is_symbolic_link (char *name ATTRIBUTE_UNUSED)
2277 {
2278 struct file_attributes attr;
2279
2280 __gnat_reset_attributes (&attr);
2281 return __gnat_is_symbolic_link_attr (name, &attr);
2282 }
2283
2284 #if defined (__sun__)
2285 /* Using fork on Solaris will duplicate all the threads. fork1, which
2286 duplicates only the active thread, must be used instead, or spawning
2287 subprocess from a program with tasking will lead into numerous problems. */
2288 #define fork fork1
2289 #endif
2290
2291 int
__gnat_portable_spawn(char * args[]ATTRIBUTE_UNUSED)2292 __gnat_portable_spawn (char *args[] ATTRIBUTE_UNUSED)
2293 {
2294 int status ATTRIBUTE_UNUSED = 0;
2295 int finished ATTRIBUTE_UNUSED;
2296 int pid ATTRIBUTE_UNUSED;
2297
2298 #if defined (__vxworks) || defined(__PikeOS__)
2299 return -1;
2300
2301 #elif defined (__DJGPP__) || defined (_WIN32)
2302 /* args[0] must be quotes as it could contain a full pathname with spaces */
2303 char *args_0 = args[0];
2304 args[0] = (char *)xmalloc (strlen (args_0) + 3);
2305 strcpy (args[0], "\"");
2306 strcat (args[0], args_0);
2307 strcat (args[0], "\"");
2308
2309 status = spawnvp (P_WAIT, args_0, (char ** const)args);
2310
2311 /* restore previous value */
2312 free (args[0]);
2313 args[0] = (char *)args_0;
2314
2315 if (status < 0)
2316 return -1;
2317 else
2318 return status;
2319
2320 #else
2321
2322 pid = fork ();
2323 if (pid < 0)
2324 return -1;
2325
2326 if (pid == 0)
2327 {
2328 /* The child. */
2329 if (execv (args[0], MAYBE_TO_PTR32 (args)) != 0)
2330 _exit (1);
2331 }
2332
2333 /* The parent. */
2334 finished = waitpid (pid, &status, 0);
2335
2336 if (finished != pid || WIFEXITED (status) == 0)
2337 return -1;
2338
2339 return WEXITSTATUS (status);
2340 #endif
2341
2342 return 0;
2343 }
2344
2345 /* Create a copy of the given file descriptor.
2346 Return -1 if an error occurred. */
2347
2348 int
__gnat_dup(int oldfd)2349 __gnat_dup (int oldfd)
2350 {
2351 #if defined (__vxworks) && !defined (__RTP__)
2352 /* Not supported on VxWorks 5.x, but supported on VxWorks 6.0 when using
2353 RTPs. */
2354 return -1;
2355 #else
2356 return dup (oldfd);
2357 #endif
2358 }
2359
2360 /* Make newfd be the copy of oldfd, closing newfd first if necessary.
2361 Return -1 if an error occurred. */
2362
2363 int
__gnat_dup2(int oldfd ATTRIBUTE_UNUSED,int newfd ATTRIBUTE_UNUSED)2364 __gnat_dup2 (int oldfd ATTRIBUTE_UNUSED, int newfd ATTRIBUTE_UNUSED)
2365 {
2366 #if defined (__vxworks) && !defined (__RTP__)
2367 /* Not supported on VxWorks 5.x, but supported on VxWorks 6.0 when using
2368 RTPs. */
2369 return -1;
2370 #elif defined (__PikeOS__)
2371 /* Not supported. */
2372 return -1;
2373 #elif defined (_WIN32)
2374 /* Special case when oldfd and newfd are identical and are the standard
2375 input, output or error as this makes Windows XP hangs. Note that we
2376 do that only for standard file descriptors that are known to be valid. */
2377 if (oldfd == newfd && newfd >= 0 && newfd <= 2)
2378 return newfd;
2379 else
2380 return dup2 (oldfd, newfd);
2381 #else
2382 return dup2 (oldfd, newfd);
2383 #endif
2384 }
2385
2386 int
__gnat_number_of_cpus(void)2387 __gnat_number_of_cpus (void)
2388 {
2389 int cores = 1;
2390
2391 #if defined (__linux__) || defined (__sun__) || defined (_AIX) \
2392 || defined (__APPLE__) || defined (__FreeBSD__) || defined (__OpenBSD__) \
2393 || defined (__DragonFly__) || defined (__NetBSD__)
2394 cores = (int) sysconf (_SC_NPROCESSORS_ONLN);
2395
2396 #elif defined (__QNX__)
2397 cores = (int) _syspage_ptr->num_cpu;
2398
2399 #elif defined (__hpux__)
2400 struct pst_dynamic psd;
2401 if (pstat_getdynamic (&psd, sizeof (psd), 1, 0) != -1)
2402 cores = (int) psd.psd_proc_cnt;
2403
2404 #elif defined (_WIN32)
2405 SYSTEM_INFO sysinfo;
2406 GetSystemInfo (&sysinfo);
2407 cores = (int) sysinfo.dwNumberOfProcessors;
2408
2409 #elif defined (_WRS_CONFIG_SMP)
2410 unsigned int vxCpuConfiguredGet (void);
2411
2412 cores = vxCpuConfiguredGet ();
2413
2414 #endif
2415
2416 return cores;
2417 }
2418
2419 /* WIN32 code to implement a wait call that wait for any child process. */
2420
2421 #if defined (_WIN32)
2422
2423 /* Synchronization code, to be thread safe. */
2424
2425 #ifdef CERT
2426
2427 /* For the Cert run times on native Windows we use dummy functions
2428 for locking and unlocking tasks since we do not support multiple
2429 threads on this configuration (Cert run time on native Windows). */
2430
EnterCS(void)2431 static void EnterCS (void) {}
LeaveCS(void)2432 static void LeaveCS (void) {}
SignalListChanged(void)2433 static void SignalListChanged (void) {}
2434
2435 #else
2436
2437 CRITICAL_SECTION ProcListCS;
2438 HANDLE ProcListEvt = NULL;
2439
EnterCS(void)2440 static void EnterCS (void)
2441 {
2442 EnterCriticalSection(&ProcListCS);
2443 }
2444
LeaveCS(void)2445 static void LeaveCS (void)
2446 {
2447 LeaveCriticalSection(&ProcListCS);
2448 }
2449
SignalListChanged(void)2450 static void SignalListChanged (void)
2451 {
2452 SetEvent (ProcListEvt);
2453 }
2454
2455 #endif
2456
2457 static HANDLE *HANDLES_LIST = NULL;
2458 static int *PID_LIST = NULL, plist_length = 0, plist_max_length = 0;
2459
2460 static void
add_handle(HANDLE h,int pid)2461 add_handle (HANDLE h, int pid)
2462 {
2463 /* -------------------- critical section -------------------- */
2464 EnterCS();
2465
2466 if (plist_length == plist_max_length)
2467 {
2468 plist_max_length += 100;
2469 HANDLES_LIST =
2470 (HANDLE *) xrealloc (HANDLES_LIST, sizeof (HANDLE) * plist_max_length);
2471 PID_LIST =
2472 (int *) xrealloc (PID_LIST, sizeof (int) * plist_max_length);
2473 }
2474
2475 HANDLES_LIST[plist_length] = h;
2476 PID_LIST[plist_length] = pid;
2477 ++plist_length;
2478
2479 SignalListChanged();
2480 LeaveCS();
2481 /* -------------------- critical section -------------------- */
2482 }
2483
2484 int
__gnat_win32_remove_handle(HANDLE h,int pid)2485 __gnat_win32_remove_handle (HANDLE h, int pid)
2486 {
2487 int j;
2488 int found = 0;
2489
2490 /* -------------------- critical section -------------------- */
2491 EnterCS();
2492
2493 for (j = 0; j < plist_length; j++)
2494 {
2495 if ((HANDLES_LIST[j] == h) || (PID_LIST[j] == pid))
2496 {
2497 CloseHandle (h);
2498 --plist_length;
2499 HANDLES_LIST[j] = HANDLES_LIST[plist_length];
2500 PID_LIST[j] = PID_LIST[plist_length];
2501 found = 1;
2502 break;
2503 }
2504 }
2505
2506 LeaveCS();
2507 /* -------------------- critical section -------------------- */
2508
2509 if (found)
2510 SignalListChanged();
2511
2512 return found;
2513 }
2514
2515 static void
win32_no_block_spawn(char * command,char * args[],HANDLE * h,int * pid)2516 win32_no_block_spawn (char *command, char *args[], HANDLE *h, int *pid)
2517 {
2518 BOOL result;
2519 STARTUPINFO SI;
2520 PROCESS_INFORMATION PI;
2521 SECURITY_ATTRIBUTES SA;
2522 int csize = 1;
2523 char *full_command;
2524 int k;
2525
2526 /* compute the total command line length */
2527 k = 0;
2528 while (args[k])
2529 {
2530 csize += strlen (args[k]) + 1;
2531 k++;
2532 }
2533
2534 full_command = (char *) xmalloc (csize);
2535
2536 /* Startup info. */
2537 SI.cb = sizeof (STARTUPINFO);
2538 SI.lpReserved = NULL;
2539 SI.lpReserved2 = NULL;
2540 SI.lpDesktop = NULL;
2541 SI.cbReserved2 = 0;
2542 SI.lpTitle = NULL;
2543 SI.dwFlags = 0;
2544 SI.wShowWindow = SW_HIDE;
2545
2546 /* Security attributes. */
2547 SA.nLength = sizeof (SECURITY_ATTRIBUTES);
2548 SA.bInheritHandle = TRUE;
2549 SA.lpSecurityDescriptor = NULL;
2550
2551 /* Prepare the command string. */
2552 strcpy (full_command, command);
2553 strcat (full_command, " ");
2554
2555 k = 1;
2556 while (args[k])
2557 {
2558 strcat (full_command, args[k]);
2559 strcat (full_command, " ");
2560 k++;
2561 }
2562
2563 {
2564 int wsize = csize * 2;
2565 TCHAR *wcommand = (TCHAR *) xmalloc (wsize);
2566
2567 S2WSC (wcommand, full_command, wsize);
2568
2569 free (full_command);
2570
2571 result = CreateProcess
2572 (NULL, wcommand, &SA, NULL, TRUE,
2573 GetPriorityClass (GetCurrentProcess()), NULL, NULL, &SI, &PI);
2574
2575 free (wcommand);
2576 }
2577
2578 if (result == TRUE)
2579 {
2580 CloseHandle (PI.hThread);
2581 *h = PI.hProcess;
2582 *pid = PI.dwProcessId;
2583 }
2584 else
2585 {
2586 *h = NULL;
2587 *pid = 0;
2588 }
2589 }
2590
2591 static int
win32_wait(int * status)2592 win32_wait (int *status)
2593 {
2594 DWORD exitcode, pid;
2595 HANDLE *hl;
2596 HANDLE h;
2597 int *pidl;
2598 DWORD res;
2599 int hl_len;
2600 int found;
2601 int pos;
2602
2603 START_WAIT:
2604
2605 if (plist_length == 0)
2606 {
2607 errno = ECHILD;
2608 return -1;
2609 }
2610
2611 /* -------------------- critical section -------------------- */
2612 EnterCS();
2613
2614 /* ??? We can't wait for more than MAXIMUM_WAIT_OBJECTS due to a Win32
2615 limitation */
2616 if (plist_length < MAXIMUM_WAIT_OBJECTS)
2617 hl_len = plist_length;
2618 else
2619 {
2620 errno = EINVAL;
2621 return -1;
2622 }
2623
2624 #ifdef CERT
2625 hl = (HANDLE *) xmalloc (sizeof (HANDLE) * hl_len);
2626 memmove (hl, HANDLES_LIST, sizeof (HANDLE) * hl_len);
2627 pidl = (int *) xmalloc (sizeof (int) * hl_len);
2628 memmove (pidl, PID_LIST, sizeof (int) * hl_len);
2629 #else
2630 /* Note that index 0 contains the event handle that is signaled when the
2631 process list has changed */
2632 hl = (HANDLE *) xmalloc (sizeof (HANDLE) * (hl_len + 1));
2633 hl[0] = ProcListEvt;
2634 memmove (&hl[1], HANDLES_LIST, sizeof (HANDLE) * hl_len);
2635 pidl = (int *) xmalloc (sizeof (int) * (hl_len + 1));
2636 memmove (&pidl[1], PID_LIST, sizeof (int) * hl_len);
2637 hl_len++;
2638 #endif
2639
2640 LeaveCS();
2641 /* -------------------- critical section -------------------- */
2642
2643 res = WaitForMultipleObjects (hl_len, hl, FALSE, INFINITE);
2644
2645 /* If there was an error, exit now */
2646 if (res == WAIT_FAILED)
2647 {
2648 free (hl);
2649 free (pidl);
2650 errno = EINVAL;
2651 return -1;
2652 }
2653
2654 /* if the ProcListEvt has been signaled then the list of processes has been
2655 updated to add or remove a handle, just loop over */
2656
2657 if (res - WAIT_OBJECT_0 == 0)
2658 {
2659 free (hl);
2660 free (pidl);
2661 goto START_WAIT;
2662 }
2663
2664 /* Handle two distinct groups of return codes: finished waits and abandoned
2665 waits */
2666
2667 if (res < WAIT_ABANDONED_0)
2668 pos = res - WAIT_OBJECT_0;
2669 else
2670 pos = res - WAIT_ABANDONED_0;
2671
2672 h = hl[pos];
2673 GetExitCodeProcess (h, &exitcode);
2674 pid = pidl [pos];
2675
2676 found = __gnat_win32_remove_handle (h, -1);
2677
2678 free (hl);
2679 free (pidl);
2680
2681 /* if not found another process waiting has already handled this process */
2682
2683 if (!found)
2684 {
2685 goto START_WAIT;
2686 }
2687
2688 *status = (int) exitcode;
2689 return (int) pid;
2690 }
2691
2692 #endif
2693
2694 int
__gnat_portable_no_block_spawn(char * args[]ATTRIBUTE_UNUSED)2695 __gnat_portable_no_block_spawn (char *args[] ATTRIBUTE_UNUSED)
2696 {
2697
2698 #if defined (__vxworks) || defined (__PikeOS__)
2699 /* Not supported. */
2700 return -1;
2701
2702 #elif defined(__DJGPP__)
2703 if (spawnvp (P_WAIT, args[0], args) != 0)
2704 return -1;
2705 else
2706 return 0;
2707
2708 #elif defined (_WIN32)
2709
2710 HANDLE h = NULL;
2711 int pid;
2712
2713 win32_no_block_spawn (args[0], args, &h, &pid);
2714 if (h != NULL)
2715 {
2716 add_handle (h, pid);
2717 return pid;
2718 }
2719 else
2720 return -1;
2721
2722 #else
2723
2724 int pid = fork ();
2725
2726 if (pid == 0)
2727 {
2728 /* The child. */
2729 if (execv (args[0], MAYBE_TO_PTR32 (args)) != 0)
2730 _exit (1);
2731 }
2732
2733 return pid;
2734
2735 #endif
2736 }
2737
2738 int
__gnat_portable_wait(int * process_status)2739 __gnat_portable_wait (int *process_status)
2740 {
2741 int status = 0;
2742 int pid = 0;
2743
2744 #if defined (__vxworks) || defined (__PikeOS__)
2745 /* Not sure what to do here, so do nothing but return zero. */
2746
2747 #elif defined (_WIN32)
2748
2749 pid = win32_wait (&status);
2750
2751 #elif defined (__DJGPP__)
2752 /* Child process has already ended in case of DJGPP.
2753 No need to do anything. Just return success. */
2754 #else
2755
2756 pid = waitpid (-1, &status, 0);
2757 status = status & 0xffff;
2758 #endif
2759
2760 *process_status = status;
2761 return pid;
2762 }
2763
2764 int
__gnat_portable_no_block_wait(int * process_status)2765 __gnat_portable_no_block_wait (int *process_status)
2766 {
2767 int status = 0;
2768 int pid = 0;
2769
2770 #if defined (__vxworks) || defined (__PikeOS__) || defined (_WIN32)
2771 /* Not supported. */
2772 status = -1;
2773
2774 #else
2775
2776 pid = waitpid (-1, &status, WNOHANG);
2777 status = status & 0xffff;
2778 #endif
2779
2780 *process_status = status;
2781 return pid;
2782 }
2783
2784 void
__gnat_os_exit(int status)2785 __gnat_os_exit (int status)
2786 {
2787 exit (status);
2788 }
2789
2790 int
__gnat_current_process_id(void)2791 __gnat_current_process_id (void)
2792 {
2793 #if defined (__vxworks) || defined (__PikeOS__)
2794 return -1;
2795
2796 #elif defined (_WIN32)
2797
2798 return (int)GetCurrentProcessId();
2799
2800 #else
2801
2802 return (int)getpid();
2803 #endif
2804 }
2805
2806 /* Locate file on path, that matches a predicate */
2807
2808 char *
__gnat_locate_file_with_predicate(char * file_name,char * path_val,int (* predicate)(char *))2809 __gnat_locate_file_with_predicate (char *file_name, char *path_val,
2810 int (*predicate)(char *))
2811 {
2812 char *ptr;
2813 char *file_path = (char *) alloca (strlen (file_name) + 1);
2814 int absolute;
2815
2816 /* Return immediately if file_name is empty */
2817
2818 if (*file_name == '\0')
2819 return 0;
2820
2821 /* Remove quotes around file_name if present */
2822
2823 ptr = file_name;
2824 if (*ptr == '"')
2825 ptr++;
2826
2827 strcpy (file_path, ptr);
2828
2829 ptr = file_path + strlen (file_path) - 1;
2830
2831 if (*ptr == '"')
2832 *ptr = '\0';
2833
2834 /* Handle absolute pathnames. */
2835
2836 absolute = __gnat_is_absolute_path (file_path, strlen (file_name));
2837
2838 if (absolute)
2839 {
2840 if (predicate (file_path))
2841 return xstrdup (file_path);
2842
2843 return 0;
2844 }
2845
2846 /* If file_name include directory separator(s), try it first as
2847 a path name relative to the current directory */
2848 for (ptr = file_name; *ptr && *ptr != '/' && *ptr != DIR_SEPARATOR; ptr++)
2849 ;
2850
2851 if (*ptr != 0)
2852 {
2853 if (predicate (file_name))
2854 return xstrdup (file_name);
2855 }
2856
2857 if (path_val == 0)
2858 return 0;
2859
2860 {
2861 /* The result has to be smaller than path_val + file_name. */
2862 char *file_path =
2863 (char *) alloca (strlen (path_val) + strlen (file_name) + 2);
2864
2865 for (;;)
2866 {
2867 /* Skip the starting quote */
2868
2869 if (*path_val == '"')
2870 path_val++;
2871
2872 for (ptr = file_path; *path_val && *path_val != PATH_SEPARATOR; )
2873 *ptr++ = *path_val++;
2874
2875 /* If directory is empty, it is the current directory*/
2876
2877 if (ptr == file_path)
2878 {
2879 *ptr = '.';
2880 }
2881 else
2882 ptr--;
2883
2884 /* Skip the ending quote */
2885
2886 if (*ptr == '"')
2887 ptr--;
2888
2889 if (*ptr != '/' && *ptr != DIR_SEPARATOR)
2890 *++ptr = DIR_SEPARATOR;
2891
2892 strcpy (++ptr, file_name);
2893
2894 if (predicate (file_path))
2895 return xstrdup (file_path);
2896
2897 if (*path_val == 0)
2898 return 0;
2899
2900 /* Skip path separator */
2901
2902 path_val++;
2903 }
2904 }
2905
2906 return 0;
2907 }
2908
2909 /* Locate an executable file, give a Path value. */
2910
2911 char *
__gnat_locate_executable_file(char * file_name,char * path_val)2912 __gnat_locate_executable_file (char *file_name, char *path_val)
2913 {
2914 return __gnat_locate_file_with_predicate
2915 (file_name, path_val, &__gnat_is_executable_file);
2916 }
2917
2918 /* Locate a regular file, give a Path value. */
2919
2920 char *
__gnat_locate_regular_file(char * file_name,char * path_val)2921 __gnat_locate_regular_file (char *file_name, char *path_val)
2922 {
2923 return __gnat_locate_file_with_predicate
2924 (file_name, path_val, &__gnat_is_regular_file);
2925 }
2926
2927 /* Locate an executable given a Path argument. This routine is only used by
2928 gnatbl and should not be used otherwise. Use locate_exec_on_path
2929 instead. */
2930
2931 char *
__gnat_locate_exec(char * exec_name,char * path_val)2932 __gnat_locate_exec (char *exec_name, char *path_val)
2933 {
2934 const unsigned int len = strlen (HOST_EXECUTABLE_SUFFIX);
2935 char *ptr;
2936
2937 if (len > 0 && !strstr (exec_name, HOST_EXECUTABLE_SUFFIX))
2938 {
2939 char *full_exec_name = (char *) alloca (strlen (exec_name) + len + 1);
2940
2941 strcpy (full_exec_name, exec_name);
2942 strcat (full_exec_name, HOST_EXECUTABLE_SUFFIX);
2943 ptr = __gnat_locate_executable_file (full_exec_name, path_val);
2944
2945 if (ptr == 0)
2946 return __gnat_locate_executable_file (exec_name, path_val);
2947 return ptr;
2948 }
2949 else
2950 return __gnat_locate_executable_file (exec_name, path_val);
2951 }
2952
2953 /* Locate an executable using the Systems default PATH. */
2954
2955 char *
__gnat_locate_exec_on_path(char * exec_name)2956 __gnat_locate_exec_on_path (char *exec_name)
2957 {
2958 char *apath_val;
2959
2960 #if defined (_WIN32)
2961 TCHAR *wpath_val = _tgetenv (_T("PATH"));
2962 TCHAR *wapath_val;
2963 /* In Win32 systems we expand the PATH as for XP environment
2964 variables are not automatically expanded. We also prepend the
2965 ".;" to the path to match normal NT path search semantics */
2966
2967 #define EXPAND_BUFFER_SIZE 32767
2968
2969 wapath_val = (TCHAR *) alloca (EXPAND_BUFFER_SIZE);
2970
2971 wapath_val [0] = '.';
2972 wapath_val [1] = ';';
2973
2974 DWORD res = ExpandEnvironmentStrings
2975 (wpath_val, &wapath_val[2], EXPAND_BUFFER_SIZE - 2);
2976
2977 if (!res) wapath_val [0] = _T('\0');
2978
2979 apath_val = (char *) alloca (EXPAND_BUFFER_SIZE);
2980
2981 WS2SC (apath_val, wapath_val, EXPAND_BUFFER_SIZE);
2982
2983 #else
2984 const char *path_val = getenv ("PATH");
2985
2986 /* If PATH is not defined, proceed with __gnat_locate_exec anyway, so we can
2987 find files that contain directory names. */
2988
2989 if (path_val == NULL) path_val = "";
2990 apath_val = (char *) alloca (strlen (path_val) + 1);
2991 strcpy (apath_val, path_val);
2992 #endif
2993
2994 return __gnat_locate_exec (exec_name, apath_val);
2995 }
2996
2997 /* Dummy functions for Osint import for non-VMS systems.
2998 ??? To be removed. */
2999
3000 int
__gnat_to_canonical_file_list_init(char * dirspec ATTRIBUTE_UNUSED,int onlydirs ATTRIBUTE_UNUSED)3001 __gnat_to_canonical_file_list_init (char *dirspec ATTRIBUTE_UNUSED,
3002 int onlydirs ATTRIBUTE_UNUSED)
3003 {
3004 return 0;
3005 }
3006
3007 char *
__gnat_to_canonical_file_list_next(void)3008 __gnat_to_canonical_file_list_next (void)
3009 {
3010 static char empty[] = "";
3011 return empty;
3012 }
3013
3014 void
__gnat_to_canonical_file_list_free(void)3015 __gnat_to_canonical_file_list_free (void)
3016 {
3017 }
3018
3019 char *
__gnat_to_canonical_dir_spec(char * dirspec,int prefixflag ATTRIBUTE_UNUSED)3020 __gnat_to_canonical_dir_spec (char *dirspec, int prefixflag ATTRIBUTE_UNUSED)
3021 {
3022 return dirspec;
3023 }
3024
3025 char *
__gnat_to_canonical_file_spec(char * filespec)3026 __gnat_to_canonical_file_spec (char *filespec)
3027 {
3028 return filespec;
3029 }
3030
3031 char *
__gnat_to_canonical_path_spec(char * pathspec)3032 __gnat_to_canonical_path_spec (char *pathspec)
3033 {
3034 return pathspec;
3035 }
3036
3037 char *
__gnat_to_host_dir_spec(char * dirspec,int prefixflag ATTRIBUTE_UNUSED)3038 __gnat_to_host_dir_spec (char *dirspec, int prefixflag ATTRIBUTE_UNUSED)
3039 {
3040 return dirspec;
3041 }
3042
3043 char *
__gnat_to_host_file_spec(char * filespec)3044 __gnat_to_host_file_spec (char *filespec)
3045 {
3046 return filespec;
3047 }
3048
3049 void
__gnat_adjust_os_resource_limits(void)3050 __gnat_adjust_os_resource_limits (void)
3051 {
3052 }
3053
3054 #if defined (__mips_vxworks)
3055 int
_flush_cache(void)3056 _flush_cache (void)
3057 {
3058 CACHE_USER_FLUSH (0, ENTIRE_CACHE);
3059 }
3060 #endif
3061
3062 #if defined (_WIN32)
3063 int __gnat_argument_needs_quote = 1;
3064 #else
3065 int __gnat_argument_needs_quote = 0;
3066 #endif
3067
3068 /* This option is used to enable/disable object files handling from the
3069 binder file by the GNAT Project module. For example, this is disabled on
3070 Windows (prior to GCC 3.4) as it is already done by the mdll module.
3071 Stating with GCC 3.4 the shared libraries are not based on mdll
3072 anymore as it uses the GCC's -shared option */
3073 #if defined (_WIN32) \
3074 && ((__GNUC__ < 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ < 4)))
3075 int __gnat_prj_add_obj_files = 0;
3076 #else
3077 int __gnat_prj_add_obj_files = 1;
3078 #endif
3079
3080 /* char used as prefix/suffix for environment variables */
3081 #if defined (_WIN32)
3082 char __gnat_environment_char = '%';
3083 #else
3084 char __gnat_environment_char = '$';
3085 #endif
3086
3087 /* This functions copy the file attributes from a source file to a
3088 destination file.
3089
3090 mode = 0 : In this mode copy only the file time stamps (last access and
3091 last modification time stamps).
3092
3093 mode = 1 : In this mode, time stamps and read/write/execute attributes are
3094 copied.
3095
3096 mode = 2 : In this mode, only read/write/execute attributes are copied
3097
3098 Returns 0 if operation was successful and -1 in case of error. */
3099
3100 int
__gnat_copy_attribs(char * from ATTRIBUTE_UNUSED,char * to ATTRIBUTE_UNUSED,int mode ATTRIBUTE_UNUSED)3101 __gnat_copy_attribs (char *from ATTRIBUTE_UNUSED, char *to ATTRIBUTE_UNUSED,
3102 int mode ATTRIBUTE_UNUSED)
3103 {
3104 #if (defined (__vxworks) && _WRS_VXWORKS_MAJOR < 6)
3105 return -1;
3106
3107 #elif defined (_WIN32)
3108 TCHAR wfrom [GNAT_MAX_PATH_LEN + 2];
3109 TCHAR wto [GNAT_MAX_PATH_LEN + 2];
3110 BOOL res;
3111 FILETIME fct, flat, flwt;
3112 HANDLE hfrom, hto;
3113
3114 S2WSC (wfrom, from, GNAT_MAX_PATH_LEN + 2);
3115 S2WSC (wto, to, GNAT_MAX_PATH_LEN + 2);
3116
3117 /* Do we need to copy the timestamp ? */
3118
3119 if (mode != 2) {
3120 /* retrieve from times */
3121
3122 hfrom = CreateFile
3123 (wfrom, GENERIC_READ, 0, NULL, OPEN_EXISTING,
3124 FILE_ATTRIBUTE_NORMAL, NULL);
3125
3126 if (hfrom == INVALID_HANDLE_VALUE)
3127 return -1;
3128
3129 res = GetFileTime (hfrom, &fct, &flat, &flwt);
3130
3131 CloseHandle (hfrom);
3132
3133 if (res == 0)
3134 return -1;
3135
3136 /* retrieve from times */
3137
3138 hto = CreateFile
3139 (wto, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
3140 FILE_ATTRIBUTE_NORMAL, NULL);
3141
3142 if (hto == INVALID_HANDLE_VALUE)
3143 return -1;
3144
3145 res = SetFileTime (hto, NULL, &flat, &flwt);
3146
3147 CloseHandle (hto);
3148
3149 if (res == 0)
3150 return -1;
3151 }
3152
3153 /* Do we need to copy the permissions ? */
3154 /* Set file attributes in full mode. */
3155
3156 if (mode != 0)
3157 {
3158 DWORD attribs = GetFileAttributes (wfrom);
3159
3160 if (attribs == INVALID_FILE_ATTRIBUTES)
3161 return -1;
3162
3163 res = SetFileAttributes (wto, attribs);
3164 if (res == 0)
3165 return -1;
3166 }
3167
3168 return 0;
3169
3170 #else
3171 GNAT_STRUCT_STAT fbuf;
3172 struct utimbuf tbuf;
3173
3174 if (GNAT_STAT (from, &fbuf) == -1) {
3175 return -1;
3176 }
3177
3178 /* Do we need to copy timestamp ? */
3179 if (mode != 2) {
3180 tbuf.actime = fbuf.st_atime;
3181 tbuf.modtime = fbuf.st_mtime;
3182
3183 if (utime (to, &tbuf) == -1) {
3184 return -1;
3185 }
3186 }
3187
3188 /* Do we need to copy file permissions ? */
3189 if (mode != 0 && (chmod (to, fbuf.st_mode) == -1)) {
3190 return -1;
3191 }
3192
3193 return 0;
3194 #endif
3195 }
3196
3197 int
__gnat_lseek(int fd,long offset,int whence)3198 __gnat_lseek (int fd, long offset, int whence)
3199 {
3200 return (int) lseek (fd, offset, whence);
3201 }
3202
3203 /* This function returns the major version number of GCC being used. */
3204 int
get_gcc_version(void)3205 get_gcc_version (void)
3206 {
3207 #ifdef IN_RTS
3208 return __GNUC__;
3209 #else
3210 return (int) (version_string[0] - '0');
3211 #endif
3212 }
3213
3214 /*
3215 * Set Close_On_Exec as indicated.
3216 * Note: this is used for both GNAT.OS_Lib and GNAT.Sockets.
3217 */
3218
3219 int
__gnat_set_close_on_exec(int fd ATTRIBUTE_UNUSED,int close_on_exec_p ATTRIBUTE_UNUSED)3220 __gnat_set_close_on_exec (int fd ATTRIBUTE_UNUSED,
3221 int close_on_exec_p ATTRIBUTE_UNUSED)
3222 {
3223 #if defined (F_GETFD) && defined (FD_CLOEXEC) && ! defined (__vxworks)
3224 int flags = fcntl (fd, F_GETFD, 0);
3225 if (flags < 0)
3226 return flags;
3227 if (close_on_exec_p)
3228 flags |= FD_CLOEXEC;
3229 else
3230 flags &= ~FD_CLOEXEC;
3231 return fcntl (fd, F_SETFD, flags);
3232 #elif defined(_WIN32)
3233 HANDLE h = (HANDLE) _get_osfhandle (fd);
3234 if (h == (HANDLE) -1)
3235 return -1;
3236 if (close_on_exec_p)
3237 return ! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0);
3238 return ! SetHandleInformation (h, HANDLE_FLAG_INHERIT,
3239 HANDLE_FLAG_INHERIT);
3240 #else
3241 /* TODO: Unimplemented. */
3242 return -1;
3243 #endif
3244 }
3245
3246 /* Indicates if platforms supports automatic initialization through the
3247 constructor mechanism */
3248 int
__gnat_binder_supports_auto_init(void)3249 __gnat_binder_supports_auto_init (void)
3250 {
3251 return 1;
3252 }
3253
3254 /* Indicates that Stand-Alone Libraries are automatically initialized through
3255 the constructor mechanism */
3256 int
__gnat_sals_init_using_constructors(void)3257 __gnat_sals_init_using_constructors (void)
3258 {
3259 #if defined (__vxworks) || defined (__Lynx__)
3260 return 0;
3261 #else
3262 return 1;
3263 #endif
3264 }
3265
3266 #if defined (__linux__) || defined (__ANDROID__)
3267 /* There is no function in the glibc to retrieve the LWP of the current
3268 thread. We need to do a system call in order to retrieve this
3269 information. */
3270 #include <sys/syscall.h>
3271 void *
__gnat_lwp_self(void)3272 __gnat_lwp_self (void)
3273 {
3274 return (void *) syscall (__NR_gettid);
3275 }
3276 #endif
3277
3278 #if defined (__APPLE__)
3279 # if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060
3280 # include <mach/thread_info.h>
3281 # include <mach/mach_init.h>
3282 # include <mach/thread_act.h>
3283 # else
3284 # include <pthread.h>
3285 # endif
3286
3287 /* System-wide thread identifier. Note it could be truncated on 32 bit
3288 hosts.
3289 Previously was: pthread_mach_thread_np (pthread_self ()). */
3290 void *
__gnat_lwp_self(void)3291 __gnat_lwp_self (void)
3292 {
3293 #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060
3294 thread_identifier_info_data_t data;
3295 mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
3296 kern_return_t kret;
3297
3298 kret = thread_info (mach_thread_self (), THREAD_IDENTIFIER_INFO,
3299 (thread_info_t) &data, &count);
3300 if (kret == KERN_SUCCESS)
3301 return (void *)(uintptr_t)data.thread_id;
3302 else
3303 return 0;
3304 #else
3305 return (void *)pthread_mach_thread_np (pthread_self ());
3306 #endif
3307 }
3308 #endif
3309
3310 #if defined (__linux__)
3311 #include <sched.h>
3312
3313 /* glibc versions earlier than 2.7 do not define the routines to handle
3314 dynamically allocated CPU sets. For these targets, we use the static
3315 versions. */
3316
3317 #ifdef CPU_ALLOC
3318
3319 /* Dynamic cpu sets */
3320
3321 cpu_set_t *
__gnat_cpu_alloc(size_t count)3322 __gnat_cpu_alloc (size_t count)
3323 {
3324 return CPU_ALLOC (count);
3325 }
3326
3327 size_t
__gnat_cpu_alloc_size(size_t count)3328 __gnat_cpu_alloc_size (size_t count)
3329 {
3330 return CPU_ALLOC_SIZE (count);
3331 }
3332
3333 void
__gnat_cpu_free(cpu_set_t * set)3334 __gnat_cpu_free (cpu_set_t *set)
3335 {
3336 CPU_FREE (set);
3337 }
3338
3339 void
__gnat_cpu_zero(size_t count,cpu_set_t * set)3340 __gnat_cpu_zero (size_t count, cpu_set_t *set)
3341 {
3342 CPU_ZERO_S (count, set);
3343 }
3344
3345 void
__gnat_cpu_set(int cpu,size_t count,cpu_set_t * set)3346 __gnat_cpu_set (int cpu, size_t count, cpu_set_t *set)
3347 {
3348 /* Ada handles CPU numbers starting from 1, while C identifies the first
3349 CPU by a 0, so we need to adjust. */
3350 CPU_SET_S (cpu - 1, count, set);
3351 }
3352
3353 #else /* !CPU_ALLOC */
3354
3355 /* Static cpu sets */
3356
3357 cpu_set_t *
__gnat_cpu_alloc(size_t count ATTRIBUTE_UNUSED)3358 __gnat_cpu_alloc (size_t count ATTRIBUTE_UNUSED)
3359 {
3360 return (cpu_set_t *) xmalloc (sizeof (cpu_set_t));
3361 }
3362
3363 size_t
__gnat_cpu_alloc_size(size_t count ATTRIBUTE_UNUSED)3364 __gnat_cpu_alloc_size (size_t count ATTRIBUTE_UNUSED)
3365 {
3366 return sizeof (cpu_set_t);
3367 }
3368
3369 void
__gnat_cpu_free(cpu_set_t * set)3370 __gnat_cpu_free (cpu_set_t *set)
3371 {
3372 free (set);
3373 }
3374
3375 void
__gnat_cpu_zero(size_t count ATTRIBUTE_UNUSED,cpu_set_t * set)3376 __gnat_cpu_zero (size_t count ATTRIBUTE_UNUSED, cpu_set_t *set)
3377 {
3378 CPU_ZERO (set);
3379 }
3380
3381 void
__gnat_cpu_set(int cpu,size_t count ATTRIBUTE_UNUSED,cpu_set_t * set)3382 __gnat_cpu_set (int cpu, size_t count ATTRIBUTE_UNUSED, cpu_set_t *set)
3383 {
3384 /* Ada handles CPU numbers starting from 1, while C identifies the first
3385 CPU by a 0, so we need to adjust. */
3386 CPU_SET (cpu - 1, set);
3387 }
3388 #endif /* !CPU_ALLOC */
3389 #endif /* __linux__ */
3390
3391 /* Return the load address of the executable, or 0 if not known. In the
3392 specific case of error, (void *)-1 can be returned. Beware: this unit may
3393 be in a shared library. As low-level units are needed, we allow #include
3394 here. */
3395
3396 #if defined (__APPLE__)
3397 #include <mach-o/dyld.h>
3398 #endif
3399
3400 const void *
__gnat_get_executable_load_address(void)3401 __gnat_get_executable_load_address (void)
3402 {
3403 #if defined (__APPLE__)
3404 return _dyld_get_image_header (0);
3405
3406 #elif 0 && defined (__linux__)
3407 /* Currently disabled as it needs at least -ldl. */
3408 struct link_map *map = _r_debug.r_map;
3409
3410 return (const void *)map->l_addr;
3411
3412 #else
3413 return NULL;
3414 #endif
3415 }
3416
3417 void
__gnat_kill(int pid,int sig,int close ATTRIBUTE_UNUSED)3418 __gnat_kill (int pid, int sig, int close ATTRIBUTE_UNUSED)
3419 {
3420 #if defined(_WIN32)
3421 HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
3422 if (h == NULL)
3423 return;
3424 if (sig == 9)
3425 {
3426 TerminateProcess (h, 1);
3427 }
3428 else if (sig == SIGINT)
3429 GenerateConsoleCtrlEvent (CTRL_C_EVENT, pid);
3430 else if (sig == SIGBREAK)
3431 GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid);
3432 /* ??? The last two alternatives don't really work. SIGBREAK requires setting
3433 up process groups at start time which we don't do; treating SIGINT is just
3434 not possible apparently. So we really only support signal 9. Fortunately
3435 that's all we use in GNAT.Expect */
3436
3437 CloseHandle (h);
3438 #elif defined (__vxworks)
3439 /* Not implemented */
3440 #else
3441 kill (pid, sig);
3442 #endif
3443 }
3444
__gnat_killprocesstree(int pid,int sig_num)3445 void __gnat_killprocesstree (int pid, int sig_num)
3446 {
3447 #if defined(_WIN32)
3448 PROCESSENTRY32 pe;
3449
3450 memset(&pe, 0, sizeof(PROCESSENTRY32));
3451 pe.dwSize = sizeof(PROCESSENTRY32);
3452
3453 HANDLE hSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
3454
3455 /* cannot take snapshot, just kill the parent process */
3456
3457 if (hSnap == INVALID_HANDLE_VALUE)
3458 {
3459 __gnat_kill (pid, sig_num, 1);
3460 return;
3461 }
3462
3463 if (Process32First(hSnap, &pe))
3464 {
3465 BOOL bContinue = TRUE;
3466
3467 /* kill child processes first */
3468
3469 while (bContinue)
3470 {
3471 if (pe.th32ParentProcessID == (DWORD)pid)
3472 __gnat_killprocesstree (pe.th32ProcessID, sig_num);
3473
3474 bContinue = Process32Next (hSnap, &pe);
3475 }
3476 }
3477
3478 CloseHandle (hSnap);
3479
3480 /* kill process */
3481
3482 __gnat_kill (pid, sig_num, 1);
3483
3484 #elif defined (__vxworks)
3485 /* not implemented */
3486
3487 #elif defined (__linux__)
3488 DIR *dir;
3489 struct dirent *d;
3490
3491 /* read all processes' pid and ppid */
3492
3493 dir = opendir ("/proc");
3494
3495 /* cannot open proc, just kill the parent process */
3496
3497 if (!dir)
3498 {
3499 __gnat_kill (pid, sig_num, 1);
3500 return;
3501 }
3502
3503 /* kill child processes first */
3504
3505 while ((d = readdir (dir)) != NULL)
3506 {
3507 if ((d->d_type & DT_DIR) == DT_DIR)
3508 {
3509 char statfile[64];
3510 int _pid, _ppid;
3511
3512 /* read /proc/<PID>/stat */
3513
3514 if (strlen (d->d_name) >= sizeof (statfile) - strlen ("/proc//stat"))
3515 continue;
3516 strcpy (statfile, "/proc/");
3517 strcat (statfile, d->d_name);
3518 strcat (statfile, "/stat");
3519
3520 FILE *fd = fopen (statfile, "r");
3521
3522 if (fd)
3523 {
3524 const int match = fscanf (fd, "%d %*s %*s %d", &_pid, &_ppid);
3525 fclose (fd);
3526
3527 if (match == 2 && _ppid == pid)
3528 __gnat_killprocesstree (_pid, sig_num);
3529 }
3530 }
3531 }
3532
3533 closedir (dir);
3534
3535 /* kill process */
3536
3537 __gnat_kill (pid, sig_num, 1);
3538 #else
3539 __gnat_kill (pid, sig_num, 1);
3540 #endif
3541 /* Note on Solaris it is possible to read /proc/<PID>/status.
3542 The 5th and 6th words are the pid and the 7th and 8th the ppid.
3543 See: /usr/include/sys/procfs.h (struct pstatus).
3544 */
3545 }
3546
3547 #ifdef __cplusplus
3548 }
3549 #endif
3550