1#!perl -T
2
3BEGIN {
4    use Config;
5    use Test::More;
6    plan skip_all => "POSIX is unavailable" if $Config{'extensions'} !~ m!\bPOSIX\b!;
7}
8
9use strict;
10use File::Spec;
11use POSIX;
12
13sub check(@) {
14    grep { eval "&$_;1" or $@!~/vendor has not defined POSIX macro/ } @_
15}
16
17my @path_consts = check qw(
18    _PC_CHOWN_RESTRICTED _PC_LINK_MAX _PC_NAME_MAX
19    _PC_NO_TRUNC _PC_PATH_MAX
20);
21
22my @path_consts_terminal = check qw(
23    _PC_MAX_CANON _PC_MAX_INPUT _PC_VDISABLE
24);
25
26my @path_consts_fifo = check qw(
27    _PC_PIPE_BUF
28);
29
30my @sys_consts = check qw(
31    _SC_ARG_MAX _SC_CHILD_MAX _SC_CLK_TCK _SC_JOB_CONTROL
32    _SC_NGROUPS_MAX _SC_OPEN_MAX _SC_PAGESIZE _SC_SAVED_IDS
33    _SC_STREAM_MAX _SC_VERSION _SC_TZNAME_MAX
34);
35
36my $tests = 2 * 2 * @path_consts +
37            2 * 2 * @path_consts_terminal +
38            2 * 2 * @path_consts_fifo +
39                1 * @sys_consts;
40plan $tests
41     ? (tests => $tests)
42     : (skip_all => "No tests to run on this OS")
43;
44
45# Don't test on "." as it can be networked storage which returns EINVAL
46# Testing on "/" may not be portable to non-Unix as it may not be readable
47# "/tmp" should be readable and likely also local.
48my $testdir = File::Spec->tmpdir;
49$testdir = VMS::Filespec::fileify($testdir) if $^O eq 'VMS';
50
51my $r;
52
53my $TTY = "/dev/tty";
54
55sub _check_and_report {
56    my ($sub, $constant, $description) = @_;
57    $! = 0;
58    my $return_val = eval {$sub->(eval "$constant()")};
59    my $errno = $!; # Grab this before anything else changes it.
60    is($@, '', $description);
61
62    # We can't test sysconf further without investigating the type of argument
63    # provided
64    return if $description =~ /sysconf/;
65
66    if (defined $return_val) {
67	like($return_val, qr/\A(?:-?[1-9][0-9]*|0 but true)\z/,
68	     'the returned value should be a signed integer');
69    } else {
70      SKIP:
71	{
72	    # POSIX specifies EINVAL is returned if the f?pathconf()
73	    # isn't implemented for the specific path
74	    skip "$description not implemented for this path", 1
75		if $errno == EINVAL && $description =~ /pathconf/;
76	    cmp_ok($errno, '==', 0, 'errno should be 0 as before the call')
77		or diag("\$!: $errno");
78	}
79    }
80}
81
82# testing fpathconf() on a non-terminal file
83SKIP: {
84    my $fd = POSIX::open($testdir, O_RDONLY)
85        or skip "could not open test directory '$testdir' ($!)",
86	  2 * @path_consts;
87
88    for my $constant (@path_consts) {
89        SKIP: {
90            skip "pathconf($constant) hangs on Android", 2 if $constant eq '_PC_LINK_MAX' && $^O =~ /android/;
91            _check_and_report(sub { fpathconf($fd, shift) }, $constant,
92			  "calling fpathconf($fd, $constant)");
93        }
94    }
95
96    POSIX::close($fd);
97}
98
99# testing pathconf() on a non-terminal file
100for my $constant (@path_consts) {
101   SKIP: {
102      skip "pathconf($constant) hangs on Android", 2 if $constant eq '_PC_LINK_MAX' && $^O =~ /android/;
103    _check_and_report(sub { pathconf($testdir, shift) }, $constant,
104		      "calling pathconf('$testdir', $constant)");
105   }
106}
107
108SKIP: {
109    my $n = 2 * 2 * @path_consts_terminal;
110
111    -c $TTY
112	or skip("$TTY not a character file", $n);
113    open(TTY, '<', $TTY)
114	or skip("failed to open $TTY: $!", $n);
115    -t TTY
116	or skip("TTY ($TTY) not a terminal file", $n);
117
118    my $fd = fileno(TTY);
119
120    # testing fpathconf() on a terminal file
121    for my $constant (@path_consts_terminal) {
122	_check_and_report(sub { fpathconf($fd, shift) }, $constant,
123			  "calling fpathconf($fd, $constant) ($TTY)");
124    }
125
126    close($fd);
127    # testing pathconf() on a terminal file
128    for my $constant (@path_consts_terminal) {
129	_check_and_report(sub { pathconf($TTY, shift) }, $constant,
130			  "calling pathconf($TTY, $constant)");
131    }
132}
133
134my $fifo = "fifo$$";
135
136SKIP: {
137    eval { mkfifo($fifo, 0666) }
138	or skip("could not create fifo $fifo ($!)", 2 * 2 * @path_consts_fifo);
139
140  SKIP: {
141      my $fd = POSIX::open($fifo, O_RDONLY | O_NONBLOCK)
142	  or skip("could not open $fifo ($!)", 2 * @path_consts_fifo);
143
144      for my $constant (@path_consts_fifo) {
145	  _check_and_report(sub { fpathconf($fd, shift) }, $constant,
146			    "calling fpathconf($fd, $constant) ($fifo)");
147      }
148
149      POSIX::close($fd);
150  }
151
152  # testing pathconf() on a fifo file
153  for my $constant (@path_consts_fifo) {
154      _check_and_report(sub { pathconf($fifo, shift) }, $constant,
155			"calling pathconf($fifo, $constant");
156  }
157}
158
159END {
160    1 while unlink($fifo);
161}
162
163SKIP: {
164    if($^O eq 'cygwin') {
165        pop @sys_consts;
166        skip("No _SC_TZNAME_MAX on Cygwin", 1);
167    }
168
169}
170# testing sysconf()
171for my $constant (@sys_consts) {
172    _check_and_report(sub {sysconf(shift)}, $constant,
173		      "calling sysconf($constant)");
174}
175