Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • Haskell-mouse/unix
1 result
Show changes
Commits on Source (392)
Showing
with 1135 additions and 215 deletions
# task:
# name: FreeBSD
# freebsd_instance:
# image_family: freebsd-13-2
# install_script: pkg install -y ghc hs-cabal-install git autoconf
# script:
# - cabal update
# - autoreconf -i
# - cabal test --test-show-details=direct
task:
name: OpenBSD
compute_engine_instance:
image_project: pg-ci-images
# See https://github.com/anarazel/pg-vm-images/blob/main/packer/openbsd.pkrvars.hcl
image: family/pg-ci-openbsd-vanilla
platform: openbsd
install_script: pkg_add ghc cabal-install git autoconf-2.71
script:
- export AUTOCONF_VERSION=2.71
- export CABAL_DIR=/tmp/.cabal
- ghc --version
- cabal --version
- cabal update
- autoreconf -i
- cabal test --test-show-details=direct
task:
name: NetBSD
compute_engine_instance:
image_project: pg-ci-images
# See https://github.com/anarazel/pg-vm-images/blob/main/packer/netbsd.pkrvars.hcl
image: family/pg-ci-netbsd-vanilla
platform: netbsd
install_script:
# Folders should be updated in line with
# http://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/x86_64/
- export PKG_PATH="http://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r|cut -f '1 2' -d.)/All/;http://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/x86_64/9.0_2023Q1/All/"
- pkg_add ghc cabal-install git autoconf
script:
- export CABAL_DIR=/tmp/.cabal
- ghc --version
- cabal --version
- cabal update
- autoreconf -i
# Select a build plan which does not involve 'text'
- cabal test --test-show-details=direct --constraint 'text < 0'
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'status: needs triage, type: bug'
assignees: ''
---
<!--
When filing an issue, please fill out as much of the information below as you can.
This helps us to debug your issue, but is not required!
-->
### Your environment
Which OS do you use:
<!-- MacOS, Ubuntu, ArchLinux, etc... -->
Describe your project (alternative: link to the project):
<!-- stack.yaml, package.yaml, *.cabal files, cabal.project -->
### Steps to reproduce
<!-- Tell us how to reproduce this issue. -->
### Expected behaviour
<!-- Tell us what should happen. -->
### Actual behaviour
<!-- Tell us what happens instead. -->
### Include debug information
<!-- Include any useful debug information, such as your 'HsUnixConfig.h' file (usually found inside 'dist-newstyle/') -->
### API breaking changes
<!-- If a bugfix causes API breaking changes, consider whether there's a solution without breaking API. -->
### Posix compliance
<!-- Research about relevant parts of the POSIX spec: https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/ -->
---
name: Feature request
about: Suggest an enhancement to the package
title: ''
labels: 'status: needs triage, type: enhancement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context about the feature request here. -->
**API breaking changes**
<!-- If this feature causes API breaking changes, explain why it's worth it. -->
**Posix compliance**
<!-- Research about relevant parts of the POSIX spec: https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/ -->
name: ci-js
on:
- push
- pull_request
jobs:
build:
runs-on: ubuntu-22.04
env:
GHC: 9.12.1
EMSDK: 3.1.74
steps:
- name: Install GHCup
id: ghcup
uses: haskell/ghcup-setup@v1
with:
cabal: latest-prerelease
config: |
url-source:
- GHCupURL
- cross
- prereleases
- uses: actions/checkout@v4
- name: setup GHCJS
run: |
set -eux
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git checkout ${{ env.EMSDK }}
./emsdk install ${{ env.EMSDK }}
./emsdk activate ${{ env.EMSDK }}
source ./emsdk_env.sh
emconfigure ghcup install ghc --set javascript-unknown-ghcjs-${{ env.GHC }}
- name: test
run: |
set -eux
source ./emsdk/emsdk_env.sh
autoreconf -i
cabal update
cabal --project-file=cabal.project.js build --with-hsc2hs=javascript-unknown-ghcjs-hsc2hs
$(cabal --project-file=cabal.project.js list-bin T13660)
$(cabal --project-file=cabal.project.js list-bin unix-tests)
name: ci-wasm32-wasi
on:
- push
- pull_request
jobs:
build:
runs-on: ubuntu-22.04
env:
GHC: 9.10.1.20241021
steps:
- name: Install GHCup
id: ghcup
uses: haskell/ghcup-setup@v1
with:
cabal: latest-prerelease
config: |
url-source:
- GHCupURL
- cross
- prereleases
- name: setup-ghc-wasm32-wasi
run: |
set -eux
pushd $(mktemp -d)
git clone https://gitlab.haskell.org/ghc/ghc-wasm-meta.git
cd ghc-wasm-meta/
export SKIP_GHC=yes
./setup.sh
popd
source ~/.ghc-wasm/env
ghc=$(ghcup -s cross list -r -t ghc -o | grep wasm | tail -1 | awk '{ print $2 }')
ghcup install ghc --set ${ghc} -- --host=x86_64-linux --with-intree-gmp --with-system-libffi
- uses: actions/checkout@v4
- name: test
run: |
set -eux
source ~/.ghc-wasm/env
cabal update
cp ~/.ghc-wasm/wasi-sdk/share/misc/config.* .
autoreconf -i
cabal --project-file=cabal.project.wasm32-wasi build -w wasm32-wasi-ghc --with-ghc-pkg=wasm32-wasi-ghc-pkg --with-hsc2hs=wasm32-wasi-hsc2hs --with-gcc=wasm32-wasi-clang
./test-wasm32-wasi.mjs
name: ci
on:
push:
pull_request:
schedule:
- cron: 0 0 * * *
defaults:
run:
shell: bash
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-24.04, macOS-latest]
ghc: ['9.12', '9.10', '9.8', '9.6', '9.4', '9.2', '9.0', '8.10', '8.8', '8.6']
exclude:
- os: macos-latest
ghc: '9.0'
- os: macos-latest
ghc: '8.10'
- os: macos-latest
ghc: '8.8'
- os: macos-latest
ghc: '8.6'
steps:
- uses: actions/checkout@v4
- name: Install GHCup
uses: haskell/ghcup-setup@v1
with:
ghc: ${{ matrix.ghc }}
cabal: latest
- if: runner.os == 'macOS'
name: Install system deps via brew
run: brew install coreutils autoconf automake
- uses: actions/cache@v4
name: Cache cabal stuff
with:
path: |
~/.cabal/store
dist-newstyle
key: ${{ runner.os }}-${{ matrix.ghc }}-${{ github.sha }}
restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-
- name: Build
run: |
ghc --version
cabal --version
cabal update
autoreconf --version
autoreconf -i
cabal sdist -z -o .
cabal get unix-*.tar.gz
cd unix-*/
cabal test all --test-show-details=direct
- name: Haddock
run: |
cabal haddock --disable-documentation
redhat-ubi9:
runs-on: ubuntu-24.04
container:
image: redhat/ubi9:latest
steps:
- name: Install prerequisites
run: |
yum install -y gcc gmp gmp-devel make ncurses xz perl autoconf
- name: Install GHCup
uses: haskell/ghcup-setup@v1
with:
ghc: latest
cabal: latest
- uses: actions/checkout@v4
- name: Test
run: |
cabal --version
cabal update
autoreconf --version
autoreconf -i
cabal test all --test-show-details=direct
fedora37:
runs-on: ubuntu-latest
container:
image: fedora:37
steps:
- name: Install prerequisites
run: |
dnf install -y gcc gmp gmp-devel make ncurses ncurses-compat-libs xz perl autoconf
- name: Install GHCup
uses: haskell/ghcup-setup@v1
with:
ghc: latest
cabal: latest
- uses: actions/checkout@v4
- name: Test
run: |
cabal --version
cabal update
autoreconf --version
autoreconf -i
# test filepath >= 1.5
cabal test --constraint='filepath >= 1.5.0.0' all --test-show-details=direct
i386:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run build (32 bit linux)
uses: docker://hasufell/i386-alpine-haskell:3.12
with:
args: sh -c "apk update && apk add --no-cache autoconf automake make && cabal update && autoreconf --version && autoreconf -i && cabal v2-test --constraint 'optparse-applicative -process' --constraint 'QuickCheck +old-random' --constraint 'tasty -unix' all"
arm:
runs-on: [self-hosted, Linux, ARM64]
strategy:
fail-fast: false
matrix:
arch: [arm32v7, arm64v8]
steps:
- uses: docker://hasufell/arm64v8-ubuntu-haskell:focal
name: Cleanup
with:
args: "find . -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +"
- name: Checkout code
uses: actions/checkout@v4
- if: matrix.arch == 'arm32v7'
uses: docker://hasufell/arm32v7-ubuntu-haskell:focal
name: Run build (arm32v7 linux)
with:
args: sh -c "cabal update && autoreconf -i && cabal test all --test-show-details=direct"
- if: matrix.arch == 'arm64v8'
uses: docker://hasufell/arm64v8-ubuntu-haskell:focal
name: Run build (arm64v8 linux)
with:
args: sh -c "cabal update && autoreconf -i && cabal test all --test-show-details=direct"
freebsd:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: [self-hosted, FreeBSD, X64]
ghc: 9.4
- os: [self-hosted, FreeBSD, X64]
ghc: 9.6
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install prerequisites
run: |
sudo pkg install -y curl gcc gmp gmake ncurses perl5 libffi libiconv git bash misc/compat10x misc/compat11x misc/compat12x gmake autoconf
- name: Install GHCup
uses: haskell/ghcup-setup@v1
with:
ghc: ${{ matrix.ghc }}
cabal: latest
- name: Run build
run: |
autoreconf --version
autoreconf -i
cabal update
cabal sdist -z -o .
cabal get unix-*.tar.gz
cd unix-*/
cabal test all --test-show-details=direct
# Specific generated files
GNUmakefile
autom4te.cache/
.cabal-sandbox/
cabal.project.local
cabal.project.local~
cabal.sandbox.config
config.log
config.status
config.sub
configure
dist/
dist-install/
dist-newstyle/
.ghc.environment.*
ghc.mk
include/HsUnixConfig.h
include/HsUnixConfig.h.in
.stack-work/
stack*.yaml.lock
unix.buildinfo
tests/.hpc.*
tests/*.eventlog
tests/*.genscript
tests/*.o
tests/*.hi
*~
\ No newline at end of file
tests/*.normalised
*~
.vscode
env:
- GHCVER=7.4.1
- GHCVER=7.4.2
- GHCVER=7.6.1
- GHCVER=7.6.2
- GHCVER=7.6.3
before_install:
- sudo add-apt-repository -y ppa:hvr/ghc
- sudo apt-get update
- sudo apt-get install cabal-install-1.18 ghc-$GHCVER autoconf
- export PATH=/opt/ghc/$GHCVER/bin:$PATH
install:
- cabal-1.18 update
- ghc --version
script:
- autoreconf -i
- cabal-1.18 configure -v2
- cabal-1.18 build
- cabal-1.18 check
- cabal-1.18 sdist
- export SRC_TGZ=$(cabal-1.18 info . | awk '{print $2 ".tar.gz";exit}') ;
cd dist/;
if [ -f "$SRC_TGZ" ]; then
cabal-1.18 install "$SRC_TGZ";
else
echo "expected '$SRC_TGZ' not found";
exit 1;
fi
\ No newline at end of file
The `unix` Package [![Build Status](https://travis-ci.org/ghc/packages-unix.png?branch=master)](https://travis-ci.org/ghc/packages-unix)
The `unix` Package [![Hackage](https://img.shields.io/hackage/v/unix.svg)](https://hackage.haskell.org/package/unix) [![GitHub Build Status](https://github.com/haskell/unix/workflows/ci/badge.svg)](https://github.com/haskell/unix/actions?query=workflow%3Aci)
==================
See [`unix` on Hackage](http://hackage.haskell.org/package/unix) for
......
......@@ -3,4 +3,4 @@ module Main (main) where
import Distribution.Simple
main :: IO ()
main = defaultMainWithHooks defaultUserHooks
main = defaultMainWithHooks autoconfUserHooks
{-# LANGUAGE CPP #-}
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE Safe #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix
-- Copyright : (c) The University of Glasgow 2002
-- License : BSD-style (see the file libraries/base/LICENSE)
--
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
--
-- POSIX support
-- <http://pubs.opengroup.org/onlinepubs/9699919799/ POSIX.1-2008> support
--
-----------------------------------------------------------------------------
......@@ -62,7 +60,7 @@ Here we detail our support for the IEEE Std 1003.1-2001 standard. For
each header file defined by the standard, we categorise its
functionality as
- "supported"
- "supported"
Full equivalent functionality is provided by the specified Haskell
module.
......@@ -71,7 +69,7 @@ functionality as
The functionality is not currently provided.
- "to be supported"
- "to be supported"
Currently unsupported, but support is planned for the future.
......@@ -133,17 +131,17 @@ Unsupported interfaces
aio.h
assert.h
complex.h
cpio.h
ctype.h
cpio.h
ctype.h
fenv.h
float.h
fmtmsg.h
fnmatch.h
ftw.h
glob.h
iconv.h
inttypes.h
iso646.h
iconv.h
inttypes.h
iso646.h
langinfo.h
libgen.h
locale.h (see System.Locale)
......
{-# LANGUAGE CPP #-}
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE Safe #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.ByteString
-- Copyright : (c) The University of Glasgow 2002
-- License : BSD-style (see the file libraries/base/LICENSE)
--
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
--
-- POSIX support with ByteString file paths and environment strings.
-- <http://pubs.opengroup.org/onlinepubs/9699919799/ POSIX.1-2008>
-- support with 'ByteString' file paths and environment strings.
--
-- This module exports exactly the same API as "System.Posix", except
-- that all file paths and environment strings are represented by
......
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE Trustworthy #-}
#endif
{-# LANGUAGE TypeApplications #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.ByteString.FilePath
-- Copyright : (c) The University of Glasgow 2002
-- License : BSD-style (see the file libraries/base/LICENSE)
--
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
......@@ -26,7 +26,8 @@ module System.Posix.ByteString.FilePath (
throwErrnoPathIf_,
throwErrnoPathIfNull,
throwErrnoPathIfMinus1,
throwErrnoPathIfMinus1_
throwErrnoPathIfMinus1_,
throwErrnoTwoPathsIfMinus1_
) where
import Foreign hiding ( void )
......@@ -39,15 +40,23 @@ import Foreign.C hiding (
throwErrnoPathIfMinus1_ )
import Control.Monad
import Data.ByteString
import Control.Exception
import Data.ByteString.Internal (c_strlen)
import GHC.Foreign as GHC ( peekCStringLen )
import GHC.IO.Encoding ( getFileSystemEncoding )
import GHC.IO.Exception
import Data.ByteString as B
import Data.ByteString.Char8 as BC
import Prelude hiding (FilePath)
#if !MIN_VERSION_base(4, 11, 0)
import Data.Monoid ((<>))
#endif
-- | A literal POSIX file path
type RawFilePath = ByteString
withFilePath :: RawFilePath -> (CString -> IO a) -> IO a
withFilePath = useAsCString
withFilePath path = useAsCStringSafe path
peekFilePath :: CString -> IO RawFilePath
peekFilePath = packCString
......@@ -88,7 +97,8 @@ throwErrnoPath :: String -> RawFilePath -> IO a
throwErrnoPath loc path =
do
errno <- getErrno
ioError (errnoToIOError loc errno Nothing (Just (BC.unpack path)))
path' <- either (const (BC.unpack path)) id <$> try @IOException (decodeWithBasePosix path)
ioError (errnoToIOError loc errno Nothing (Just path'))
-- | as 'throwErrnoIf', but exceptions include the given path when
-- appropriate.
......@@ -122,3 +132,42 @@ throwErrnoPathIfMinus1 = throwErrnoPathIf (== -1)
--
throwErrnoPathIfMinus1_ :: (Eq a, Num a) => String -> RawFilePath -> IO a -> IO ()
throwErrnoPathIfMinus1_ = throwErrnoPathIf_ (== -1)
-- | as 'throwErrnoTwoPathsIfMinus1_', but exceptions include two paths when appropriate.
--
throwErrnoTwoPathsIfMinus1_ :: (Eq a, Num a) => String -> RawFilePath -> RawFilePath -> IO a -> IO ()
throwErrnoTwoPathsIfMinus1_ loc path1 path2 action = do
path1' <- either (const (BC.unpack path1)) id <$> try @IOException (decodeWithBasePosix path1)
path2' <- either (const (BC.unpack path2)) id <$> try @IOException (decodeWithBasePosix path2)
throwErrnoIfMinus1_ (loc <> " '" <> path1' <> "' to '" <> path2' <> "'") action
-- | This mimics the filepath decoder base uses on unix,
-- with the small distinction that we're not truncating at NUL bytes (because we're not at
-- the outer FFI layer).
decodeWithBasePosix :: RawFilePath -> IO String
decodeWithBasePosix ba = B.useAsCStringLen ba $ \fp -> peekFilePathPosix fp
where
peekFilePathPosix :: CStringLen -> IO String
peekFilePathPosix fp = getFileSystemEncoding >>= \enc -> GHC.peekCStringLen enc fp
-- | Wrapper around 'useAsCString', checking the encoded 'FilePath' for internal NUL octets as these are
-- disallowed in POSIX filepaths. See https://gitlab.haskell.org/ghc/ghc/-/issues/13660
useAsCStringSafe :: RawFilePath -> (CString -> IO a) -> IO a
useAsCStringSafe path f = useAsCString path $ \ptr -> do
let len = B.length path
clen <- c_strlen ptr
if clen == fromIntegral len
then f ptr
else do
path' <- either (const (BC.unpack path)) id <$> try @IOException (decodeWithBasePosix path)
ioError (err path')
where
err path' =
IOError
{ ioe_handle = Nothing
, ioe_type = InvalidArgument
, ioe_location = "checkForInteriorNuls"
, ioe_description = "POSIX filepaths must not contain internal NUL octets."
, ioe_errno = Nothing
, ioe_filename = Just path'
}
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE NondecreasingIndentation #-}
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE Trustworthy #-}
#endif
{-# LANGUAGE Safe #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.Directory
-- Copyright : (c) The University of Glasgow 2002
-- License : BSD-style (see the file libraries/base/LICENSE)
--
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
......@@ -19,6 +18,11 @@
#include "HsUnix.h"
-- hack copied from System.Posix.Files
#if !defined(PATH_MAX)
# define PATH_MAX 4096
#endif
module System.Posix.Directory (
-- * Creating and removing directories
createDirectory, removeDirectory,
......@@ -27,7 +31,8 @@ module System.Posix.Directory (
DirStream,
openDirStream,
readDirStream,
rewindDirStream,
readDirStreamMaybe,
rewindDirStream,
closeDirStream,
DirStreamOffset,
#ifdef HAVE_TELLDIR
......@@ -43,7 +48,8 @@ module System.Posix.Directory (
changeWorkingDirectoryFd,
) where
import System.IO.Error
import Control.Monad ((>=>))
import Data.Maybe
import System.Posix.Error
import System.Posix.Types
import Foreign
......@@ -52,13 +58,13 @@ import Foreign.C
import System.Posix.Directory.Common
import System.Posix.Internals (withFilePath, peekFilePath)
-- | @createDirectory dir mode@ calls @mkdir@ to
-- | @createDirectory dir mode@ calls @mkdir@ to
-- create a new directory, @dir@, with permissions based on
-- @mode@.
createDirectory :: FilePath -> FileMode -> IO ()
createDirectory name mode =
withFilePath name $ \s ->
throwErrnoPathIfMinus1Retry_ "createDirectory" name (c_mkdir s mode)
withFilePath name $ \s ->
throwErrnoPathIfMinus1Retry_ "createDirectory" name (c_mkdir s mode)
-- POSIX doesn't allow mkdir() to return EINTR, but it does on
-- OS X (#5184), so we need the Retry variant here.
......@@ -73,87 +79,65 @@ openDirStream name =
dirp <- throwErrnoPathIfNullRetry "openDirStream" name $ c_opendir s
return (DirStream dirp)
foreign import ccall unsafe "__hsunix_opendir"
foreign import capi unsafe "HsUnix.h opendir"
c_opendir :: CString -> IO (Ptr CDir)
-- | @readDirStream dp@ calls @readdir@ to obtain the
-- next directory entry (@struct dirent@) for the open directory
-- stream @dp@, and returns the @d_name@ member of that
-- structure.
-- structure.
--
-- Note that this function returns an empty filepath if the end of the
-- directory stream is reached. For a safer alternative use
-- 'readDirStreamMaybe'.
readDirStream :: DirStream -> IO FilePath
readDirStream (DirStream dirp) =
alloca $ \ptr_dEnt -> loop ptr_dEnt
where
loop ptr_dEnt = do
resetErrno
r <- c_readdir dirp ptr_dEnt
if (r == 0)
then do dEnt <- peek ptr_dEnt
if (dEnt == nullPtr)
then return []
else do
entry <- (d_name dEnt >>= peekFilePath)
c_freeDirEnt dEnt
return entry
else do errno <- getErrno
if (errno == eINTR) then loop ptr_dEnt else do
let (Errno eo) = errno
if (eo == 0)
then return []
else throwErrno "readDirStream"
-- traversing directories
foreign import ccall unsafe "__hscore_readdir"
c_readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
foreign import ccall unsafe "__hscore_free_dirent"
c_freeDirEnt :: Ptr CDirent -> IO ()
foreign import ccall unsafe "__hscore_d_name"
d_name :: Ptr CDirent -> IO CString
readDirStream = fmap (fromMaybe "") . readDirStreamMaybe
-- | @readDirStreamMaybe dp@ calls @readdir@ to obtain the
-- next directory entry (@struct dirent@) for the open directory
-- stream @dp@. It returns the @d_name@ member of that
-- structure wrapped in a @Just d_name@ if an entry was read and @Nothing@ if
-- the end of the directory stream was reached.
readDirStreamMaybe :: DirStream -> IO (Maybe FilePath)
readDirStreamMaybe = readDirStreamWith (dirEntName >=> peekFilePath)
-- | @getWorkingDirectory@ calls @getcwd@ to obtain the name
-- of the current working directory.
getWorkingDirectory :: IO FilePath
getWorkingDirectory = do
p <- mallocBytes long_path_size
go p long_path_size
where go p bytes = do
p' <- c_getcwd p (fromIntegral bytes)
if p' /= nullPtr
then do s <- peekFilePath p'
free p'
return s
else do errno <- getErrno
if errno == eRANGE
then do let bytes' = bytes * 2
p'' <- reallocBytes p bytes'
go p'' bytes'
else throwErrno "getCurrentDirectory"
getWorkingDirectory = go (#const PATH_MAX)
where
go bytes = do
r <- allocaBytes bytes $ \buf -> do
buf' <- c_getcwd buf (fromIntegral bytes)
if buf' /= nullPtr
then do s <- peekFilePath buf
return (Just s)
else do errno <- getErrno
if errno == eRANGE
-- we use Nothing to indicate that we should
-- try again with a bigger buffer
then return Nothing
else throwErrno "getWorkingDirectory"
maybe (go (2 * bytes)) return r
foreign import ccall unsafe "getcwd"
c_getcwd :: Ptr CChar -> CSize -> IO (Ptr CChar)
foreign import ccall unsafe "__hsunix_long_path_size"
long_path_size :: Int
-- | @changeWorkingDirectory dir@ calls @chdir@ to change
-- the current working directory to @dir@.
changeWorkingDirectory :: FilePath -> IO ()
changeWorkingDirectory path =
modifyIOError (`ioeSetFileName` path) $
withFilePath path $ \s ->
throwErrnoIfMinus1Retry_ "changeWorkingDirectory" (c_chdir s)
withFilePath path $ \s ->
throwErrnoPathIfMinus1Retry_ "changeWorkingDirectory" path (c_chdir s)
foreign import ccall unsafe "chdir"
c_chdir :: CString -> IO CInt
removeDirectory :: FilePath -> IO ()
removeDirectory path =
modifyIOError (`ioeSetFileName` path) $
withFilePath path $ \s ->
throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
withFilePath path $ \s ->
throwErrnoPathIfMinus1Retry_ "removeDirectory" path (c_rmdir s)
foreign import ccall unsafe "rmdir"
c_rmdir :: CString -> IO CInt
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE NondecreasingIndentation #-}
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE Trustworthy #-}
#endif
{-# LANGUAGE Safe #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.Directory.ByteString
-- Copyright : (c) The University of Glasgow 2002
-- License : BSD-style (see the file libraries/base/LICENSE)
--
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
......@@ -19,6 +18,11 @@
#include "HsUnix.h"
-- hack copied from System.Posix.Files
#if !defined(PATH_MAX)
# define PATH_MAX 4096
#endif
module System.Posix.Directory.ByteString (
-- * Creating and removing directories
createDirectory, removeDirectory,
......@@ -27,7 +31,8 @@ module System.Posix.Directory.ByteString (
DirStream,
openDirStream,
readDirStream,
rewindDirStream,
readDirStreamMaybe,
rewindDirStream,
closeDirStream,
DirStreamOffset,
#ifdef HAVE_TELLDIR
......@@ -37,13 +42,14 @@ module System.Posix.Directory.ByteString (
seekDirStream,
#endif
-- * The working dirctory
-- * The working directory
getWorkingDirectory,
changeWorkingDirectory,
changeWorkingDirectoryFd,
) where
import System.IO.Error
import Control.Monad ((>=>))
import Data.Maybe
import System.Posix.Types
import Foreign
import Foreign.C
......@@ -55,11 +61,11 @@ import System.Posix.ByteString.FilePath
-- | @createDirectory dir mode@ calls @mkdir@ to
-- create a new directory, @dir@, with permissions based on
-- @mode@.
-- @mode@.
createDirectory :: RawFilePath -> FileMode -> IO ()
createDirectory name mode =
withFilePath name $ \s ->
throwErrnoPathIfMinus1Retry_ "createDirectory" name (c_mkdir s mode)
withFilePath name $ \s ->
throwErrnoPathIfMinus1Retry_ "createDirectory" name (c_mkdir s mode)
-- POSIX doesn't allow mkdir() to return EINTR, but it does on
-- OS X (#5184), so we need the Retry variant here.
......@@ -74,87 +80,65 @@ openDirStream name =
dirp <- throwErrnoPathIfNullRetry "openDirStream" name $ c_opendir s
return (DirStream dirp)
foreign import ccall unsafe "__hsunix_opendir"
foreign import capi unsafe "HsUnix.h opendir"
c_opendir :: CString -> IO (Ptr CDir)
-- | @readDirStream dp@ calls @readdir@ to obtain the
-- next directory entry (@struct dirent@) for the open directory
-- stream @dp@, and returns the @d_name@ member of that
-- structure.
-- structure.
--
-- Note that this function returns an empty filepath if the end of the
-- directory stream is reached. For a safer alternative use
-- 'readDirStreamMaybe'.
readDirStream :: DirStream -> IO RawFilePath
readDirStream (DirStream dirp) =
alloca $ \ptr_dEnt -> loop ptr_dEnt
where
loop ptr_dEnt = do
resetErrno
r <- c_readdir dirp ptr_dEnt
if (r == 0)
then do dEnt <- peek ptr_dEnt
if (dEnt == nullPtr)
then return BC.empty
else do
entry <- (d_name dEnt >>= peekFilePath)
c_freeDirEnt dEnt
return entry
else do errno <- getErrno
if (errno == eINTR) then loop ptr_dEnt else do
let (Errno eo) = errno
if (eo == 0)
then return BC.empty
else throwErrno "readDirStream"
-- traversing directories
foreign import ccall unsafe "__hscore_readdir"
c_readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
foreign import ccall unsafe "__hscore_free_dirent"
c_freeDirEnt :: Ptr CDirent -> IO ()
foreign import ccall unsafe "__hscore_d_name"
d_name :: Ptr CDirent -> IO CString
readDirStream = fmap (fromMaybe BC.empty) . readDirStreamMaybe
-- | @readDirStreamMaybe dp@ calls @readdir@ to obtain the
-- next directory entry (@struct dirent@) for the open directory
-- stream @dp@. It returns the @d_name@ member of that
-- structure wrapped in a @Just d_name@ if an entry was read and @Nothing@ if
-- the end of the directory stream was reached.
readDirStreamMaybe :: DirStream -> IO (Maybe RawFilePath)
readDirStreamMaybe = readDirStreamWith (dirEntName >=> peekFilePath)
-- | @getWorkingDirectory@ calls @getcwd@ to obtain the name
-- of the current working directory.
getWorkingDirectory :: IO RawFilePath
getWorkingDirectory = do
p <- mallocBytes long_path_size
go p long_path_size
where go p bytes = do
p' <- c_getcwd p (fromIntegral bytes)
if p' /= nullPtr
then do s <- peekFilePath p'
free p'
return s
else do errno <- getErrno
if errno == eRANGE
then do let bytes' = bytes * 2
p'' <- reallocBytes p bytes'
go p'' bytes'
else throwErrno "getCurrentDirectory"
getWorkingDirectory = go (#const PATH_MAX)
where
go bytes = do
r <- allocaBytes bytes $ \buf -> do
buf' <- c_getcwd buf (fromIntegral bytes)
if buf' /= nullPtr
then do s <- peekFilePath buf
return (Just s)
else do errno <- getErrno
if errno == eRANGE
-- we use Nothing to indicate that we should
-- try again with a bigger buffer
then return Nothing
else throwErrno "getWorkingDirectory"
maybe (go (2 * bytes)) return r
foreign import ccall unsafe "getcwd"
c_getcwd :: Ptr CChar -> CSize -> IO (Ptr CChar)
foreign import ccall unsafe "__hsunix_long_path_size"
long_path_size :: Int
-- | @changeWorkingDirectory dir@ calls @chdir@ to change
-- the current working directory to @dir@.
changeWorkingDirectory :: RawFilePath -> IO ()
changeWorkingDirectory path =
modifyIOError (`ioeSetFileName` (BC.unpack path)) $
withFilePath path $ \s ->
throwErrnoIfMinus1Retry_ "changeWorkingDirectory" (c_chdir s)
withFilePath path $ \s ->
throwErrnoPathIfMinus1Retry_ "changeWorkingDirectory" path (c_chdir s)
foreign import ccall unsafe "chdir"
c_chdir :: CString -> IO CInt
removeDirectory :: RawFilePath -> IO ()
removeDirectory path =
modifyIOError (`ioeSetFileName` BC.unpack path) $
withFilePath path $ \s ->
throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
withFilePath path $ \s ->
throwErrnoPathIfMinus1Retry_ "removeDirectory" path (c_rmdir s)
foreign import ccall unsafe "rmdir"
c_rmdir :: CString -> IO CInt
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE Trustworthy #-}
#endif
{-# LANGUAGE CPP, Safe, CApiFFI, MultiWayIf, PatternSynonyms #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.Directory.Common
-- Copyright : (c) The University of Glasgow 2002
-- License : BSD-style (see the file libraries/base/LICENSE)
--
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
......@@ -17,9 +15,46 @@
-----------------------------------------------------------------------------
#include "HsUnix.h"
#include "HsUnixConfig.h"
##include "HsUnixConfig.h"
module System.Posix.Directory.Common (
DirStream(..), CDir, CDirent, DirStreamOffset(..),
DirStream(..),
CDir,
CDirent,
DirStreamOffset(..),
DirStreamWithPath(..),
fromDirStreamWithPath,
toDirStreamWithPath,
DirEnt(..),
dirEntName,
dirEntType,
DirType( DirType
, UnknownType
, NamedPipeType
, CharacterDeviceType
, DirectoryType
, BlockDeviceType
, RegularFileType
, SymbolicLinkType
, SocketType
, WhiteoutType
),
isUnknownType,
isNamedPipeType,
isCharacterDeviceType,
isDirectoryType,
isBlockDeviceType,
isRegularFileType,
isSymbolicLinkType,
isSocketType,
isWhiteoutType,
getRealDirType,
unsafeOpenDirStreamFd,
readDirStreamWith,
readDirStreamWithPtr,
rewindDirStream,
closeDirStream,
#ifdef HAVE_SEEKDIR
......@@ -31,14 +66,291 @@ module System.Posix.Directory.Common (
changeWorkingDirectoryFd,
) where
import Control.Exception (mask_)
import Control.Monad (void, when)
import System.Posix.Types
import Foreign
import Foreign hiding (void)
import Foreign.C
#if !defined(HAVE_FCHDIR)
import System.IO.Error ( ioeSetLocation )
import GHC.IO.Exception ( unsupportedOperation )
#endif
import System.Posix.Files.Common
newtype DirStream = DirStream (Ptr CDir)
type CDir = ()
type CDirent = ()
-- | @since 2.8.6.0
newtype DirStreamWithPath a = DirStreamWithPath (a, Ptr CDir)
-- | Convert a 'DirStreamWithPath' to a 'DirStream'.
-- Note that the underlying pointer is shared by both values, hence any
-- modification to the resulting 'DirStream' will also modify the original
-- 'DirStreamWithPath'.
--
-- @since 2.8.6.0
fromDirStreamWithPath :: DirStreamWithPath a -> DirStream
fromDirStreamWithPath (DirStreamWithPath (_, ptr)) = DirStream ptr
-- | Construct a 'DirStreamWithPath' from a 'DirStream'.
-- Note that the underlying pointer is shared by both values, hence any
-- modification to the pointer of the resulting 'DirStreamWithPath' will also
-- modify the original 'DirStream'.
--
-- @since 2.8.6.0
toDirStreamWithPath :: a -> DirStream -> DirStreamWithPath a
toDirStreamWithPath path (DirStream ptr) = DirStreamWithPath (path, ptr)
-- | @since 2.8.6.0
newtype DirEnt = DirEnt (Ptr CDirent)
-- We provide a hand-written instance here since GeneralizedNewtypeDeriving and
-- DerivingVia are not allowed in Safe Haskell.
instance Storable DirEnt where
sizeOf _ = sizeOf (undefined :: Ptr CDirent)
{-# INLINE sizeOf #-}
alignment _ = alignment (undefined :: Ptr CDirent)
{-# INLINE alignment #-}
peek ptr = DirEnt <$> peek (castPtr ptr)
{-# INLINE peek #-}
poke ptr (DirEnt dEnt) = poke (castPtr ptr) dEnt
{-# INLINE poke#-}
data {-# CTYPE "DIR" #-} CDir
data {-# CTYPE "struct dirent" #-} CDirent
-- | The value of the @d_type@ field of a @dirent@ struct.
-- Note that the possible values of that type depend on the filesystem that is
-- queried. From @readdir(3)@:
--
-- > Currently, only some filesystems (among them: Btrfs, ext2, ext3, and ext4)
-- > have full support for returning the file type in d_type. All applications
-- > must properly handle a return of DT_UNKNOWN.
--
-- For example, JFS is a filesystem that does not support @d_type@;
-- See https://github.com/haskell/ghcup-hs/issues/766
--
-- Furthermore, @dirent@ or the constants represented by the associated pattern
-- synonyms of this type may not be provided by the underlying platform. In that
-- case none of those patterns will match and the application must handle that
-- case accordingly.
--
-- @since 2.8.6.0
newtype DirType = DirType CChar
deriving (Eq, Ord, Show)
-- | The 'DirType' refers to an entry of unknown type.
pattern UnknownType :: DirType
pattern UnknownType = DirType (CONST_DT_UNKNOWN)
-- | The 'DirType' refers to an entry that is a named pipe.
pattern NamedPipeType :: DirType
pattern NamedPipeType = DirType (CONST_DT_FIFO)
-- | The 'DirType' refers to an entry that is a character device.
pattern CharacterDeviceType :: DirType
pattern CharacterDeviceType = DirType (CONST_DT_CHR)
-- | The 'DirType' refers to an entry that is a directory.
pattern DirectoryType :: DirType
pattern DirectoryType = DirType (CONST_DT_DIR)
-- | The 'DirType' refers to an entry that is a block device.
pattern BlockDeviceType :: DirType
pattern BlockDeviceType = DirType (CONST_DT_BLK)
-- | The 'DirType' refers to an entry that is a regular file.
pattern RegularFileType :: DirType
pattern RegularFileType = DirType (CONST_DT_REG)
-- | The 'DirType' refers to an entry that is a symbolic link.
pattern SymbolicLinkType :: DirType
pattern SymbolicLinkType = DirType (CONST_DT_LNK)
-- | The 'DirType' refers to an entry that is a socket.
pattern SocketType :: DirType
pattern SocketType = DirType (CONST_DT_SOCK)
-- | The 'DirType' refers to an entry that is a whiteout.
pattern WhiteoutType :: DirType
pattern WhiteoutType = DirType (CONST_DT_WHT)
-- | Checks if this 'DirType' refers to an entry of unknown type.
--
-- @since 2.8.6.0
isUnknownType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a block device entry.
--
-- @since 2.8.6.0
isBlockDeviceType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a character device entry.
--
-- @since 2.8.6.0
isCharacterDeviceType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a named pipe entry.
--
-- @since 2.8.6.0
isNamedPipeType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a regular file entry.
--
-- @since 2.8.6.0
isRegularFileType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a directory entry.
--
-- @since 2.8.6.0
isDirectoryType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a symbolic link entry.
--
-- @since 2.8.6.0
isSymbolicLinkType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a socket entry.
--
-- @since 2.8.6.0
isSocketType :: DirType -> Bool
-- | Checks if this 'DirType' refers to a whiteout entry.
--
-- @since 2.8.6.0
isWhiteoutType :: DirType -> Bool
isUnknownType dtype = dtype == UnknownType
isBlockDeviceType dtype = dtype == BlockDeviceType
isCharacterDeviceType dtype = dtype == CharacterDeviceType
isNamedPipeType dtype = dtype == NamedPipeType
isRegularFileType dtype = dtype == RegularFileType
isDirectoryType dtype = dtype == DirectoryType
isSymbolicLinkType dtype = dtype == SymbolicLinkType
isSocketType dtype = dtype == SocketType
isWhiteoutType dtype = dtype == WhiteoutType
-- | @since 2.8.6.0
getRealDirType :: IO FileStatus -> DirType -> IO DirType
getRealDirType _ BlockDeviceType = return BlockDeviceType
getRealDirType _ CharacterDeviceType = return CharacterDeviceType
getRealDirType _ NamedPipeType = return NamedPipeType
getRealDirType _ RegularFileType = return RegularFileType
getRealDirType _ DirectoryType = return DirectoryType
getRealDirType _ SymbolicLinkType = return SymbolicLinkType
getRealDirType _ SocketType = return SocketType
getRealDirType _ WhiteoutType = return WhiteoutType
getRealDirType getFileStatus _ = do
stat <- getFileStatus
return $ if | isRegularFile stat -> RegularFileType
| isDirectory stat -> DirectoryType
| isSymbolicLink stat -> SymbolicLinkType
| isBlockDevice stat -> BlockDeviceType
| isCharacterDevice stat -> CharacterDeviceType
| isNamedPipe stat -> NamedPipeType
| isSocket stat -> SocketType
| otherwise -> UnknownType
-- | Call @fdopendir@ to obtain a directory stream for @fd@. @fd@ must not be
-- otherwise used after this.
--
-- On success, it is owned by the returned 'DirStream', which should be closed
-- via 'closeDirStream' when no longer needed. On error, the file descriptor
-- is automatically closed and then an exception is thrown. There is no code
-- path in which the file descriptor remains open and yet not owned by a
-- returned 'DirStream'.
--
-- The input file descriptor must not have been used with @threadWaitRead@ or
-- @threadWaitWrite@.
--
-- @since 2.8.6.0
unsafeOpenDirStreamFd :: Fd -> IO DirStream
unsafeOpenDirStreamFd (Fd fd) = mask_ $ do
ptr <- c_fdopendir fd
when (ptr == nullPtr) $ do
errno <- getErrno
void $ c_close fd
ioError (errnoToIOError "openDirStreamFd" errno Nothing Nothing)
return $ DirStream ptr
-- We need c_close here, because 'closeFd' throws exceptions on error,
-- but we want to silently close the (presumably directory) descriptor.
foreign import ccall unsafe "HsUnix.h close"
c_close :: CInt -> IO CInt
-- NOTE: It is /critical/ to use "capi" and "dirent.h" here, because system
-- headers on e.g. macOS alias this function, and linking directly to the
-- "fdopendir" symbol in libc leads to a crash!
--
foreign import capi unsafe "dirent.h fdopendir"
c_fdopendir :: CInt -> IO (Ptr CDir)
-- | @readDirStreamWith f dp@ calls @readdir@ to obtain the next directory entry
-- (@struct dirent@) for the open directory stream @dp@. If an entry is read,
-- it passes the pointer to that structure to the provided function @f@ for
-- processing. It returns the result of that function call wrapped in a @Just@
-- if an entry was read and @Nothing@ if the end of the directory stream was
-- reached.
--
-- __NOTE:__ The lifetime of the pointer wrapped in the `DirEnt` is limited to
-- invocation of the callback and it will be freed automatically after. Do not
-- pass it to the outside world!
--
-- @since 2.8.6.0
readDirStreamWith :: (DirEnt -> IO a) -> DirStream -> IO (Maybe a)
readDirStreamWith f dstream = alloca
(\ptr_dEnt -> readDirStreamWithPtr ptr_dEnt f dstream)
-- | A version of 'readDirStreamWith' that takes a pre-allocated pointer in
-- addition to the other arguments. This pointer is used to store the pointer
-- to the next directory entry, if there is any. This function is intended for
-- use cases where you need to read a lot of directory entries and want to
-- reuse the pointer for each of them. Using for example 'readDirStream' or
-- 'readDirStreamWith' in this scenario would allocate a new pointer for each
-- call of these functions.
--
-- __NOTE__: You are responsible for releasing the pointer after you are done.
--
-- @since 2.8.6.0
readDirStreamWithPtr :: Ptr DirEnt -> (DirEnt -> IO a) -> DirStream -> IO (Maybe a)
readDirStreamWithPtr ptr_dEnt f dstream@(DirStream dirp) = do
resetErrno
r <- c_readdir dirp (castPtr ptr_dEnt)
if (r == 0)
then do dEnt@(DirEnt dEntPtr) <- peek ptr_dEnt
if (dEntPtr == nullPtr)
then return Nothing
else do
res <- f dEnt
c_freeDirEnt dEntPtr
return (Just res)
else do errno <- getErrno
if (errno == eINTR)
then readDirStreamWithPtr ptr_dEnt f dstream
else do
let (Errno eo) = errno
if (eo == 0)
then return Nothing
else throwErrno "readDirStream"
-- | @since 2.8.6.0
dirEntName :: DirEnt -> IO CString
dirEntName (DirEnt dEntPtr) = d_name dEntPtr
foreign import ccall unsafe "__hscore_d_name"
d_name :: Ptr CDirent -> IO CString
-- | @since 2.8.6.0
dirEntType :: DirEnt -> IO DirType
dirEntType (DirEnt dEntPtr) = DirType <$> d_type dEntPtr
foreign import ccall unsafe "__hscore_d_type"
d_type :: Ptr CDirent -> IO CChar
-- traversing directories
foreign import ccall unsafe "__hscore_readdir"
c_readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
foreign import ccall unsafe "__hscore_free_dirent"
c_freeDirEnt :: Ptr CDirent -> IO ()
-- | @rewindDirStream dp@ calls @rewinddir@ to reposition
-- the directory stream @dp@ at the beginning of the directory.
......@@ -62,25 +374,35 @@ newtype DirStreamOffset = DirStreamOffset COff
#ifdef HAVE_SEEKDIR
seekDirStream :: DirStream -> DirStreamOffset -> IO ()
seekDirStream (DirStream dirp) (DirStreamOffset off) =
c_seekdir dirp off
c_seekdir dirp (fromIntegral off) -- TODO: check for CLong/COff overflow
foreign import ccall unsafe "seekdir"
c_seekdir :: Ptr CDir -> COff -> IO ()
c_seekdir :: Ptr CDir -> CLong -> IO ()
#endif
#ifdef HAVE_TELLDIR
tellDirStream :: DirStream -> IO DirStreamOffset
tellDirStream (DirStream dirp) = do
off <- c_telldir dirp
return (DirStreamOffset off)
return (DirStreamOffset (fromIntegral off)) -- TODO: check for overflow
foreign import ccall unsafe "telldir"
c_telldir :: Ptr CDir -> IO COff
c_telldir :: Ptr CDir -> IO CLong
#endif
#if defined(HAVE_FCHDIR)
changeWorkingDirectoryFd :: Fd -> IO ()
changeWorkingDirectoryFd (Fd fd) =
changeWorkingDirectoryFd (Fd fd) =
throwErrnoIfMinus1Retry_ "changeWorkingDirectoryFd" (c_fchdir fd)
foreign import ccall unsafe "fchdir"
c_fchdir :: CInt -> IO CInt
#else
{-# WARNING changeWorkingDirectoryFd "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_FCHDIR@)" #-}
changeWorkingDirectoryFd :: Fd -> IO ()
changeWorkingDirectoryFd _ = ioError (ioeSetLocation unsupportedOperation "changeWorkingDirectoryFd")
#endif // HAVE_FCHDIR
#include "HsUnix.h"
module System.Posix.Directory.Fd (
unsafeOpenDirStreamFd
) where
import System.Posix.Directory.Common
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.Directory.Internals
-- Copyright : (c) The University of Glasgow 2022
-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
--
-- POSIX directory support (internal module, no PVP guarantees)
--
-----------------------------------------------------------------------------
module System.Posix.Directory.Internals (
DirStream(..),
CDir,
CDirent,
DirStreamOffset(..),
DirStreamWithPath(..),
fromDirStreamWithPath,
toDirStreamWithPath,
DirEnt(..),
dirEntName,
dirEntType,
DirType( DirType
, UnknownType
, NamedPipeType
, CharacterDeviceType
, DirectoryType
, BlockDeviceType
, RegularFileType
, SymbolicLinkType
, SocketType
, WhiteoutType
),
isUnknownType,
isNamedPipeType,
isCharacterDeviceType,
isDirectoryType,
isBlockDeviceType,
isRegularFileType,
isSymbolicLinkType,
isSocketType,
isWhiteoutType,
getRealDirType,
readDirStreamWith,
readDirStreamWithPtr,
) where
import System.Posix.Directory.Common
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE NondecreasingIndentation #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.Directory.PosixPath
-- Copyright : (c) The University of Glasgow 2002
-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer : libraries@haskell.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
--
-- PosixPath based POSIX directory support
--
-----------------------------------------------------------------------------
#include "HsUnix.h"
-- hack copied from System.Posix.Files
#if !defined(PATH_MAX)
# define PATH_MAX 4096
#endif
module System.Posix.Directory.PosixPath (
-- * Creating and removing directories
createDirectory, removeDirectory,
-- * Reading directories
Common.DirStream,
openDirStream,
readDirStream,
readDirStreamMaybe,
Common.rewindDirStream,
Common.closeDirStream,
Common.DirStreamOffset,
#ifdef HAVE_TELLDIR
Common.tellDirStream,
#endif
#ifdef HAVE_SEEKDIR
Common.seekDirStream,
#endif
-- * The working directory
getWorkingDirectory,
changeWorkingDirectory,
Common.changeWorkingDirectoryFd,
) where
import Control.Monad ((>=>))
import Data.Maybe
import System.Posix.Types
import Foreign
import Foreign.C
import System.OsPath.Posix
import qualified System.Posix.Directory.Common as Common
import System.Posix.PosixPath.FilePath
-- | @createDirectory dir mode@ calls @mkdir@ to
-- create a new directory, @dir@, with permissions based on
-- @mode@.
createDirectory :: PosixPath -> FileMode -> IO ()
createDirectory name mode =
withFilePath name $ \s ->
throwErrnoPathIfMinus1Retry_ "createDirectory" name (c_mkdir s mode)
-- POSIX doesn't allow mkdir() to return EINTR, but it does on
-- OS X (#5184), so we need the Retry variant here.
foreign import ccall unsafe "mkdir"
c_mkdir :: CString -> CMode -> IO CInt
-- | @openDirStream dir@ calls @opendir@ to obtain a
-- directory stream for @dir@.
openDirStream :: PosixPath -> IO Common.DirStream
openDirStream name =
withFilePath name $ \s -> do
dirp <- throwErrnoPathIfNullRetry "openDirStream" name $ c_opendir s
return (Common.DirStream dirp)
foreign import capi unsafe "HsUnix.h opendir"
c_opendir :: CString -> IO (Ptr Common.CDir)
-- | @readDirStream dp@ calls @readdir@ to obtain the
-- next directory entry (@struct dirent@) for the open directory
-- stream @dp@, and returns the @d_name@ member of that
-- structure.
--
-- Note that this function returns an empty filepath if the end of the
-- directory stream is reached. For a safer alternative use
-- 'readDirStreamMaybe'.
readDirStream :: Common.DirStream -> IO PosixPath
readDirStream = fmap (fromMaybe mempty) . readDirStreamMaybe
-- | @readDirStreamMaybe dp@ calls @readdir@ to obtain the
-- next directory entry (@struct dirent@) for the open directory
-- stream @dp@. It returns the @d_name@ member of that
-- structure wrapped in a @Just d_name@ if an entry was read and @Nothing@ if
-- the end of the directory stream was reached.
readDirStreamMaybe :: Common.DirStream -> IO (Maybe PosixPath)
readDirStreamMaybe = Common.readDirStreamWith
(Common.dirEntName >=> peekFilePath)
-- | @getWorkingDirectory@ calls @getcwd@ to obtain the name
-- of the current working directory.
getWorkingDirectory :: IO PosixPath
getWorkingDirectory = go (#const PATH_MAX)
where
go bytes = do
r <- allocaBytes bytes $ \buf -> do
buf' <- c_getcwd buf (fromIntegral bytes)
if buf' /= nullPtr
then do s <- peekFilePath buf
return (Just s)
else do errno <- getErrno
if errno == eRANGE
-- we use Nothing to indicate that we should
-- try again with a bigger buffer
then return Nothing
else throwErrno "getWorkingDirectory"
maybe (go (2 * bytes)) return r
foreign import ccall unsafe "getcwd"
c_getcwd :: Ptr CChar -> CSize -> IO (Ptr CChar)
-- | @changeWorkingDirectory dir@ calls @chdir@ to change
-- the current working directory to @dir@.
changeWorkingDirectory :: PosixPath -> IO ()
changeWorkingDirectory path =
withFilePath path $ \s ->
throwErrnoPathIfMinus1Retry_ "changeWorkingDirectory" path (c_chdir s)
foreign import ccall unsafe "chdir"
c_chdir :: CString -> IO CInt
removeDirectory :: PosixPath -> IO ()
removeDirectory path =
withFilePath path $ \s ->
throwErrnoPathIfMinus1Retry_ "removeDirectory" path (c_rmdir s)
foreign import ccall unsafe "rmdir"
c_rmdir :: CString -> IO CInt
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE Trustworthy #-}
#endif
{-# LANGUAGE Safe #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.DynamicLinker
-- Copyright : (c) Volker Stolz <vs@foldr.org> 2003
-- License : BSD-style (see the file libraries/base/LICENSE)
--
--
-- Maintainer : vs@foldr.org
-- Stability : provisional
-- Portability : non-portable (requires POSIX)
......@@ -27,16 +25,16 @@ module System.Posix.DynamicLinker (
-- Usage:
-- ******
--
--
-- Let's assume you want to open a local shared library \'foo\' (.\/libfoo.so)
-- offering a function
-- @char \* mogrify (char\*,int)@
-- and invoke @str = mogrify("test",1)@:
--
--
--
--
-- type Fun = CString -> Int -> IO CString
-- foreign import dynamic unsafe fun__ :: FunPtr Fun -> Fun
--
--
-- withDL "libfoo.so" [RTLD_NOW] \$ \\ mod -> do
-- funptr <- dlsym mod "mogrify"
-- let fun = fun__ funptr
......@@ -44,7 +42,7 @@ module System.Posix.DynamicLinker (
-- strptr <- fun str 1
-- strstr <- peekCString strptr
-- ...
--
--
where
......@@ -54,17 +52,15 @@ import System.Posix.DynamicLinker.Prim
#include "HsUnix.h"
import Control.Exception ( bracket )
import Control.Monad ( liftM )
import Foreign
import System.Posix.Internals ( withFilePath )
dlopen :: FilePath -> [RTLDFlags] -> IO DL
dlopen path flags = do
withFilePath path $ \ p -> do
liftM DLHandle $ throwDLErrorIf "dlopen" (== nullPtr) $ c_dlopen p (packRTLDFlags flags)
dlopen path flags = withFilePath path $ \p -> DLHandle <$>
throwDLErrorIf "dlopen" (== nullPtr) (c_dlopen p (packRTLDFlags flags))
withDL :: String -> [RTLDFlags] -> (DL -> IO a) -> IO a
withDL :: FilePath -> [RTLDFlags] -> (DL -> IO a) -> IO a
withDL file flags f = bracket (dlopen file flags) (dlclose) f
withDL_ :: String -> [RTLDFlags] -> (DL -> IO a) -> IO ()
withDL_ :: FilePath -> [RTLDFlags] -> (DL -> IO a) -> IO ()
withDL_ file flags f = withDL file flags f >> return ()