DELETED .cirrus.yml
Index: .cirrus.yml
==================================================================
--- .cirrus.yml
+++ .cirrus.yml
@@ -1,22 +0,0 @@
-task:
- name: FreeBSD 12.1
- freebsd_instance:
- image_family: freebsd-12-1
- install_script:
- pkg install -y autoconf automake
- shared_script:
- - ./autogen.sh
- - ./configure
- - make -j4 install
- static_script:
- - ./autogen.sh
- - ./configure --disable-shared
- - make -j4 install
- shared_seluid24_script:
- - ./autogen.sh
- - ./configure --enable-seluid24
- - make -j4 install
- static_seluid24_script:
- - ./autogen.sh
- - ./configure --disable-shared --enable-seluid24
- - make -j4 install
Index: .fossil-settings/clean-glob
==================================================================
--- .fossil-settings/clean-glob
+++ .fossil-settings/clean-glob
@@ -48,9 +48,10 @@
tests/tests.arm9
tests/tests.nds
tests/tests.nro
tests/tests.rpx
utils/objfw-config
+utils/objfw-new/objfw-new
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp
Index: .fossil-settings/ignore-glob
==================================================================
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -53,9 +53,10 @@
tests/tests.arm9
tests/tests.nds
tests/tests.nro
tests/tests.rpx
utils/objfw-config
+utils/objfw-new/objfw-new
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp
Index: .github/workflows/amiga-gcc.yml
==================================================================
--- .github/workflows/amiga-gcc.yml
+++ .github/workflows/amiga-gcc.yml
@@ -1,37 +1,21 @@
name: amiga-gcc
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
+ container: amigadev/crosstools:m68k-amigaos
strategy:
matrix:
configure_flags:
-
- --disable-amiga-lib
steps:
- - name: Install dependencies
- run: docker pull amigadev/crosstools:m68k-amigaos
- uses: actions/checkout@v2
- name: autogen.sh
run: ./autogen.sh
- name: configure
- run: |
- docker run \
- -e PATH="/opt/m68k-amigaos/bin:$PATH" \
- -v "$PWD:/objfw" \
- amigadev/crosstools:m68k-amigaos \
- sh -c 'cd /objfw && ./configure --host=m68k-amigaos ${{ matrix.configure_flags }}'
+ run: ./configure --host=m68k-amigaos ${{ matrix.configure_flags }}
- name: make
- run: |
- docker run \
- -e PATH="/opt/m68k-amigaos/bin:$PATH" \
- -v "$PWD:/objfw" \
- amigadev/crosstools:m68k-amigaos \
- sh -c "cd /objfw && make -j$(nproc)"
+ run: make -j$(nproc)
- name: make install
- run: |
- docker run \
- -e PATH="/opt/m68k-amigaos/bin:$PATH" \
- -v "$PWD:/objfw" \
- amigadev/crosstools:m68k-amigaos \
- sh -c "cd /objfw && make -j$(nproc)"
+ run: make install
DELETED .github/workflows/macos-10.15.yml
Index: .github/workflows/macos-10.15.yml
==================================================================
--- .github/workflows/macos-10.15.yml
+++ .github/workflows/macos-10.15.yml
@@ -1,31 +0,0 @@
-name: macos-10.15
-on: [push, pull_request]
-jobs:
- tests:
- runs-on: macos-10.15
- strategy:
- matrix:
- configure_flags:
- -
- - --disable-threads
- - --disable-threads --disable-sockets
- - --disable-threads --disable-files
- - --disable-threads --disable-sockets --disable-files
- - --disable-sockets
- - --disable-sockets --disable-files
- - --disable-files
- - --disable-shared
- steps:
- - name: Install dependencies
- run: brew install autoconf automake
- - uses: actions/checkout@v2
- - name: autogen.sh
- run: ./autogen.sh
- - name: configure
- run: ./configure ${{ matrix.configure_flags }}
- - name: make
- run: make -j$(sysctl -n hw.logicalcpu)
- - name: make check
- run: make check
- - name: make install
- run: sudo make install
ADDED .github/workflows/macos-12.yml
Index: .github/workflows/macos-12.yml
==================================================================
--- .github/workflows/macos-12.yml
+++ .github/workflows/macos-12.yml
@@ -0,0 +1,31 @@
+name: macos-12
+on: [push, pull_request]
+jobs:
+ tests:
+ runs-on: macos-12
+ strategy:
+ matrix:
+ configure_flags:
+ -
+ - --disable-threads
+ - --disable-threads --disable-sockets
+ - --disable-threads --disable-files
+ - --disable-threads --disable-sockets --disable-files
+ - --disable-sockets
+ - --disable-sockets --disable-files
+ - --disable-files
+ - --disable-shared
+ steps:
+ - name: Install dependencies
+ run: brew install autoconf automake
+ - uses: actions/checkout@v2
+ - name: autogen.sh
+ run: ./autogen.sh
+ - name: configure
+ run: ./configure ${{ matrix.configure_flags }}
+ - name: make
+ run: make -j$(sysctl -n hw.logicalcpu)
+ - name: make check
+ run: make check
+ - name: make install
+ run: sudo make install
Index: .github/workflows/morphos.yml
==================================================================
--- .github/workflows/morphos.yml
+++ .github/workflows/morphos.yml
@@ -1,37 +1,21 @@
name: morphos
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
+ container: amigadev/crosstools:ppc-morphos
strategy:
matrix:
configure_flags:
-
- --disable-amiga-lib
steps:
- - name: Install dependencies
- run: docker pull amigadev/crosstools:ppc-morphos
- uses: actions/checkout@v2
- name: autogen.sh
run: ./autogen.sh
- name: configure
- run: |
- docker run \
- -e PATH="/opt/ppc-morphos/bin:$PATH" \
- -v "$PWD:/objfw" \
- amigadev/crosstools:ppc-morphos \
- sh -c 'cd /objfw && ./configure --host=ppc-morphos ${{ matrix.configure_flags }}'
+ run: ./configure --host=ppc-morphos ${{ matrix.configure_flags }}
- name: make
- run: |
- docker run \
- -e PATH="/opt/ppc-morphos/bin:$PATH" \
- -v "$PWD:/objfw" \
- amigadev/crosstools:ppc-morphos \
- sh -c "cd /objfw && make -j$(nproc)"
+ run: make -j$(nproc)
- name: make install
- run: |
- docker run \
- -e PATH="/opt/ppc-morphos/bin:$PATH" \
- -v "$PWD:/objfw" \
- amigadev/crosstools:ppc-morphos \
- sh -c "cd /objfw && make -j$(nproc)"
+ run: make install
Index: .github/workflows/nintendo-3ds.yml
==================================================================
--- .github/workflows/nintendo-3ds.yml
+++ .github/workflows/nintendo-3ds.yml
@@ -30,6 +30,6 @@
docker run \
-e DEVKITPRO=/opt/devkitpro \
-e PATH="/opt/devkitpro/devkitARM/bin:$PATH" \
-v "$PWD:/objfw" \
devkitpro/devkitarm \
- sh -c "cd /objfw && make -j$(nproc)"
+ sh -c "cd /objfw && make install"
Index: .github/workflows/nintendo-ds.yml
==================================================================
--- .github/workflows/nintendo-ds.yml
+++ .github/workflows/nintendo-ds.yml
@@ -30,6 +30,6 @@
docker run \
-e DEVKITPRO=/opt/devkitpro \
-e PATH="/opt/devkitpro/devkitARM/bin:$PATH" \
-v "$PWD:/objfw" \
devkitpro/devkitarm \
- sh -c "cd /objfw && make -j$(nproc)"
+ sh -c "cd /objfw && make install"
Index: .github/workflows/nintendo-switch.yml
==================================================================
--- .github/workflows/nintendo-switch.yml
+++ .github/workflows/nintendo-switch.yml
@@ -30,6 +30,6 @@
docker run \
-e DEVKITPRO=/opt/devkitpro \
-e PATH="/opt/devkitpro/devkitA64/bin:$PATH" \
-v "$PWD:/objfw" \
devkitpro/devkita64 \
- sh -c "cd /objfw && make -j$(nproc)"
+ sh -c "cd /objfw && make install"
DELETED .github/workflows/ubuntu-18.04-32bit.yml
Index: .github/workflows/ubuntu-18.04-32bit.yml
==================================================================
--- .github/workflows/ubuntu-18.04-32bit.yml
+++ .github/workflows/ubuntu-18.04-32bit.yml
@@ -1,37 +0,0 @@
-name: ubuntu-18.04, 32 bit
-on: [push, pull_request]
-jobs:
- tests:
- runs-on: ubuntu-18.04
- strategy:
- matrix:
- configure_flags:
- - --without-tls
- - --without-tls --enable-seluid24
- - --without-tls --disable-compiler-tls
- - --without-tls --disable-threads
- - --without-tls --disable-threads --disable-sockets
- - --without-tls --disable-threads --disable-files
- - --without-tls --disable-threads --disable-sockets --disable-files
- - --without-tls --disable-sockets
- - --without-tls --disable-sockets --disable-files
- - --without-tls --disable-files
- - --without-tls --disable-shared
- - --without-tls --disable-shared --enable-seluid24
- - --without-tls --disable-compiler-tls --disable-threads
- steps:
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install gcc-multilib
- - uses: actions/checkout@v2
- - name: autogen.sh
- run: ./autogen.sh
- - name: configure
- run: ./configure OBJC="clang -m32" ${{ matrix.configure_flags }}
- - name: make
- run: make -j$(nproc)
- - name: make check
- run: make check
- - name: make install
- run: sudo make install
DELETED .github/workflows/ubuntu-18.04-gcc-32bit.yml
Index: .github/workflows/ubuntu-18.04-gcc-32bit.yml
==================================================================
--- .github/workflows/ubuntu-18.04-gcc-32bit.yml
+++ .github/workflows/ubuntu-18.04-gcc-32bit.yml
@@ -1,37 +0,0 @@
-name: ubuntu-18.04, GCC, 32 bit
-on: [push, pull_request]
-jobs:
- tests:
- runs-on: ubuntu-18.04
- strategy:
- matrix:
- configure_flags:
- - --without-tls
- - --without-tls --enable-seluid24
- - --without-tls --disable-compiler-tls
- - --without-tls --disable-threads
- - --without-tls --disable-threads --disable-sockets
- - --without-tls --disable-threads --disable-files
- - --without-tls --disable-threads --disable-sockets --disable-files
- - --without-tls --disable-sockets
- - --without-tls --disable-sockets --disable-files
- - --without-tls --disable-files
- - --without-tls --disable-shared
- - --without-tls --disable-shared --enable-seluid24
- - --without-tls --disable-compiler-tls --disable-threads
- steps:
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install gcc-multilib gobjc
- - uses: actions/checkout@v2
- - name: autogen.sh
- run: ./autogen.sh
- - name: configure
- run: ./configure OBJC="gcc -m32" ${{ matrix.configure_flags }}
- - name: make
- run: make -j$(nproc)
- - name: make check
- run: make check
- - name: make install
- run: sudo make install
DELETED .github/workflows/ubuntu-18.04-gcc.yml
Index: .github/workflows/ubuntu-18.04-gcc.yml
==================================================================
--- .github/workflows/ubuntu-18.04-gcc.yml
+++ .github/workflows/ubuntu-18.04-gcc.yml
@@ -1,39 +0,0 @@
-name: ubuntu-18.04, GCC
-on: [push, pull_request]
-jobs:
- tests:
- runs-on: ubuntu-18.04
- strategy:
- matrix:
- configure_flags:
- -
- - --enable-seluid24
- - --disable-compiler-tls
- - --disable-threads
- - --disable-threads --disable-sockets
- - --disable-threads --disable-files
- - --disable-threads --disable-sockets --disable-files
- - --disable-sockets
- - --disable-sockets --disable-files
- - --disable-files
- - --disable-shared
- - --disable-shared --enable-seluid24
- - --disable-compiler-tls --disable-threads
- - --with-tls=gnutls
- - --with-tls=gnutls --disable-shared
- steps:
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install gobjc libssl-dev gnutls-dev
- - uses: actions/checkout@v2
- - name: autogen.sh
- run: ./autogen.sh
- - name: configure
- run: ./configure OBJC="gcc" ${{ matrix.configure_flags }}
- - name: make
- run: make -j$(nproc)
- - name: make check
- run: make check
- - name: make install
- run: sudo make install
DELETED .github/workflows/ubuntu-18.04.yml
Index: .github/workflows/ubuntu-18.04.yml
==================================================================
--- .github/workflows/ubuntu-18.04.yml
+++ .github/workflows/ubuntu-18.04.yml
@@ -1,39 +0,0 @@
-name: ubuntu-18.04
-on: [push, pull_request]
-jobs:
- tests:
- runs-on: ubuntu-18.04
- strategy:
- matrix:
- configure_flags:
- -
- - --enable-seluid24
- - --disable-compiler-tls
- - --disable-threads
- - --disable-threads --disable-sockets
- - --disable-threads --disable-files
- - --disable-threads --disable-sockets --disable-files
- - --disable-sockets
- - --disable-sockets --disable-files
- - --disable-files
- - --disable-shared
- - --disable-shared --enable-seluid24
- - --disable-compiler-tls --disable-threads
- - --with-tls=gnutls
- - --with-tls=gnutls --disable-shared
- steps:
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install libssl-dev gnutls-dev
- - uses: actions/checkout@v2
- - name: autogen.sh
- run: ./autogen.sh
- - name: configure
- run: ./configure ${{ matrix.configure_flags }}
- - name: make
- run: make -j$(nproc)
- - name: make check
- run: make check
- - name: make install
- run: sudo make install
ADDED .github/workflows/ubuntu-latest-32bit.yml
Index: .github/workflows/ubuntu-latest-32bit.yml
==================================================================
--- .github/workflows/ubuntu-latest-32bit.yml
+++ .github/workflows/ubuntu-latest-32bit.yml
@@ -0,0 +1,37 @@
+name: ubuntu-latest, 32 bit
+on: [push, pull_request]
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ configure_flags:
+ - --without-tls
+ - --without-tls --enable-seluid24
+ - --without-tls --disable-compiler-tls
+ - --without-tls --disable-threads
+ - --without-tls --disable-threads --disable-sockets
+ - --without-tls --disable-threads --disable-files
+ - --without-tls --disable-threads --disable-sockets --disable-files
+ - --without-tls --disable-sockets
+ - --without-tls --disable-sockets --disable-files
+ - --without-tls --disable-files
+ - --without-tls --disable-shared
+ - --without-tls --disable-shared --enable-seluid24
+ - --without-tls --disable-compiler-tls --disable-threads
+ steps:
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install gcc-multilib
+ - uses: actions/checkout@v2
+ - name: autogen.sh
+ run: ./autogen.sh
+ - name: configure
+ run: ./configure OBJC="clang -m32" ${{ matrix.configure_flags }}
+ - name: make
+ run: make -j$(nproc)
+ - name: make check
+ run: make check
+ - name: make install
+ run: sudo make install
ADDED .github/workflows/ubuntu-latest-gcc-32bit.yml
Index: .github/workflows/ubuntu-latest-gcc-32bit.yml
==================================================================
--- .github/workflows/ubuntu-latest-gcc-32bit.yml
+++ .github/workflows/ubuntu-latest-gcc-32bit.yml
@@ -0,0 +1,37 @@
+name: ubuntu-latest, GCC, 32 bit
+on: [push, pull_request]
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ configure_flags:
+ - --without-tls
+ - --without-tls --enable-seluid24
+ - --without-tls --disable-compiler-tls
+ - --without-tls --disable-threads
+ - --without-tls --disable-threads --disable-sockets
+ - --without-tls --disable-threads --disable-files
+ - --without-tls --disable-threads --disable-sockets --disable-files
+ - --without-tls --disable-sockets
+ - --without-tls --disable-sockets --disable-files
+ - --without-tls --disable-files
+ - --without-tls --disable-shared
+ - --without-tls --disable-shared --enable-seluid24
+ - --without-tls --disable-compiler-tls --disable-threads
+ steps:
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install gcc-multilib gobjc
+ - uses: actions/checkout@v2
+ - name: autogen.sh
+ run: ./autogen.sh
+ - name: configure
+ run: ./configure OBJC="gcc -m32" ${{ matrix.configure_flags }}
+ - name: make
+ run: make -j$(nproc)
+ - name: make check
+ run: make check
+ - name: make install
+ run: sudo make install
ADDED .github/workflows/ubuntu-latest-gcc.yml
Index: .github/workflows/ubuntu-latest-gcc.yml
==================================================================
--- .github/workflows/ubuntu-latest-gcc.yml
+++ .github/workflows/ubuntu-latest-gcc.yml
@@ -0,0 +1,39 @@
+name: ubuntu-latest, GCC
+on: [push, pull_request]
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ configure_flags:
+ -
+ - --enable-seluid24
+ - --disable-compiler-tls
+ - --disable-threads
+ - --disable-threads --disable-sockets
+ - --disable-threads --disable-files
+ - --disable-threads --disable-sockets --disable-files
+ - --disable-sockets
+ - --disable-sockets --disable-files
+ - --disable-files
+ - --disable-shared
+ - --disable-shared --enable-seluid24
+ - --disable-compiler-tls --disable-threads
+ - --with-tls=gnutls
+ - --with-tls=gnutls --disable-shared
+ steps:
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install gobjc libssl-dev gnutls-dev
+ - uses: actions/checkout@v2
+ - name: autogen.sh
+ run: ./autogen.sh
+ - name: configure
+ run: ./configure OBJC="gcc" ${{ matrix.configure_flags }}
+ - name: make
+ run: make -j$(nproc)
+ - name: make check
+ run: make check
+ - name: make install
+ run: sudo make install
ADDED .github/workflows/ubuntu-latest.yml
Index: .github/workflows/ubuntu-latest.yml
==================================================================
--- .github/workflows/ubuntu-latest.yml
+++ .github/workflows/ubuntu-latest.yml
@@ -0,0 +1,39 @@
+name: ubuntu-latest
+on: [push, pull_request]
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ configure_flags:
+ -
+ - --enable-seluid24
+ - --disable-compiler-tls
+ - --disable-threads
+ - --disable-threads --disable-sockets
+ - --disable-threads --disable-files
+ - --disable-threads --disable-sockets --disable-files
+ - --disable-sockets
+ - --disable-sockets --disable-files
+ - --disable-files
+ - --disable-shared
+ - --disable-shared --enable-seluid24
+ - --disable-compiler-tls --disable-threads
+ - --with-tls=gnutls
+ - --with-tls=gnutls --disable-shared
+ steps:
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install libssl-dev gnutls-dev
+ - uses: actions/checkout@v2
+ - name: autogen.sh
+ run: ./autogen.sh
+ - name: configure
+ run: ./configure ${{ matrix.configure_flags }}
+ - name: make
+ run: make -j$(nproc)
+ - name: make check
+ run: make check
+ - name: make install
+ run: sudo make install
Index: .github/workflows/wii-u.yml
==================================================================
--- .github/workflows/wii-u.yml
+++ .github/workflows/wii-u.yml
@@ -30,6 +30,6 @@
docker run \
-e DEVKITPRO=/opt/devkitpro \
-e PATH="/opt/devkitpro/devkitPPC/bin:$PATH" \
-v "$PWD:/objfw" \
devkitpro/devkitppc \
- sh -c "cd /objfw && make -j$(nproc)"
+ sh -c "cd /objfw && make install"
Index: .github/workflows/wii.yml
==================================================================
--- .github/workflows/wii.yml
+++ .github/workflows/wii.yml
@@ -30,6 +30,6 @@
docker run \
-e DEVKITPRO=/opt/devkitpro \
-e PATH="/opt/devkitpro/devkitPPC/bin:$PATH" \
-v "$PWD:/objfw" \
devkitpro/devkitppc \
- sh -c "cd /objfw && make -j$(nproc)"
+ sh -c "cd /objfw && make install"
Index: .gitignore
==================================================================
--- .gitignore
+++ .gitignore
@@ -53,9 +53,10 @@
tests/tests.arm9
tests/tests.nds
tests/tests.nro
tests/tests.rpx
utils/objfw-config
+utils/objfw-new/objfw-new
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp
Index: Doxyfile
==================================================================
--- Doxyfile
+++ Doxyfile
@@ -7,36 +7,36 @@
GENERATE_LATEX = NO
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_MEMBERS = YES
TYPEDEF_HIDES_STRUCT = YES
PREDEFINED = __OBJC__ \
- _Nonnull \
- _Nullable \
+ _Nonnull= \
+ _Nullable= \
DOXYGEN \
- OF_BOXABLE \
- OF_CONSUMED \
- OF_DESIGNATED_INITIALIZER \
+ OF_BOXABLE= \
+ OF_CONSUMED= \
+ OF_DESIGNATED_INITIALIZER= \
OF_GENERIC(...)= \
OF_HAVE_BLOCKS \
OF_HAVE_FILES \
OF_HAVE_SANDBOX \
OF_HAVE_SOCKETS \
OF_HAVE_THREADS \
OF_KINDOF(...)= \
- OF_NO_RETURN \
- OF_NO_RETURN_FUNC \
+ OF_NO_RETURN= \
+ OF_NO_RETURN_FUNC= \
OF_NULLABLE_PROPERTY(...)= \
OF_NULL_RESETTABLE_PROPERTY(...)= \
- OF_REQUIRES_SUPER \
- OF_RETURNS_INNER_POINTER \
- OF_RETURNS_NOT_RETAINED \
- OF_RETURNS_RETAINED \
- OF_ROOT_CLASS \
- OF_SENTINEL \
- OF_WARN_UNUSED_RESULT \
- OF_WEAK_UNAVAILABLE \
+ OF_REQUIRES_SUPER= \
+ OF_RETURNS_INNER_POINTER= \
+ OF_RETURNS_NOT_RETAINED= \
+ OF_RETURNS_RETAINED= \
+ OF_ROOT_CLASS= \
+ OF_SENTINEL= \
+ OF_WARN_UNUSED_RESULT= \
+ OF_WEAK_UNAVAILABLE= \
SIGHUP \
SIGUSR1 \
SIGUSR2
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
IGNORE_PREFIX = OF of_
Index: Makefile
==================================================================
--- Makefile
+++ Makefile
@@ -26,14 +26,13 @@
release: docs
echo "Generating tarball for version ${PACKAGE_VERSION}..."
rm -fr objfw-${PACKAGE_VERSION} objfw-${PACKAGE_VERSION}.tar \
objfw-${PACKAGE_VERSION}.tar.gz
fossil tarball --name objfw-${PACKAGE_VERSION} current - \
- --exclude '.cirrus*,.fossil*,.git*' | ofarc -ttgz -xq -
+ --exclude '.fossil*,.git*' | ofarc -ttgz -xq -
cp configure config.h.in objfw-${PACKAGE_VERSION}/
- ofarc -cq objfw-${PACKAGE_VERSION}.tar \
- $$(find objfw-${PACKAGE_VERSION} | sort)
+ ofarc -cq objfw-${PACKAGE_VERSION}.tar objfw-${PACKAGE_VERSION}
rm -fr objfw-${PACKAGE_VERSION}
gzip -9 objfw-${PACKAGE_VERSION}.tar
rm -f objfw-${PACKAGE_VERSION}.tar
gpg -b objfw-${PACKAGE_VERSION}.tar.gz || true
echo "Generating documentation..."
@@ -42,10 +41,10 @@
rm -fr objfw-docs-${PACKAGE_VERSION} objfw-docs-${PACKAGE_VERSION}.tar \
objfw-docs-${PACKAGE_VERSION}.tar.gz
mv docs objfw-docs-${PACKAGE_VERSION}
echo "Generating docs tarball for version ${PACKAGE_VERSION}..."
ofarc -cq objfw-docs-${PACKAGE_VERSION}.tar \
- $$(find objfw-docs-${PACKAGE_VERSION} | sort)
+ objfw-docs-${PACKAGE_VERSION}
rm -fr objfw-docs-${PACKAGE_VERSION}
gzip -9 objfw-docs-${PACKAGE_VERSION}.tar
rm -f objfw-docs-${PACKAGE_VERSION}.tar
gpg -b objfw-docs-${PACKAGE_VERSION}.tar.gz || true
Index: README.md
==================================================================
--- README.md
+++ README.md
@@ -227,31 +227,41 @@
signed, so make sure you download it via HTTPS. However, packages you
download and install via MSYS2 are cryptographically signed.
Setting up MSYS2
- MSYS2 currently supports 5 different
+ MSYS2 currently supports 7 different
[environments](https://www.msys2.org/docs/environments/). All of them except
for the one called just "MSYS" are supported, but which packages you need to
- install depends on the environment(s) you want to use.
-
- For MINGW64, use:
-
- $ pacman -Syu mingw-w64-x86_64-clang mingw-w64-x86_64-fossil
-
- For UCRT64, use:
-
- $ pacman -Syu mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-fossil
+ install depends on the environment(s) you want to use. If you only want to
+ target Windows 10 and newer, the CLANG64 and CLANG32 environments are the
+ recommended ones.
For CLANG64, use:
$ pacman -Syu mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-fossil
+ For CLANG32, use:
+
+ $ pacman -Syu mingw-w64-clang-i686-clang mingw-w64-clang-i686-fossil
+
+ For CLANGARM64, use (you need to use Fossil via another environment):
+
+ $ pacman -Syu mingw-w64-clang-aarch64-clang
+
+ For MINGW64, use:
+
+ $ pacman -Syu mingw-w64-x86_64-clang mingw-w64-x86_64-fossil
+
For MINGW32, use:
$ pacman -Syu mingw-w64-i686-clang mingw-w64-i686-fossil
+ For UCRT64, use:
+
+ $ pacman -Syu mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-fossil
+
When using `pacman` to install the packages, `pacman` might tell you to close
the window. If it does so, close the window, restart MSYS2 and execute the
`pacman` command again.
There is nothing wrong with installing multiple environments, as MSYS2 has
@@ -309,11 +319,11 @@
Writing your first application with ObjFW
To create your first, empty application, you can use `objfw-new`:
- $ objfw-new app MyFirstApp
+ $ objfw-new --app MyFirstApp
This creates a file `MyFirstApp.m`. The `-[applicationDidFinishLaunching]`
method is called as soon as ObjFW finished all initialization. Use this as
the entry point to your own code. For example, you could add the following
line there to create a "Hello World":
Index: build-aux/m4/buildsys.m4
==================================================================
--- build-aux/m4/buildsys.m4
+++ build-aux/m4/buildsys.m4
@@ -207,11 +207,11 @@
LIB_CFLAGS=''
LIB_LDFLAGS='-shared -Wl,--export-all-symbols'
LIB_LDFLAGS_INSTALL_NAME=''
LIB_PREFIX=''
LIB_SUFFIX='${LIB_MAJOR}.dll'
- LINK_LIB='&& ${LN_S} $$out lib$${out%${LIB_SUFFIX}}.dll.a'
+ LINK_LIB='&& rm -f lib$${out%${LIB_SUFFIX}}.dll.a && ${LN_S} $$out lib$${out%${LIB_SUFFIX}}.dll.a'
PLUGIN_CFLAGS=''
PLUGIN_LDFLAGS='-shared -Wl,--export-all-symbols'
PLUGIN_SUFFIX='.dll'
LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
INSTALL_LIB='&& ${MKDIR_P} ${DESTDIR}${bindir} && ${INSTALL} -m 755 $$i ${DESTDIR}${bindir}/$$i && ${INSTALL} -m 755 lib$${i%${LIB_SUFFIX}}.dll.a ${DESTDIR}${libdir}/lib$${i%${LIB_SUFFIX}}.dll.a'
Index: buildsys.mk.in
==================================================================
--- buildsys.mk.in
+++ buildsys.mk.in
@@ -79,11 +79,11 @@
SHELL = @SHELL@
MSGFMT = @MSGFMT@
JAVAC = @JAVAC@
JAVACFLAGS = @JAVACFLAGS@
JAR = @JAR@
-WINDRES = @WINDRES@
+RC = @RC@
BUILD_AND_HOST_ARE_DARWIN = @BUILD_AND_HOST_ARE_DARWIN@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
@@ -577,11 +577,11 @@
.rc.o .rc.lib.o .rc.plugin.o:
${COMPILE_STATUS}
in="$<"; \
out="$@"; \
- if ${WINDRES} ${CPPFLAGS} -J rc -O coff -o $@ $<; then \
+ if ${RC} ${RCFLAGS} ${CPPFLAGS} -J rc -O coff -o $@ $<; then \
${COMPILE_OK}; \
else \
${COMPILE_FAILED}; \
fi
@@ -771,11 +771,11 @@
${DIR_LEAVE}; \
done
for i in "" ${SHARED_LIB}; do \
test x"$$i" = x"" && continue; \
- if test -f ${DESTDIR}${libdir}/$$i; then \
+ if test -f ${DESTDIR}${libdir}/$$i -o -f ${DESTDIR}${bindir}/$$i; then \
if : @UNINSTALL_LIB@; then \
${DELETE_OK}; \
else \
${DELETE_FAILED}; \
fi \
Index: configure.ac
==================================================================
--- configure.ac
+++ configure.ac
@@ -97,11 +97,11 @@
enable_shared="no"
enable_threads="no"
enable_sockets="no"
;;
*-*-mingw*)
- LDFLAGS="$LDFLAGS -Wl,--allow-multiple-definition"
+ LDFLAGS="$LDFLAGS -Wl,--allow-multiple-definition -static-libgcc"
LIBS="$LIBS -lversion"
AC_SUBST(USE_SRCS_WINDOWS, '${SRCS_WINDOWS}')
;;
*-psp-*)
@@ -399,10 +399,16 @@
AC_MSG_ERROR(Compiler does not support properties!)
])
AC_CHECK_TOOL(AR, ar)
AC_PROG_RANLIB
+
+case "$host_os" in
+mingw*)
+ AC_CHECK_TOOL(RC, windres)
+ ;;
+esac
AC_ARG_ENABLE(shared,
AS_HELP_STRING([--disable-shared], [do not build shared library]))
AS_IF([test x"$enable_shared" != x"no"], [
BUILDSYS_SHARED_LIB
@@ -477,10 +483,11 @@
defined(__NEWLIB__) || defined(__MORPHOS__) || defined(__MINT__)
egrep_cpp_yes
#endif
], [
AC_MSG_RESULT(yes)
+ CPPFLAGS="-D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 $CPPFLAGS"
CPPFLAGS="-D_GNU_SOURCE $CPPFLAGS"
gnu_source="yes"
], [
AC_MSG_RESULT(no)
])
@@ -1049,10 +1056,14 @@
;;
m68k-*-amigaos* | powerpc-*-amigaos*)
dnl Compiler TLS is broken on AmigaOS
enable_compiler_tls="no"
;;
+ *-*-mingw*)
+ dnl Causes emutls to be pulled in, which pulls in winpthread.
+ enable_compiler_tls="no"
+ ;;
*-*-morphos*)
dnl Compiler TLS needs helpers that we don't want in the
dnl .library
enable_compiler_tls="no"
;;
@@ -1225,10 +1236,11 @@
AC_ARG_ENABLE(files,
AS_HELP_STRING([--disable-files], [disable file support]))
AS_IF([test x"$enable_files" != x"no"], [
AC_DEFINE(OF_HAVE_FILES, 1, [Whether we have files])
AC_SUBST(USE_SRCS_FILES, '${SRCS_FILES}')
+ AC_SUBST(OBJFW_NEW, "objfw-new")
AC_SUBST(OFARC, "ofarc")
AC_SUBST(OFHASH, "ofhash")
case "$host_os" in
msdosdjgpp*)
@@ -1382,27 +1394,24 @@
AC_CHECK_HEADER(sys/socket.h, [
AC_DEFINE(OF_HAVE_SYS_SOCKET_H, 1,
[Whether we have sys/socket.h])
])
+
AC_CHECK_HEADER(netinet/in.h, [
AC_DEFINE(OF_HAVE_NETINET_IN_H, 1,
[Whether we have netinet/in.h])
])
AC_CHECK_HEADER(netinet/tcp.h, [
AC_DEFINE(OF_HAVE_NETINET_TCP_H, 1,
[Whether we have netinet/tcp.h])
])
- AC_CHECK_HEADERS([arpa/inet.h netdb.h])
+ AC_CHECK_HEADERS([arpa/inet.h netdb.h net/if.h])
+ AC_CHECK_FUNCS([if_indextoname if_nametoindex])
AC_CHECK_HEADER(sys/un.h, [
AC_DEFINE(OF_HAVE_SYS_UN_H, 1, [Whether we have sys/un.h])
])
- AC_CHECK_HEADER(netipx/ipx.h, [
- AC_DEFINE(OF_HAVE_NETIPX_IPX_H, 1,
- [Whether we have netipx/ipx.h])
- ])
-
AC_CHECK_MEMBER([struct sockaddr_in6.sin6_addr], [
AC_EGREP_CPP(egrep_cpp_yes, [
#ifdef _WIN32
typedef int BOOL;
#endif
@@ -1485,10 +1494,14 @@
#ifdef __MINT__
# error Gives invalid argument at runtime
#endif
])
+ AC_CHECK_HEADER(netipx/ipx.h, [
+ AC_DEFINE(OF_HAVE_NETIPX_IPX_H, 1,
+ [Whether we have netipx/ipx.h])
+ ])
AC_CHECK_MEMBER(struct sockaddr_ipx.sipx_network, [], [
AC_CHECK_MEMBER(struct sockaddr_ipx.sa_netnum, [], [], [
#ifdef _WIN32
typedef int BOOL;
#endif
@@ -1556,10 +1569,101 @@
], [
AC_DEFINE(OF_HAVE_IPX, 1, [Whether we have IPX/SPX])
AC_SUBST(USE_SRCS_IPX, '${SRCS_IPX}')
])
])
+
+ AC_CHECK_HEADER(netat/appletalk.h, [
+ AC_DEFINE(OF_HAVE_NETAT_APPLETALK_H, 1,
+ [Whether we have netat/appletalk.h])
+ ])
+ AC_CHECK_HEADER(netatalk/at.h, [
+ AC_DEFINE(OF_HAVE_NETATALK_AT_H, 1,
+ [Whether we have netatalk/at.h])
+ ])
+ AC_CHECK_MEMBER(struct sockaddr_at.sat_addr, [], [
+ AC_CHECK_MEMBER(struct sockaddr_at.sat_net, [], [], [
+ #ifdef _WIN32
+ typedef int BOOL;
+ #endif
+
+ #ifdef OF_HAVE_SYS_TYPES_H
+ # include
+ #endif
+ #if defined(OF_HAVE_NETAT_APPLETALK_H)
+ # include
+ #elif defined(OF_HAVE_NETATALK_AT_H)
+ # include
+ #endif
+
+ #ifdef _WIN32
+ # ifdef __MINGW32__
+ # include <_mingw.h>
+ # ifdef __MINGW64_VERSION_MAJOR
+ # include
+ # endif
+ # endif
+ # include
+ # include
+ #endif
+ ])
+ ], [
+ #ifdef _WIN32
+ typedef int BOOL;
+ #endif
+
+ #ifdef OF_HAVE_SYS_TYPES_H
+ # include
+ #endif
+ #if defined(OF_HAVE_NETAT_APPLETALK_H)
+ # include
+ #elif defined(OF_HAVE_NETATALK_AT_H)
+ # include
+ #endif
+
+ #ifdef _WIN32
+ # ifdef __MINGW32__
+ # include <_mingw.h>
+ # ifdef __MINGW64_VERSION_MAJOR
+ # include
+ # endif
+ # endif
+ # include
+ # include
+ #endif
+ ])
+ AS_IF([test x"$ac_cv_member_struct_sockaddr_at_sat_addr" = x"yes" \
+ -o x"$ac_cv_member_struct_sockaddr_at_sat_net" = x"yes"], [
+ AC_EGREP_CPP(egrep_cpp_yes, [
+ #ifdef _WIN32
+ typedef int BOOL;
+ #endif
+
+ #ifdef OF_HAVE_SYS_SOCKET_H
+ # include
+ #endif
+
+ #ifdef _WIN32
+ # ifdef __MINGW32__
+ # include <_mingw.h>
+ # ifdef __MINGW64_VERSION_MAJOR
+ # include
+ # endif
+ # endif
+ # include
+ # include
+ #endif
+
+ #ifdef AF_APPLETALK
+ egrep_cpp_yes
+ #endif
+ ], [
+ AC_DEFINE(OF_HAVE_APPLETALK, 1,
+ [Whether we have AppleTalk])
+ AC_SUBST(USE_SRCS_APPLETALK, '${SRCS_APPLETALK}')
+ ])
+ ])
AC_CHECK_FUNCS(paccept accept4, break)
AC_CHECK_FUNCS(kqueue1 kqueue, [
AC_DEFINE(HAVE_KQUEUE, 1, [Whether we have kqueue])
@@ -1632,10 +1736,27 @@
], [])
LIBS="$old_LIBS"
])
])
+
+ AS_IF([test x"$with_tls" = x"gnutls" \
+ -o \( x"$with_tls" = x"yes" -a x"$tls_support" = x"no" \)], [
+ PKG_CHECK_MODULES(gnutls, [gnutls >= 3.5.0], [
+ AC_DEFINE(HAVE_GNUTLS, 1, [Whether we have GnuTLS])
+
+ tls_support="GnuTLS"
+ TLS_CPPFLAGS="$gnutls_CFLAGS $TLS_CPPFLAGS"
+ TLS_LIBS="$gnutls_LIBS $TLS_LIBS"
+
+ AC_SUBST(OF_GNUTLS_TLS_STREAM_M, "OFGnuTLSTLSStream.m")
+ ], [
+ dnl Disable default action-if-not-found, which exits
+ dnl configure with an error.
+ :
+ ])
+ ])
AS_IF([test x"$with_tls" = x"openssl" \
-o \( x"$with_tls" = x"yes" -a x"$tls_support" = x"no" \)], [
case "$host_os" in
morphos*)
@@ -1660,27 +1781,10 @@
"OFOpenSSLTLSStream.m")
])
], [], [-l$crypto])
])
- AS_IF([test x"$with_tls" = x"gnutls" \
- -o \( x"$with_tls" = x"yes" -a x"$tls_support" = x"no" \)], [
- PKG_CHECK_MODULES(gnutls, [gnutls >= 3.5.0], [
- AC_DEFINE(HAVE_GNUTLS, 1, [Whether we have GnuTLS])
-
- tls_support="GnuTLS"
- TLS_CPPFLAGS="$gnutls_CFLAGS $TLS_CPPFLAGS"
- TLS_LIBS="$gnutls_LIBS $TLS_LIBS"
-
- AC_SUBST(OF_GNUTLS_TLS_STREAM_M, "OFGnuTLSTLSStream.m")
- ], [
- dnl Disable default action-if-not-found, which exits
- dnl configure with an error.
- :
- ])
- ])
-
AS_IF([test x"$tls_support" != x"no"], [
AC_SUBST(TLS, "tls")
AC_SUBST(TLS_CPPFLAGS)
AC_SUBST(TLS_LIBS)
AC_DEFINE(HAVE_TLS_SUPPORT, 1,
@@ -1992,11 +2096,12 @@
# define OF_DEALLOC_UNSUPPORTED \
[self doesNotRecognizeSelector: _cmd]; \
\
abort(); \
\
- _Pragma("clang diagnostic push ignore \
+ _Pragma("clang diagnostic push"); \
+ _Pragma("clang diagnostic ignored \
\"-Wunreachable-code\""); \
[super dealloc]; \
_Pragma("clang diagnostic pop");
#else
# define OF_DEALLOC_UNSUPPORTED \
Index: extra.mk.in
==================================================================
--- extra.mk.in
+++ extra.mk.in
@@ -41,10 +41,11 @@
LOOKUP_ASM_A = @LOOKUP_ASM_A@
LOOKUP_ASM_AMIGALIB_A = @LOOKUP_ASM_AMIGALIB_A@
LOOKUP_ASM_LIB_A = @LOOKUP_ASM_LIB_A@
MAP_LDFLAGS = @MAP_LDFLAGS@
OBJC_SYNC = @OBJC_SYNC@
+OBJFW_NEW = @OBJFW_NEW@
OFARC = @OFARC@
OFDNS = @OFDNS@
OFHASH = @OFHASH@
OFHTTP = @OFHTTP@
OFHTTP_LIBS = @OFHTTP_LIBS@
@@ -76,13 +77,14 @@
TLS = @TLS@
TLS_CPPFLAGS = @TLS_CPPFLAGS@
TLS_LIBS = @TLS_LIBS@
UNICODE_M = @UNICODE_M@
USE_INCLUDES_ATOMIC = @USE_INCLUDES_ATOMIC@
+USE_SRCS_APPLETALK = @USE_SRCS_APPLETALK@
USE_SRCS_FILES = @USE_SRCS_FILES@
USE_SRCS_IPX = @USE_SRCS_IPX@
USE_SRCS_PLUGINS = @USE_SRCS_PLUGINS@
USE_SRCS_SOCKETS = @USE_SRCS_SOCKETS@
USE_SRCS_THREADS = @USE_SRCS_THREADS@
USE_SRCS_UNIX_SOCKETS = @USE_SRCS_UNIX_SOCKETS@
USE_SRCS_WINDOWS = @USE_SRCS_WINDOWS@
WRAPPER = @WRAPPER@
Index: generators/library/LibraryGenerator.m
==================================================================
--- generators/library/LibraryGenerator.m
+++ generators/library/LibraryGenerator.m
@@ -16,11 +16,11 @@
#include "config.h"
#import "OFApplication.h"
#import "OFFile.h"
#import "OFFileManager.h"
-#import "OFURL.h"
+#import "OFURI.h"
#import "OFXMLElement.h"
#import "FuncArrayGenerator.h"
#import "GlueGenerator.h"
#import "LinkLibGenerator.h"
@@ -31,36 +31,36 @@
OF_APPLICATION_DELEGATE(LibraryGenerator)
@implementation LibraryGenerator
- (void)applicationDidFinishLaunching
{
- OFURL *sourcesURL = [[OFFileManager defaultManager].currentDirectoryURL
- URLByAppendingPathComponent: @"../../src"];
- OFURL *runtimeLibraryURL = [sourcesURL
- URLByAppendingPathComponent: @"runtime/amiga-library.xml"];
- OFURL *runtimeLinkLibURL = [sourcesURL
- URLByAppendingPathComponent: @"runtime/linklib/linklib.m"];
- OFURL *runtimeGlueHeaderURL = [sourcesURL
- URLByAppendingPathComponent: @"runtime/amiga-glue.h"];
- OFURL *runtimeGlueURL = [sourcesURL
- URLByAppendingPathComponent: @"runtime/amiga-glue.m"];
- OFURL *runtimeFuncArrayURL = [sourcesURL
- URLByAppendingPathComponent: @"runtime/amiga-funcarray.inc"];
+ OFURI *sourcesURI = [[OFFileManager defaultManager].currentDirectoryURI
+ URIByAppendingPathComponent: @"../../src"];
+ OFURI *runtimeLibraryURI = [sourcesURI
+ URIByAppendingPathComponent: @"runtime/amiga-library.xml"];
+ OFURI *runtimeLinkLibURI = [sourcesURI
+ URIByAppendingPathComponent: @"runtime/linklib/linklib.m"];
+ OFURI *runtimeGlueHeaderURI = [sourcesURI
+ URIByAppendingPathComponent: @"runtime/amiga-glue.h"];
+ OFURI *runtimeGlueURI = [sourcesURI
+ URIByAppendingPathComponent: @"runtime/amiga-glue.m"];
+ OFURI *runtimeFuncArrayURI = [sourcesURI
+ URIByAppendingPathComponent: @"runtime/amiga-funcarray.inc"];
OFXMLElement *runtimeLibrary = [OFXMLElement elementWithStream:
- [OFFile fileWithPath: runtimeLibraryURL.fileSystemRepresentation
+ [OFFile fileWithPath: runtimeLibraryURI.fileSystemRepresentation
mode: @"r"]];
OFFile *runtimeLinkLib =
- [OFFile fileWithPath: runtimeLinkLibURL.fileSystemRepresentation
+ [OFFile fileWithPath: runtimeLinkLibURI.fileSystemRepresentation
mode: @"w"];
OFFile *runtimeGlueHeader =
- [OFFile fileWithPath: runtimeGlueHeaderURL.fileSystemRepresentation
+ [OFFile fileWithPath: runtimeGlueHeaderURI.fileSystemRepresentation
mode: @"w"];
OFFile *runtimeGlue =
- [OFFile fileWithPath: runtimeGlueURL.fileSystemRepresentation
+ [OFFile fileWithPath: runtimeGlueURI.fileSystemRepresentation
mode: @"w"];
OFFile *runtimeFuncArray =
- [OFFile fileWithPath: runtimeFuncArrayURL.fileSystemRepresentation
+ [OFFile fileWithPath: runtimeFuncArrayURI.fileSystemRepresentation
mode: @"w"];
LinkLibGenerator *runtimeLinkLibGenerator = [[[LinkLibGenerator alloc]
initWithLibrary: runtimeLibrary
implementation: runtimeLinkLib] autorelease];
GlueGenerator *runtimeGlueGenerator = [[[GlueGenerator alloc]
Index: generators/unicode/TableGenerator.m
==================================================================
--- generators/unicode/TableGenerator.m
+++ generators/unicode/TableGenerator.m
@@ -18,11 +18,11 @@
#include
#import "OFString.h"
#import "OFArray.h"
#import "OFApplication.h"
-#import "OFURL.h"
+#import "OFURI.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFHTTPClient.h"
#import "OFFile.h"
#import "OFStdIOStream.h"
@@ -30,13 +30,13 @@
#import "OFOutOfRangeException.h"
#import "TableGenerator.h"
#import "copyright.h"
-static OFString *const unicodeDataURL =
+static OFString *const unicodeDataURI =
@"http://www.unicode.org/Public/UNIDATA/UnicodeData.txt";
-static OFString *const caseFoldingURL =
+static OFString *const caseFoldingURI =
@"http://www.unicode.org/Public/UNIDATA/CaseFolding.txt";
OF_APPLICATION_DELEGATE(TableGenerator)
@implementation TableGenerator
@@ -66,12 +66,12 @@
{
OFHTTPRequest *request;
[OFStdOut writeString: @"Downloading UnicodeData.txt…"];
_state = stateUnicodeData;
- request = [OFHTTPRequest requestWithURL:
- [OFURL URLWithString: unicodeDataURL]];
+ request = [OFHTTPRequest requestWithURI:
+ [OFURI URIWithString: unicodeDataURI]];
[_HTTPClient asyncPerformRequest: request];
}
- (void)client: (OFHTTPClient *)client
didPerformRequest: (OFHTTPRequest *)request
@@ -135,11 +135,11 @@
bool compat = false;
OFMutableString *string;
if ([decomposed.firstObject hasPrefix: @"<"]) {
decomposed = [decomposed objectsInRange:
- OFRangeMake(1, decomposed.count - 1)];
+ OFMakeRange(1, decomposed.count - 1)];
compat = true;
}
string = [OFMutableString string];
@@ -166,12 +166,12 @@
[OFStdOut writeLine: @" done"];
[OFStdOut writeString: @"Downloading CaseFolding.txt…"];
_state = stateCaseFolding;
- request = [OFHTTPRequest requestWithURL:
- [OFURL URLWithString: caseFoldingURL]];
+ request = [OFHTTPRequest requestWithURI:
+ [OFURI URIWithString: caseFoldingURI]];
[_HTTPClient asyncPerformRequest: request];
}
- (void)parseCaseFolding: (OFHTTPResponse *)response
{
@@ -268,19 +268,19 @@
} while (!done);
}
- (void)writeFiles
{
- OFURL *URL;
+ OFURI *URI;
[OFStdOut writeString: @"Writing files…"];
- URL = [OFURL fileURLWithPath: @"../../src/unicode.m"];
- [self writeTablesToFile: URL.fileSystemRepresentation];
+ URI = [OFURI fileURIWithPath: @"../../src/unicode.m"];
+ [self writeTablesToFile: URI.fileSystemRepresentation];
- URL = [OFURL fileURLWithPath: @"../../src/unicode.h"];
- [self writeHeaderToFile: URL.fileSystemRepresentation];
+ URI = [OFURI fileURIWithPath: @"../../src/unicode.h"];
+ [self writeHeaderToFile: URI.fileSystemRepresentation];
[OFStdOut writeLine: @" done"];
[OFApplication terminate];
}
Index: objfw.spec
==================================================================
--- objfw.spec
+++ objfw.spec
@@ -30,11 +30,11 @@
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: clang
BuildRequires: make
-BuildRequires: pkgconfig(openssl)
+BuildRequires: pkgconfig(gnutls)
Requires: %{libobjfw_pkgname}%{_isa} = %{version}-%{release}
Requires: %{libobjfw_pkgname}-devel = %{version}-%{release}
Requires: %{libobjfwrt_pkgname}%{_isa} = %{version}-%{release}
Requires: %{libobjfwrt_pkgname}-devel = %{version}-%{release}
Requires: ofarc%{_isa} = %{version}-%{release}
@@ -87,11 +87,11 @@
The %{libobjfwrt_pkgname}-devel package contains header files and libraries for
ObjFW's Objective-C runtime library.
%package -n %{libobjfwtls_pkgname}
Summary: TLS support for ObjFW
-Requires: openssl%{_isa} >= 1.1.1
+Requires: gnutls%{_isa} >= 3.0.5
%description -n %{libobjfwtls_pkgname}
The %{libobjfwtls_pkgname} package contains TLS support for ObjFW
%package -n %{libobjfwtls_pkgname}-devel
@@ -136,14 +136,14 @@
Requires: %{libobjfw_pkgname}%{_isa} = %{version}-%{release}
Requires: %{libobjfwrt_pkgname}%{_isa} = %{version}-%{release}
Requires: %{libobjfwtls_pkgname}%{_isa} = %{version}-%{release}
%description -n ofhttp
-ofhttp is a command line downloader for HTTP and HTTPS (via ObjOpenSSL) using
-ObjFW's OFHTTPClient class. It supports all features one would expect from a
-modern command line downloader such as resuming of downloads, using a SOCKS5
-proxy, a modern terminal-based UI, etc.
+ofhttp is a command line downloader for HTTP and HTTPS using ObjFW's
+OFHTTPClient class. It supports all features one would expect from a modern
+command line downloader such as resuming of downloads, using a SOCKS5 proxy, a
+modern terminal-based UI, etc.
%prep
%autosetup
./autogen.sh
Index: src/Makefile
==================================================================
--- src/Makefile
+++ src/Makefile
@@ -32,18 +32,16 @@
OFData+ASN1DERParsing.m \
OFData+CryptographicHashing.m \
OFData+MessagePackParsing.m \
OFDate.m \
OFDictionary.m \
- OFEmbeddedFileURLHandler.m \
OFEnumerator.m \
OFFileManager.m \
OFGZIPStream.m \
OFHMAC.m \
OFINICategory.m \
OFINIFile.m \
- OFINIFileSettings.m \
OFInflate64Stream.m \
OFInflateStream.m \
OFInvocation.m \
OFLHAArchive.m \
OFLHAArchiveEntry.m \
@@ -61,11 +59,11 @@
OFMutablePair.m \
OFMutableSet.m \
OFMutableString.m \
OFMutableTarArchiveEntry.m \
OFMutableTriple.m \
- OFMutableURL.m \
+ OFMutableURI.m \
OFMutableZIPArchiveEntry.m \
OFNotification.m \
OFNotificationCenter.m \
OFNull.m \
OFNumber.m \
@@ -94,13 +92,13 @@
OFStdIOStream.m \
OFStream.m \
OFString.m \
OFString+CryptographicHashing.m \
OFString+JSONParsing.m \
+ OFString+PercentEncoding.m \
OFString+PropertyListParsing.m \
OFString+Serialization.m \
- OFString+URLEncoding.m \
OFString+XMLEscaping.m \
OFString+XMLUnescaping.m \
${OF_SUBPROCESS_M} \
OFSettings.m \
OFSystemInfo.m \
@@ -107,12 +105,12 @@
OFTarArchive.m \
OFTarArchiveEntry.m \
OFThread.m \
OFTimer.m \
OFTriple.m \
- OFURL.m \
- OFURLHandler.m \
+ OFURI.m \
+ OFURIHandler.m \
OFUUID.m \
OFValue.m \
OFXMLAttribute.m \
OFXMLCDATA.m \
OFXMLCharacters.m \
@@ -148,12 +146,14 @@
OFSocket.m \
OFStreamSocket.m \
OFTCPSocket.m \
OFTLSStream.m \
OFUDPSocket.m \
+ ${USE_SRCS_APPLETALK} \
${USE_SRCS_IPX} \
${USE_SRCS_UNIX_SOCKETS}
+SRCS_APPLETALK = OFDDPSocket.m
SRCS_IPX = OFIPXSocket.m \
OFSPXSocket.m \
OFSPXStreamSocket.m
SRCS_UNIX_SOCKETS = OFUNIXDatagramSocket.m \
OFUNIXStreamSocket.m
@@ -162,44 +162,48 @@
OFPlainCondition.m \
OFPlainMutex.m \
OFPlainThread.m \
OFRecursiveMutex.m \
OFTLSKey.m
-SRCS_WINDOWS = OFWin32ConsoleStdIOStream.m \
- OFWindowsRegistryKey.m
+SRCS_WINDOWS = OFWindowsRegistryKey.m
INCLUDES_ATOMIC = OFAtomic.h \
platform/GCC4/OFAtomic.h \
platform/GCC4.7/OFAtomic.h \
platform/PowerPC/OFAtomic.h \
platform/macOS/OFAtomic.h \
platform/x86/OFAtomic.h
INCLUDES := ${SRCS:.m=.h} \
OFASN1DERRepresentation.h \
+ OFArchiveEntry.h \
OFCollection.h \
OFCryptographicHash.h \
OFJSONRepresentation.h \
OFKernelEventObserver.h \
OFKeyValueCoding.h \
OFLocking.h \
OFMessagePackRepresentation.h \
+ OFMutableArchiveEntry.h \
ObjFW.h \
macros.h \
objfw-defs.h \
platform.h \
${USE_INCLUDES_ATOMIC}
SRCS += OFASPrintF.m \
OFAdjacentArray.m \
OFAdjacentSubarray.m \
+ OFArchiveURIHandler.m \
OFBase64.m \
OFBitSetCharacterSet.m \
OFBytesValue.m \
OFCRC16.m \
OFCRC32.m \
OFCountedMapTableSet.m \
+ OFEmbeddedURIHandler.m \
OFHuffmanTree.m \
+ OFINIFileSettings.m \
OFInvertedCharacterSet.m \
OFLHADecompressingStream.m \
OFMapTableDictionary.m \
OFMapTableSet.m \
OFMutableAdjacentArray.m \
@@ -218,22 +222,24 @@
OFSubarray.m \
OFUTF8String.m \
${LIBBASES_M} \
${RUNTIME_AUTORELEASE_M} \
${RUNTIME_INSTANCE_M} \
- ${UNICODE_M}
-SRCS_FILES += OFFileURLHandler.m
-SRCS_SOCKETS += OFDNSResolverSettings.m \
+ ${UNICODE_M}
+SRCS_FILES += OFFileURIHandler.m
+SRCS_SOCKETS += OFAsyncIPSocketConnector.m \
+ OFDNSResolverSettings.m \
${OF_EPOLL_KERNEL_EVENT_OBSERVER_M} \
- OFHTTPURLHandler.m \
+ OFHTTPURIHandler.m \
OFHostAddressResolver.m \
- OFIPSocketAsyncConnector.m \
OFKernelEventObserver.m \
${OF_KQUEUE_KERNEL_EVENT_OBSERVER_M} \
${OF_POLL_KERNEL_EVENT_OBSERVER_M} \
${OF_SELECT_KERNEL_EVENT_OBSERVER_M} \
OFTCPSocketSOCKS5Connector.m
+SRCS_WINDOWS += platform/Windows/OFWin32ConsoleStdIOStream.m \
+ versioninfo.rc
OBJS_EXTRA = exceptions/exceptions.a \
encodings/encodings.a \
forwarding/forwarding.a
LIB_OBJS_EXTRA = exceptions/exceptions.lib.a \
@@ -247,5 +253,10 @@
FRAMEWORK_LIBS := -Fruntime \
${RUNTIME_FRAMEWORK_LIBS} \
${REEXPORT_RUNTIME_FRAMEWORK} \
${LIBS}
LIBS := -Lruntime ${RUNTIME_LIBS} ${REEXPORT_RUNTIME} ${LIBS}
+RCFLAGS = --use-temp-file \
+ -DOBJFW_LIB_MAJOR=${OBJFW_LIB_MAJOR} \
+ -DOBJFW_LIB_MINOR=${OBJFW_LIB_MINOR} \
+ -DOBJFW_LIB_VERSION=\"${OBJFW_LIB_MAJOR}.${OBJFW_LIB_MINOR}\" \
+ -DOBJFW_SHARED_LIB=\"${OBJFW_SHARED_LIB}\"
Index: src/OFASN1BitString.m
==================================================================
--- src/OFASN1BitString.m
+++ src/OFASN1BitString.m
@@ -89,11 +89,11 @@
if (SIZE_MAX / 8 < count - 1)
@throw [OFOutOfRangeException exception];
length = (count - 1) * 8;
bitString = [DEREncodedContents subdataWithRange:
- OFRangeMake(1, count - 1)];
+ OFMakeRange(1, count - 1)];
if (unusedBits != 0)
length -= unusedBits;
} @catch (id e) {
[self release];
Index: src/OFASN1Value.m
==================================================================
--- src/OFASN1Value.m
+++ src/OFASN1Value.m
@@ -101,13 +101,13 @@
{
unsigned long hash;
OFHashInit(&hash);
- OFHashAdd(&hash, _tagClass & 0xFF);
- OFHashAdd(&hash, _tagNumber & 0xFF);
- OFHashAdd(&hash, _constructed);
+ OFHashAddByte(&hash, _tagClass & 0xFF);
+ OFHashAddByte(&hash, _tagNumber & 0xFF);
+ OFHashAddByte(&hash, _constructed);
OFHashAddHash(&hash, _DEREncodedContents.hash);
OFHashFinalize(&hash);
return hash;
Index: src/OFASPrintF.m
==================================================================
--- src/OFASPrintF.m
+++ src/OFASPrintF.m
@@ -580,11 +580,11 @@
/*
* If there's no asprintf_l, we have no other choice than to
* use this ugly hack to replace the locale's decimal point
* back to ".".
*/
- point = [OFLocale decimalPoint];
+ point = [OFLocale decimalSeparator];
if (!ctx->useLocale && point != nil && ![point isEqual: @"."]) {
void *pool = objc_autoreleasePoolPush();
char *tmp2;
Index: src/OFApplication.m
==================================================================
--- src/OFApplication.m
+++ src/OFApplication.m
@@ -40,14 +40,14 @@
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFThread+Private.h"
#import "OFThread.h"
+#import "OFActivateSandboxFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
-#import "OFSandboxActivationFailedException.h"
#if defined(OF_MACOS)
# include
#elif defined(OF_WINDOWS)
# include
@@ -652,11 +652,11 @@
sandbox->_unveiledPathsIndex = unveiledPathsCount;
promises = [sandbox.pledgeString cStringWithEncoding: encoding];
if (pledge(promises, NULL) != 0)
- @throw [OFSandboxActivationFailedException
+ @throw [OFActivateSandboxFailedException
exceptionWithSandbox: sandbox
errNo: errno];
objc_autoreleasePoolPop(pool);
@@ -680,11 +680,11 @@
promises = [sandbox.pledgeString
cStringWithEncoding: [OFLocale encoding]];
if (pledge(NULL, promises) != 0)
- @throw [OFSandboxActivationFailedException
+ @throw [OFActivateSandboxFailedException
exceptionWithSandbox: sandbox
errNo: errno];
objc_autoreleasePoolPop(pool);
ADDED src/OFArchiveEntry.h
Index: src/OFArchiveEntry.h
==================================================================
--- src/OFArchiveEntry.h
+++ src/OFArchiveEntry.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFObject.h"
+#import "OFString.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFDate;
+@class OFNumber;
+
+/**
+ * @protocol OFArchiveEntry OFArchiveEntry.h ObjFW/OFArchiveEntry.h
+ *
+ * @brief A class which represents an entry in an archive.
+ */
+@protocol OFArchiveEntry
+
+/**
+ * @brief The file name of the entry.
+ */
+@property (readonly, copy, nonatomic) OFString *fileName;
+
+/**
+ * @brief The compressed size of the entry's file.
+ */
+@property (readonly, nonatomic) unsigned long long compressedSize;
+
+/**
+ * @brief The uncompressed size of the entry's file.
+ */
+@property (readonly, nonatomic) unsigned long long uncompressedSize;
+
+@optional
+/**
+ * @brief The modification date of the file.
+ */
+@property (readonly, retain, nonatomic) OFDate *modificationDate;
+
+/**
+ * @brief The comment of the entry's file.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
+ OFString *fileComment;
+
+/**
+ * @brief The POSIX permissions of the file.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic)
+ OFNumber *POSIXPermissions;
+
+/**
+ * @brief The file owner's account ID.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic)
+ OFNumber *ownerAccountID;
+
+/**
+ * @brief The file owner's group account ID.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic)
+ OFNumber *groupOwnerAccountID;
+
+/**
+ * @brief The file owner's account name.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic)
+ OFString *ownerAccountName;
+
+/**
+ * @brief The file owner's group account name.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic)
+ OFString *groupOwnerAccountName;
+@end
+
+OF_ASSUME_NONNULL_END
+
+#import "OFMutableArchiveEntry.h"
ADDED src/OFArchiveURIHandler.h
Index: src/OFArchiveURIHandler.h
==================================================================
--- src/OFArchiveURIHandler.h
+++ src/OFArchiveURIHandler.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFURIHandler.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@interface OFArchiveURIHandler: OFURIHandler
+@end
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern OFURI *OFArchiveURIHandlerURIForFileInArchive(OFString *, OFString *,
+ OFURI *);
+#ifdef __cplusplus
+}
+#endif
+
+OF_ASSUME_NONNULL_END
ADDED src/OFArchiveURIHandler.m
Index: src/OFArchiveURIHandler.m
==================================================================
--- src/OFArchiveURIHandler.m
+++ src/OFArchiveURIHandler.m
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+
+#import "OFArchiveURIHandler.h"
+#import "OFCharacterSet.h"
+#import "OFGZIPStream.h"
+#import "OFLHAArchive.h"
+#import "OFStream.h"
+#import "OFTarArchive.h"
+#import "OFURI.h"
+#import "OFZIPArchive.h"
+
+#import "OFInvalidArgumentException.h"
+#import "OFOpenItemFailedException.h"
+
+@interface OFArchiveURIHandlerPathAllowedCharacterSet: OFCharacterSet
+{
+ OFCharacterSet *_characterSet;
+ bool (*_characterIsMember)(id, SEL, OFUnichar);
+}
+@end
+
+static OFCharacterSet *pathAllowedCharacters;
+
+static void
+initPathAllowedCharacters(void)
+{
+ pathAllowedCharacters =
+ [[OFArchiveURIHandlerPathAllowedCharacterSet alloc] init];
+}
+
+@implementation OFArchiveURIHandler
+- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *scheme = URI.scheme;
+ OFString *percentEncodedPath, *path;
+ size_t pos;
+ OFURI *archiveURI;
+ OFStream *stream;
+
+ if (URI.host != nil || URI.port != nil || URI.user != nil ||
+ URI.password != nil || URI.query != nil || URI.fragment != nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![mode isEqual: @"r"])
+ /*
+ * Writing has some implications that are not decided yet: Will
+ * it always append to an archive? What happens if the file
+ * already exists?
+ */
+ @throw [OFInvalidArgumentException exception];
+
+ /*
+ * GZIP only compresses one file and thus has no path inside an
+ * archive.
+ */
+ if ([scheme isEqual: @"gzip"]) {
+ stream = [OFURIHandler openItemAtURI: [OFURI URIWithString:
+ URI.path]
+ mode: @"r"];
+ stream = [OFGZIPStream streamWithStream: stream mode: @"r"];
+ goto end;
+ }
+
+ percentEncodedPath = URI.percentEncodedPath;
+ pos = [percentEncodedPath rangeOfString: @"!"].location;
+
+ if (pos == OFNotFound)
+ @throw [OFInvalidArgumentException exception];
+
+ archiveURI = [OFURI URIWithString:
+ [percentEncodedPath substringWithRange: OFMakeRange(0, pos)]
+ .stringByRemovingPercentEncoding];
+ path = [percentEncodedPath substringWithRange:
+ OFMakeRange(pos + 1, percentEncodedPath.length - pos - 1)]
+ .stringByRemovingPercentEncoding;
+
+ if ([scheme isEqual: @"lha"]) {
+ OFLHAArchive *archive = [OFLHAArchive archiveWithURI: archiveURI
+ mode: @"r"];
+ OFLHAArchiveEntry *entry;
+
+ while ((entry = [archive nextEntry]) != nil) {
+ if ([entry.fileName isEqual: path]) {
+ stream = [archive streamForReadingCurrentEntry];
+ goto end;
+ }
+ }
+
+ @throw [OFOpenItemFailedException exceptionWithURI: URI
+ mode: mode
+ errNo: ENOENT];
+ } else if ([scheme isEqual: @"tar"]) {
+ OFTarArchive *archive = [OFTarArchive archiveWithURI: archiveURI
+ mode: @"r"];
+ OFTarArchiveEntry *entry;
+
+ while ((entry = [archive nextEntry]) != nil) {
+ if ([entry.fileName isEqual: path]) {
+ stream = [archive streamForReadingCurrentEntry];
+ goto end;
+ }
+ }
+
+ @throw [OFOpenItemFailedException exceptionWithURI: URI
+ mode: mode
+ errNo: ENOENT];
+ } else if ([scheme isEqual: @"zip"]) {
+ OFZIPArchive *archive = [OFZIPArchive archiveWithURI: archiveURI
+ mode: @"r"];
+
+ stream = [archive streamForReadingFile: path];
+ } else
+ @throw [OFInvalidArgumentException exception];
+
+end:
+ stream = [stream retain];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [stream autorelease];
+}
+@end
+
+@implementation OFArchiveURIHandlerPathAllowedCharacterSet
+- (instancetype)init
+{
+ self = [super init];
+
+ @try {
+ _characterSet =
+ [[OFCharacterSet URIPathAllowedCharacterSet] retain];
+ _characterIsMember = (bool (*)(id, SEL, OFUnichar))
+ [_characterSet methodForSelector:
+ @selector(characterIsMember:)];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_characterSet release];
+
+ [super dealloc];
+}
+
+- (bool)characterIsMember: (OFUnichar)character
+{
+ return (character != '!' && _characterIsMember(_characterSet,
+ @selector(characterIsMember:), character));
+}
+@end
+
+OFURI *
+OFArchiveURIHandlerURIForFileInArchive(OFString *scheme,
+ OFString *pathInArchive, OFURI *archiveURI)
+{
+ static OFOnceControl onceControl = OFOnceControlInitValue;
+ OFMutableURI *ret = [OFMutableURI URIWithScheme: scheme];
+ void *pool = objc_autoreleasePoolPush();
+ OFString *archiveURIString;
+
+ OFOnce(&onceControl, initPathAllowedCharacters);
+
+ pathInArchive = [pathInArchive
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ pathAllowedCharacters];
+ archiveURIString = [archiveURI.string
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ pathAllowedCharacters];
+
+ ret.percentEncodedPath = [OFString
+ stringWithFormat: @"%@!%@", archiveURIString, pathInArchive];
+ [ret makeImmutable];
+
+ objc_autoreleasePoolPop(pool);
+
+ return ret;
+}
Index: src/OFArray.m
==================================================================
--- src/OFArray.m
+++ src/OFArray.m
@@ -240,11 +240,11 @@
size_t count = self.count;
id *buffer = OFAllocMemory(count, sizeof(id));
id const *ret;
@try {
- [self getObjects: buffer inRange: OFRangeMake(0, count)];
+ [self getObjects: buffer inRange: OFMakeRange(0, count)];
ret = [[OFData dataWithItemsNoCopy: buffer
count: count
itemSize: sizeof(id)
freeWhenDone: true] items];
@@ -533,11 +533,11 @@
pool = objc_autoreleasePoolPush();
ret = [[self componentsJoinedByString: @",\n"] mutableCopy];
@try {
- [ret prependString: @"(\n"];
+ [ret insertString: @"(\n" atIndex: 0];
[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];
[ret appendString: @"\n)"];
} @catch (id e) {
[ret release];
@throw e;
@@ -749,11 +749,11 @@
- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
objects: (id *)objects
count: (int)count
{
- OFRange range = OFRangeMake(state->state, count);
+ OFRange range = OFMakeRange(state->state, count);
if (range.length > SIZE_MAX - range.location)
@throw [OFOutOfRangeException exception];
if (range.location + range.length > self.count)
ADDED src/OFAsyncIPSocketConnector.h
Index: src/OFAsyncIPSocketConnector.h
==================================================================
--- src/OFAsyncIPSocketConnector.h
+++ src/OFAsyncIPSocketConnector.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFDNSResolver.h"
+#import "OFRunLoop.h"
+#import "OFRunLoop+Private.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@protocol OFAsyncIPSocketConnecting
+- (bool)of_createSocketForAddress: (const OFSocketAddress *)address
+ errNo: (int *)errNo;
+- (bool)of_connectSocketToAddress: (const OFSocketAddress *)address
+ errNo: (int *)errNo;
+- (void)of_closeSocket;
+@end
+
+@interface OFAsyncIPSocketConnector: OFObject
+{
+ id _socket;
+ OFString *_host;
+ uint16_t _port;
+ id _Nullable _delegate;
+ id _Nullable _block;
+ id _Nullable _exception;
+ OFData *_Nullable _socketAddresses;
+ size_t _socketAddressesIndex;
+}
+
+- (instancetype)initWithSocket: (id)sock
+ host: (OFString *)host
+ port: (uint16_t)port
+ delegate: (nullable id)delegate
+ block: (nullable id)block;
+- (void)didConnect;
+- (void)tryNextAddressWithRunLoopMode: (OFRunLoopMode)runLoopMode;
+- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFAsyncIPSocketConnector.m
Index: src/OFAsyncIPSocketConnector.m
==================================================================
--- src/OFAsyncIPSocketConnector.m
+++ src/OFAsyncIPSocketConnector.m
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+
+#import "OFAsyncIPSocketConnector.h"
+#import "OFData.h"
+#import "OFTCPSocket.h"
+#import "OFThread.h"
+#import "OFTimer.h"
+
+#import "OFConnectIPSocketFailedException.h"
+#import "OFInvalidFormatException.h"
+
+@implementation OFAsyncIPSocketConnector
+- (instancetype)initWithSocket: (id)sock
+ host: (OFString *)host
+ port: (uint16_t)port
+ delegate: (id)delegate
+ block: (id)block
+{
+ self = [super init];
+
+ @try {
+ _socket = [sock retain];
+ _host = [host copy];
+ _port = port;
+ _delegate = [delegate retain];
+ _block = [block copy];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_socket release];
+ [_host release];
+ [_delegate release];
+ [_block release];
+ [_exception release];
+ [_socketAddresses release];
+
+ [super dealloc];
+}
+
+- (void)didConnect
+{
+ if (_exception == nil)
+ [_socket setCanBlock: true];
+
+#ifdef OF_HAVE_BLOCKS
+ if (_block != NULL) {
+ if ([_socket isKindOfClass: [OFTCPSocket class]])
+ ((OFTCPSocketAsyncConnectBlock)_block)(_exception);
+ else
+ OFEnsure(0);
+ } else {
+#endif
+ if ([_delegate respondsToSelector:
+ @selector(socket:didConnectToHost:port:exception:)])
+ [_delegate socket: _socket
+ didConnectToHost: _host
+ port: _port
+ exception: _exception];
+#ifdef OF_HAVE_BLOCKS
+ }
+#endif
+}
+
+- (void)of_socketDidConnect: (id)sock exception: (id)exception
+{
+ if (exception != nil) {
+ /*
+ * self might be retained only by the pending async requests,
+ * which we're about to cancel.
+ */
+ [[self retain] autorelease];
+
+ [sock cancelAsyncRequests];
+ [sock of_closeSocket];
+
+ if (_socketAddressesIndex >= _socketAddresses.count) {
+ _exception = [exception retain];
+ [self didConnect];
+ } else {
+ /*
+ * We must not call it before returning, as otherwise
+ * the new socket would be removed from the queue upon
+ * return.
+ */
+ OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
+ SEL selector =
+ @selector(tryNextAddressWithRunLoopMode:);
+ OFTimer *timer = [OFTimer
+ timerWithTimeInterval: 0
+ target: self
+ selector: selector
+ object: runLoop.currentMode
+ repeats: false];
+ [runLoop addTimer: timer forMode: runLoop.currentMode];
+ }
+
+ return;
+ }
+
+ [self didConnect];
+}
+
+- (id)of_connectionFailedExceptionForErrNo: (int)errNo
+{
+ return [OFConnectIPSocketFailedException exceptionWithHost: _host
+ port: _port
+ socket: _socket
+ errNo: errNo];
+}
+
+- (void)tryNextAddressWithRunLoopMode: (OFRunLoopMode)runLoopMode
+{
+ OFSocketAddress address = *(const OFSocketAddress *)
+ [_socketAddresses itemAtIndex: _socketAddressesIndex++];
+ int errNo;
+
+ OFSocketAddressSetIPPort(&address, _port);
+
+ if (![_socket of_createSocketForAddress: &address errNo: &errNo]) {
+ if (_socketAddressesIndex >= _socketAddresses.count) {
+ _exception = [[OFConnectIPSocketFailedException alloc]
+ initWithHost: _host
+ port: _port
+ socket: _socket
+ errNo: errNo];
+ [self didConnect];
+ return;
+ }
+
+ [self tryNextAddressWithRunLoopMode: runLoopMode];
+ return;
+ }
+
+#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
+ /*
+ * On Wii and 3DS, connect() fails if non-blocking is enabled.
+ *
+ * Additionally, on Wii, there is no getsockopt(), so it would not be
+ * possible to get the error (or success) after connecting anyway.
+ *
+ * So for now, connecting is blocking on Wii and 3DS.
+ *
+ * FIXME: Use a different thread as a work around.
+ */
+ [_socket setCanBlock: true];
+#else
+ [_socket setCanBlock: false];
+#endif
+
+ if (![_socket of_connectSocketToAddress: &address errNo: &errNo]) {
+#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
+# ifdef OF_WINDOWS
+ if (errNo == EINPROGRESS || errNo == EWOULDBLOCK) {
+# else
+ if (errNo == EINPROGRESS) {
+# endif
+ [OFRunLoop of_addAsyncConnectForSocket: _socket
+ mode: runLoopMode
+ delegate: self];
+ return;
+ } else {
+#endif
+ [_socket of_closeSocket];
+
+ if (_socketAddressesIndex >= _socketAddresses.count) {
+ _exception = [[OFConnectIPSocketFailedException
+ alloc] initWithHost: _host
+ port: _port
+ socket: _socket
+ errNo: errNo];
+ [self didConnect];
+ return;
+ }
+
+ [self tryNextAddressWithRunLoopMode: runLoopMode];
+ return;
+#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
+ }
+#endif
+ }
+
+#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
+ [_socket setCanBlock: false];
+#endif
+
+ [self didConnect];
+}
+
+- (void)resolver: (OFDNSResolver *)resolver
+ didResolveHost: (OFString *)host
+ addresses: (OFData *)addresses
+ exception: (id)exception
+{
+ if (exception != nil) {
+ _exception = [exception retain];
+ [self didConnect];
+ return;
+ }
+
+ _socketAddresses = [addresses copy];
+
+ [self tryNextAddressWithRunLoopMode:
+ [OFRunLoop currentRunLoop].currentMode];
+}
+
+- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode
+{
+ @try {
+ OFSocketAddress address = OFSocketAddressParseIP(_host, _port);
+
+ _socketAddresses = [[OFData alloc]
+ initWithItems: &address
+ count: 1
+ itemSize: sizeof(address)];
+
+ [self tryNextAddressWithRunLoopMode: runLoopMode];
+ return;
+ } @catch (OFInvalidFormatException *e) {
+ }
+
+ [[OFThread DNSResolver]
+ asyncResolveAddressesForHost: _host
+ addressFamily: OFSocketAddressFamilyAny
+ runLoopMode: runLoopMode
+ delegate: self];
+}
+@end
Index: src/OFColor.m
==================================================================
--- src/OFColor.m
+++ src/OFColor.m
@@ -126,23 +126,23 @@
OFHashInit(&hash);
tmp = OFToLittleEndianFloat(_red);
for (uint_fast8_t i = 0; i < sizeof(float); i++)
- OFHashAdd(&hash, ((char *)&tmp)[i]);
+ OFHashAddByte(&hash, ((char *)&tmp)[i]);
tmp = OFToLittleEndianFloat(_green);
for (uint_fast8_t i = 0; i < sizeof(float); i++)
- OFHashAdd(&hash, ((char *)&tmp)[i]);
+ OFHashAddByte(&hash, ((char *)&tmp)[i]);
tmp = OFToLittleEndianFloat(_blue);
for (uint_fast8_t i = 0; i < sizeof(float); i++)
- OFHashAdd(&hash, ((char *)&tmp)[i]);
+ OFHashAddByte(&hash, ((char *)&tmp)[i]);
tmp = OFToLittleEndianFloat(_alpha);
for (uint_fast8_t i = 0; i < sizeof(float); i++)
- OFHashAdd(&hash, ((char *)&tmp)[i]);
+ OFHashAddByte(&hash, ((char *)&tmp)[i]);
OFHashFinalize(&hash);
return hash;
}
Index: src/OFCondition.h
==================================================================
--- src/OFCondition.h
+++ src/OFCondition.h
@@ -43,10 +43,12 @@
* @brief Blocks the current thread until another thread calls @ref signal or
* @ref broadcast.
*
* @note Waiting might have been interrupted by a signal. It is thus recommended
* to check the condition again after @ref wait returned!
+ *
+ * @throw OFWaitForConditionFailedException Waiting for the condition failed
*/
- (void)wait;
#ifdef OF_AMIGAOS
/**
@@ -55,10 +57,11 @@
*
* @note This is only available on AmigaOS!
*
* @param signalMask A pointer to a signal mask of Exec Signals to receive.
* This is modified and set to the mask of signals received.
+ * @throw OFWaitForConditionFailedException Waiting for the condition failed
*/
- (void)waitForConditionOrExecSignal: (ULONG *)signalMask;
#endif
/**
@@ -68,10 +71,11 @@
* @note Waiting might have been interrupted by a signal. It is thus recommended
* to check the condition again after @ref waitForTimeInterval: returned!
*
* @param timeInterval The time interval until the timeout is reached
* @return Whether the condition has been signaled
+ * @throw OFWaitForConditionFailedException Waiting for the condition failed
*/
- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval;
#ifdef OF_AMIGAOS
/**
@@ -82,10 +86,11 @@
*
* @param timeInterval The time interval until the timeout is reached
* @param signalMask A pointer to a signal mask of Exec Signals to receive.
* This is modified and set to the mask of signals received.
* @return Whether the condition has been signaled or a signal received
+ * @throw OFWaitForConditionFailedException Waiting for the condition failed
*/
- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval
orExecSignal: (ULONG *)signalMask;
#endif
@@ -96,10 +101,11 @@
* @note Waiting might have been interrupted by a signal. It is thus recommended
* to check the condition again after @ref waitUntilDate: returned!
*
* @param date The date at which the timeout is reached
* @return Whether the condition has been signaled
+ * @throw OFWaitForConditionFailedException Waiting for the condition failed
*/
- (bool)waitUntilDate: (OFDate *)date;
#ifdef OF_AMIGAOS
/**
@@ -110,21 +116,26 @@
*
* @param date The date at which the timeout is reached
* @param signalMask A pointer to a signal mask of Exec Signals to receive.
* This is modified and set to the mask of signals received.
* @return Whether the condition has been signaled or a signal received
+ * @throw OFWaitForConditionFailedException Waiting for the condition failed
*/
- (bool)waitUntilDate: (OFDate *)date orExecSignal: (ULONG *)signalMask;
#endif
/**
* @brief Signals the next waiting thread to continue.
+ *
+ * @throw OFSignalConditionFailedException Signaling the condition failed
*/
- (void)signal;
/**
* @brief Signals all threads to continue.
+ *
+ * @throw OFBroadcastConditionFailedException Broadcasting the condition failed
*/
- (void)broadcast;
@end
OF_ASSUME_NONNULL_END
Index: src/OFCondition.m
==================================================================
--- src/OFCondition.m
+++ src/OFCondition.m
@@ -18,15 +18,15 @@
#include
#import "OFCondition.h"
#import "OFDate.h"
-#import "OFConditionBroadcastFailedException.h"
-#import "OFConditionSignalFailedException.h"
+#import "OFBroadcastConditionFailedException.h"
#import "OFConditionStillWaitingException.h"
-#import "OFConditionWaitFailedException.h"
#import "OFInitializationFailedException.h"
+#import "OFSignalConditionFailedException.h"
+#import "OFWaitForConditionFailedException.h"
@implementation OFCondition
+ (instancetype)condition
{
return [[[self alloc] init] autorelease];
@@ -66,11 +66,11 @@
- (void)wait
{
int error = OFPlainConditionWait(&_condition, &_mutex);
if (error != 0)
- @throw [OFConditionWaitFailedException
+ @throw [OFWaitForConditionFailedException
exceptionWithCondition: self
errNo: error];
}
#ifdef OF_AMIGAOS
@@ -78,11 +78,11 @@
{
int error = OFPlainConditionWaitOrExecSignal(&_condition, &_mutex,
signalMask);
if (error != 0)
- @throw [OFConditionWaitFailedException
+ @throw [OFWaitForConditionFailedException
exceptionWithCondition: self
errNo: error];
}
#endif
@@ -93,11 +93,11 @@
if (error == ETIMEDOUT)
return false;
if (error != 0)
- @throw [OFConditionWaitFailedException
+ @throw [OFWaitForConditionFailedException
exceptionWithCondition: self
errNo: error];
return true;
}
@@ -111,11 +111,11 @@
if (error == ETIMEDOUT)
return false;
if (error != 0)
- @throw [OFConditionWaitFailedException
+ @throw [OFWaitForConditionFailedException
exceptionWithCondition: self
errNo: error];
return true;
}
@@ -137,20 +137,20 @@
- (void)signal
{
int error = OFPlainConditionSignal(&_condition);
if (error != 0)
- @throw [OFConditionSignalFailedException
+ @throw [OFSignalConditionFailedException
exceptionWithCondition: self
errNo: error];
}
- (void)broadcast
{
int error = OFPlainConditionBroadcast(&_condition);
if (error != 0)
- @throw [OFConditionBroadcastFailedException
+ @throw [OFBroadcastConditionFailedException
exceptionWithCondition: self
errNo: error];
}
@end
Index: src/OFConstantString.m
==================================================================
--- src/OFConstantString.m
+++ src/OFConstantString.m
@@ -354,16 +354,10 @@
{
[self finishInitialization];
return [self stringByAppendingPathExtension: extension];
}
-- (OFString *)stringByPrependingString: (OFString *)string
-{
- [self finishInitialization];
- return [self stringByPrependingString: string];
-}
-
- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
withString: (OFString *)replacement
{
[self finishInitialization];
return [self stringByReplacingOccurrencesOfString: string
@@ -481,11 +475,11 @@
{
[self finishInitialization];
return self.longLongValue;
}
-- (long long)longLongValueWithBase: (int)base
+- (long long)longLongValueWithBase: (unsigned char)base
{
[self finishInitialization];
return [self longLongValueWithBase: base];
}
@@ -493,11 +487,11 @@
{
[self finishInitialization];
return self.unsignedLongLongValue;
}
-- (unsigned long long)unsignedLongLongValueWithBase: (int)base
+- (unsigned long long)unsignedLongLongValueWithBase: (unsigned char)base
{
[self finishInitialization];
return [self unsignedLongLongValueWithBase: base];
}
@@ -589,20 +583,20 @@
[self finishInitialization];
[self writeToFile: path encoding: encoding];
}
#endif
-- (void)writeToURL: (OFURL *)URL
+- (void)writeToURI: (OFURI *)URI
{
[self finishInitialization];
- [self writeToURL: URL];
+ [self writeToURI: URI];
}
-- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
+- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
[self finishInitialization];
- [self writeToURL: URL encoding: encoding];
+ [self writeToURI: URI encoding: encoding];
}
#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block
{
Index: src/OFCryptographicHash.h
==================================================================
--- src/OFCryptographicHash.h
+++ src/OFCryptographicHash.h
@@ -56,10 +56,12 @@
/**
* @brief A buffer containing the cryptographic hash.
*
* The size of the buffer depends on the hash used. The buffer is part of the
* receiver's memory pool.
+ *
+ * @throw OFHashNotCalculatedException The hash hasn't been calculated yet
*/
@property (readonly, nonatomic) const unsigned char *digest
OF_RETURNS_INNER_POINTER;
/**
@@ -95,15 +97,18 @@
/**
* @brief Adds a buffer to the cryptographic hash to be calculated.
*
* @param buffer The buffer which should be included into the calculation
* @param length The length of the buffer
+ * @throw OFHashAlreadyCalculatedException The hash has already been calculated
*/
- (void)updateWithBuffer: (const void *)buffer length: (size_t)length;
/**
* @brief Performs the final calculation of the cryptographic hash.
+ *
+ * @throw OFHashAlreadyCalculatedException The hash has already been calculated
*/
- (void)calculate;
/**
* @brief Resets all state so that a new hash can be calculated.
ADDED src/OFDDPSocket.h
Index: src/OFDDPSocket.h
==================================================================
--- src/OFDDPSocket.h
+++ src/OFDDPSocket.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFDatagramSocket.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFString;
+
+/**
+ * @protocol OFDDPSocketDelegate OFDDPSocket.h ObjFW/OFDDPSocket.h
+ *
+ * @brief A delegate for OFDDPSocket.
+ */
+@protocol OFDDPSocketDelegate
+@end
+
+/**
+ * @class OFDDPSocket OFDDPSocket.h ObjFW/OFDDPSocket.h
+ *
+ * @brief A class which provides methods to create and use AppleTalk DDP
+ * sockets.
+ *
+ * Addresses are of type @ref OFSocketAddress. You can use
+ * @ref OFSocketAddressMakeAppleTalk to create an address or
+ * @ref OFSocketAddressAppleTalkNetwork to get the AppleTalk network,
+ * @ref OFSocketAddressAppleTalkNode to get the AppleTalk node and
+ * @ref OFSocketAddressAppleTalkPort to get the port (sometimes also called
+ * socket number).
+ *
+ * @note On some systems, packets received with the wrong protocol type just
+ * get filtered by the kernel, however, on other systems, the packet is
+ * queued up and will raise an @ref OFReadFailedException with the
+ * @ref errNo set to `ENOMSG` when being received.
+ *
+ * @warning Even though the OFCopying protocol is implemented, it does *not*
+ * return an independent copy of the socket, but instead retains it.
+ * This is so that the socket can be used as a key for a dictionary,
+ * so context can be associated with a socket. Using a socket in more
+ * than one thread at the same time is not thread-safe, even if copy
+ * was called to create one "instance" for every thread!
+ */
+@interface OFDDPSocket: OFDatagramSocket
+{
+#if !defined(OF_MACOS) && !defined(OF_WINDOWS)
+ uint8_t _protocolType;
+#endif
+ OF_RESERVE_IVARS(OFDDPSocket, 4)
+}
+
+/**
+ * @brief The delegate for asynchronous operations on the socket.
+ *
+ * @note The delegate is retained for as long as asynchronous operations are
+ * still ongoing.
+ */
+@property OF_NULLABLE_PROPERTY (assign, nonatomic)
+ id delegate;
+
+/**
+ * @brief Bind the socket to the specified network, node and port.
+ *
+ * @param network The network to bind to. 0 means any.
+ * @param node The node to bind to. 0 means "this node".
+ * @param port The port to bind to. 0 means to pick one and return it via the
+ * returned socket address.
+ * @param protocolType The DDP protocol type to use. Must not be 0. If you want
+ * to use DDP directly and not a protocol built on top of
+ * it, use 11 for compatibility with Open Transport.
+ * @return The address on which this socket can be reached
+ * @throw OFBindDDPSockeFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already bound
+ */
+- (OFSocketAddress)bindToNetwork: (uint16_t)network
+ node: (uint8_t)node
+ port: (uint8_t)port
+ protocolType: (uint8_t)protocolType;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFDDPSocket.m
Index: src/OFDDPSocket.m
==================================================================
--- src/OFDDPSocket.m
+++ src/OFDDPSocket.m
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+
+#ifdef HAVE_FCNTL_H
+# include
+#endif
+
+#import "OFDDPSocket.h"
+#import "OFSocket.h"
+#import "OFSocket+Private.h"
+
+#import "OFAlreadyConnectedException.h"
+#import "OFBindDDPSocketFailedException.h"
+#import "OFInvalidArgumentException.h"
+#import "OFNotOpenException.h"
+#import "OFReadFailedException.h"
+#import "OFWriteFailedException.h"
+
+#ifdef OF_HAVE_NETAT_APPLETALK_H
+# include
+# include
+
+/* Unfortulately, there is no struct for the following in userland headers */
+struct ATInterfaceConfig {
+ char interfaceName[16];
+ unsigned int flags;
+ struct at_addr address, router;
+ unsigned short netStart, netEnd;
+ at_nvestr_t zoneName;
+};
+#endif
+
+@implementation OFDDPSocket
+@dynamic delegate;
+
+- (OFSocketAddress)bindToNetwork: (uint16_t)network
+ node: (uint8_t)node
+ port: (uint8_t)port
+ protocolType: (uint8_t)protocolType
+{
+#ifdef OF_MACOS
+ const int one = 1;
+ struct ATInterfaceConfig config = { { 0 } };
+#endif
+ OFSocketAddress address;
+#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
+ int flags;
+#endif
+
+ if (protocolType == 0)
+ @throw [OFInvalidArgumentException exception];
+
+ if (_socket != OFInvalidSocketHandle)
+ @throw [OFAlreadyConnectedException exceptionWithSocket: self];
+
+ address = OFSocketAddressMakeAppleTalk(network, node, port);
+
+#if defined(OF_MACOS)
+ if ((_socket = socket(address.sockaddr.at.sat_family,
+ SOCK_RAW | SOCK_CLOEXEC, protocolType)) == OFInvalidSocketHandle)
+#elif defined(OF_WINDOWS)
+ if ((_socket = socket(address.sockaddr.at.sat_family,
+ SOCK_DGRAM | SOCK_CLOEXEC, ATPROTO_BASE + protocolType)) ==
+ OFInvalidSocketHandle)
+#else
+ if ((_socket = socket(address.sockaddr.at.sat_family,
+ SOCK_DGRAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
+#endif
+ @throw [OFBindDDPSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ protocolType: protocolType
+ socket: self
+ errNo: OFSocketErrNo()];
+
+ _canBlock = true;
+
+#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
+ if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
+ fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
+#endif
+
+ if (bind(_socket, (struct sockaddr *)&address.sockaddr,
+ address.length) != 0) {
+ int errNo = OFSocketErrNo();
+
+ closesocket(_socket);
+ _socket = OFInvalidSocketHandle;
+
+ @throw [OFBindDDPSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ protocolType: protocolType
+ socket: self
+ errNo: errNo];
+ }
+
+ memset(&address, 0, sizeof(address));
+ address.family = OFSocketAddressFamilyAppleTalk;
+ address.length = (socklen_t)sizeof(address.sockaddr);
+
+ if (OFGetSockName(_socket, (struct sockaddr *)&address.sockaddr,
+ &address.length) != 0) {
+ int errNo = OFSocketErrNo();
+
+ closesocket(_socket);
+ _socket = OFInvalidSocketHandle;
+
+ @throw [OFBindDDPSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ protocolType: protocolType
+ socket: self
+ errNo: errNo];
+ }
+
+ if (address.sockaddr.at.sat_family != AF_APPLETALK) {
+ closesocket(_socket);
+ _socket = OFInvalidSocketHandle;
+
+ @throw [OFBindDDPSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ protocolType: protocolType
+ socket: self
+ errNo: EAFNOSUPPORT];
+ }
+
+#ifdef OF_MACOS
+ if (setsockopt(_socket, ATPROTO_NONE, DDP_STRIPHDR, &one,
+ sizeof(one)) != 0 || ioctl(_socket, _IOWR('a', 2,
+ struct ATInterfaceConfig), &config) != 0)
+ @throw [OFBindDDPSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ protocolType: protocolType
+ socket: self
+ errNo: OFSocketErrNo()];
+
+ OFSocketAddressSetAppleTalkNetwork(&address, config.address.s_net);
+ OFSocketAddressSetAppleTalkNode(&address, config.address.s_node);
+#endif
+
+#if !defined(OF_MACOS) && !defined(OF_WINDOWS)
+ _protocolType = protocolType;
+#endif
+
+ return address;
+}
+
+/*
+ * Everybody but macOS and Windows is probably using a netatalk-compatible
+ * implementation, which includes the protocol type as the first byte of the
+ * data instead of considering it part of the header.
+ *
+ * The following overrides prepend the protocol type when sending and compare
+ * and strip it when receiving.
+ *
+ * Unfortunately, the downside of this is that the only way to handle receiving
+ * a packet with the wrong protocol type is to throw an exception with errNo
+ * ENOMSG, while macOS and Windows just filter those out in the kernel.
+ * Returning 0 would mean this is indistinguishable from an empty packet, so it
+ * has to be an exception.
+ */
+#if !defined(OF_MACOS) && !defined(OF_WINDOWS)
+- (size_t)receiveIntoBuffer: (void *)buffer
+ length: (size_t)length
+ sender: (OFSocketAddress *)sender
+{
+ ssize_t ret;
+ uint8_t protocolType;
+ struct iovec iov[2] = {
+ { &protocolType, 1 },
+ { buffer, length }
+ };
+ struct msghdr msg = {
+ .msg_name = (sender != NULL
+ ? (struct sockaddr *)&sender->sockaddr : NULL),
+ .msg_namelen = (sender != NULL
+ ? (socklen_t)sizeof(sender->sockaddr) : 0),
+ .msg_iov = iov,
+ .msg_iovlen = 2
+ };
+
+ if (_socket == OFInvalidSocketHandle)
+ @throw [OFNotOpenException exceptionWithObject: self];
+
+ if ((ret = recvmsg(_socket, &msg, 0)) < 0)
+ @throw [OFReadFailedException
+ exceptionWithObject: self
+ requestedLength: length
+ errNo: OFSocketErrNo()];
+
+ if (ret < 1 || protocolType != _protocolType)
+ @throw [OFReadFailedException exceptionWithObject: self
+ requestedLength: length
+ errNo: ENOMSG];
+
+ if (sender != NULL) {
+ sender->length = msg.msg_namelen;
+ sender->family = OFSocketAddressFamilyAppleTalk;
+ }
+
+ return ret - 1;
+}
+
+- (void)sendBuffer: (const void *)buffer
+ length: (size_t)length
+ receiver: (const OFSocketAddress *)receiver
+{
+ struct iovec iov[2] = {
+ { &_protocolType, 1 },
+ { (void *)buffer, length }
+ };
+ struct msghdr msg = {
+ .msg_name = (struct sockaddr *)&receiver->sockaddr,
+ .msg_namelen = receiver->length,
+ .msg_iov = iov,
+ .msg_iovlen = 2
+ };
+ ssize_t bytesWritten;
+
+ if (_socket == OFInvalidSocketHandle)
+ @throw [OFNotOpenException exceptionWithObject: self];
+
+ if ((bytesWritten = sendmsg(_socket, &msg, 0)) < 0)
+ @throw [OFWriteFailedException
+ exceptionWithObject: self
+ requestedLength: length
+ bytesWritten: 0
+ errNo: OFSocketErrNo()];
+
+ if ((size_t)bytesWritten != length + 1) {
+ bytesWritten--;
+
+ if (bytesWritten < 0)
+ bytesWritten = 0;
+
+ @throw [OFWriteFailedException exceptionWithObject: self
+ requestedLength: length
+ bytesWritten: bytesWritten
+ errNo: 0];
+ }
+}
+#endif
+@end
Index: src/OFDNSQuery.h
==================================================================
--- src/OFDNSQuery.h
+++ src/OFDNSQuery.h
@@ -23,16 +23,16 @@
/**
* @class OFDNSQuery OFDNSQuery.h ObjFW/OFDNSQuery.h
*
* @brief A class representing a DNS query.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFDNSQuery: OFObject
{
OFString *_domainName;
OFDNSClass _DNSClass;
OFDNSRecordType _recordType;
- OF_RESERVE_IVARS(OFDNSQuery, 4)
}
/**
* @brief The domain name of the query.
*/
Index: src/OFDNSQuery.m
==================================================================
--- src/OFDNSQuery.m
+++ src/OFDNSQuery.m
@@ -95,12 +95,12 @@
{
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _domainName.hash);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType);
OFHashFinalize(&hash);
return hash;
}
Index: src/OFDNSResolver.h
==================================================================
--- src/OFDNSResolver.h
+++ src/OFDNSResolver.h
@@ -258,10 +258,12 @@
* @brief Synchronously resolves the specified host to socket addresses.
*
* @param host The host to resolve
* @param addressFamily The desired socket address family
* @return OFData containing several OFSocketAddress
+ * @throw OFInvalidServerResponseException The received response was invalid
+ * @throw OFTruncatedDataException The received response was truncated
*/
- (OFData *)resolveAddressesForHost: (OFString *)host
addressFamily: (OFSocketAddressFamily)addressFamily;
/**
Index: src/OFDNSResolver.m
==================================================================
--- src/OFDNSResolver.m
+++ src/OFDNSResolver.m
@@ -36,11 +36,11 @@
#import "OFDNSQueryFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
-#import "OFInvalidServerReplyException.h"
+#import "OFInvalidServerResponseException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#ifndef SOCK_DNS
@@ -126,21 +126,21 @@
if (componentLength & 0xC0) {
size_t j;
OFString *suffix;
if (pointerLevel == 0)
- @throw [OFInvalidServerReplyException
+ @throw [OFInvalidServerResponseException
exception];
if (*i >= length)
@throw [OFTruncatedDataException exception];
j = ((componentLength & 0x3F) << 8) | buffer[(*i)++];
if (j == *i - 2)
/* Pointing to itself?! */
- @throw [OFInvalidServerReplyException
+ @throw [OFInvalidServerResponseException
exception];
suffix = parseName(buffer, length, &j,
pointerLevel - 1);
@@ -173,11 +173,11 @@
{
if (recordType == OFDNSRecordTypeA && DNSClass == OFDNSClassIN) {
OFSocketAddress address;
if (dataLength != 4)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
memset(&address, 0, sizeof(address));
address.family = OFSocketAddressFamilyIPv4;
address.length = sizeof(address.sockaddr.in);
@@ -192,11 +192,11 @@
size_t j = i;
OFString *authoritativeHost = parseName(buffer, length, &j,
maxAllowedPointers);
if (j != i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
return [[[OFNSDNSResourceRecord alloc]
initWithName: name
DNSClass: DNSClass
authoritativeHost: authoritativeHost
@@ -205,11 +205,11 @@
size_t j = i;
OFString *alias = parseName(buffer, length, &j,
maxAllowedPointers);
if (j != i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
return [[[OFCNAMEDNSResourceRecord alloc]
initWithName: name
DNSClass: DNSClass
alias: alias
@@ -221,17 +221,17 @@
OFString *responsiblePerson;
uint32_t serialNumber, refreshInterval, retryInterval;
uint32_t expirationInterval, minTTL;
if (j > i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
responsiblePerson = parseName(buffer, length, &j,
maxAllowedPointers);
if (dataLength - (j - i) != 20)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
serialNumber = (buffer[j] << 24) | (buffer[j + 1] << 16) |
(buffer[j + 2] << 8) | buffer[j + 3];
refreshInterval = (buffer[j + 4] << 24) |
(buffer[j + 5] << 16) | (buffer[j + 6] << 8) |
@@ -259,11 +259,11 @@
size_t j = i;
OFString *domainName = parseName(buffer, length, &j,
maxAllowedPointers);
if (j != i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
return [[[OFPTRDNSResourceRecord alloc]
initWithName: name
DNSClass: DNSClass
domainName: domainName
@@ -272,16 +272,16 @@
size_t j = i;
OFString *CPU = parseString(buffer, length, &j);
OFString *OS;
if (j > i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
OS = parseString(buffer, length, &j);
if (j != i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
return [[[OFHINFODNSResourceRecord alloc]
initWithName: name
DNSClass: DNSClass
CPU: CPU
@@ -291,20 +291,20 @@
uint16_t preference;
size_t j;
OFString *mailExchange;
if (dataLength < 2)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
preference = (buffer[i] << 8) | buffer[i + 1];
j = i + 2;
mailExchange = parseName(buffer, length, &j,
maxAllowedPointers);
if (j != i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
return [[[OFMXDNSResourceRecord alloc]
initWithName: name
DNSClass: DNSClass
preference: preference
@@ -316,11 +316,11 @@
while (dataLength > 0) {
uint_fast8_t stringLength = buffer[i++];
dataLength--;
if (stringLength > dataLength)
- @throw [OFInvalidServerReplyException
+ @throw [OFInvalidServerResponseException
exception];
[textStrings addObject:
[OFData dataWithItems: buffer + i
count: stringLength]];
@@ -341,17 +341,17 @@
OFString *mailbox = parseName(buffer, length, &j,
maxAllowedPointers);
OFString *TXTDomainName;
if (j > i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
TXTDomainName = parseName(buffer, length, &j,
maxAllowedPointers);
if (j != i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
return [[[OFRPDNSResourceRecord alloc]
initWithName: name
DNSClass: DNSClass
mailbox: mailbox
@@ -360,11 +360,11 @@
} else if (recordType == OFDNSRecordTypeAAAA &&
DNSClass == OFDNSClassIN) {
OFSocketAddress address;
if (dataLength != 16)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
memset(&address, 0, sizeof(address));
address.family = OFSocketAddressFamilyIPv6;
address.length = sizeof(address.sockaddr.in6);
@@ -384,21 +384,21 @@
uint16_t priority, weight, port;
size_t j;
OFString *target;
if (dataLength < 6)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
priority = (buffer[i] << 8) | buffer[i + 1];
weight = (buffer[i + 2] << 8) | buffer[i + 3];
port = (buffer[i + 4] << 8) | buffer[i + 5];
j = i + 6;
target = parseName(buffer, length, &j, maxAllowedPointers);
if (j != i + dataLength)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
return [[[OFSRVDNSResourceRecord alloc]
initWithName: name
priority: priority
weight: weight
@@ -908,11 +908,12 @@
return true;
if (context->_TCPSocket != nil) {
if ([_TCPQueries objectForKey: context->_TCPSocket] != context)
return true;
- } else if (!OFSocketAddressEqual(sender, &context->_usedNameServer))
+ } else if (sender == NULL ||
+ !OFSocketAddressEqual(sender, &context->_usedNameServer))
return true;
[context->_cancelTimer invalidate];
[context->_cancelTimer release];
context->_cancelTimer = nil;
@@ -935,15 +936,15 @@
queryDataBuffer = context->_queryData.items;
/* QR */
if ((buffer[2] & 0x80) == 0)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
/* Opcode */
if ((buffer[2] & 0x78) != (queryDataBuffer[2] & 0x78))
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
/* TC */
if (buffer[2] & 0x02) {
OFRunLoopMode runLoopMode;
Index: src/OFDNSResolverSettings.m
==================================================================
--- src/OFDNSResolverSettings.m
+++ src/OFDNSResolverSettings.m
@@ -180,11 +180,11 @@
parseNetStackArray(OFString *string)
{
if (![string hasPrefix: @"["] || ![string hasSuffix: @"]"])
return nil;
- string = [string substringWithRange: OFRangeMake(1, string.length - 2)];
+ string = [string substringWithRange: OFMakeRange(1, string.length - 2)];
return [string componentsSeparatedByString: @"|"];
}
#endif
@@ -266,11 +266,12 @@
return;
}
staticHosts = [OFMutableDictionary dictionary];
- while ((line = [file readLine]) != nil) {
+ while ((line =
+ [file readLineWithEncoding: [OFLocale encoding]]) != nil) {
OFArray *components, *hosts;
size_t pos;
OFString *address;
pos = [line rangeOfString: @"#"].location;
@@ -284,11 +285,11 @@
if (components.count < 2)
continue;
address = components.firstObject;
hosts = [components objectsInRange:
- OFRangeMake(1, components.count - 1)];
+ OFMakeRange(1, components.count - 1)];
for (OFString *host in hosts) {
OFMutableArray *addresses =
[staticHosts objectForKey: host];
@@ -366,11 +367,12 @@
}
if (nameServers == nil)
nameServers = [OFMutableArray array];
- while ((line = [file readLine]) != nil) {
+ while ((line =
+ [file readLineWithEncoding: [OFLocale encoding]]) != nil) {
void *pool2 = objc_autoreleasePoolPush();
size_t pos;
OFArray *components, *arguments;
OFString *option;
@@ -387,11 +389,11 @@
continue;
}
option = components.firstObject;
arguments = [components objectsInRange:
- OFRangeMake(1, components.count - 1)];
+ OFMakeRange(1, components.count - 1)];
if ([option isEqual: @"nameserver"]) {
if (arguments.count != 1) {
objc_autoreleasePoolPop(pool2);
continue;
@@ -486,11 +488,11 @@
if (components.count < 2)
continue;
address = components.firstObject;
hosts = [components objectsInRange:
- OFRangeMake(1, components.count - 1)];
+ OFMakeRange(1, components.count - 1)];
for (OFString *host in hosts) {
OFMutableArray *addresses =
[staticHosts objectForKey: host];
@@ -621,11 +623,12 @@
#if defined(OF_WINDOWS)
# ifdef OF_HAVE_FILES
OFWindowsRegistryKey *key = [[OFWindowsRegistryKey localMachineKey]
openSubkeyAtPath: @"SYSTEM\\CurrentControlSet\\Services\\"
@"Tcpip\\Parameters"
- securityAndAccessRights: KEY_QUERY_VALUE];
+ accessRights: KEY_QUERY_VALUE
+ options: 0];
path = [[[key stringForValueNamed: @"DataBasePath"]
stringByAppendingPathComponent: @"hosts"]
stringByExpandingWindowsEnvironmentStrings];
if (path != nil)
Index: src/OFDNSResourceRecord.h
==================================================================
--- src/OFDNSResourceRecord.h
+++ src/OFDNSResourceRecord.h
@@ -639,20 +639,22 @@
/**
* @brief Parses the specified string as an @ref OFDNSClass.
*
* @param string The string to parse as an @ref OFDNSClass
* @return The parsed OFDNSClass
+ * @throw OFInvalidFormatException The specified string is not valid DNS class
*/
extern OFDNSClass OFDNSClassParseName(OFString *_Nonnull string);
/**
* @brief Parses the specified string as an @ref OFDNSRecordType.
*
* @param string The string to parse as an @ref OFDNSRecordType
* @return The parsed OFDNSRecordType
+ * @throw OFInvalidFormatException The specified string is not valid DNS class
*/
extern OFDNSRecordType OFDNSRecordTypeParseName(OFString *_Nonnull string);
#ifdef __cplusplus
}
#endif
OF_ASSUME_NONNULL_END
Index: src/OFDNSResourceRecord.m
==================================================================
--- src/OFDNSResourceRecord.m
+++ src/OFDNSResourceRecord.m
@@ -17,11 +17,10 @@
#import "OFDNSResourceRecord.h"
#import "OFArray.h"
#import "OFData.h"
-#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
OFString *
OFDNSClassName(OFDNSClass DNSClass)
{
@@ -77,16 +76,12 @@
string = string.uppercaseString;
if ([string isEqual: @"IN"])
DNSClass = OFDNSClassIN;
else {
- @try {
- DNSClass = (OFDNSClass)
- [string unsignedLongLongValueWithBase: 0];
- } @catch (OFInvalidFormatException *e) {
- @throw [OFInvalidArgumentException exception];
- }
+ DNSClass =
+ (OFDNSClass)[string unsignedLongLongValueWithBase: 0];
}
objc_autoreleasePoolPop(pool);
return DNSClass;
@@ -123,16 +118,12 @@
else if ([string isEqual: @"SRV"])
recordType = OFDNSRecordTypeSRV;
else if ([string isEqual: @"ALL"])
recordType = OFDNSRecordTypeAll;
else {
- @try {
- recordType = (OFDNSRecordType)
- [string unsignedLongLongValueWithBase: 0];
- } @catch (OFInvalidFormatException *e) {
- @throw [OFInvalidArgumentException exception];
- }
+ recordType =
+ (OFDNSRecordType)[string unsignedLongLongValueWithBase: 0];
}
objc_autoreleasePoolPop(pool);
return recordType;
@@ -248,14 +239,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, OFSocketAddressHash(&_address));
OFHashFinalize(&hash);
return hash;
@@ -333,14 +324,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, OFSocketAddressHash(&_address));
OFHashFinalize(&hash);
return hash;
@@ -428,14 +419,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, _alias.hash);
OFHashFinalize(&hash);
return hash;
@@ -530,14 +521,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, _CPU.hash);
OFHashAddHash(&hash, _OS.hash);
OFHashFinalize(&hash);
@@ -634,16 +625,16 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
- OFHashAdd(&hash, _preference >> 8);
- OFHashAdd(&hash, _preference);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
+ OFHashAddByte(&hash, _preference >> 8);
+ OFHashAddByte(&hash, _preference);
OFHashAddHash(&hash, _mailExchange.hash);
OFHashFinalize(&hash);
return hash;
@@ -735,14 +726,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, _authoritativeHost.hash);
OFHashFinalize(&hash);
return hash;
@@ -833,14 +824,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, _domainName.hash);
OFHashFinalize(&hash);
return hash;
@@ -938,14 +929,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, _mailbox.hash);
OFHashAddHash(&hash, _TXTDomainName.hash);
OFHashFinalize(&hash);
@@ -1074,36 +1065,36 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, _primaryNameServer.hash);
OFHashAddHash(&hash, _responsiblePerson.hash);
- OFHashAdd(&hash, _serialNumber >> 24);
- OFHashAdd(&hash, _serialNumber >> 16);
- OFHashAdd(&hash, _serialNumber >> 8);
- OFHashAdd(&hash, _serialNumber);
- OFHashAdd(&hash, _refreshInterval >> 24);
- OFHashAdd(&hash, _refreshInterval >> 16);
- OFHashAdd(&hash, _refreshInterval >> 8);
- OFHashAdd(&hash, _refreshInterval);
- OFHashAdd(&hash, _retryInterval >> 24);
- OFHashAdd(&hash, _retryInterval >> 16);
- OFHashAdd(&hash, _retryInterval >> 8);
- OFHashAdd(&hash, _retryInterval);
- OFHashAdd(&hash, _expirationInterval >> 24);
- OFHashAdd(&hash, _expirationInterval >> 16);
- OFHashAdd(&hash, _expirationInterval >> 8);
- OFHashAdd(&hash, _expirationInterval);
- OFHashAdd(&hash, _minTTL >> 24);
- OFHashAdd(&hash, _minTTL >> 16);
- OFHashAdd(&hash, _minTTL >> 8);
- OFHashAdd(&hash, _minTTL);
+ OFHashAddByte(&hash, _serialNumber >> 24);
+ OFHashAddByte(&hash, _serialNumber >> 16);
+ OFHashAddByte(&hash, _serialNumber >> 8);
+ OFHashAddByte(&hash, _serialNumber);
+ OFHashAddByte(&hash, _refreshInterval >> 24);
+ OFHashAddByte(&hash, _refreshInterval >> 16);
+ OFHashAddByte(&hash, _refreshInterval >> 8);
+ OFHashAddByte(&hash, _refreshInterval);
+ OFHashAddByte(&hash, _retryInterval >> 24);
+ OFHashAddByte(&hash, _retryInterval >> 16);
+ OFHashAddByte(&hash, _retryInterval >> 8);
+ OFHashAddByte(&hash, _retryInterval);
+ OFHashAddByte(&hash, _expirationInterval >> 24);
+ OFHashAddByte(&hash, _expirationInterval >> 16);
+ OFHashAddByte(&hash, _expirationInterval >> 8);
+ OFHashAddByte(&hash, _expirationInterval);
+ OFHashAddByte(&hash, _minTTL >> 24);
+ OFHashAddByte(&hash, _minTTL >> 16);
+ OFHashAddByte(&hash, _minTTL >> 8);
+ OFHashAddByte(&hash, _minTTL);
OFHashFinalize(&hash);
return hash;
}
@@ -1215,21 +1206,21 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
- OFHashAdd(&hash, _priority >> 8);
- OFHashAdd(&hash, _priority);
- OFHashAdd(&hash, _weight >> 8);
- OFHashAdd(&hash, _weight);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
+ OFHashAddByte(&hash, _priority >> 8);
+ OFHashAddByte(&hash, _priority);
+ OFHashAddByte(&hash, _weight >> 8);
+ OFHashAddByte(&hash, _weight);
OFHashAddHash(&hash, _target.hash);
- OFHashAdd(&hash, _port >> 8);
- OFHashAdd(&hash, _port);
+ OFHashAddByte(&hash, _port >> 8);
+ OFHashAddByte(&hash, _port);
OFHashFinalize(&hash);
return hash;
}
@@ -1320,14 +1311,14 @@
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
- OFHashAdd(&hash, _DNSClass >> 8);
- OFHashAdd(&hash, _DNSClass);
- OFHashAdd(&hash, _recordType >> 8);
- OFHashAdd(&hash, _recordType);
+ OFHashAddByte(&hash, _DNSClass >> 8);
+ OFHashAddByte(&hash, _DNSClass);
+ OFHashAddByte(&hash, _recordType >> 8);
+ OFHashAddByte(&hash, _recordType);
OFHashAddHash(&hash, _textStrings.hash);
OFHashFinalize(&hash);
return hash;
Index: src/OFDNSResponse.h
==================================================================
--- src/OFDNSResponse.h
+++ src/OFDNSResponse.h
@@ -27,16 +27,16 @@
/**
* @class OFDNSResponse OFDNSResponse.h ObjFW/OFDNSResponse.h
*
* @brief A class storing a response from @ref OFDNSResolver.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFDNSResponse: OFObject
{
OFString *_domainName;
OFDNSResponseRecords _answerRecords, _authorityRecords;
OFDNSResponseRecords _additionalRecords;
- OF_RESERVE_IVARS(OFDNSResponse, 4)
}
/**
* @brief The domain name of the response.
*/
Index: src/OFData+ASN1DERParsing.m
==================================================================
--- src/OFData+ASN1DERParsing.m
+++ src/OFData+ASN1DERParsing.m
@@ -59,11 +59,11 @@
objectLength = parseObject(contents, &object, depthLimit);
count -= objectLength;
contents = [contents subdataWithRange:
- OFRangeMake(objectLength, count)];
+ OFMakeRange(objectLength, count)];
[ret addObject: object];
}
[ret makeImmutable];
@@ -86,20 +86,20 @@
size_t objectLength;
OFData *objectData;
objectLength = parseObject(contents, &object, depthLimit);
objectData = [contents subdataWithRange:
- OFRangeMake(0, objectLength)];
+ OFMakeRange(0, objectLength)];
if (previousObjectData != nil &&
[objectData compare: previousObjectData] !=
OFOrderedDescending)
@throw [OFInvalidFormatException exception];
count -= objectLength;
contents = [contents subdataWithRange:
- OFRangeMake(objectLength, count)];
+ OFMakeRange(objectLength, count)];
[ret addObject: object];
previousObjectData = objectData;
}
@@ -153,11 +153,11 @@
if (count - bytesConsumed < contentsLength)
@throw [OFTruncatedDataException exception];
contents = [self subdataWithRange:
- OFRangeMake(bytesConsumed, contentsLength)];
+ OFMakeRange(bytesConsumed, contentsLength)];
bytesConsumed += contentsLength;
switch (tag & ~tagConstructedMask) {
case OFASN1TagNumberBoolean:
valueClass = [OFASN1Boolean class];
Index: src/OFData+MessagePackParsing.h
==================================================================
--- src/OFData+MessagePackParsing.h
+++ src/OFData+MessagePackParsing.h
@@ -27,19 +27,30 @@
@interface OFData (MessagePackParsing)
/**
* @brief The data interpreted as MessagePack representation and parsed as an
* object.
+ *
+ * @throw OFInvalidFormatException The MessagePack representation contained in
+ * the data contained an invalid format
+ * @throw OFTruncatedDataException The MessagePack representation contained in
+ * the data is truncated
+ * @throw OFOutOfRangeException The depth limit has been exceeded
*/
@property (readonly, nonatomic) id objectByParsingMessagePack;
/**
* @brief Parses the MessagePack representation and returns it as an object.
*
* @param depthLimit The maximum depth the parser should accept (defaults to 32
* if not specified, 0 means no limit (insecure!))
* @return The MessagePack representation as an object
+ * @throw OFInvalidFormatException The MessagePack representation contained in
+ * the data contained an invalid format
+ * @throw OFTruncatedDataException The MessagePack representation contained in
+ * the data is truncated
+ * @throw OFOutOfRangeException The depth limit has been exceeded
*/
- (id)objectByParsingMessagePackWithDepthLimit: (size_t)depthLimit;
@end
OF_ASSUME_NONNULL_END
Index: src/OFData.h
==================================================================
--- src/OFData.h
+++ src/OFData.h
@@ -20,11 +20,11 @@
/*! @file */
OF_ASSUME_NONNULL_BEGIN
@class OFString;
-@class OFURL;
+@class OFURI;
/**
* @brief Options for searching in data.
*
* This is a bit mask.
@@ -165,32 +165,36 @@
+ (instancetype)dataWithContentsOfFile: (OFString *)path;
#endif
/**
* @brief Creates a new OFData with an item size of 1, containing the data of
- * the specified URL.
+ * the specified URI.
*
- * @param URL The URL to the contents for the OFData
+ * @param URI The URI to the contents for the OFData
* @return A new autoreleased OFData
*/
-+ (instancetype)dataWithContentsOfURL: (OFURL *)URL;
++ (instancetype)dataWithContentsOfURI: (OFURI *)URI;
/**
* @brief Creates a new OFData with an item size of 1, containing the data of
* the hex string representation.
*
* @param string The hex string representation of the data
* @return A new autoreleased OFData
+ * @throw OFInvalidFormatException The specified string is not correctly
+ * formatted
*/
+ (instancetype)dataWithStringRepresentation: (OFString *)string;
/**
* @brief Creates a new OFData with an item size of 1, containing the data of
* the Base64-encoded string.
*
* @param string The string with the Base64-encoded data
* @return A new autoreleased OFData
+ * @throw OFInvalidFormatException The specified string is not correctly
+ * formatted
*/
+ (instancetype)dataWithBase64EncodedString: (OFString *)string;
/**
* @brief Initializes an already allocated OFData with the specified `count`
@@ -264,32 +268,36 @@
- (instancetype)initWithContentsOfFile: (OFString *)path;
#endif
/**
* @brief Initializes an already allocated OFData with an item size of 1,
- * containing the data of the specified URL.
+ * containing the data of the specified URI.
*
- * @param URL The URL to the contents for the OFData
+ * @param URI The URI to the contents for the OFData
* @return A new autoreleased OFData
*/
-- (instancetype)initWithContentsOfURL: (OFURL *)URL;
+- (instancetype)initWithContentsOfURI: (OFURI *)URI;
/**
* @brief Initializes an already allocated OFData with an item size of 1,
* containing the data of the hex string representation.
*
* @param string The hex string representation of the data
* @return A new autoreleased OFData
+ * @throw OFInvalidFormatException The specified string is not correctly
+ * formatted
*/
- (instancetype)initWithStringRepresentation: (OFString *)string;
/**
* @brief Initializes an already allocated OFData with an item size of 1,
* containing the data of the Base64-encoded string.
*
* @param string The string with the Base64-encoded data
* @return An initialized OFData
+ * @throw OFInvalidFormatException The specified string is not correctly
+ * formatted
*/
- (instancetype)initWithBase64EncodedString: (OFString *)string;
/**
* @brief Compares the data to other data.
@@ -336,18 +344,18 @@
*/
- (void)writeToFile: (OFString *)path;
#endif
/**
- * @brief Writes the OFData to the specified URL.
+ * @brief Writes the OFData to the specified URI.
*
- * @param URL The URL to write to
+ * @param URI The URI to write to
*/
-- (void)writeToURL: (OFURL *)URL;
+- (void)writeToURI: (OFURI *)URI;
@end
OF_ASSUME_NONNULL_END
#import "OFMutableData.h"
#import "OFData+ASN1DERParsing.h"
#import "OFData+CryptographicHashing.h"
#import "OFData+MessagePackParsing.h"
Index: src/OFData.m
==================================================================
--- src/OFData.m
+++ src/OFData.m
@@ -27,17 +27,17 @@
# import "OFFileManager.h"
#endif
#import "OFStream.h"
#import "OFString.h"
#import "OFSystemInfo.h"
-#import "OFURL.h"
-#import "OFURLHandler.h"
+#import "OFURI.h"
+#import "OFURIHandler.h"
#import "OFXMLElement.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
-#import "OFInvalidServerReplyException.h"
+#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
@@ -92,13 +92,13 @@
{
return [[[self alloc] initWithContentsOfFile: path] autorelease];
}
#endif
-+ (instancetype)dataWithContentsOfURL: (OFURL *)URL
++ (instancetype)dataWithContentsOfURI: (OFURI *)URI
{
- return [[[self alloc] initWithContentsOfURL: URL] autorelease];
+ return [[[self alloc] initWithContentsOfURI: URI] autorelease];
}
+ (instancetype)dataWithStringRepresentation: (OFString *)string
{
return [[[self alloc]
@@ -174,40 +174,36 @@
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
char *buffer = NULL;
- unsigned long long size;
+ OFStreamOffset fileSize;
@try {
- OFFile *file;
+ void *pool = objc_autoreleasePoolPush();
+ OFFile *file = [OFFile fileWithPath: path mode: @"r"];
+ fileSize = [file seekToOffset: 0 whence: OFSeekEnd];
- size = [[OFFileManager defaultManager]
- attributesOfItemAtPath: path].fileSize;
-
-# if ULLONG_MAX > SIZE_MAX
- if (size > SIZE_MAX)
+ if (fileSize < 0 || (unsigned long long)fileSize > SIZE_MAX)
@throw [OFOutOfRangeException exception];
-# endif
-
- buffer = OFAllocMemory((size_t)size, 1);
- file = [[OFFile alloc] initWithPath: path mode: @"r"];
- @try {
- [file readIntoBuffer: buffer exactLength: (size_t)size];
- } @finally {
- [file release];
- }
+
+ [file seekToOffset: 0 whence: OFSeekSet];
+
+ buffer = OFAllocMemory((size_t)fileSize, 1);
+ [file readIntoBuffer: buffer exactLength: (size_t)fileSize];
+
+ objc_autoreleasePoolPop(pool);
} @catch (id e) {
OFFreeMemory(buffer);
[self release];
@throw e;
}
@try {
self = [self initWithItemsNoCopy: buffer
- count: (size_t)size
+ count: (size_t)fileSize
freeWhenDone: true];
} @catch (id e) {
OFFreeMemory(buffer);
@throw e;
}
@@ -214,27 +210,20 @@
return self;
}
#endif
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
{
self = [super init];
@try {
void *pool = objc_autoreleasePoolPush();
- OFURLHandler *URLHandler;
- OFStream *stream;
+ OFStream *stream = [OFURIHandler openItemAtURI: URI mode: @"r"];
size_t pageSize;
unsigned char *buffer;
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException
- exceptionWithURL: URL];
-
- stream = [URLHandler openItemAtURL: URL mode: @"r"];
-
_count = 0;
_itemSize = 1;
_freeWhenDone = true;
pageSize = [OFSystemInfo pageSize];
@@ -483,11 +472,11 @@
unsigned long hash;
OFHashInit(&hash);
for (size_t i = 0; i < _count * _itemSize; i++)
- OFHashAdd(&hash, ((uint8_t *)_items)[i]);
+ OFHashAddByte(&hash, ((uint8_t *)_items)[i]);
OFHashFinalize(&hash);
return hash;
}
@@ -557,22 +546,22 @@
if (data == nil || data.itemSize != _itemSize)
@throw [OFInvalidArgumentException exception];
if ((searchLength = data.count) == 0)
- return OFRangeMake(0, 0);
+ return OFMakeRange(0, 0);
if (searchLength > range.length)
- return OFRangeMake(OFNotFound, 0);
+ return OFMakeRange(OFNotFound, 0);
search = data.items;
if (options & OFDataSearchBackwards) {
for (size_t i = range.length - searchLength;; i--) {
if (memcmp(_items + i * _itemSize, search,
searchLength * _itemSize) == 0)
- return OFRangeMake(i, searchLength);
+ return OFMakeRange(i, searchLength);
/* No match and we're at the last item */
if (i == 0)
break;
}
@@ -579,14 +568,14 @@
} else {
for (size_t i = range.location;
i <= range.length - searchLength; i++)
if (memcmp(_items + i * _itemSize, search,
searchLength * _itemSize) == 0)
- return OFRangeMake(i, searchLength);
+ return OFMakeRange(i, searchLength);
}
- return OFRangeMake(OFNotFound, 0);
+ return OFMakeRange(OFNotFound, 0);
}
#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
@@ -597,19 +586,15 @@
[file release];
}
}
#endif
-- (void)writeToURL: (OFURL *)URL
+- (void)writeToURI: (OFURI *)URI
{
void *pool = objc_autoreleasePoolPush();
- OFURLHandler *URLHandler;
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
-
- [[URLHandler openItemAtURL: URL mode: @"w"] writeData: self];
+ [[OFURIHandler openItemAtURI: URI mode: @"w"] writeData: self];
objc_autoreleasePoolPop(pool);
}
- (OFXMLElement *)XMLElementBySerializing
@@ -616,11 +601,12 @@
{
void *pool;
OFXMLElement *element;
if (_itemSize != 1)
- @throw [OFInvalidArgumentException exception];
+ @throw [OFNotImplementedException exceptionWithSelector: _cmd
+ object: self];
pool = objc_autoreleasePoolPush();
element = [OFXMLElement
elementWithName: self.className
namespace: OFSerializationNS
@@ -636,11 +622,12 @@
- (OFData *)messagePackRepresentation
{
OFMutableData *data;
if (_itemSize != 1)
- @throw [OFInvalidArgumentException exception];
+ @throw [OFNotImplementedException exceptionWithSelector: _cmd
+ object: self];
if (_count <= UINT8_MAX) {
uint8_t type = 0xC4;
uint8_t tmp = (uint8_t)_count;
Index: src/OFDatagramSocket.h
==================================================================
--- src/OFDatagramSocket.h
+++ src/OFDatagramSocket.h
@@ -115,15 +115,19 @@
/**
* @brief Whether the socket can block.
*
* By default, a socket can block.
+ *
+ * @throw OFSetOptionFailedException The option could not be set
*/
@property (nonatomic) bool canBlock;
/**
* @brief Whether the socket can send to broadcast addresses.
+ *
+ * @throw OFSetOptionFailedException The option could not be set
*/
@property (nonatomic) bool canSendToBroadcastAddresses;
/**
* @brief The delegate for asynchronous operations on the socket.
@@ -149,14 +153,16 @@
* @param buffer The buffer to write the datagram to
* @param length The length of the buffer
* @param sender A pointer to an @ref OFSocketAddress, which will be set to the
* address of the sender
* @return The length of the received datagram
+ * @throw OFReadFailedException Receiving failed
+ * @throw OFNotOpenException The socket is not open
*/
- (size_t)receiveIntoBuffer: (void *)buffer
length: (size_t)length
- sender: (OFSocketAddress *)sender;
+ sender: (nullable OFSocketAddress *)sender;
/**
* @brief Asynchronously receives a datagram and stores it into the specified
* buffer.
*
@@ -228,10 +234,12 @@
*
* @param buffer The buffer to send as a datagram
* @param length The length of the buffer
* @param receiver A pointer to an @ref OFSocketAddress to which the datagram
* should be sent
+ * @throw OFWriteFailedException Sending failed
+ * @throw OFNotOpenException The socket is not open
*/
- (void)sendBuffer: (const void *)buffer
length: (size_t)length
receiver: (const OFSocketAddress *)receiver;
@@ -295,10 +303,12 @@
- (void)cancelAsyncRequests;
/**
* @brief Closes the socket so that it can neither receive nor send any more
* datagrams.
+ *
+ * @throw OFNotOpenException The socket is not open
*/
- (void)close;
@end
OF_ASSUME_NONNULL_END
Index: src/OFDatagramSocket.m
==================================================================
--- src/OFDatagramSocket.m
+++ src/OFDatagramSocket.m
@@ -171,53 +171,63 @@
ssize_t ret;
if (_socket == OFInvalidSocketHandle)
@throw [OFNotOpenException exceptionWithObject: self];
- sender->length = (socklen_t)sizeof(sender->sockaddr);
+ if (sender != NULL)
+ sender->length = (socklen_t)sizeof(sender->sockaddr);
#ifndef OF_WINDOWS
if ((ret = recvfrom(_socket, buffer, length, 0,
- (struct sockaddr *)&sender->sockaddr, &sender->length)) < 0)
+ (sender != NULL ? (struct sockaddr *)&sender->sockaddr : NULL),
+ (sender != NULL ? &sender->length : NULL))) < 0)
@throw [OFReadFailedException
exceptionWithObject: self
requestedLength: length
errNo: OFSocketErrNo()];
#else
if (length > INT_MAX)
@throw [OFOutOfRangeException exception];
if ((ret = recvfrom(_socket, buffer, (int)length, 0,
- (struct sockaddr *)&sender->sockaddr, &sender->length)) < 0)
+ (sender != NULL ? (struct sockaddr *)&sender->sockaddr : NULL),
+ (sender != NULL ? &sender->length : NULL))) < 0)
@throw [OFReadFailedException
exceptionWithObject: self
requestedLength: length
errNo: OFSocketErrNo()];
#endif
- switch (((struct sockaddr *)&sender->sockaddr)->sa_family) {
- case AF_INET:
- sender->family = OFSocketAddressFamilyIPv4;
- break;
+ if (sender != NULL) {
+ switch (((struct sockaddr *)&sender->sockaddr)->sa_family) {
+ case AF_INET:
+ sender->family = OFSocketAddressFamilyIPv4;
+ break;
#ifdef OF_HAVE_IPV6
- case AF_INET6:
- sender->family = OFSocketAddressFamilyIPv6;
- break;
+ case AF_INET6:
+ sender->family = OFSocketAddressFamilyIPv6;
+ break;
#endif
#ifdef OF_HAVE_UNIX_SOCKETS
- case AF_UNIX:
- sender->family = OFSocketAddressFamilyUNIX;
- break;
+ case AF_UNIX:
+ sender->family = OFSocketAddressFamilyUNIX;
+ break;
#endif
#ifdef OF_HAVE_IPX
- case AF_IPX:
- sender->family = OFSocketAddressFamilyIPX;
- break;
+ case AF_IPX:
+ sender->family = OFSocketAddressFamilyIPX;
+ break;
+#endif
+#ifdef OF_HAVE_APPLETALK
+ case AF_APPLETALK:
+ sender->family = OFSocketAddressFamilyAppleTalk;
+ break;
#endif
- default:
- sender->family = OFSocketAddressFamilyUnknown;
- break;
+ default:
+ sender->family = OFSocketAddressFamilyUnknown;
+ break;
+ }
}
return ret;
}
Index: src/OFDate.h
==================================================================
--- src/OFDate.h
+++ src/OFDate.h
@@ -169,10 +169,11 @@
* %%t.
*
* @param string The string describing the date
* @param format The format of the string describing the date
* @return A new, autoreleased OFDate with the specified date and time
+ * @throw OFInvalidFormatException The specified format is invalid
*/
+ (instancetype)dateWithDateString: (OFString *)string
format: (OFString *)format;
/**
@@ -186,10 +187,11 @@
* %%t.
*
* @param string The string describing the date
* @param format The format of the string describing the date
* @return A new, autoreleased OFDate with the specified date and time
+ * @throw OFInvalidFormatException The specified format is invalid
*/
+ (instancetype)dateWithLocalDateString: (OFString *)string
format: (OFString *)format;
/**
@@ -243,10 +245,11 @@
* %%d, %%e, %%H, %%m, %%M, %%S, %%y, %%Y, %%, %%n and %%t.
*
* @param string The string describing the date
* @param format The format of the string describing the date
* @return An initialized OFDate with the specified date and time
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (instancetype)initWithDateString: (OFString *)string
format: (OFString *)format;
/**
@@ -261,10 +264,11 @@
* %%d, %%e, %%H, %%m, %%M, %%S, %%y, %%Y, %%, %%n and %%t.
*
* @param string The string describing the date
* @param format The format of the string describing the date
* @return An initialized OFDate with the specified date and time
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (instancetype)initWithLocalDateString: (OFString *)string
format: (OFString *)format;
/**
@@ -280,10 +284,11 @@
*
* See the man page for `strftime` for information on the format.
*
* @param format The format for the date string
* @return A new, autoreleased OFString
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (OFString *)dateStringWithFormat: (OFConstantString *)format;
/**
* @brief Creates a string of the local date with the specified format.
@@ -290,10 +295,11 @@
*
* See the man page for `strftime` for information on the format.
*
* @param format The format for the date string
* @return A new, autoreleased OFString
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (OFString *)localDateStringWithFormat: (OFConstantString *)format;
/**
* @brief Returns the earlier of the two dates.
Index: src/OFDate.m
==================================================================
--- src/OFDate.m
+++ src/OFDate.m
@@ -31,10 +31,11 @@
# import "OFMutex.h"
#endif
#import "OFStrPTime.h"
#import "OFString.h"
#import "OFSystemInfo.h"
+#import "OFXMLAttribute.h"
#import "OFXMLElement.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
@@ -504,10 +505,14 @@
unsigned long long value;
if (![element.name isEqual: @"OFDate"] ||
![element.namespace isEqual: OFSerializationNS])
@throw [OFInvalidArgumentException exception];
+
+ if (![[element attributeForName: @"encoding"].stringValue
+ isEqual: @"hex"])
+ @throw [OFInvalidFormatException exception];
value = [element unsignedLongLongValueWithBase: 16];
if (value > UINT64_MAX)
@throw [OFOutOfRangeException exception];
@@ -550,11 +555,11 @@
OFHashInit(&hash);
tmp = OFToLittleEndianDouble(self.timeIntervalSince1970);
for (size_t i = 0; i < sizeof(double); i++)
- OFHashAdd(&hash, ((char *)&tmp)[i]);
+ OFHashAddByte(&hash, ((char *)&tmp)[i]);
OFHashFinalize(&hash);
return hash;
}
@@ -588,10 +593,11 @@
OFXMLElement *element;
element = [OFXMLElement elementWithName: @"OFDate"
namespace: OFSerializationNS];
+ [element addAttributeWithName: @"encoding" stringValue: @"hex"];
element.stringValue = [OFString stringWithFormat: @"%016" PRIx64,
OFFromBigEndian64(OFDoubleToRawUInt64(OFToBigEndianDouble(
self.timeIntervalSince1970)))];
[element retain];
Index: src/OFDictionary.h
==================================================================
--- src/OFDictionary.h
+++ src/OFDictionary.h
@@ -91,15 +91,10 @@
/**
* @brief An array of all objects.
*/
@property (readonly, nonatomic) OFArray OF_GENERIC(ObjectType) *allObjects;
-/**
- * @brief A URL-encoded string with the contents of the dictionary.
- */
-@property (readonly, nonatomic) OFString *stringByURLEncoding;
-
/**
* @brief Creates a new OFDictionary.
*
* @return A new autoreleased OFDictionary
*/
@@ -245,15 +240,15 @@
- (nullable id)valueForKey: (OFString *)key;
/**
* @brief Sets a value for a key.
*
- * This is equivalent to OFMutableDictionary#setObject:forKey:. If the
- * dictionary is immutable, an @ref OFUndefinedKeyException is thrown.
+ * This is equivalent to OFMutableDictionary#setObject:forKey:.
*
* @param key The key to set
* @param value The value to set the key to
+ * @throw OFUndefinedKeyException The dictionary is immutable
*/
- (void)setValue: (nullable id)value forKey: (OFString *)key;
/**
* @brief Checks whether the dictionary contains an object equal to the
Index: src/OFDictionary.m
==================================================================
--- src/OFDictionary.m
+++ src/OFDictionary.m
@@ -34,11 +34,11 @@
static struct {
Class isa;
} placeholder;
-static OFCharacterSet *URLQueryPartAllowedCharacterSet = nil;
+static OFCharacterSet *URIQueryPartAllowedCharacterSet = nil;
@interface OFDictionary ()
- (OFString *)
of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
depth: (size_t)depth;
@@ -56,12 +56,12 @@
- (instancetype)initWithDictionary: (OFDictionary *)dictionary;
@end
OF_DIRECT_MEMBERS
-@interface OFURLQueryPartAllowedCharacterSet: OFCharacterSet
-+ (OFCharacterSet *)URLQueryPartAllowedCharacterSet;
+@interface OFURIQueryPartAllowedCharacterSet: OFCharacterSet
++ (OFCharacterSet *)URIQueryPartAllowedCharacterSet;
@end
@implementation OFDictionaryPlaceholder
- (instancetype)init
{
@@ -139,23 +139,23 @@
{
OF_DEALLOC_UNSUPPORTED
}
@end
-@implementation OFURLQueryPartAllowedCharacterSet
+@implementation OFURIQueryPartAllowedCharacterSet
+ (void)initialize
{
- if (self != [OFURLQueryPartAllowedCharacterSet class])
+ if (self != [OFURIQueryPartAllowedCharacterSet class])
return;
- URLQueryPartAllowedCharacterSet =
- [[OFURLQueryPartAllowedCharacterSet alloc] init];
+ URIQueryPartAllowedCharacterSet =
+ [[OFURIQueryPartAllowedCharacterSet alloc] init];
}
-+ (OFCharacterSet *)URLQueryPartAllowedCharacterSet
++ (OFCharacterSet *)URIQueryPartAllowedCharacterSet
{
- return URLQueryPartAllowedCharacterSet;
+ return URIQueryPartAllowedCharacterSet;
}
- (instancetype)autorelease
{
return self;
@@ -630,42 +630,10 @@
objc_autoreleasePoolPop(pool2);
}
[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];
[ret appendString: @";\n}"];
- [ret makeImmutable];
-
- objc_autoreleasePoolPop(pool);
-
- return ret;
-}
-
-- (OFString *)stringByURLEncoding
-{
- OFMutableString *ret = [OFMutableString string];
- void *pool = objc_autoreleasePoolPush();
- OFEnumerator *keyEnumerator = [self keyEnumerator];
- OFEnumerator *objectEnumerator = [self objectEnumerator];
- OFCharacterSet *allowed = [OFURLQueryPartAllowedCharacterSet
- URLQueryPartAllowedCharacterSet];
- bool first = true;
- OFObject *key, *object;
-
- while ((key = [keyEnumerator nextObject]) != nil &&
- (object = [objectEnumerator nextObject]) != nil) {
- if OF_UNLIKELY (first)
- first = false;
- else
- [ret appendString: @"&"];
-
- [ret appendString: [key.description
- stringByURLEncodingWithAllowedCharacters: allowed]];
- [ret appendString: @"="];
- [ret appendString: [object.description
- stringByURLEncodingWithAllowedCharacters: allowed]];
- }
-
[ret makeImmutable];
objc_autoreleasePoolPop(pool);
return ret;
DELETED src/OFEmbeddedFileURLHandler.h
Index: src/OFEmbeddedFileURLHandler.h
==================================================================
--- src/OFEmbeddedFileURLHandler.h
+++ src/OFEmbeddedFileURLHandler.h
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFURLHandler.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-@interface OFEmbeddedFileURLHandler: OFURLHandler
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/OFEmbeddedFileURLHandler.m
Index: src/OFEmbeddedFileURLHandler.m
==================================================================
--- src/OFEmbeddedFileURLHandler.m
+++ src/OFEmbeddedFileURLHandler.m
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#include
-#include
-#include
-
-#import "OFEmbeddedFileURLHandler.h"
-#import "OFMemoryStream.h"
-#import "OFURL.h"
-
-#import "OFInvalidArgumentException.h"
-#import "OFOpenItemFailedException.h"
-
-#ifdef OF_HAVE_THREADS
-# import "OFOnce.h"
-# import "OFPlainMutex.h"
-#endif
-
-struct EmbeddedFile {
- const char *name;
- const uint8_t *bytes;
- size_t size;
-} *embeddedFiles = NULL;
-size_t numEmbeddedFiles = 0;
-#ifdef OF_HAVE_THREADS
-static OFPlainMutex mutex;
-
-static void
-init(void)
-{
- OFEnsure(OFPlainMutexNew(&mutex) == 0);
-}
-#endif
-
-void
-OFRegisterEmbeddedFile(const char *name, const uint8_t *bytes, size_t size)
-{
-#ifdef OF_HAVE_THREADS
- static OFOnceControl onceControl = OFOnceControlInitValue;
- OFOnce(&onceControl, init);
-
- OFEnsure(OFPlainMutexLock(&mutex) == 0);
-#endif
-
- embeddedFiles = realloc(embeddedFiles,
- sizeof(*embeddedFiles) * (numEmbeddedFiles + 1));
- OFEnsure(embeddedFiles != NULL);
-
- embeddedFiles[numEmbeddedFiles].name = name;
- embeddedFiles[numEmbeddedFiles].bytes = bytes;
- embeddedFiles[numEmbeddedFiles].size = size;
- numEmbeddedFiles++;
-
-#ifdef OF_HAVE_THREADS
- OFEnsure(OFPlainMutexUnlock(&mutex) == 0);
-#endif
-}
-
-@implementation OFEmbeddedFileURLHandler
-- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode
-{
- const char *path;
-
- if (![URL.scheme isEqual: @"objfw-embedded"] || URL.host != nil ||
- URL.port != nil || URL.user != nil || URL.password != nil ||
- URL.query != nil || URL.fragment != nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![mode isEqual: @"r"])
- @throw [OFOpenItemFailedException exceptionWithURL: URL
- mode: mode
- errNo: EROFS];
-
- if ((path = URL.path.UTF8String) == NULL) {
- @throw [OFInvalidArgumentException exception];
- }
-
-#ifdef OF_HAVE_THREADS
- OFEnsure(OFPlainMutexLock(&mutex) == 0);
- @try {
-#endif
- for (size_t i = 0; i < numEmbeddedFiles; i++) {
- if (strcmp(embeddedFiles[i].name, path) != 0)
- continue;
-
- return [OFMemoryStream
- streamWithMemoryAddress: (void *)
- embeddedFiles[i].bytes
- size: embeddedFiles[i].size
- writable: false];
- }
-#ifdef OF_HAVE_THREADS
- } @finally {
- OFEnsure(OFPlainMutexUnlock(&mutex) == 0);
- }
-#endif
-
- @throw [OFOpenItemFailedException exceptionWithURL: URL
- mode: mode
- errNo: ENOENT];
-}
-@end
ADDED src/OFEmbeddedURIHandler.h
Index: src/OFEmbeddedURIHandler.h
==================================================================
--- src/OFEmbeddedURIHandler.h
+++ src/OFEmbeddedURIHandler.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFURIHandler.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@interface OFEmbeddedURIHandler: OFURIHandler
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFEmbeddedURIHandler.m
Index: src/OFEmbeddedURIHandler.m
==================================================================
--- src/OFEmbeddedURIHandler.m
+++ src/OFEmbeddedURIHandler.m
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+
+#import "OFEmbeddedURIHandler.h"
+#import "OFMemoryStream.h"
+#import "OFURI.h"
+
+#import "OFInvalidArgumentException.h"
+#import "OFOpenItemFailedException.h"
+
+#ifdef OF_HAVE_THREADS
+# import "OFOnce.h"
+# import "OFPlainMutex.h"
+#endif
+
+struct EmbeddedFile {
+ const char *name;
+ const uint8_t *bytes;
+ size_t size;
+} *embeddedFiles = NULL;
+size_t numEmbeddedFiles = 0;
+#ifdef OF_HAVE_THREADS
+static OFPlainMutex mutex;
+
+static void
+init(void)
+{
+ OFEnsure(OFPlainMutexNew(&mutex) == 0);
+}
+#endif
+
+void
+OFRegisterEmbeddedFile(const char *name, const uint8_t *bytes, size_t size)
+{
+#ifdef OF_HAVE_THREADS
+ static OFOnceControl onceControl = OFOnceControlInitValue;
+ OFOnce(&onceControl, init);
+
+ OFEnsure(OFPlainMutexLock(&mutex) == 0);
+#endif
+
+ embeddedFiles = realloc(embeddedFiles,
+ sizeof(*embeddedFiles) * (numEmbeddedFiles + 1));
+ OFEnsure(embeddedFiles != NULL);
+
+ embeddedFiles[numEmbeddedFiles].name = name;
+ embeddedFiles[numEmbeddedFiles].bytes = bytes;
+ embeddedFiles[numEmbeddedFiles].size = size;
+ numEmbeddedFiles++;
+
+#ifdef OF_HAVE_THREADS
+ OFEnsure(OFPlainMutexUnlock(&mutex) == 0);
+#endif
+}
+
+@implementation OFEmbeddedURIHandler
+- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode
+{
+ const char *path;
+
+ if (![URI.scheme isEqual: @"embedded"] || URI.host.length > 0 ||
+ URI.port != nil || URI.user != nil || URI.password != nil ||
+ URI.query != nil || URI.fragment != nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![mode isEqual: @"r"])
+ @throw [OFOpenItemFailedException exceptionWithURI: URI
+ mode: mode
+ errNo: EROFS];
+
+ if ((path = URI.path.UTF8String) == NULL) {
+ @throw [OFInvalidArgumentException exception];
+ }
+
+#ifdef OF_HAVE_THREADS
+ OFEnsure(OFPlainMutexLock(&mutex) == 0);
+ @try {
+#endif
+ for (size_t i = 0; i < numEmbeddedFiles; i++) {
+ if (strcmp(embeddedFiles[i].name, path) != 0)
+ continue;
+
+ return [OFMemoryStream
+ streamWithMemoryAddress: (void *)
+ embeddedFiles[i].bytes
+ size: embeddedFiles[i].size
+ writable: false];
+ }
+#ifdef OF_HAVE_THREADS
+ } @finally {
+ OFEnsure(OFPlainMutexUnlock(&mutex) == 0);
+ }
+#endif
+
+ @throw [OFOpenItemFailedException exceptionWithURI: URI
+ mode: mode
+ errNo: ENOENT];
+}
+@end
Index: src/OFEnumerator.h
==================================================================
--- src/OFEnumerator.h
+++ src/OFEnumerator.h
@@ -80,10 +80,12 @@
* @param state Context information for the enumeration
* @param objects A pointer to an array where to put the objects
* @param count The number of objects that can be stored at objects
* @return The number of objects returned in objects or 0 when the enumeration
* finished.
+ * @throw OFEnumerationMutationException The object was mutated during
+ * enumeration
*/
- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
objects: (id __unsafe_unretained _Nonnull *_Nonnull)
objects
count: (int)count;
@@ -96,18 +98,16 @@
*/
@interface OFEnumerator OF_GENERIC(ObjectType): OFObject
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# define ObjectType id
#endif
-{
- OF_RESERVE_IVARS(OFEnumerator, 4)
-}
-
/**
* @brief Returns the next object or `nil` if there is none left.
*
* @return The next object or `nil` if there is none left
+ * @throw OFEnumerationMutationException The object was mutated during
+ * enumeration
*/
- (nullable ObjectType)nextObject;
/**
* @brief Returns an array of all remaining objects in the collection.
Index: src/OFEpollKernelEventObserver.m
==================================================================
--- src/OFEpollKernelEventObserver.m
+++ src/OFEpollKernelEventObserver.m
@@ -29,11 +29,11 @@
#import "OFArray.h"
#import "OFMapTable.h"
#import "OFNull.h"
#import "OFInitializationFailedException.h"
-#import "OFObserveFailedException.h"
+#import "OFObserveKernelEventsFailedException.h"
#define eventListSize 64
static const OFMapTableFunctions mapFunctions = { NULL };
@@ -102,12 +102,13 @@
event.events = (int)events | addEvents;
event.data.ptr = object;
if (epoll_ctl(_epfd, (events == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD),
fd, &event) == -1)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
[_FDToEvents setObject: (void *)(events | addEvents)
forKey: (void *)((intptr_t)fd + 1)];
}
@@ -127,11 +128,11 @@
* When an async connect fails, it seems the socket is
* automatically removed from epoll, meaning ENOENT is
* returned when we try to remove it after it failed.
*/
if (errno != ENOENT)
- @throw [OFObserveFailedException
+ @throw [OFObserveKernelEventsFailedException
exceptionWithObserver: self
errNo: errno];
[_FDToEvents removeObjectForKey: (void *)((intptr_t)fd + 1)];
} else {
@@ -140,11 +141,11 @@
memset(&event, 0, sizeof(event));
event.events = (int)events;
event.data.ptr = object;
if (epoll_ctl(_epfd, EPOLL_CTL_MOD, fd, &event) == -1)
- @throw [OFObserveFailedException
+ @throw [OFObserveKernelEventsFailedException
exceptionWithObserver: self
errNo: errno];
[_FDToEvents setObject: (void *)events
forKey: (void *)((intptr_t)fd + 1)];
@@ -198,12 +199,13 @@
events = epoll_wait(_epfd, eventList, eventListSize,
(timeInterval != -1 ? timeInterval * 1000 : -1));
if (events < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
for (int i = 0; i < events; i++) {
if (eventList[i].events & EPOLLIN) {
void *pool = objc_autoreleasePoolPush();
Index: src/OFFile.h
==================================================================
--- src/OFFile.h
+++ src/OFFile.h
@@ -25,11 +25,11 @@
static const OFFileHandle OFInvalidFileHandle = NULL;
#endif
OF_ASSUME_NONNULL_BEGIN
-@class OFURL;
+@class OFURI;
/**
* @class OFFile OFFile.h ObjFW/OFFile.h
*
* @brief A class which provides methods to read and write files.
@@ -60,10 +60,11 @@
* `w+` | Read-write, create or truncate
* `w+x` | Read-write, create or fail, exclusive
* `a` | Write-only, create or append
* `a+` | Read-write, create or append
* @return A new autoreleased OFFile
+ * @throw OFOpenItemFailedException Opening the file failed
*/
+ (instancetype)fileWithPath: (OFString *)path mode: (OFString *)mode;
/**
* @brief Creates a new OFFile with the specified native file handle.
@@ -94,10 +95,11 @@
* `a` | write-only, create, append
* `ab` | write-only, create, append, binary
* `a+` | read-write, create, append
* `ab+` or `a+b` | read-write, create, append, binary
* @return An initialized OFFile
+ * @throw OFOpenItemFailedException Opening the file failed
*/
- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode;
/**
* @brief Initializes an already allocated OFFile.
Index: src/OFFile.m
==================================================================
--- src/OFFile.m
+++ src/OFFile.m
@@ -31,11 +31,11 @@
#import "OFFile.h"
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"
-#import "OFURL.h"
+#import "OFURI.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotOpenException.h"
#import "OFOpenItemFailedException.h"
@@ -448,24 +448,43 @@
#endif
return (size_t)bytesWritten;
}
-- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence
+- (OFStreamOffset)lowlevelSeekToOffset: (OFStreamOffset)offset
+ whence: (OFSeekWhence)whence
{
- OFFileOffset ret;
+ OFStreamOffset ret;
if (_handle == OFInvalidFileHandle)
@throw [OFNotOpenException exceptionWithObject: self];
#ifndef OF_AMIGAOS
+ int translatedWhence;
+
+ switch (whence) {
+ case OFSeekSet:
+ translatedWhence = SEEK_SET;
+ break;
+ case OFSeekCurrent:
+ translatedWhence = SEEK_CUR;
+ break;
+ case OFSeekEnd:
+ translatedWhence = SEEK_END;
+ break;
+ default:
+ @throw [OFSeekFailedException exceptionWithStream: self
+ offset: offset
+ whence: whence
+ errNo: EINVAL];
+ }
# if defined(OF_WINDOWS)
- ret = _lseeki64(_handle, offset, whence);
+ ret = _lseeki64(_handle, offset, translatedWhence);
# elif defined(HAVE_LSEEK64)
- ret = lseek64(_handle, offset, whence);
+ ret = lseek64(_handle, offset, translatedWhence);
# else
- ret = lseek(_handle, offset, whence);
+ ret = lseek(_handle, offset, translatedWhence);
# endif
if (ret == -1)
@throw [OFSeekFailedException exceptionWithStream: self
offset: offset
@@ -473,17 +492,17 @@
errNo: errno];
#else
LONG translatedWhence;
switch (whence) {
- case SEEK_SET:
+ case OFSeekSet:
translatedWhence = OFFSET_BEGINNING;
break;
- case SEEK_CUR:
+ case OFSeekCurrent:
translatedWhence = OFFSET_CURRENT;
break;
- case SEEK_END:
+ case OFSeekEnd:
translatedWhence = OFFSET_END;
break;
default:
@throw [OFSeekFailedException exceptionWithStream: self
offset: offset
Index: src/OFFileManager.h
==================================================================
--- src/OFFileManager.h
+++ src/OFFileManager.h
@@ -37,16 +37,16 @@
@class OFArray OF_GENERIC(ObjectType);
@class OFConstantString;
@class OFDate;
@class OFString;
-@class OFURL;
+@class OFURI;
/**
* @brief A key for a file attribute in the file attributes dictionary.
*
- * Possible keys for file URLs are:
+ * Possible keys for file URIs are:
*
* * @ref OFFileSize
* * @ref OFFileType
* * @ref OFFilePOSIXPermissions
* * @ref OFFileOwnerAccountID
@@ -57,18 +57,18 @@
* * @ref OFFileModificationDate
* * @ref OFFileStatusChangeDate
* * @ref OFFileCreationDate
* * @ref OFFileSymbolicLinkDestination
*
- * Other URL schemes might not have all keys and might have keys not listed.
+ * Other URI schemes might not have all keys and might have keys not listed.
*/
typedef OFConstantString *OFFileAttributeKey;
/**
* @brief The type of a file.
*
- * Possibles values for file URLs are:
+ * Possibles values for file URIs are:
*
* * @ref OFFileTypeRegular
* * @ref OFFileTypeDirectory
* * @ref OFFileTypeSymbolicLink
* * @ref OFFileTypeFIFO
@@ -75,11 +75,11 @@
* * @ref OFFileTypeCharacterSpecial
* * @ref OFFileTypeBlockSpecial
* * @ref OFFileTypeSocket
* * @ref OFFileTypeUnknown
*
- * Other URL schemes might not have all types and might have types not listed.
+ * Other URI schemes might not have all types and might have types not listed.
*/
typedef OFConstantString *OFFileAttributeType;
/**
* @brief A dictionary mapping keys of type @ref OFFileAttributeKey to their
@@ -257,17 +257,21 @@
#endif
#ifdef OF_HAVE_FILES
/**
* @brief The path of the current working directory.
+ *
+ * @throw OFGetCurrentDirectoryFailedException Couldn't get current directory
*/
@property (readonly, nonatomic) OFString *currentDirectoryPath;
/**
- * @brief The URL of the current working directory.
+ * @brief The URI of the current working directory.
+ *
+ * @throw OFGetCurrentDirectoryFailedException Couldn't get current directory
*/
-@property (readonly, nonatomic) OFURL *currentDirectoryURL;
+@property (readonly, nonatomic) OFURI *currentDirectoryURI;
#endif
/**
* @brief Returns the default file manager.
*/
@@ -278,45 +282,63 @@
* @brief Returns the attributes for the item at the specified path.
*
* @param path The path to return the attributes for
* @return A dictionary of attributes for the specified path, with the keys of
* type @ref OFFileAttributeKey
+ * @throw OFGetItemAttributesFailedException Failed to get the attributes of
+ * the item
*/
- (OFFileAttributes)attributesOfItemAtPath: (OFString *)path;
#endif
/**
- * @brief Returns the attributes for the item at the specified URL.
+ * @brief Returns the attributes for the item at the specified URI.
*
- * @param URL The URL to return the attributes for
- * @return A dictionary of attributes for the specified URL, with the keys of
+ * @param URI The URI to return the attributes for
+ * @return A dictionary of attributes for the specified URI, with the keys of
* type @ref OFFileAttributeKey
+ * @throw OFGetItemAttributesFailedException Failed to get the attributes of
+ * the item
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL;
+- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI;
#ifdef OF_HAVE_FILES
/**
* @brief Sets the attributes for the item at the specified path.
*
* All attributes not part of the dictionary are left unchanged.
*
* @param attributes The attributes to set for the specified path
* @param path The path of the item to set the attributes for
+ * @throw OFSetItemAttributesFailedException Failed to set the attributes of
+ * the item
+ * @throw OFNotImplementedException Setting one or more of the specified
+ * attributes is not implemented for the
+ * specified item
*/
- (void)setAttributes: (OFFileAttributes)attributes
ofItemAtPath: (OFString *)path;
#endif
/**
- * @brief Sets the attributes for the item at the specified URL.
+ * @brief Sets the attributes for the item at the specified URI.
*
* All attributes not part of the dictionary are left unchanged.
*
- * @param attributes The attributes to set for the specified URL
- * @param URL The URL of the item to set the attributes for
+ * @param attributes The attributes to set for the specified URI
+ * @param URI The URI of the item to set the attributes for
+ * @throw OFSetItemAttributesFailedException Failed to set the attributes of
+ * the item
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
+ * @throw OFNotImplementedException Setting one or more of the specified
+ * attributes is not implemented for the
+ * specified item
*/
-- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL;
+- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI;
#ifdef OF_HAVE_FILES
/**
* @brief Checks whether a file exists at the specified path.
*
@@ -325,16 +347,18 @@
*/
- (bool)fileExistsAtPath: (OFString *)path;
#endif
/**
- * @brief Checks whether a file exists at the specified URL.
+ * @brief Checks whether a file exists at the specified URI.
*
- * @param URL The URL to check
- * @return A boolean whether there is a file at the specified URL
+ * @param URI The URI to check
+ * @return A boolean whether there is a file at the specified URI
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (bool)fileExistsAtURL: (OFURL *)URL;
+- (bool)fileExistsAtURI: (OFURI *)URI;
#ifdef OF_HAVE_FILES
/**
* @brief Checks whether a directory exists at the specified path.
*
@@ -343,72 +367,90 @@
*/
- (bool)directoryExistsAtPath: (OFString *)path;
#endif
/**
- * @brief Checks whether a directory exists at the specified URL.
+ * @brief Checks whether a directory exists at the specified URI.
*
- * @param URL The URL to check
- * @return A boolean whether there is a directory at the specified URL
+ * @param URI The URI to check
+ * @return A boolean whether there is a directory at the specified URI
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (bool)directoryExistsAtURL: (OFURL *)URL;
+- (bool)directoryExistsAtURI: (OFURI *)URI;
#ifdef OF_HAVE_FILES
/**
* @brief Creates a directory at the specified path.
*
* @param path The path of the directory to create
+ * @throw OFCreateDirectoryFailedException Creating the directory failed
*/
- (void)createDirectoryAtPath: (OFString *)path;
/**
* @brief Creates a directory at the specified path.
*
* @param path The path of the directory to create
* @param createParents Whether to create the parents of the directory
+ * @throw OFCreateDirectoryFailedException Creating the directory or one of its
+ * parents failed
*/
- (void)createDirectoryAtPath: (OFString *)path
createParents: (bool)createParents;
#endif
/**
- * @brief Creates a directory at the specified URL.
+ * @brief Creates a directory at the specified URI.
*
- * @param URL The URL of the directory to create
+ * @param URI The URI of the directory to create
+ * @throw OFCreateDirectoryFailedException Creating the directory failed
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (void)createDirectoryAtURL: (OFURL *)URL;
+- (void)createDirectoryAtURI: (OFURI *)URI;
/**
- * @brief Creates a directory at the specified URL.
+ * @brief Creates a directory at the specified URI.
*
- * @param URL The URL of the directory to create
+ * @param URI The URI of the directory to create
* @param createParents Whether to create the parents of the directory
+ * @throw OFCreateDirectoryFailedException Creating the directory or one of its
+ * parents failed
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (void)createDirectoryAtURL: (OFURL *)URL createParents: (bool)createParents;
+- (void)createDirectoryAtURI: (OFURI *)URI createParents: (bool)createParents;
#ifdef OF_HAVE_FILES
/**
* @brief Returns an array with the items in the specified directory.
*
* @note `.` and `..` are not part of the returned array.
*
* @param path The path to the directory whose items should be returned
* @return An array of OFString with the items in the specified directory
+ * @throw OFOpenItemFailedException Opening the directory failed
+ * @throw OFReadFailedException Reading from the directory failed
*/
- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path;
#endif
/**
- * @brief Returns an array with the URLs of the items in the specified
+ * @brief Returns an array with the URIs of the items in the specified
* directory.
*
* @note `.` and `..` are not part of the returned array.
*
- * @param URL The URL to the directory whose items should be returned
- * @return An array with the URLs of the items in the specified directory
+ * @param URI The URI to the directory whose items should be returned
+ * @return An array with the URIs of the items in the specified directory
+ * @throw OFOpenItemFailedException Opening the directory failed
+ * @throw OFReadFailedException Reading from the directory failed
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL;
+- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI;
#ifdef OF_HAVE_FILES
/**
* @brief Returns an array with all subpaths of the specified directory.
*
@@ -422,19 +464,23 @@
/**
* @brief Changes the current working directory.
*
* @param path The new directory to change to
+ * @throw OFChangeCurrentDirectoryFailedException Changing the current working
+ * directory failed
*/
- (void)changeCurrentDirectoryPath: (OFString *)path;
/**
* @brief Changes the current working directory.
*
- * @param URL The new directory to change to
+ * @param URI The new directory to change to
+ * @throw OFChangeCurrentDirectoryFailedException Changing the current working
+ * directory failed
*/
-- (void)changeCurrentDirectoryURL: (OFURL *)URL;
+- (void)changeCurrentDirectoryURI: (OFURI *)URI;
/**
* @brief Copies a file, directory or symbolic link (if supported by the OS).
*
* The destination path must be a full path, which means it must include the
@@ -444,28 +490,36 @@
* if a directory is copied and an item already exists in the destination
* directory.
*
* @param source The file, directory or symbolic link to copy
* @param destination The destination path
+ * @throw OFCopyItemFailedException Copying failed
+ * @throw OFCreateDirectoryFailedException Creating a destination directory
+ * failed
*/
- (void)copyItemAtPath: (OFString *)source toPath: (OFString *)destination;
#endif
/**
* @brief Copies a file, directory or symbolic link (if supported by the OS).
*
- * The destination URL must have a full path, which means it must include the
+ * The destination URI must have a full path, which means it must include the
* name of the item.
*
* If an item already exists, the copy operation fails. This is also the case
* if a directory is copied and an item already exists in the destination
* directory.
*
* @param source The file, directory or symbolic link to copy
- * @param destination The destination URL
+ * @param destination The destination URI
+ * @throw OFCopyItemFailedException Copying failed
+ * @throw OFCreateDirectoryFailedException Creating a destination directory
+ * failed
+ * @throw OFUnsupportedProtocolException No handler is registered for either of
+ * the URI's scheme
*/
-- (void)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination;
+- (void)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination;
#ifdef OF_HAVE_FILES
/**
* @brief Moves an item.
*
@@ -476,48 +530,70 @@
* copied to the destination using @ref copyItemAtPath:toPath: and the source
* removed using @ref removeItemAtPath:.
*
* @param source The item to rename
* @param destination The new name for the item
+ * @throw OFMoveItemFailedException Moving failed
+ * @throw OFCopyItemFailedException Copying (to move between different devices)
+ * failed
+ * @throw OFRemoveItemFailedException Removing the source after copying to the
+ * destination (to move between different
+ * devices) failed
+ * @throw OFCreateDirectoryFailedException Creating a destination directory
+ * failed
*/
- (void)moveItemAtPath: (OFString *)source toPath: (OFString *)destination;
#endif
/**
* @brief Moves an item.
*
- * The destination URL must have a full path, which means it must include the
+ * The destination URI must have a full path, which means it must include the
* name of the item.
*
* If the destination is on a different logical device or uses a different
* scheme, the source will be copied to the destination using
- * @ref copyItemAtURL:toURL: and the source removed using @ref removeItemAtURL:.
+ * @ref copyItemAtURI:toURI: and the source removed using @ref removeItemAtURI:.
*
* @param source The item to rename
* @param destination The new name for the item
+ * @throw OFMoveItemFailedException Moving failed
+ * @throw OFCopyItemFailedException Copying (to move between different devices)
+ * failed
+ * @throw OFRemoveItemFailedException Removing the source after copying to the
+ * destination (to move between different
+ * devices) failed
+ * @throw OFCreateDirectoryFailedException Creating a destination directory
+ * failed
+ * @throw OFUnsupportedProtocolException No handler is registered for either of
+ * the URI's scheme
*/
-- (void)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination;
+- (void)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination;
#ifdef OF_HAVE_FILES
/**
* @brief Removes the item at the specified path.
*
* If the item at the specified path is a directory, it is removed recursively.
*
* @param path The path to the item which should be removed
+ * @throw OFRemoveItemFailedException Removing the item failed
*/
- (void)removeItemAtPath: (OFString *)path;
#endif
/**
- * @brief Removes the item at the specified URL.
+ * @brief Removes the item at the specified URI.
*
- * If the item at the specified URL is a directory, it is removed recursively.
+ * If the item at the specified URI is a directory, it is removed recursively.
*
- * @param URL The URL to the item which should be removed
+ * @param URI The URI to the item which should be removed
+ * @throw OFRemoveItemFailedException Removing the item failed
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (void)removeItemAtURL: (OFURL *)URL;
+- (void)removeItemAtURI: (OFURI *)URI;
#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS
/**
* @brief Creates a hard link for the specified item.
*
@@ -526,26 +602,34 @@
*
* This method is not available on some systems.
*
* @param source The path to the item for which a link should be created
* @param destination The path to the item which should link to the source
+ * @throw OFLinkItemFailedException Linking the item failed
+ * @throw OFNotImplementedException Hardlinks are not implemented for the
+ * specified URI
*/
- (void)linkItemAtPath: (OFString *)source toPath: (OFString *)destination;
#endif
/**
* @brief Creates a hard link for the specified item.
*
- * The destination URL must have a full path, which means it must include the
+ * The destination URI must have a full path, which means it must include the
* name of the item.
*
- * This method is not available for all URLs.
+ * This method is not available for all URIs.
*
- * @param source The URL to the item for which a link should be created
- * @param destination The URL to the item which should link to the source
+ * @param source The URI to the item for which a link should be created
+ * @param destination The URI to the item which should link to the source
+ * @throw OFLinkItemFailedException Linking the item failed
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
+ * @throw OFNotImplementedException Hardlinks are not implemented for the
+ * specified URI
*/
-- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination;
+- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination;
#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
/**
* @brief Creates a symbolic link for an item.
*
@@ -557,115 +641,120 @@
* @note On Windows, this requires at least Windows Vista and administrator
* privileges!
*
* @param path The path to the item which should symbolically link to the target
* @param target The target of the symbolic link
+ * @throw OFCreateSymbolicLinkFailedException Creating the symbolic link failed
+ * @throw OFNotImplementedException Symbolic links are not implemented for the
+ * specified URI
*/
- (void)createSymbolicLinkAtPath: (OFString *)path
withDestinationPath: (OFString *)target;
#endif
/**
* @brief Creates a symbolic link for an item.
*
- * The destination uRL must have a full path, which means it must include the
+ * The destination URI must have a full path, which means it must include the
* name of the item.
*
- * This method is not available for all URLs.
+ * This method is not available for all URIs.
*
* @note On Windows, this requires at least Windows Vista and administrator
* privileges!
*
- * @param URL The URL to the item which should symbolically link to the target
+ * @param URI The URI to the item which should symbolically link to the target
* @param target The target of the symbolic link
+ * @throw OFUnsupportedProtocolException No handler is registered for the URI's
+ * scheme
*/
-- (void)createSymbolicLinkAtURL: (OFURL *)URL
+- (void)createSymbolicLinkAtURI: (OFURI *)URI
withDestinationPath: (OFString *)target;
@end
@interface OFDictionary (FileAttributes)
/**
* @brief The @ref OFFileSize key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) unsigned long long fileSize;
/**
* @brief The @ref OFFileType key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFFileAttributeType fileType;
/**
* @brief The @ref OFFilePOSIXPermissions key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) unsigned long filePOSIXPermissions;
/**
* @brief The @ref OFFileOwnerAccountID key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) unsigned long fileOwnerAccountID;
/**
* @brief The @ref OFFileGroupOwnerAccountID key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) unsigned long fileGroupOwnerAccountID;
/**
* @brief The @ref OFFileOwnerAccountName key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFString *fileOwnerAccountName;
/**
* @brief The @ref OFFileGroupOwnerAccountName key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFString *fileGroupOwnerAccountName;
/**
* @brief The @ref OFFileLastAccessDate key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFDate *fileLastAccessDate;
/**
* @brief The @ref OFFileModificationDate key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFDate *fileModificationDate;
/**
* @brief The @ref OFFileStatusChangeDate key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFDate *fileStatusChangeDate;
/**
* @brief The @ref OFFileCreationDate key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFDate *fileCreationDate;
/**
* @brief The @ref OFFileSymbolicLinkDestination key from the dictionary.
*
- * Raises an @ref OFUndefinedKeyException if the key is missing.
+ * @throw OFUndefinedKeyException The key is missing
*/
@property (readonly, nonatomic) OFString *fileSymbolicLinkDestination;
@end
OF_ASSUME_NONNULL_END
Index: src/OFFileManager.m
==================================================================
--- src/OFFileManager.m
+++ src/OFFileManager.m
@@ -19,11 +19,11 @@
#include
#include
#include "unistd_wrapper.h"
-#import "platform.h"
+#include "platform.h"
#ifdef OF_PSP
# include
#endif
@@ -37,25 +37,25 @@
#import "OFLocale.h"
#import "OFNumber.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFSystemInfo.h"
-#import "OFURL.h"
-#import "OFURLHandler.h"
+#import "OFURI.h"
+#import "OFURIHandler.h"
-#import "OFChangeCurrentDirectoryPathFailedException.h"
+#import "OFChangeCurrentDirectoryFailedException.h"
#import "OFCopyItemFailedException.h"
#import "OFCreateDirectoryFailedException.h"
-#import "OFGetCurrentDirectoryPathFailedException.h"
+#import "OFGetCurrentDirectoryFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFMoveItemFailedException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFRemoveItemFailedException.h"
-#import "OFRetrieveItemAttributesFailedException.h"
+#import "OFGetItemAttributesFailedException.h"
#import "OFUndefinedKeyException.h"
#import "OFUnsupportedProtocolException.h"
#ifdef OF_WINDOWS
# include
@@ -169,11 +169,11 @@
encoding: [OFLocale encoding]];
# else
char buffer[PATH_MAX];
if ((getcwd(buffer, PATH_MAX)) == NULL)
- @throw [OFGetCurrentDirectoryPathFailedException
+ @throw [OFGetCurrentDirectoryFailedException
exceptionWithErrNo: errno];
# ifdef OF_DJGPP
/*
* For some reason, getcwd() returns forward slashes on DJGPP, even
@@ -187,158 +187,157 @@
return [OFString stringWithCString: buffer
encoding: [OFLocale encoding]];
# endif
}
-- (OFURL *)currentDirectoryURL
+- (OFURI *)currentDirectoryURI
{
void *pool = objc_autoreleasePoolPush();
- OFURL *ret;
+ OFURI *ret;
- ret = [OFURL fileURLWithPath: self.currentDirectoryPath];
+ ret = [OFURI fileURIWithPath: self.currentDirectoryPath];
[ret retain];
objc_autoreleasePoolPop(pool);
return [ret autorelease];
}
#endif
-- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL
+- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI
{
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- return [URLHandler attributesOfItemAtURL: URL];
+ return [URIHandler attributesOfItemAtURI: URI];
}
#ifdef OF_HAVE_FILES
- (OFFileAttributes)attributesOfItemAtPath: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
OFFileAttributes ret;
- ret = [self attributesOfItemAtURL: [OFURL fileURLWithPath: path]];
+ ret = [self attributesOfItemAtURI: [OFURI fileURIWithPath: path]];
[ret retain];
objc_autoreleasePoolPop(pool);
return [ret autorelease];
}
#endif
-- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL
+- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI
{
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- [URLHandler setAttributes: attributes ofItemAtURL: URL];
+ [URIHandler setAttributes: attributes ofItemAtURI: URI];
}
#ifdef OF_HAVE_FILES
- (void)setAttributes: (OFFileAttributes)attributes
ofItemAtPath: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
[self setAttributes: attributes
- ofItemAtURL: [OFURL fileURLWithPath: path]];
+ ofItemAtURI: [OFURI fileURIWithPath: path]];
objc_autoreleasePoolPop(pool);
}
#endif
-- (bool)fileExistsAtURL: (OFURL *)URL
+- (bool)fileExistsAtURI: (OFURI *)URI
{
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- return [URLHandler fileExistsAtURL: URL];
+ return [URIHandler fileExistsAtURI: URI];
}
#ifdef OF_HAVE_FILES
- (bool)fileExistsAtPath: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
bool ret;
- ret = [self fileExistsAtURL: [OFURL fileURLWithPath: path]];
+ ret = [self fileExistsAtURI: [OFURI fileURIWithPath: path]];
objc_autoreleasePoolPop(pool);
return ret;
}
#endif
-- (bool)directoryExistsAtURL: (OFURL *)URL
+- (bool)directoryExistsAtURI: (OFURI *)URI
{
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- return [URLHandler directoryExistsAtURL: URL];
+ return [URIHandler directoryExistsAtURI: URI];
}
#ifdef OF_HAVE_FILES
- (bool)directoryExistsAtPath: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
bool ret;
- ret = [self directoryExistsAtURL: [OFURL fileURLWithPath: path]];
+ ret = [self directoryExistsAtURI: [OFURI fileURIWithPath: path]];
objc_autoreleasePoolPop(pool);
return ret;
}
#endif
-- (void)createDirectoryAtURL: (OFURL *)URL
+- (void)createDirectoryAtURI: (OFURI *)URI
{
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- [URLHandler createDirectoryAtURL: URL];
+ [URIHandler createDirectoryAtURI: URI];
}
-- (void)createDirectoryAtURL: (OFURL *)URL
- createParents: (bool)createParents
+- (void)createDirectoryAtURI: (OFURI *)URI createParents: (bool)createParents
{
void *pool = objc_autoreleasePoolPush();
- OFMutableURL *mutableURL;
+ OFMutableURI *mutableURI;
OFArray OF_GENERIC(OFString *) *components;
- OFMutableArray OF_GENERIC(OFURL *) *componentURLs;
- size_t componentURLsCount;
+ OFMutableArray OF_GENERIC(OFURI *) *componentURIs;
+ size_t componentURIsCount;
ssize_t i;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
if (!createParents) {
- [self createDirectoryAtURL: URL];
+ [self createDirectoryAtURI: URI];
return;
}
/*
* Try blindly creating the directory first.
@@ -345,15 +344,15 @@
*
* The reason for this is that we might be sandboxed, so attempting to
* create any of the parent directories will fail, while creating the
* directory itself will work.
*/
- if ([self directoryExistsAtURL: URL])
+ if ([self directoryExistsAtURI: URI])
return;
@try {
- [self createDirectoryAtURL: URL];
+ [self createDirectoryAtURI: URI];
return;
} @catch (OFCreateDirectoryFailedException *e) {
/*
* If we didn't fail because any of the parents is missing,
* there is no point in trying to create the parents.
@@ -361,98 +360,98 @@
if (e.errNo != ENOENT)
@throw e;
}
/*
- * Because we might be sandboxed (and for remote URLs don't even know
- * anything at all), we generate the URL for every component. We then
+ * Because we might be sandboxed (and for remote URIs don't even know
+ * anything at all), we generate the URI for every component. We then
* iterate them in reverse order until we find the first existing
* directory, and then create subdirectories from there.
*/
- mutableURL = [[URL mutableCopy] autorelease];
- mutableURL.URLEncodedPath = @"/";
- components = URL.pathComponents;
- componentURLs = [OFMutableArray arrayWithCapacity: components.count];
+ mutableURI = [[URI mutableCopy] autorelease];
+ mutableURI.percentEncodedPath = @"/";
+ components = URI.pathComponents;
+ componentURIs = [OFMutableArray arrayWithCapacity: components.count];
for (OFString *component in components) {
- [mutableURL appendPathComponent: component];
+ [mutableURI appendPathComponent: component];
- if (![mutableURL.URLEncodedPath isEqual: @"/"])
- [componentURLs addObject:
- [[mutableURL copy] autorelease]];
+ if (![mutableURI.percentEncodedPath isEqual: @"/"])
+ [componentURIs addObject:
+ [[mutableURI copy] autorelease]];
}
- componentURLsCount = componentURLs.count;
- for (i = componentURLsCount - 1; i > 0; i--) {
- if ([self directoryExistsAtURL:
- [componentURLs objectAtIndex: i]])
+ componentURIsCount = componentURIs.count;
+ for (i = componentURIsCount - 1; i > 0; i--) {
+ if ([self directoryExistsAtURI:
+ [componentURIs objectAtIndex: i]])
break;
}
- if (++i == (ssize_t)componentURLsCount) {
+ if (++i == (ssize_t)componentURIsCount) {
/*
- * The URL exists, even though before we made sure it did not.
+ * The URI exists, even though before we made sure it did not.
* That means it was created in the meantime by something else,
* so we're done here.
*/
objc_autoreleasePoolPop(pool);
return;
}
- for (; i < (ssize_t)componentURLsCount; i++)
- [self createDirectoryAtURL: [componentURLs objectAtIndex: i]];
+ for (; i < (ssize_t)componentURIsCount; i++)
+ [self createDirectoryAtURI: [componentURIs objectAtIndex: i]];
objc_autoreleasePoolPop(pool);
}
#ifdef OF_HAVE_FILES
- (void)createDirectoryAtPath: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
- [self createDirectoryAtURL: [OFURL fileURLWithPath: path]];
+ [self createDirectoryAtURI: [OFURI fileURIWithPath: path]];
objc_autoreleasePoolPop(pool);
}
- (void)createDirectoryAtPath: (OFString *)path
createParents: (bool)createParents
{
void *pool = objc_autoreleasePoolPush();
- [self createDirectoryAtURL: [OFURL fileURLWithPath: path]
+ [self createDirectoryAtURI: [OFURI fileURIWithPath: path]
createParents: createParents];
objc_autoreleasePoolPop(pool);
}
#endif
-- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL
+- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI
{
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- return [URLHandler contentsOfDirectoryAtURL: URL];
+ return [URIHandler contentsOfDirectoryAtURI: URI];
}
#ifdef OF_HAVE_FILES
- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
- OFArray OF_GENERIC(OFURL *) *URLs;
+ OFArray OF_GENERIC(OFURI *) *URIs;
OFMutableArray OF_GENERIC(OFString *) *ret;
- URLs = [self contentsOfDirectoryAtURL: [OFURL fileURLWithPath: path]];
- ret = [OFMutableArray arrayWithCapacity: URLs.count];
+ URIs = [self contentsOfDirectoryAtURI: [OFURI fileURIWithPath: path]];
+ ret = [OFMutableArray arrayWithCapacity: URIs.count];
- for (OFURL *URL in URLs)
- [ret addObject: URL.lastPathComponent];
+ for (OFURI *URI in URIs)
+ [ret addObject: URI.lastPathComponent];
[ret makeImmutable];
[ret retain];
objc_autoreleasePoolPop(pool);
@@ -513,11 +512,11 @@
default:
errNo = 0;
break;
}
- @throw [OFChangeCurrentDirectoryPathFailedException
+ @throw [OFChangeCurrentDirectoryFailedException
exceptionWithPath: path
errNo: errNo];
}
oldLock = CurrentDir(lock);
@@ -538,77 +537,77 @@
# endif
status = chdir(
[path cStringWithEncoding: [OFLocale encoding]]);
if (status != 0)
- @throw [OFChangeCurrentDirectoryPathFailedException
+ @throw [OFChangeCurrentDirectoryFailedException
exceptionWithPath: path
errNo: errno];
# endif
}
-- (void)changeCurrentDirectoryURL: (OFURL *)URL
+- (void)changeCurrentDirectoryURI: (OFURI *)URI
{
void *pool = objc_autoreleasePoolPush();
- [self changeCurrentDirectoryPath: URL.fileSystemRepresentation];
+ [self changeCurrentDirectoryPath: URI.fileSystemRepresentation];
objc_autoreleasePoolPop(pool);
}
- (void)copyItemAtPath: (OFString *)source toPath: (OFString *)destination
{
void *pool = objc_autoreleasePoolPush();
- [self copyItemAtURL: [OFURL fileURLWithPath: source]
- toURL: [OFURL fileURLWithPath: destination]];
+ [self copyItemAtURI: [OFURI fileURIWithPath: source]
+ toURI: [OFURI fileURIWithPath: destination]];
objc_autoreleasePoolPop(pool);
}
#endif
-- (void)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination
+- (void)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination
{
void *pool;
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
OFFileAttributes attributes;
OFFileAttributeType type;
if (source == nil || destination == nil)
@throw [OFInvalidArgumentException exception];
pool = objc_autoreleasePoolPush();
- if ((URLHandler = [OFURLHandler handlerForURL: source]) == nil)
+ if ((URIHandler = [OFURIHandler handlerForURI: source]) == nil)
@throw [OFUnsupportedProtocolException
- exceptionWithURL: source];
+ exceptionWithURI: source];
- if ([URLHandler copyItemAtURL: source toURL: destination])
+ if ([URIHandler copyItemAtURI: source toURI: destination])
return;
- if ([self fileExistsAtURL: destination])
+ if ([self fileExistsAtURI: destination])
@throw [OFCopyItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: EEXIST];
@try {
- attributes = [self attributesOfItemAtURL: source];
- } @catch (OFRetrieveItemAttributesFailedException *e) {
+ attributes = [self attributesOfItemAtURI: source];
+ } @catch (OFGetItemAttributesFailedException *e) {
@throw [OFCopyItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: e.errNo];
}
type = attributes.fileType;
if ([type isEqual: OFFileTypeDirectory]) {
- OFArray OF_GENERIC(OFURL *) *contents;
+ OFArray OF_GENERIC(OFURI *) *contents;
@try {
- [self createDirectoryAtURL: destination];
+ [self createDirectoryAtURI: destination];
@try {
OFFileAttributeKey key = OFFilePOSIXPermissions;
OFNumber *permissions =
[attributes objectForKey: key];
@@ -618,39 +617,39 @@
destinationAttributes = [OFDictionary
dictionaryWithObject: permissions
forKey: key];
[self
setAttributes: destinationAttributes
- ofItemAtURL: destination];
+ ofItemAtURI: destination];
}
} @catch (OFNotImplementedException *e) {
}
- contents = [self contentsOfDirectoryAtURL: source];
+ contents = [self contentsOfDirectoryAtURI: source];
} @catch (id e) {
/*
* Only convert exceptions to OFCopyItemFailedException
* that have an errNo property. This covers all I/O
* related exceptions from the operations used to copy
* an item, all others should be left as is.
*/
if ([e respondsToSelector: @selector(errNo)])
@throw [OFCopyItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: [e errNo]];
@throw e;
}
- for (OFURL *item in contents) {
+ for (OFURI *item in contents) {
void *pool2 = objc_autoreleasePoolPush();
- OFURL *destinationURL = [destination
- URLByAppendingPathComponent:
+ OFURI *destinationURI = [destination
+ URIByAppendingPathComponent:
item.lastPathComponent];
- [self copyItemAtURL: item toURL: destinationURL];
+ [self copyItemAtURI: item toURI: destinationURI];
objc_autoreleasePoolPop(pool2);
}
} else if ([type isEqual: OFFileTypeRegular]) {
size_t pageSize = [OFSystemInfo pageSize];
@@ -658,16 +657,15 @@
OFStream *destinationStream = nil;
char *buffer;
buffer = OFAllocMemory(1, pageSize);
@try {
- sourceStream = [[OFURLHandler handlerForURL: source]
- openItemAtURL: source
- mode: @"r"];
- destinationStream = [[OFURLHandler handlerForURL:
- destination] openItemAtURL: destination
- mode: @"w"];
+ sourceStream = [OFURIHandler openItemAtURI: source
+ mode: @"r"];
+ destinationStream = [OFURIHandler
+ openItemAtURI: destination
+ mode: @"w"];
while (!sourceStream.atEndOfStream) {
size_t length;
length = [sourceStream
@@ -687,11 +685,11 @@
destinationAttributes = [OFDictionary
dictionaryWithObject: permissions
forKey: key];
[self
setAttributes: destinationAttributes
- ofItemAtURL: destination];
+ ofItemAtURI: destination];
}
} @catch (OFNotImplementedException *e) {
}
} @catch (id e) {
/*
@@ -700,12 +698,12 @@
* related exceptions from the operations used to copy
* an item, all others should be left as is.
*/
if ([e respondsToSelector: @selector(errNo)])
@throw [OFCopyItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: [e errNo]];
@throw e;
} @finally {
[sourceStream close];
@@ -715,11 +713,11 @@
} else if ([type isEqual: OFFileTypeSymbolicLink]) {
@try {
OFString *linkDestination =
attributes.fileSymbolicLinkDestination;
- [self createSymbolicLinkAtURL: destination
+ [self createSymbolicLinkAtURI: destination
withDestinationPath: linkDestination];
} @catch (id e) {
/*
* Only convert exceptions to OFCopyItemFailedException
* that have an errNo property. This covers all I/O
@@ -726,165 +724,165 @@
* related exceptions from the operations used to copy
* an item, all others should be left as is.
*/
if ([e respondsToSelector: @selector(errNo)])
@throw [OFCopyItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: [e errNo]];
@throw e;
}
} else
@throw [OFCopyItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: EINVAL];
objc_autoreleasePoolPop(pool);
}
#ifdef OF_HAVE_FILES
- (void)moveItemAtPath: (OFString *)source toPath: (OFString *)destination
{
void *pool = objc_autoreleasePoolPush();
- [self moveItemAtURL: [OFURL fileURLWithPath: source]
- toURL: [OFURL fileURLWithPath: destination]];
+ [self moveItemAtURI: [OFURI fileURIWithPath: source]
+ toURI: [OFURI fileURIWithPath: destination]];
objc_autoreleasePoolPop(pool);
}
#endif
-- (void)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination
+- (void)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination
{
void *pool;
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
if (source == nil || destination == nil)
@throw [OFInvalidArgumentException exception];
pool = objc_autoreleasePoolPush();
- if ((URLHandler = [OFURLHandler handlerForURL: source]) == nil)
+ if ((URIHandler = [OFURIHandler handlerForURI: source]) == nil)
@throw [OFUnsupportedProtocolException
- exceptionWithURL: source];
+ exceptionWithURI: source];
@try {
- if ([URLHandler moveItemAtURL: source toURL: destination])
+ if ([URIHandler moveItemAtURI: source toURI: destination])
return;
} @catch (OFMoveItemFailedException *e) {
if (e.errNo != EXDEV)
@throw e;
}
- if ([self fileExistsAtURL: destination])
+ if ([self fileExistsAtURI: destination])
@throw [OFMoveItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: EEXIST];
@try {
- [self copyItemAtURL: source toURL: destination];
+ [self copyItemAtURI: source toURI: destination];
} @catch (OFCopyItemFailedException *e) {
- [self removeItemAtURL: destination];
+ [self removeItemAtURI: destination];
@throw [OFMoveItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: e.errNo];
}
@try {
- [self removeItemAtURL: source];
+ [self removeItemAtURI: source];
} @catch (OFRemoveItemFailedException *e) {
@throw [OFMoveItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
+ exceptionWithSourceURI: source
+ destinationURI: destination
errNo: e.errNo];
}
objc_autoreleasePoolPop(pool);
}
-- (void)removeItemAtURL: (OFURL *)URL
+- (void)removeItemAtURI: (OFURI *)URI
{
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil)
+ if (URI == nil)
@throw [OFInvalidArgumentException exception];
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- [URLHandler removeItemAtURL: URL];
+ [URIHandler removeItemAtURI: URI];
}
#ifdef OF_HAVE_FILES
- (void)removeItemAtPath: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
- [self removeItemAtURL: [OFURL fileURLWithPath: path]];
+ [self removeItemAtURI: [OFURI fileURIWithPath: path]];
objc_autoreleasePoolPop(pool);
}
#endif
-- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination
+- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination
{
void *pool = objc_autoreleasePoolPush();
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
if (source == nil || destination == nil)
@throw [OFInvalidArgumentException exception];
if (![destination.scheme isEqual: source.scheme])
@throw [OFInvalidArgumentException exception];
- URLHandler = [OFURLHandler handlerForURL: source];
+ URIHandler = [OFURIHandler handlerForURI: source];
- if (URLHandler == nil)
+ if (URIHandler == nil)
@throw [OFUnsupportedProtocolException
- exceptionWithURL: source];
+ exceptionWithURI: source];
- [URLHandler linkItemAtURL: source toURL: destination];
+ [URIHandler linkItemAtURI: source toURI: destination];
objc_autoreleasePoolPop(pool);
}
#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS
- (void)linkItemAtPath: (OFString *)source toPath: (OFString *)destination
{
void *pool = objc_autoreleasePoolPush();
- [self linkItemAtURL: [OFURL fileURLWithPath: source]
- toURL: [OFURL fileURLWithPath: destination]];
+ [self linkItemAtURI: [OFURI fileURIWithPath: source]
+ toURI: [OFURI fileURIWithPath: destination]];
objc_autoreleasePoolPop(pool);
}
#endif
-- (void)createSymbolicLinkAtURL: (OFURL *)URL
+- (void)createSymbolicLinkAtURI: (OFURI *)URI
withDestinationPath: (OFString *)target
{
void *pool = objc_autoreleasePoolPush();
- OFURLHandler *URLHandler;
+ OFURIHandler *URIHandler;
- if (URL == nil || target == nil)
+ if (URI == nil || target == nil)
@throw [OFInvalidArgumentException exception];
- URLHandler = [OFURLHandler handlerForURL: URL];
+ URIHandler = [OFURIHandler handlerForURI: URI];
- if (URLHandler == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ if (URIHandler == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
- [URLHandler createSymbolicLinkAtURL: URL withDestinationPath: target];
+ [URIHandler createSymbolicLinkAtURI: URI withDestinationPath: target];
objc_autoreleasePoolPop(pool);
}
#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
- (void)createSymbolicLinkAtPath: (OFString *)path
withDestinationPath: (OFString *)target
{
void *pool = objc_autoreleasePoolPush();
- [self createSymbolicLinkAtURL: [OFURL fileURLWithPath: path]
+ [self createSymbolicLinkAtURI: [OFURI fileURIWithPath: path]
withDestinationPath: target];
objc_autoreleasePoolPop(pool);
}
#endif
@end
ADDED src/OFFileURIHandler.h
Index: src/OFFileURIHandler.h
==================================================================
--- src/OFFileURIHandler.h
+++ src/OFFileURIHandler.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFURIHandler.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@interface OFFileURIHandler: OFURIHandler
++ (bool)of_directoryExistsAtPath: (OFString *)path OF_DIRECT;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFFileURIHandler.m
Index: src/OFFileURIHandler.m
==================================================================
--- src/OFFileURIHandler.m
+++ src/OFFileURIHandler.m
@@ -0,0 +1,1476 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#define _LARGEFILE64_SOURCE
+
+#include
+#include
+
+#ifdef HAVE_DIRENT_H
+# include
+#endif
+#include "unistd_wrapper.h"
+
+#include "platform.h"
+#ifdef HAVE_SYS_STAT_H
+# include
+#endif
+#include
+#ifdef OF_WINDOWS
+# include
+#endif
+
+#ifdef HAVE_PWD_H
+# include
+#endif
+#ifdef HAVE_GRP_H
+# include
+#endif
+
+#import "OFFileURIHandler.h"
+#import "OFArray.h"
+#import "OFDate.h"
+#import "OFFile.h"
+#import "OFFileManager.h"
+#import "OFLocale.h"
+#import "OFNumber.h"
+#import "OFSystemInfo.h"
+#import "OFURI.h"
+
+#ifdef OF_HAVE_THREADS
+# import "OFMutex.h"
+#endif
+
+#import "OFCreateDirectoryFailedException.h"
+#import "OFCreateSymbolicLinkFailedException.h"
+#import "OFGetItemAttributesFailedException.h"
+#import "OFInitializationFailedException.h"
+#import "OFInvalidArgumentException.h"
+#import "OFLinkItemFailedException.h"
+#import "OFMoveItemFailedException.h"
+#import "OFNotImplementedException.h"
+#import "OFOpenItemFailedException.h"
+#import "OFOutOfRangeException.h"
+#import "OFReadFailedException.h"
+#import "OFRemoveItemFailedException.h"
+#import "OFSetItemAttributesFailedException.h"
+
+#ifdef OF_WINDOWS
+# include
+# include
+# include
+# include
+#endif
+
+#ifdef OF_AMIGAOS
+# include
+# include
+# include
+# ifdef OF_AMIGAOS4
+# define DeleteFile(path) Delete(path)
+# endif
+#endif
+
+#if defined(OF_WINDOWS) || defined(OF_AMIGAOS)
+typedef struct {
+ OFStreamOffset st_size;
+ unsigned int st_mode;
+ OFTimeInterval st_atime, st_mtime, st_ctime;
+# ifdef OF_WINDOWS
+# define HAVE_STRUCT_STAT_ST_BIRTHTIME
+ OFTimeInterval st_birthtime;
+ DWORD fileAttributes;
+# endif
+} Stat;
+#elif defined(HAVE_STAT64)
+typedef struct stat64 Stat;
+#else
+typedef struct stat Stat;
+#endif
+
+#ifdef OF_WINDOWS
+# define S_IFLNK 0x10000
+# define S_ISLNK(mode) (mode & S_IFLNK)
+#endif
+
+#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
+static OFMutex *passwdMutex;
+
+static void
+releasePasswdMutex(void)
+{
+ [passwdMutex release];
+}
+#endif
+#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS)
+static OFMutex *readdirMutex;
+
+static void
+releaseReaddirMutex(void)
+{
+ [readdirMutex release];
+}
+#endif
+
+#ifdef OF_WINDOWS
+static int (*_wutime64FuncPtr)(const wchar_t *, struct __utimbuf64 *);
+static WINAPI BOOLEAN (*createSymbolicLinkWFuncPtr)(LPCWSTR, LPCWSTR, DWORD);
+static WINAPI BOOLEAN (*createHardLinkWFuncPtr)(LPCWSTR, LPCWSTR,
+ LPSECURITY_ATTRIBUTES);
+#endif
+
+#ifdef OF_WINDOWS
+static OFTimeInterval
+filetimeToTimeInterval(const FILETIME *filetime)
+{
+ return (double)((int64_t)filetime->dwHighDateTime << 32 |
+ filetime->dwLowDateTime) / 10000000.0 - 11644473600.0;
+}
+
+static int
+lastError(void)
+{
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_NO_MORE_FILES:
+ return ENOENT;
+ case ERROR_ACCESS_DENIED:
+ return EACCES;
+ case ERROR_DIRECTORY:
+ return ENOTDIR;
+ case ERROR_NOT_READY:
+ return EBUSY;
+ default:
+ return EIO;
+ }
+}
+#endif
+
+#ifdef OF_AMIGAOS
+static int
+lastError(void)
+{
+ switch (IoErr()) {
+ case ERROR_DELETE_PROTECTED:
+ case ERROR_READ_PROTECTED:
+ case ERROR_WRITE_PROTECTED:
+ return EACCES;
+ case ERROR_DISK_NOT_VALIDATED:
+ case ERROR_OBJECT_IN_USE:
+ return EBUSY;
+ case ERROR_OBJECT_EXISTS:
+ return EEXIST;
+ case ERROR_DIR_NOT_FOUND:
+ case ERROR_NO_MORE_ENTRIES:
+ case ERROR_OBJECT_NOT_FOUND:
+ return ENOENT;
+ case ERROR_NO_FREE_STORE:
+ return ENOMEM;
+ case ERROR_DISK_FULL:
+ return ENOSPC;
+ case ERROR_DIRECTORY_NOT_EMPTY:
+ return ENOTEMPTY;
+ case ERROR_DISK_WRITE_PROTECTED:
+ return EROFS;
+ case ERROR_RENAME_ACROSS_DEVICES:
+ return EXDEV;
+ default:
+ return EIO;
+ }
+}
+#endif
+
+static int
+statWrapper(OFString *path, Stat *buffer)
+{
+#if defined(OF_WINDOWS)
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ bool success;
+
+ if ([OFSystemInfo isWindowsNT])
+ success = GetFileAttributesExW(path.UTF16String,
+ GetFileExInfoStandard, &data);
+ else
+ success = GetFileAttributesExA(
+ [path cStringWithEncoding: [OFLocale encoding]],
+ GetFileExInfoStandard, &data);
+
+ if (!success)
+ return lastError();
+
+ buffer->st_size = (uint64_t)data.nFileSizeHigh << 32 |
+ data.nFileSizeLow;
+
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ buffer->st_mode = S_IFDIR;
+ else if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ /*
+ * No need to use A functions in this branch: This is only
+ * available on NTFS (and hence Windows NT) anyway.
+ */
+ WIN32_FIND_DATAW findData;
+ HANDLE findHandle;
+
+ if ((findHandle = FindFirstFileW(path.UTF16String,
+ &findData)) == INVALID_HANDLE_VALUE)
+ return lastError();
+
+ @try {
+ if (!(findData.dwFileAttributes &
+ FILE_ATTRIBUTE_REPARSE_POINT))
+ /* Race? Indicate to try again. */
+ return EAGAIN;
+
+ buffer->st_mode =
+ (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK
+ ? S_IFLNK : S_IFREG);
+ } @finally {
+ FindClose(findHandle);
+ }
+ } else
+ buffer->st_mode = S_IFREG;
+
+ buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY
+ ? (S_IRUSR | S_IXUSR) : (S_IRUSR | S_IWUSR | S_IXUSR));
+
+ buffer->st_atime = filetimeToTimeInterval(&data.ftLastAccessTime);
+ buffer->st_mtime = filetimeToTimeInterval(&data.ftLastWriteTime);
+ buffer->st_ctime = buffer->st_birthtime =
+ filetimeToTimeInterval(&data.ftCreationTime);
+ buffer->fileAttributes = data.dwFileAttributes;
+
+ return 0;
+#elif defined(OF_AMIGAOS)
+ BPTR lock;
+# ifdef OF_AMIGAOS4
+ struct ExamineData *ed;
+# else
+ struct FileInfoBlock fib;
+# endif
+ OFTimeInterval timeInterval;
+ struct Locale *locale;
+ struct DateStamp *date;
+
+ if ((lock = Lock([path cStringWithEncoding: [OFLocale encoding]],
+ SHARED_LOCK)) == 0)
+ return lastError();
+
+# if defined(OF_MORPHOS)
+ if (!Examine64(lock, &fib, TAG_DONE)) {
+# elif defined(OF_AMIGAOS4)
+ if ((ed = ExamineObjectTags(EX_FileLockInput, lock, TAG_END)) == NULL) {
+# else
+ if (!Examine(lock, &fib)) {
+# endif
+ int error = lastError();
+ UnLock(lock);
+ return error;
+ }
+
+ UnLock(lock);
+
+# if defined(OF_MORPHOS)
+ buffer->st_size = fib.fib_Size64;
+# elif defined(OF_AMIGAOS4)
+ buffer->st_size = ed->FileSize;
+# else
+ buffer->st_size = fib.fib_Size;
+# endif
+# ifdef OF_AMIGAOS4
+ buffer->st_mode = (EXD_IS_DIRECTORY(ed) ? S_IFDIR : S_IFREG);
+# else
+ buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG);
+# endif
+
+ timeInterval = 252460800; /* 1978-01-01 */
+
+ locale = OpenLocale(NULL);
+ /*
+ * FIXME: This does not take DST into account. But unfortunately, there
+ * is no way to figure out if DST was in effect when the file was
+ * modified.
+ */
+ timeInterval += locale->loc_GMTOffset * 60.0;
+ CloseLocale(locale);
+
+# ifdef OF_AMIGAOS4
+ date = &ed->Date;
+# else
+ date = &fib.fib_Date;
+# endif
+ timeInterval += date->ds_Days * 86400.0;
+ timeInterval += date->ds_Minute * 60.0;
+ timeInterval += date->ds_Tick / (OFTimeInterval)TICKS_PER_SECOND;
+
+ buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval;
+
+# ifdef OF_AMIGAOS4
+ FreeDosObject(DOS_EXAMINEDATA, ed);
+# endif
+
+ return 0;
+#elif defined(HAVE_STAT64)
+ if (stat64([path cStringWithEncoding: [OFLocale encoding]],
+ buffer) != 0)
+ return errno;
+
+ return 0;
+#else
+ if (stat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0)
+ return errno;
+
+ return 0;
+#endif
+}
+
+static int
+lstatWrapper(OFString *path, Stat *buffer)
+{
+#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS) && \
+ !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
+# ifdef HAVE_LSTAT64
+ if (lstat64([path cStringWithEncoding: [OFLocale encoding]],
+ buffer) != 0)
+ return errno;
+# else
+ if (lstat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0)
+ return errno;
+# endif
+
+ return 0;
+#else
+ return statWrapper(path, buffer);
+#endif
+}
+
+static void
+setTypeAttribute(OFMutableFileAttributes attributes, Stat *s)
+{
+ if (S_ISREG(s->st_mode))
+ [attributes setObject: OFFileTypeRegular forKey: OFFileType];
+ else if (S_ISDIR(s->st_mode))
+ [attributes setObject: OFFileTypeDirectory forKey: OFFileType];
+#ifdef S_ISLNK
+ else if (S_ISLNK(s->st_mode))
+ [attributes setObject: OFFileTypeSymbolicLink
+ forKey: OFFileType];
+#endif
+#ifdef S_ISFIFO
+ else if (S_ISFIFO(s->st_mode))
+ [attributes setObject: OFFileTypeFIFO forKey: OFFileType];
+#endif
+#ifdef S_ISCHR
+ else if (S_ISCHR(s->st_mode))
+ [attributes setObject: OFFileTypeCharacterSpecial
+ forKey: OFFileType];
+#endif
+#ifdef S_ISBLK
+ else if (S_ISBLK(s->st_mode))
+ [attributes setObject: OFFileTypeBlockSpecial
+ forKey: OFFileType];
+#endif
+#ifdef S_ISSOCK
+ else if (S_ISSOCK(s->st_mode))
+ [attributes setObject: OFFileTypeSocket forKey: OFFileType];
+#endif
+ else
+ [attributes setObject: OFFileTypeUnknown forKey: OFFileType];
+}
+
+static void
+setDateAttributes(OFMutableFileAttributes attributes, Stat *s)
+{
+ /* FIXME: We could be more precise on some OSes */
+ [attributes
+ setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime]
+ forKey: OFFileLastAccessDate];
+ [attributes
+ setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime]
+ forKey: OFFileModificationDate];
+ [attributes
+ setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime]
+ forKey: OFFileStatusChangeDate];
+#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
+ [attributes
+ setObject: [OFDate dateWithTimeIntervalSince1970: s->st_birthtime]
+ forKey: OFFileCreationDate];
+#endif
+}
+
+static void
+setOwnerAndGroupAttributes(OFMutableFileAttributes attributes, Stat *s)
+{
+#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
+ [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_uid]
+ forKey: OFFileOwnerAccountID];
+ [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_gid]
+ forKey: OFFileGroupOwnerAccountID];
+
+# ifdef OF_HAVE_THREADS
+ [passwdMutex lock];
+ @try {
+# endif
+ OFStringEncoding encoding = [OFLocale encoding];
+ struct passwd *passwd = getpwuid(s->st_uid);
+ struct group *group_ = getgrgid(s->st_gid);
+
+ if (passwd != NULL) {
+ OFString *owner = [OFString
+ stringWithCString: passwd->pw_name
+ encoding: encoding];
+
+ [attributes setObject: owner
+ forKey: OFFileOwnerAccountName];
+ }
+
+ if (group_ != NULL) {
+ OFString *group = [OFString
+ stringWithCString: group_->gr_name
+ encoding: encoding];
+
+ [attributes setObject: group
+ forKey: OFFileGroupOwnerAccountName];
+ }
+# ifdef OF_HAVE_THREADS
+ } @finally {
+ [passwdMutex unlock];
+ }
+# endif
+#endif
+}
+
+#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
+static void
+setSymbolicLinkDestinationAttribute(OFMutableFileAttributes attributes,
+ OFURI *URI)
+{
+ OFString *path = URI.fileSystemRepresentation;
+# ifndef OF_WINDOWS
+ OFStringEncoding encoding = [OFLocale encoding];
+ char destinationC[PATH_MAX];
+ ssize_t length;
+ OFString *destination;
+
+ length = readlink([path cStringWithEncoding: encoding], destinationC,
+ PATH_MAX);
+
+ if (length < 0)
+ @throw [OFGetItemAttributesFailedException
+ exceptionWithURI: URI
+ errNo: errno];
+
+ destination = [OFString stringWithCString: destinationC
+ encoding: encoding
+ length: length];
+
+ [attributes setObject: destination
+ forKey: OFFileSymbolicLinkDestination];
+# else
+ HANDLE handle;
+ OFString *destination;
+
+ if (createSymbolicLinkWFuncPtr == NULL)
+ return;
+
+ if ((handle = CreateFileW(path.UTF16String, 0, (FILE_SHARE_READ |
+ FILE_SHARE_WRITE), NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == INVALID_HANDLE_VALUE)
+ @throw [OFGetItemAttributesFailedException
+ exceptionWithURI: URI
+ errNo: lastError()];
+
+ @try {
+ union {
+ char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ REPARSE_DATA_BUFFER data;
+ } buffer;
+ DWORD size;
+ wchar_t *tmp;
+
+ if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
+ buffer.bytes, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size,
+ NULL))
+ @throw [OFGetItemAttributesFailedException
+ exceptionWithURI: URI
+ errNo: lastError()];
+
+ if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK)
+ @throw [OFGetItemAttributesFailedException
+ exceptionWithURI: URI
+ errNo: lastError()];
+
+# define slrb buffer.data.SymbolicLinkReparseBuffer
+ tmp = slrb.PathBuffer +
+ (slrb.SubstituteNameOffset / sizeof(wchar_t));
+
+ destination = [OFString
+ stringWithUTF16String: tmp
+ length: slrb.SubstituteNameLength /
+ sizeof(wchar_t)];
+
+ [attributes setObject: OFFileTypeSymbolicLink
+ forKey: OFFileType];
+ [attributes setObject: destination
+ forKey: OFFileSymbolicLinkDestination];
+# undef slrb
+ } @finally {
+ CloseHandle(handle);
+ }
+# endif
+}
+#endif
+
+@implementation OFFileURIHandler
++ (void)initialize
+{
+#ifdef OF_WINDOWS
+ HMODULE module;
+#endif
+
+ if (self != [OFFileURIHandler class])
+ return;
+
+#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
+ passwdMutex = [[OFMutex alloc] init];
+ atexit(releasePasswdMutex);
+#endif
+#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS)
+ readdirMutex = [[OFMutex alloc] init];
+ atexit(releaseReaddirMutex);
+#endif
+
+#ifdef OF_WINDOWS
+ if ((module = LoadLibrary("msvcrt.dll")) != NULL)
+ _wutime64FuncPtr = (int (*)(const wchar_t *,
+ struct __utimbuf64 *))GetProcAddress(module, "_wutime64");
+
+ if ((module = LoadLibrary("kernel32.dll")) != NULL) {
+ createSymbolicLinkWFuncPtr =
+ (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD))
+ GetProcAddress(module, "CreateSymbolicLinkW");
+ createHardLinkWFuncPtr =
+ (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR,
+ LPSECURITY_ATTRIBUTES))
+ GetProcAddress(module, "CreateHardLinkW");
+ }
+#endif
+
+ /*
+ * Make sure OFFile is initialized.
+ * On some systems, this is needed to initialize the file system driver.
+ */
+ [OFFile class];
+}
+
++ (bool)of_directoryExistsAtPath: (OFString *)path
+{
+ Stat s;
+
+ if (statWrapper(path, &s) != 0)
+ return false;
+
+ return S_ISDIR(s.st_mode);
+}
+
+- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFFile *file = [[OFFile alloc]
+ initWithPath: URI.fileSystemRepresentation
+ mode: mode];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [file autorelease];
+}
+
+- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI
+{
+ OFMutableFileAttributes ret = [OFMutableDictionary dictionary];
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path;
+ int error;
+ Stat s;
+
+ if (URI == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![[URI scheme] isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ path = URI.fileSystemRepresentation;
+
+ if ((error = lstatWrapper(path, &s)) != 0)
+ @throw [OFGetItemAttributesFailedException
+ exceptionWithURI: URI
+ errNo: error];
+
+ if (s.st_size < 0)
+ @throw [OFOutOfRangeException exception];
+
+ [ret setObject: [NSNumber numberWithUnsignedLongLong: s.st_size]
+ forKey: OFFileSize];
+
+ setTypeAttribute(ret, &s);
+
+ [ret setObject: [NSNumber numberWithUnsignedLong: s.st_mode]
+ forKey: OFFilePOSIXPermissions];
+
+ setOwnerAndGroupAttributes(ret, &s);
+ setDateAttributes(ret, &s);
+
+#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
+ if (S_ISLNK(s.st_mode))
+ setSymbolicLinkDestinationAttribute(ret, URI);
+#endif
+
+ objc_autoreleasePoolPop(pool);
+
+ return ret;
+}
+
+- (void)of_setLastAccessDate: (OFDate *)lastAccessDate
+ andModificationDate: (OFDate *)modificationDate
+ ofItemAtURI: (OFURI *)URI
+ attributes: (OFFileAttributes)attributes OF_DIRECT
+{
+ OFString *path = URI.fileSystemRepresentation;
+ OFFileAttributeKey attributeKey = (modificationDate != nil
+ ? OFFileModificationDate : OFFileLastAccessDate);
+
+ if (lastAccessDate == nil)
+ lastAccessDate = modificationDate;
+ if (modificationDate == nil)
+ modificationDate = lastAccessDate;
+
+#if defined(OF_WINDOWS)
+ if (_wutime64FuncPtr != NULL) {
+ struct __utimbuf64 times = {
+ .actime =
+ (__time64_t)lastAccessDate.timeIntervalSince1970,
+ .modtime =
+ (__time64_t)modificationDate.timeIntervalSince1970
+ };
+
+ if (_wutime64FuncPtr([path UTF16String], ×) != 0)
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: attributeKey
+ errNo: errno];
+ } else {
+ struct _utimbuf times = {
+ .actime = (time_t)lastAccessDate.timeIntervalSince1970,
+ .modtime =
+ (time_t)modificationDate.timeIntervalSince1970
+ };
+ int status;
+
+ if ([OFSystemInfo isWindowsNT])
+ status = _wutime([path UTF16String], ×);
+ else
+ status = _utime(
+ [path cStringWithEncoding: [OFLocale encoding]],
+ ×);
+
+ if (status != 0)
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: attributeKey
+ errNo: errno];
+ }
+#elif defined(OF_AMIGAOS)
+ /* AmigaOS does not support access time. */
+ OFTimeInterval modificationTime =
+ modificationDate.timeIntervalSince1970;
+ struct Locale *locale;
+ struct DateStamp date;
+
+ modificationTime -= 252460800; /* 1978-01-01 */
+
+ if (modificationTime < 0)
+ @throw [OFOutOfRangeException exception];
+
+ locale = OpenLocale(NULL);
+ /*
+ * FIXME: This does not take DST into account. But unfortunately, there
+ * is no way to figure out if DST should be in effect for the
+ * timestamp.
+ */
+ modificationTime -= locale->loc_GMTOffset * 60.0;
+ CloseLocale(locale);
+
+ date.ds_Days = modificationTime / 86400;
+ date.ds_Minute = ((LONG)modificationTime % 86400) / 60;
+ date.ds_Tick = fmod(modificationTime, 60) * TICKS_PER_SECOND;
+
+# ifdef OF_AMIGAOS4
+ if (!SetDate([path cStringWithEncoding: [OFLocale encoding]],
+ &date) != 0)
+# else
+ if (!SetFileDate([path cStringWithEncoding: [OFLocale encoding]],
+ &date) != 0)
+# endif
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: attributeKey
+ errNo: lastError()];
+#else
+ OFTimeInterval lastAccessTime = lastAccessDate.timeIntervalSince1970;
+ OFTimeInterval modificationTime =
+ modificationDate.timeIntervalSince1970;
+ struct timeval times[2] = {
+ {
+ .tv_sec = (time_t)lastAccessTime,
+ .tv_usec =
+ (int)((lastAccessTime - times[0].tv_sec) * 1000000)
+ },
+ {
+ .tv_sec = (time_t)modificationTime,
+ .tv_usec = (int)((modificationTime - times[1].tv_sec) *
+ 1000000)
+ },
+ };
+
+ if (utimes([path cStringWithEncoding: [OFLocale encoding]], times) != 0)
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: attributeKey
+ errNo: errno];
+#endif
+}
+
+- (void)of_setPOSIXPermissions: (OFNumber *)permissions
+ ofItemAtURI: (OFURI *)URI
+ attributes: (OFFileAttributes)attributes OF_DIRECT
+{
+#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
+ mode_t mode = (mode_t)permissions.unsignedLongValue;
+ OFString *path = URI.fileSystemRepresentation;
+ int status;
+
+# ifdef OF_WINDOWS
+ if ([OFSystemInfo isWindowsNT])
+ status = _wchmod(path.UTF16String, mode);
+ else
+# endif
+ status = chmod(
+ [path cStringWithEncoding: [OFLocale encoding]], mode);
+
+ if (status != 0)
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: OFFilePOSIXPermissions
+ errNo: errno];
+#else
+ OF_UNRECOGNIZED_SELECTOR
+#endif
+}
+
+- (void)of_setOwnerAccountName: (OFString *)owner
+ andGroupOwnerAccountName: (OFString *)group
+ ofItemAtURI: (OFURI *)URI
+ attributeKey: (OFFileAttributeKey)attributeKey
+ attributes: (OFFileAttributes)attributes OF_DIRECT
+{
+#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
+ OFString *path = URI.fileSystemRepresentation;
+ uid_t uid = -1;
+ gid_t gid = -1;
+ OFStringEncoding encoding;
+
+ if (owner == nil && group == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ encoding = [OFLocale encoding];
+
+# ifdef OF_HAVE_THREADS
+ [passwdMutex lock];
+ @try {
+# endif
+ if (owner != nil) {
+ struct passwd *passwd;
+
+ if ((passwd = getpwnam([owner
+ cStringWithEncoding: encoding])) == NULL)
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: attributeKey
+ errNo: errno];
+
+ uid = passwd->pw_uid;
+ }
+
+ if (group != nil) {
+ struct group *group_;
+
+ if ((group_ = getgrnam([group
+ cStringWithEncoding: encoding])) == NULL)
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: attributeKey
+ errNo: errno];
+
+ gid = group_->gr_gid;
+ }
+# ifdef OF_HAVE_THREADS
+ } @finally {
+ [passwdMutex unlock];
+ }
+# endif
+
+ if (chown([path cStringWithEncoding: encoding], uid, gid) != 0)
+ @throw [OFSetItemAttributesFailedException
+ exceptionWithURI: URI
+ attributes: attributes
+ failedAttribute: attributeKey
+ errNo: errno];
+#else
+ OF_UNRECOGNIZED_SELECTOR
+#endif
+}
+
+- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFEnumerator OF_GENERIC(OFFileAttributeKey) *keyEnumerator;
+ OFEnumerator *objectEnumerator;
+ OFFileAttributeKey key;
+ id object;
+ OFDate *lastAccessDate, *modificationDate;
+
+ if (URI == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![URI.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ keyEnumerator = [attributes keyEnumerator];
+ objectEnumerator = [attributes objectEnumerator];
+
+ while ((key = [keyEnumerator nextObject]) != nil &&
+ (object = [objectEnumerator nextObject]) != nil) {
+ if ([key isEqual: OFFileModificationDate] ||
+ [key isEqual: OFFileLastAccessDate])
+ continue;
+ else if ([key isEqual: OFFilePOSIXPermissions])
+ [self of_setPOSIXPermissions: object
+ ofItemAtURI: URI
+ attributes: attributes];
+ else if ([key isEqual: OFFileOwnerAccountName])
+ [self of_setOwnerAccountName: object
+ andGroupOwnerAccountName: nil
+ ofItemAtURI: URI
+ attributeKey: key
+ attributes: attributes];
+ else if ([key isEqual: OFFileGroupOwnerAccountName])
+ [self of_setOwnerAccountName: nil
+ andGroupOwnerAccountName: object
+ ofItemAtURI: URI
+ attributeKey: key
+ attributes: attributes];
+ else
+ @throw [OFNotImplementedException
+ exceptionWithSelector: _cmd
+ object: self];
+ }
+
+ lastAccessDate = [attributes objectForKey: OFFileLastAccessDate];
+ modificationDate = [attributes objectForKey: OFFileModificationDate];
+
+ if (lastAccessDate != nil || modificationDate != nil)
+ [self of_setLastAccessDate: lastAccessDate
+ andModificationDate: modificationDate
+ ofItemAtURI: URI
+ attributes: attributes];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (bool)fileExistsAtURI: (OFURI *)URI
+{
+ void *pool = objc_autoreleasePoolPush();
+ Stat s;
+ bool ret;
+
+ if (URI == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![URI.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ if (statWrapper(URI.fileSystemRepresentation, &s) != 0) {
+ objc_autoreleasePoolPop(pool);
+ return false;
+ }
+
+ ret = S_ISREG(s.st_mode);
+
+ objc_autoreleasePoolPop(pool);
+
+ return ret;
+}
+
+- (bool)directoryExistsAtURI: (OFURI *)URI
+{
+ void *pool = objc_autoreleasePoolPush();
+ Stat s;
+ bool ret;
+
+ if (URI == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![URI.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ if (statWrapper(URI.fileSystemRepresentation, &s) != 0) {
+ objc_autoreleasePoolPop(pool);
+ return false;
+ }
+
+ ret = S_ISDIR(s.st_mode);
+
+ objc_autoreleasePoolPop(pool);
+
+ return ret;
+}
+
+- (void)createDirectoryAtURI: (OFURI *)URI
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path;
+
+ if (URI == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![URI.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ path = URI.fileSystemRepresentation;
+
+#if defined(OF_WINDOWS)
+ int status;
+
+ if ([OFSystemInfo isWindowsNT])
+ status = _wmkdir(path.UTF16String);
+ else
+ status = _mkdir(
+ [path cStringWithEncoding: [OFLocale encoding]]);
+
+ if (status != 0)
+ @throw [OFCreateDirectoryFailedException
+ exceptionWithURI: URI
+ errNo: errno];
+#elif defined(OF_AMIGAOS)
+ BPTR lock;
+
+ if ((lock = CreateDir(
+ [path cStringWithEncoding: [OFLocale encoding]])) == 0)
+ @throw [OFCreateDirectoryFailedException
+ exceptionWithURI: URI
+ errNo: lastError()];
+
+ UnLock(lock);
+#else
+ if (mkdir([path cStringWithEncoding: [OFLocale encoding]], 0777) != 0)
+ @throw [OFCreateDirectoryFailedException
+ exceptionWithURI: URI
+ errNo: errno];
+#endif
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI
+{
+ OFMutableArray *URIs = [OFMutableArray array];
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path;
+
+ if (URI == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![URI.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ path = URI.fileSystemRepresentation;
+
+#if defined(OF_WINDOWS)
+ HANDLE handle;
+
+ path = [path stringByAppendingString: @"\\*"];
+
+ if ([OFSystemInfo isWindowsNT]) {
+ WIN32_FIND_DATAW fd;
+
+ if ((handle = FindFirstFileW(path.UTF16String,
+ &fd)) == INVALID_HANDLE_VALUE)
+ @throw [OFOpenItemFailedException
+ exceptionWithURI: URI
+ mode: nil
+ errNo: lastError()];
+
+ @try {
+ do {
+ OFString *file;
+
+ if (wcscmp(fd.cFileName, L".") == 0 ||
+ wcscmp(fd.cFileName, L"..") == 0)
+ continue;
+
+ file = [[OFString alloc]
+ initWithUTF16String: fd.cFileName];
+ @try {
+ [URIs addObject: [URI
+ URIByAppendingPathComponent: file]];
+ } @finally {
+ [file release];
+ }
+ } while (FindNextFileW(handle, &fd));
+
+ if (GetLastError() != ERROR_NO_MORE_FILES)
+ @throw [OFReadFailedException
+ exceptionWithObject: self
+ requestedLength: 0
+ errNo: lastError()];
+ } @finally {
+ FindClose(handle);
+ }
+ } else {
+ OFStringEncoding encoding = [OFLocale encoding];
+ WIN32_FIND_DATA fd;
+
+ if ((handle = FindFirstFileA(
+ [path cStringWithEncoding: encoding], &fd)) ==
+ INVALID_HANDLE_VALUE)
+ @throw [OFOpenItemFailedException
+ exceptionWithURI: URI
+ mode: nil
+ errNo: lastError()];
+
+ @try {
+ do {
+ OFString *file;
+
+ if (strcmp(fd.cFileName, ".") == 0 ||
+ strcmp(fd.cFileName, "..") == 0)
+ continue;
+
+ file = [[OFString alloc]
+ initWithCString: fd.cFileName
+ encoding: encoding];
+ @try {
+ [URIs addObject: [URI
+ URIByAppendingPathComponent: file]];
+ } @finally {
+ [file release];
+ }
+ } while (FindNextFileA(handle, &fd));
+
+ if (GetLastError() != ERROR_NO_MORE_FILES)
+ @throw [OFReadFailedException
+ exceptionWithObject: self
+ requestedLength: 0
+ errNo: lastError()];
+ } @finally {
+ FindClose(handle);
+ }
+ }
+#elif defined(OF_AMIGAOS)
+ OFStringEncoding encoding = [OFLocale encoding];
+ BPTR lock;
+
+ if ((lock = Lock([path cStringWithEncoding: encoding],
+ SHARED_LOCK)) == 0)
+ @throw [OFOpenItemFailedException
+ exceptionWithURI: URI
+ mode: nil
+ errNo: lastError()];
+
+ @try {
+# ifdef OF_AMIGAOS4
+ struct ExamineData *ed;
+ APTR context;
+
+ if ((context = ObtainDirContextTags(EX_FileLockInput, lock,
+ EX_DoCurrentDir, TRUE, EX_DataFields, EXF_NAME,
+ TAG_END)) == NULL)
+ @throw [OFOpenItemFailedException
+ exceptionWithURI: URI
+ mode: nil
+ errNo: lastError()];
+
+ @try {
+ while ((ed = ExamineDir(context)) != NULL) {
+ OFString *file = [[OFString alloc]
+ initWithCString: ed->Name
+ encoding: encoding];
+
+ @try {
+ [URIs addObject: [URI
+ URIByAppendingPathComponent: file]];
+ } @finally {
+ [file release];
+ }
+ }
+ } @finally {
+ ReleaseDirContext(context);
+ }
+# else
+ struct FileInfoBlock fib;
+
+ if (!Examine(lock, &fib))
+ @throw [OFOpenItemFailedException
+ exceptionWithURI: URI
+ mode: nil
+ errNo: lastError()];
+
+ while (ExNext(lock, &fib)) {
+ OFString *file = [[OFString alloc]
+ initWithCString: fib.fib_FileName
+ encoding: encoding];
+ @try {
+ [URIs addObject:
+ [URI URIByAppendingPathComponent: file]];
+ } @finally {
+ [file release];
+ }
+ }
+# endif
+
+ if (IoErr() != ERROR_NO_MORE_ENTRIES)
+ @throw [OFReadFailedException
+ exceptionWithObject: self
+ requestedLength: 0
+ errNo: lastError()];
+ } @finally {
+ UnLock(lock);
+ }
+#else
+ OFStringEncoding encoding = [OFLocale encoding];
+ DIR *dir;
+ if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL)
+ @throw [OFOpenItemFailedException exceptionWithURI: URI
+ mode: nil
+ errNo: errno];
+
+# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS)
+ @try {
+ [readdirMutex lock];
+ } @catch (id e) {
+ closedir(dir);
+ @throw e;
+ }
+# endif
+
+ @try {
+ for (;;) {
+ struct dirent *dirent;
+# ifdef HAVE_READDIR_R
+ struct dirent buffer;
+# endif
+ OFString *file;
+
+# ifdef HAVE_READDIR_R
+ if (readdir_r(dir, &buffer, &dirent) != 0)
+ @throw [OFReadFailedException
+ exceptionWithObject: self
+ requestedLength: 0
+ errNo: errno];
+
+ if (dirent == NULL)
+ break;
+# else
+ errno = 0;
+ if ((dirent = readdir(dir)) == NULL) {
+ if (errno == 0)
+ break;
+ else
+ @throw [OFReadFailedException
+ exceptionWithObject: self
+ requestedLength: 0
+ errNo: errno];
+ }
+# endif
+
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0)
+ continue;
+
+ file = [[OFString alloc] initWithCString: dirent->d_name
+ encoding: encoding];
+ @try {
+ [URIs addObject:
+ [URI URIByAppendingPathComponent: file]];
+ } @finally {
+ [file release];
+ }
+ }
+ } @finally {
+ closedir(dir);
+# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS)
+ [readdirMutex unlock];
+# endif
+ }
+#endif
+
+ [URIs makeImmutable];
+
+ objc_autoreleasePoolPop(pool);
+
+ return URIs;
+}
+
+- (void)removeItemAtURI: (OFURI *)URI
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path;
+ int error;
+ Stat s;
+
+ if (URI == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![URI.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ path = URI.fileSystemRepresentation;
+
+ if ((error = lstatWrapper(path, &s)) != 0)
+ @throw [OFRemoveItemFailedException exceptionWithURI: URI
+ errNo: error];
+
+ if (S_ISDIR(s.st_mode)) {
+ OFArray OF_GENERIC(OFURI *) *contents;
+
+ @try {
+ contents = [self contentsOfDirectoryAtURI: URI];
+ } @catch (id e) {
+ /*
+ * Only convert exceptions to
+ * OFRemoveItemFailedException that have an errNo
+ * property. This covers all I/O related exceptions
+ * from the operations used to remove an item, all
+ * others should be left as is.
+ */
+ if ([e respondsToSelector: @selector(errNo)])
+ @throw [OFRemoveItemFailedException
+ exceptionWithURI: URI
+ errNo: [e errNo]];
+
+ @throw e;
+ }
+
+ for (OFURI *item in contents) {
+ void *pool2 = objc_autoreleasePoolPush();
+
+ [self removeItemAtURI: item];
+
+ objc_autoreleasePoolPop(pool2);
+ }
+
+#ifndef OF_AMIGAOS
+ int status;
+
+# ifdef OF_WINDOWS
+ if ([OFSystemInfo isWindowsNT])
+ status = _wrmdir(path.UTF16String);
+ else
+# endif
+ status = rmdir(
+ [path cStringWithEncoding: [OFLocale encoding]]);
+
+ if (status != 0)
+ @throw [OFRemoveItemFailedException
+ exceptionWithURI: URI
+ errNo: errno];
+ } else {
+ int status;
+
+# ifdef OF_WINDOWS
+ if ([OFSystemInfo isWindowsNT])
+ status = _wunlink(path.UTF16String);
+ else
+# endif
+ status = unlink(
+ [path cStringWithEncoding: [OFLocale encoding]]);
+
+ if (status != 0)
+ @throw [OFRemoveItemFailedException
+ exceptionWithURI: URI
+ errNo: errno];
+#endif
+ }
+
+#ifdef OF_AMIGAOS
+ if (!DeleteFile([path cStringWithEncoding: [OFLocale encoding]]))
+ @throw [OFRemoveItemFailedException
+ exceptionWithURI: URI
+ errNo: lastError()];
+#endif
+
+ objc_autoreleasePoolPop(pool);
+}
+
+#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS
+- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *sourcePath, *destinationPath;
+
+ if (source == nil || destination == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![source.scheme isEqual: _scheme] ||
+ ![destination.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ sourcePath = source.fileSystemRepresentation;
+ destinationPath = destination.fileSystemRepresentation;
+
+# ifndef OF_WINDOWS
+ OFStringEncoding encoding = [OFLocale encoding];
+
+ if (link([sourcePath cStringWithEncoding: encoding],
+ [destinationPath cStringWithEncoding: encoding]) != 0)
+ @throw [OFLinkItemFailedException
+ exceptionWithSourceURI: source
+ destinationURI: destination
+ errNo: errno];
+# else
+ if (createHardLinkWFuncPtr == NULL)
+ @throw [OFNotImplementedException exceptionWithSelector: _cmd
+ object: self];
+
+ if (!createHardLinkWFuncPtr(destinationPath.UTF16String,
+ sourcePath.UTF16String, NULL))
+ @throw [OFLinkItemFailedException
+ exceptionWithSourceURI: source
+ destinationURI: destination
+ errNo: lastError()];
+# endif
+
+ objc_autoreleasePoolPop(pool);
+}
+#endif
+
+#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
+- (void)createSymbolicLinkAtURI: (OFURI *)URI
+ withDestinationPath: (OFString *)target
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path;
+
+ if (URI == nil || target == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ if (![URI.scheme isEqual: _scheme])
+ @throw [OFInvalidArgumentException exception];
+
+ path = URI.fileSystemRepresentation;
+
+# ifndef OF_WINDOWS
+ OFStringEncoding encoding = [OFLocale encoding];
+
+ if (symlink([target cStringWithEncoding: encoding],
+ [path cStringWithEncoding: encoding]) != 0)
+ @throw [OFCreateSymbolicLinkFailedException
+ exceptionWithURI: URI
+ target: target
+ errNo: errno];
+# else
+ if (createSymbolicLinkWFuncPtr == NULL)
+ @throw [OFNotImplementedException exceptionWithSelector: _cmd
+ object: self];
+
+ if (!createSymbolicLinkWFuncPtr(path.UTF16String, target.UTF16String,
+ 0))
+ @throw [OFCreateSymbolicLinkFailedException
+ exceptionWithURI: URI
+ target: target
+ errNo: lastError()];
+# endif
+
+ objc_autoreleasePoolPop(pool);
+}
+#endif
+
+- (bool)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination
+{
+ void *pool;
+
+ if (![source.scheme isEqual: _scheme] ||
+ ![destination.scheme isEqual: _scheme])
+ return false;
+
+ if ([self fileExistsAtURI: destination])
+ @throw [OFMoveItemFailedException
+ exceptionWithSourceURI: source
+ destinationURI: destination
+ errNo: EEXIST];
+
+ pool = objc_autoreleasePoolPush();
+
+#ifdef OF_AMIGAOS
+ OFStringEncoding encoding = [OFLocale encoding];
+
+ if (!Rename([source.fileSystemRepresentation
+ cStringWithEncoding: encoding],
+ [destination.fileSystemRepresentation
+ cStringWithEncoding: encoding]))
+ @throw [OFMoveItemFailedException
+ exceptionWithSourceURI: source
+ destinationURI: destination
+ errNo: lastError()];
+#else
+ int status;
+
+# ifdef OF_WINDOWS
+ if ([OFSystemInfo isWindowsNT])
+ status = _wrename(source.fileSystemRepresentation.UTF16String,
+ destination.fileSystemRepresentation.UTF16String);
+ else {
+# endif
+ OFStringEncoding encoding = [OFLocale encoding];
+
+ status = rename([source.fileSystemRepresentation
+ cStringWithEncoding: encoding],
+ [destination.fileSystemRepresentation
+ cStringWithEncoding: encoding]);
+# ifdef OF_WINDOWS
+ }
+# endif
+
+ if (status != 0)
+ @throw [OFMoveItemFailedException
+ exceptionWithSourceURI: source
+ destinationURI: destination
+ errNo: errno];
+#endif
+
+ objc_autoreleasePoolPop(pool);
+
+ return true;
+}
+@end
DELETED src/OFFileURLHandler.h
Index: src/OFFileURLHandler.h
==================================================================
--- src/OFFileURLHandler.h
+++ src/OFFileURLHandler.h
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFURLHandler.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-@interface OFFileURLHandler: OFURLHandler
-+ (bool)of_directoryExistsAtPath: (OFString *)path OF_DIRECT;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/OFFileURLHandler.m
Index: src/OFFileURLHandler.m
==================================================================
--- src/OFFileURLHandler.m
+++ src/OFFileURLHandler.m
@@ -1,1476 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#define _LARGEFILE64_SOURCE
-
-#include
-#include
-
-#ifdef HAVE_DIRENT_H
-# include
-#endif
-#include "unistd_wrapper.h"
-
-#import "platform.h"
-#ifdef HAVE_SYS_STAT_H
-# include
-#endif
-#include
-#ifdef OF_WINDOWS
-# include
-#endif
-
-#ifdef HAVE_PWD_H
-# include
-#endif
-#ifdef HAVE_GRP_H
-# include
-#endif
-
-#import "OFFileURLHandler.h"
-#import "OFArray.h"
-#import "OFDate.h"
-#import "OFFile.h"
-#import "OFFileManager.h"
-#import "OFLocale.h"
-#import "OFNumber.h"
-#import "OFSystemInfo.h"
-#import "OFURL.h"
-
-#ifdef OF_HAVE_THREADS
-# import "OFMutex.h"
-#endif
-
-#import "OFCreateDirectoryFailedException.h"
-#import "OFCreateSymbolicLinkFailedException.h"
-#import "OFInitializationFailedException.h"
-#import "OFInvalidArgumentException.h"
-#import "OFLinkFailedException.h"
-#import "OFMoveItemFailedException.h"
-#import "OFNotImplementedException.h"
-#import "OFOpenItemFailedException.h"
-#import "OFOutOfRangeException.h"
-#import "OFReadFailedException.h"
-#import "OFRemoveItemFailedException.h"
-#import "OFRetrieveItemAttributesFailedException.h"
-#import "OFSetItemAttributesFailedException.h"
-
-#ifdef OF_WINDOWS
-# include
-# include
-# include
-# include
-#endif
-
-#ifdef OF_AMIGAOS
-# include
-# include
-# include
-# ifdef OF_AMIGAOS4
-# define DeleteFile(path) Delete(path)
-# endif
-#endif
-
-#if defined(OF_WINDOWS) || defined(OF_AMIGAOS)
-typedef struct {
- OFFileOffset st_size;
- unsigned int st_mode;
- OFTimeInterval st_atime, st_mtime, st_ctime;
-# ifdef OF_WINDOWS
-# define HAVE_STRUCT_STAT_ST_BIRTHTIME
- OFTimeInterval st_birthtime;
- DWORD fileAttributes;
-# endif
-} Stat;
-#elif defined(HAVE_STAT64)
-typedef struct stat64 Stat;
-#else
-typedef struct stat Stat;
-#endif
-
-#ifdef OF_WINDOWS
-# define S_IFLNK 0x10000
-# define S_ISLNK(mode) (mode & S_IFLNK)
-#endif
-
-#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
-static OFMutex *passwdMutex;
-
-static void
-releasePasswdMutex(void)
-{
- [passwdMutex release];
-}
-#endif
-#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS)
-static OFMutex *readdirMutex;
-
-static void
-releaseReaddirMutex(void)
-{
- [readdirMutex release];
-}
-#endif
-
-#ifdef OF_WINDOWS
-static int (*_wutime64FuncPtr)(const wchar_t *, struct __utimbuf64 *);
-static WINAPI BOOLEAN (*createSymbolicLinkWFuncPtr)(LPCWSTR, LPCWSTR, DWORD);
-static WINAPI BOOLEAN (*createHardLinkWFuncPtr)(LPCWSTR, LPCWSTR,
- LPSECURITY_ATTRIBUTES);
-#endif
-
-#ifdef OF_WINDOWS
-static OFTimeInterval
-filetimeToTimeInterval(const FILETIME *filetime)
-{
- return (double)((int64_t)filetime->dwHighDateTime << 32 |
- filetime->dwLowDateTime) / 10000000.0 - 11644473600.0;
-}
-
-static int
-retrieveError(void)
-{
- switch (GetLastError()) {
- case ERROR_FILE_NOT_FOUND:
- case ERROR_PATH_NOT_FOUND:
- case ERROR_NO_MORE_FILES:
- return ENOENT;
- case ERROR_ACCESS_DENIED:
- return EACCES;
- case ERROR_DIRECTORY:
- return ENOTDIR;
- case ERROR_NOT_READY:
- return EBUSY;
- default:
- return EIO;
- }
-}
-#endif
-
-#ifdef OF_AMIGAOS
-static int
-retrieveError(void)
-{
- switch (IoErr()) {
- case ERROR_DELETE_PROTECTED:
- case ERROR_READ_PROTECTED:
- case ERROR_WRITE_PROTECTED:
- return EACCES;
- case ERROR_DISK_NOT_VALIDATED:
- case ERROR_OBJECT_IN_USE:
- return EBUSY;
- case ERROR_OBJECT_EXISTS:
- return EEXIST;
- case ERROR_DIR_NOT_FOUND:
- case ERROR_NO_MORE_ENTRIES:
- case ERROR_OBJECT_NOT_FOUND:
- return ENOENT;
- case ERROR_NO_FREE_STORE:
- return ENOMEM;
- case ERROR_DISK_FULL:
- return ENOSPC;
- case ERROR_DIRECTORY_NOT_EMPTY:
- return ENOTEMPTY;
- case ERROR_DISK_WRITE_PROTECTED:
- return EROFS;
- case ERROR_RENAME_ACROSS_DEVICES:
- return EXDEV;
- default:
- return EIO;
- }
-}
-#endif
-
-static int
-statWrapper(OFString *path, Stat *buffer)
-{
-#if defined(OF_WINDOWS)
- WIN32_FILE_ATTRIBUTE_DATA data;
- bool success;
-
- if ([OFSystemInfo isWindowsNT])
- success = GetFileAttributesExW(path.UTF16String,
- GetFileExInfoStandard, &data);
- else
- success = GetFileAttributesExA(
- [path cStringWithEncoding: [OFLocale encoding]],
- GetFileExInfoStandard, &data);
-
- if (!success)
- return retrieveError();
-
- buffer->st_size = (uint64_t)data.nFileSizeHigh << 32 |
- data.nFileSizeLow;
-
- if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- buffer->st_mode = S_IFDIR;
- else if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- /*
- * No need to use A functions in this branch: This is only
- * available on NTFS (and hence Windows NT) anyway.
- */
- WIN32_FIND_DATAW findData;
- HANDLE findHandle;
-
- if ((findHandle = FindFirstFileW(path.UTF16String,
- &findData)) == INVALID_HANDLE_VALUE)
- return retrieveError();
-
- @try {
- if (!(findData.dwFileAttributes &
- FILE_ATTRIBUTE_REPARSE_POINT))
- /* Race? Indicate to try again. */
- return EAGAIN;
-
- buffer->st_mode =
- (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK
- ? S_IFLNK : S_IFREG);
- } @finally {
- FindClose(findHandle);
- }
- } else
- buffer->st_mode = S_IFREG;
-
- buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY
- ? (S_IRUSR | S_IXUSR) : (S_IRUSR | S_IWUSR | S_IXUSR));
-
- buffer->st_atime = filetimeToTimeInterval(&data.ftLastAccessTime);
- buffer->st_mtime = filetimeToTimeInterval(&data.ftLastWriteTime);
- buffer->st_ctime = buffer->st_birthtime =
- filetimeToTimeInterval(&data.ftCreationTime);
- buffer->fileAttributes = data.dwFileAttributes;
-
- return 0;
-#elif defined(OF_AMIGAOS)
- BPTR lock;
-# ifdef OF_AMIGAOS4
- struct ExamineData *ed;
-# else
- struct FileInfoBlock fib;
-# endif
- OFTimeInterval timeInterval;
- struct Locale *locale;
- struct DateStamp *date;
-
- if ((lock = Lock([path cStringWithEncoding: [OFLocale encoding]],
- SHARED_LOCK)) == 0)
- return retrieveError();
-
-# if defined(OF_MORPHOS)
- if (!Examine64(lock, &fib, TAG_DONE)) {
-# elif defined(OF_AMIGAOS4)
- if ((ed = ExamineObjectTags(EX_FileLockInput, lock, TAG_END)) == NULL) {
-# else
- if (!Examine(lock, &fib)) {
-# endif
- int error = retrieveError();
- UnLock(lock);
- return error;
- }
-
- UnLock(lock);
-
-# if defined(OF_MORPHOS)
- buffer->st_size = fib.fib_Size64;
-# elif defined(OF_AMIGAOS4)
- buffer->st_size = ed->FileSize;
-# else
- buffer->st_size = fib.fib_Size;
-# endif
-# ifdef OF_AMIGAOS4
- buffer->st_mode = (EXD_IS_DIRECTORY(ed) ? S_IFDIR : S_IFREG);
-# else
- buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG);
-# endif
-
- timeInterval = 252460800; /* 1978-01-01 */
-
- locale = OpenLocale(NULL);
- /*
- * FIXME: This does not take DST into account. But unfortunately, there
- * is no way to figure out if DST was in effect when the file was
- * modified.
- */
- timeInterval += locale->loc_GMTOffset * 60.0;
- CloseLocale(locale);
-
-# ifdef OF_AMIGAOS4
- date = &ed->Date;
-# else
- date = &fib.fib_Date;
-# endif
- timeInterval += date->ds_Days * 86400.0;
- timeInterval += date->ds_Minute * 60.0;
- timeInterval += date->ds_Tick / (OFTimeInterval)TICKS_PER_SECOND;
-
- buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval;
-
-# ifdef OF_AMIGAOS4
- FreeDosObject(DOS_EXAMINEDATA, ed);
-# endif
-
- return 0;
-#elif defined(HAVE_STAT64)
- if (stat64([path cStringWithEncoding: [OFLocale encoding]],
- buffer) != 0)
- return errno;
-
- return 0;
-#else
- if (stat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0)
- return errno;
-
- return 0;
-#endif
-}
-
-static int
-lstatWrapper(OFString *path, Stat *buffer)
-{
-#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS) && \
- !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
-# ifdef HAVE_LSTAT64
- if (lstat64([path cStringWithEncoding: [OFLocale encoding]],
- buffer) != 0)
- return errno;
-# else
- if (lstat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0)
- return errno;
-# endif
-
- return 0;
-#else
- return statWrapper(path, buffer);
-#endif
-}
-
-static void
-setTypeAttribute(OFMutableFileAttributes attributes, Stat *s)
-{
- if (S_ISREG(s->st_mode))
- [attributes setObject: OFFileTypeRegular forKey: OFFileType];
- else if (S_ISDIR(s->st_mode))
- [attributes setObject: OFFileTypeDirectory forKey: OFFileType];
-#ifdef S_ISLNK
- else if (S_ISLNK(s->st_mode))
- [attributes setObject: OFFileTypeSymbolicLink
- forKey: OFFileType];
-#endif
-#ifdef S_ISFIFO
- else if (S_ISFIFO(s->st_mode))
- [attributes setObject: OFFileTypeFIFO forKey: OFFileType];
-#endif
-#ifdef S_ISCHR
- else if (S_ISCHR(s->st_mode))
- [attributes setObject: OFFileTypeCharacterSpecial
- forKey: OFFileType];
-#endif
-#ifdef S_ISBLK
- else if (S_ISBLK(s->st_mode))
- [attributes setObject: OFFileTypeBlockSpecial
- forKey: OFFileType];
-#endif
-#ifdef S_ISSOCK
- else if (S_ISSOCK(s->st_mode))
- [attributes setObject: OFFileTypeSocket forKey: OFFileType];
-#endif
- else
- [attributes setObject: OFFileTypeUnknown forKey: OFFileType];
-}
-
-static void
-setDateAttributes(OFMutableFileAttributes attributes, Stat *s)
-{
- /* FIXME: We could be more precise on some OSes */
- [attributes
- setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime]
- forKey: OFFileLastAccessDate];
- [attributes
- setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime]
- forKey: OFFileModificationDate];
- [attributes
- setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime]
- forKey: OFFileStatusChangeDate];
-#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
- [attributes
- setObject: [OFDate dateWithTimeIntervalSince1970: s->st_birthtime]
- forKey: OFFileCreationDate];
-#endif
-}
-
-static void
-setOwnerAndGroupAttributes(OFMutableFileAttributes attributes, Stat *s)
-{
-#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
- [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_uid]
- forKey: OFFileOwnerAccountID];
- [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_gid]
- forKey: OFFileGroupOwnerAccountID];
-
-# ifdef OF_HAVE_THREADS
- [passwdMutex lock];
- @try {
-# endif
- OFStringEncoding encoding = [OFLocale encoding];
- struct passwd *passwd = getpwuid(s->st_uid);
- struct group *group_ = getgrgid(s->st_gid);
-
- if (passwd != NULL) {
- OFString *owner = [OFString
- stringWithCString: passwd->pw_name
- encoding: encoding];
-
- [attributes setObject: owner
- forKey: OFFileOwnerAccountName];
- }
-
- if (group_ != NULL) {
- OFString *group = [OFString
- stringWithCString: group_->gr_name
- encoding: encoding];
-
- [attributes setObject: group
- forKey: OFFileGroupOwnerAccountName];
- }
-# ifdef OF_HAVE_THREADS
- } @finally {
- [passwdMutex unlock];
- }
-# endif
-#endif
-}
-
-#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
-static void
-setSymbolicLinkDestinationAttribute(OFMutableFileAttributes attributes,
- OFURL *URL)
-{
- OFString *path = URL.fileSystemRepresentation;
-# ifndef OF_WINDOWS
- OFStringEncoding encoding = [OFLocale encoding];
- char destinationC[PATH_MAX];
- ssize_t length;
- OFString *destination;
-
- length = readlink([path cStringWithEncoding: encoding], destinationC,
- PATH_MAX);
-
- if (length < 0)
- @throw [OFRetrieveItemAttributesFailedException
- exceptionWithURL: URL
- errNo: errno];
-
- destination = [OFString stringWithCString: destinationC
- encoding: encoding
- length: length];
-
- [attributes setObject: destination
- forKey: OFFileSymbolicLinkDestination];
-# else
- HANDLE handle;
- OFString *destination;
-
- if (createSymbolicLinkWFuncPtr == NULL)
- return;
-
- if ((handle = CreateFileW(path.UTF16String, 0, (FILE_SHARE_READ |
- FILE_SHARE_WRITE), NULL, OPEN_EXISTING,
- FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == INVALID_HANDLE_VALUE)
- @throw [OFRetrieveItemAttributesFailedException
- exceptionWithURL: URL
- errNo: retrieveError()];
-
- @try {
- union {
- char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- REPARSE_DATA_BUFFER data;
- } buffer;
- DWORD size;
- wchar_t *tmp;
-
- if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
- buffer.bytes, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size,
- NULL))
- @throw [OFRetrieveItemAttributesFailedException
- exceptionWithURL: URL
- errNo: retrieveError()];
-
- if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK)
- @throw [OFRetrieveItemAttributesFailedException
- exceptionWithURL: URL
- errNo: retrieveError()];
-
-# define slrb buffer.data.SymbolicLinkReparseBuffer
- tmp = slrb.PathBuffer +
- (slrb.SubstituteNameOffset / sizeof(wchar_t));
-
- destination = [OFString
- stringWithUTF16String: tmp
- length: slrb.SubstituteNameLength /
- sizeof(wchar_t)];
-
- [attributes setObject: OFFileTypeSymbolicLink
- forKey: OFFileType];
- [attributes setObject: destination
- forKey: OFFileSymbolicLinkDestination];
-# undef slrb
- } @finally {
- CloseHandle(handle);
- }
-# endif
-}
-#endif
-
-@implementation OFFileURLHandler
-+ (void)initialize
-{
-#ifdef OF_WINDOWS
- HMODULE module;
-#endif
-
- if (self != [OFFileURLHandler class])
- return;
-
-#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
- passwdMutex = [[OFMutex alloc] init];
- atexit(releasePasswdMutex);
-#endif
-#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS)
- readdirMutex = [[OFMutex alloc] init];
- atexit(releaseReaddirMutex);
-#endif
-
-#ifdef OF_WINDOWS
- if ((module = LoadLibrary("msvcrt.dll")) != NULL)
- _wutime64FuncPtr = (int (*)(const wchar_t *,
- struct __utimbuf64 *))GetProcAddress(module, "_wutime64");
-
- if ((module = LoadLibrary("kernel32.dll")) != NULL) {
- createSymbolicLinkWFuncPtr =
- (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD))
- GetProcAddress(module, "CreateSymbolicLinkW");
- createHardLinkWFuncPtr =
- (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR,
- LPSECURITY_ATTRIBUTES))
- GetProcAddress(module, "CreateHardLinkW");
- }
-#endif
-
- /*
- * Make sure OFFile is initialized.
- * On some systems, this is needed to initialize the file system driver.
- */
- [OFFile class];
-}
-
-+ (bool)of_directoryExistsAtPath: (OFString *)path
-{
- Stat s;
-
- if (statWrapper(path, &s) != 0)
- return false;
-
- return S_ISDIR(s.st_mode);
-}
-
-- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode
-{
- void *pool = objc_autoreleasePoolPush();
- OFFile *file = [[OFFile alloc]
- initWithPath: URL.fileSystemRepresentation
- mode: mode];
-
- objc_autoreleasePoolPop(pool);
-
- return [file autorelease];
-}
-
-- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL
-{
- OFMutableFileAttributes ret = [OFMutableDictionary dictionary];
- void *pool = objc_autoreleasePoolPush();
- OFString *path;
- int error;
- Stat s;
-
- if (URL == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![[URL scheme] isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- path = URL.fileSystemRepresentation;
-
- if ((error = lstatWrapper(path, &s)) != 0)
- @throw [OFRetrieveItemAttributesFailedException
- exceptionWithURL: URL
- errNo: error];
-
- if (s.st_size < 0)
- @throw [OFOutOfRangeException exception];
-
- [ret setObject: [NSNumber numberWithUnsignedLongLong: s.st_size]
- forKey: OFFileSize];
-
- setTypeAttribute(ret, &s);
-
- [ret setObject: [NSNumber numberWithUnsignedLong: s.st_mode]
- forKey: OFFilePOSIXPermissions];
-
- setOwnerAndGroupAttributes(ret, &s);
- setDateAttributes(ret, &s);
-
-#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
- if (S_ISLNK(s.st_mode))
- setSymbolicLinkDestinationAttribute(ret, URL);
-#endif
-
- objc_autoreleasePoolPop(pool);
-
- return ret;
-}
-
-- (void)of_setLastAccessDate: (OFDate *)lastAccessDate
- andModificationDate: (OFDate *)modificationDate
- ofItemAtURL: (OFURL *)URL
- attributes: (OFFileAttributes)attributes OF_DIRECT
-{
- OFString *path = URL.fileSystemRepresentation;
- OFFileAttributeKey attributeKey = (modificationDate != nil
- ? OFFileModificationDate : OFFileLastAccessDate);
-
- if (lastAccessDate == nil)
- lastAccessDate = modificationDate;
- if (modificationDate == nil)
- modificationDate = lastAccessDate;
-
-#if defined(OF_WINDOWS)
- if (_wutime64FuncPtr != NULL) {
- struct __utimbuf64 times = {
- .actime =
- (__time64_t)lastAccessDate.timeIntervalSince1970,
- .modtime =
- (__time64_t)modificationDate.timeIntervalSince1970
- };
-
- if (_wutime64FuncPtr([path UTF16String], ×) != 0)
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: attributeKey
- errNo: errno];
- } else {
- struct _utimbuf times = {
- .actime = (time_t)lastAccessDate.timeIntervalSince1970,
- .modtime =
- (time_t)modificationDate.timeIntervalSince1970
- };
- int status;
-
- if ([OFSystemInfo isWindowsNT])
- status = _wutime([path UTF16String], ×);
- else
- status = _utime(
- [path cStringWithEncoding: [OFLocale encoding]],
- ×);
-
- if (status != 0)
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: attributeKey
- errNo: errno];
- }
-#elif defined(OF_AMIGAOS)
- /* AmigaOS does not support access time. */
- OFTimeInterval modificationTime =
- modificationDate.timeIntervalSince1970;
- struct Locale *locale;
- struct DateStamp date;
-
- modificationTime -= 252460800; /* 1978-01-01 */
-
- if (modificationTime < 0)
- @throw [OFOutOfRangeException exception];
-
- locale = OpenLocale(NULL);
- /*
- * FIXME: This does not take DST into account. But unfortunately, there
- * is no way to figure out if DST should be in effect for the
- * timestamp.
- */
- modificationTime -= locale->loc_GMTOffset * 60.0;
- CloseLocale(locale);
-
- date.ds_Days = modificationTime / 86400;
- date.ds_Minute = ((LONG)modificationTime % 86400) / 60;
- date.ds_Tick = fmod(modificationTime, 60) * TICKS_PER_SECOND;
-
-# ifdef OF_AMIGAOS4
- if (!SetDate([path cStringWithEncoding: [OFLocale encoding]],
- &date) != 0)
-# else
- if (!SetFileDate([path cStringWithEncoding: [OFLocale encoding]],
- &date) != 0)
-# endif
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: attributeKey
- errNo: retrieveError()];
-#else
- OFTimeInterval lastAccessTime = lastAccessDate.timeIntervalSince1970;
- OFTimeInterval modificationTime =
- modificationDate.timeIntervalSince1970;
- struct timeval times[2] = {
- {
- .tv_sec = (time_t)lastAccessTime,
- .tv_usec =
- (int)((lastAccessTime - times[0].tv_sec) * 1000000)
- },
- {
- .tv_sec = (time_t)modificationTime,
- .tv_usec = (int)((modificationTime - times[1].tv_sec) *
- 1000000)
- },
- };
-
- if (utimes([path cStringWithEncoding: [OFLocale encoding]], times) != 0)
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: attributeKey
- errNo: errno];
-#endif
-}
-
-- (void)of_setPOSIXPermissions: (OFNumber *)permissions
- ofItemAtURL: (OFURL *)URL
- attributes: (OFFileAttributes)attributes OF_DIRECT
-{
-#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
- mode_t mode = (mode_t)permissions.unsignedLongValue;
- OFString *path = URL.fileSystemRepresentation;
- int status;
-
-# ifdef OF_WINDOWS
- if ([OFSystemInfo isWindowsNT])
- status = _wchmod(path.UTF16String, mode);
- else
-# endif
- status = chmod(
- [path cStringWithEncoding: [OFLocale encoding]], mode);
-
- if (status != 0)
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: OFFilePOSIXPermissions
- errNo: errno];
-#else
- OF_UNRECOGNIZED_SELECTOR
-#endif
-}
-
-- (void)of_setOwnerAccountName: (OFString *)owner
- andGroupOwnerAccountName: (OFString *)group
- ofItemAtURL: (OFURL *)URL
- attributeKey: (OFFileAttributeKey)attributeKey
- attributes: (OFFileAttributes)attributes OF_DIRECT
-{
-#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
- OFString *path = URL.fileSystemRepresentation;
- uid_t uid = -1;
- gid_t gid = -1;
- OFStringEncoding encoding;
-
- if (owner == nil && group == nil)
- @throw [OFInvalidArgumentException exception];
-
- encoding = [OFLocale encoding];
-
-# ifdef OF_HAVE_THREADS
- [passwdMutex lock];
- @try {
-# endif
- if (owner != nil) {
- struct passwd *passwd;
-
- if ((passwd = getpwnam([owner
- cStringWithEncoding: encoding])) == NULL)
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: attributeKey
- errNo: errno];
-
- uid = passwd->pw_uid;
- }
-
- if (group != nil) {
- struct group *group_;
-
- if ((group_ = getgrnam([group
- cStringWithEncoding: encoding])) == NULL)
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: attributeKey
- errNo: errno];
-
- gid = group_->gr_gid;
- }
-# ifdef OF_HAVE_THREADS
- } @finally {
- [passwdMutex unlock];
- }
-# endif
-
- if (chown([path cStringWithEncoding: encoding], uid, gid) != 0)
- @throw [OFSetItemAttributesFailedException
- exceptionWithURL: URL
- attributes: attributes
- failedAttribute: attributeKey
- errNo: errno];
-#else
- OF_UNRECOGNIZED_SELECTOR
-#endif
-}
-
-- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL
-{
- void *pool = objc_autoreleasePoolPush();
- OFEnumerator OF_GENERIC(OFFileAttributeKey) *keyEnumerator;
- OFEnumerator *objectEnumerator;
- OFFileAttributeKey key;
- id object;
- OFDate *lastAccessDate, *modificationDate;
-
- if (URL == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![URL.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- keyEnumerator = [attributes keyEnumerator];
- objectEnumerator = [attributes objectEnumerator];
-
- while ((key = [keyEnumerator nextObject]) != nil &&
- (object = [objectEnumerator nextObject]) != nil) {
- if ([key isEqual: OFFileModificationDate] ||
- [key isEqual: OFFileLastAccessDate])
- continue;
- else if ([key isEqual: OFFilePOSIXPermissions])
- [self of_setPOSIXPermissions: object
- ofItemAtURL: URL
- attributes: attributes];
- else if ([key isEqual: OFFileOwnerAccountName])
- [self of_setOwnerAccountName: object
- andGroupOwnerAccountName: nil
- ofItemAtURL: URL
- attributeKey: key
- attributes: attributes];
- else if ([key isEqual: OFFileGroupOwnerAccountName])
- [self of_setOwnerAccountName: nil
- andGroupOwnerAccountName: object
- ofItemAtURL: URL
- attributeKey: key
- attributes: attributes];
- else
- @throw [OFNotImplementedException
- exceptionWithSelector: _cmd
- object: self];
- }
-
- lastAccessDate = [attributes objectForKey: OFFileLastAccessDate];
- modificationDate = [attributes objectForKey: OFFileModificationDate];
-
- if (lastAccessDate != nil || modificationDate != nil)
- [self of_setLastAccessDate: lastAccessDate
- andModificationDate: modificationDate
- ofItemAtURL: URL
- attributes: attributes];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (bool)fileExistsAtURL: (OFURL *)URL
-{
- void *pool = objc_autoreleasePoolPush();
- Stat s;
- bool ret;
-
- if (URL == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![URL.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- if (statWrapper(URL.fileSystemRepresentation, &s) != 0) {
- objc_autoreleasePoolPop(pool);
- return false;
- }
-
- ret = S_ISREG(s.st_mode);
-
- objc_autoreleasePoolPop(pool);
-
- return ret;
-}
-
-- (bool)directoryExistsAtURL: (OFURL *)URL
-{
- void *pool = objc_autoreleasePoolPush();
- Stat s;
- bool ret;
-
- if (URL == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![URL.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- if (statWrapper(URL.fileSystemRepresentation, &s) != 0) {
- objc_autoreleasePoolPop(pool);
- return false;
- }
-
- ret = S_ISDIR(s.st_mode);
-
- objc_autoreleasePoolPop(pool);
-
- return ret;
-}
-
-- (void)createDirectoryAtURL: (OFURL *)URL
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *path;
-
- if (URL == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![URL.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- path = URL.fileSystemRepresentation;
-
-#if defined(OF_WINDOWS)
- int status;
-
- if ([OFSystemInfo isWindowsNT])
- status = _wmkdir(path.UTF16String);
- else
- status = _mkdir(
- [path cStringWithEncoding: [OFLocale encoding]]);
-
- if (status != 0)
- @throw [OFCreateDirectoryFailedException
- exceptionWithURL: URL
- errNo: errno];
-#elif defined(OF_AMIGAOS)
- BPTR lock;
-
- if ((lock = CreateDir(
- [path cStringWithEncoding: [OFLocale encoding]])) == 0)
- @throw [OFCreateDirectoryFailedException
- exceptionWithURL: URL
- errNo: retrieveError()];
-
- UnLock(lock);
-#else
- if (mkdir([path cStringWithEncoding: [OFLocale encoding]], 0777) != 0)
- @throw [OFCreateDirectoryFailedException
- exceptionWithURL: URL
- errNo: errno];
-#endif
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL
-{
- OFMutableArray *URLs = [OFMutableArray array];
- void *pool = objc_autoreleasePoolPush();
- OFString *path;
-
- if (URL == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![URL.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- path = URL.fileSystemRepresentation;
-
-#if defined(OF_WINDOWS)
- HANDLE handle;
-
- path = [path stringByAppendingString: @"\\*"];
-
- if ([OFSystemInfo isWindowsNT]) {
- WIN32_FIND_DATAW fd;
-
- if ((handle = FindFirstFileW(path.UTF16String,
- &fd)) == INVALID_HANDLE_VALUE)
- @throw [OFOpenItemFailedException
- exceptionWithURL: URL
- mode: nil
- errNo: retrieveError()];
-
- @try {
- do {
- OFString *file;
-
- if (wcscmp(fd.cFileName, L".") == 0 ||
- wcscmp(fd.cFileName, L"..") == 0)
- continue;
-
- file = [[OFString alloc]
- initWithUTF16String: fd.cFileName];
- @try {
- [URLs addObject: [URL
- URLByAppendingPathComponent: file]];
- } @finally {
- [file release];
- }
- } while (FindNextFileW(handle, &fd));
-
- if (GetLastError() != ERROR_NO_MORE_FILES)
- @throw [OFReadFailedException
- exceptionWithObject: self
- requestedLength: 0
- errNo: retrieveError()];
- } @finally {
- FindClose(handle);
- }
- } else {
- OFStringEncoding encoding = [OFLocale encoding];
- WIN32_FIND_DATA fd;
-
- if ((handle = FindFirstFileA(
- [path cStringWithEncoding: encoding], &fd)) ==
- INVALID_HANDLE_VALUE)
- @throw [OFOpenItemFailedException
- exceptionWithURL: URL
- mode: nil
- errNo: retrieveError()];
-
- @try {
- do {
- OFString *file;
-
- if (strcmp(fd.cFileName, ".") == 0 ||
- strcmp(fd.cFileName, "..") == 0)
- continue;
-
- file = [[OFString alloc]
- initWithCString: fd.cFileName
- encoding: encoding];
- @try {
- [URLs addObject: [URL
- URLByAppendingPathComponent: file]];
- } @finally {
- [file release];
- }
- } while (FindNextFileA(handle, &fd));
-
- if (GetLastError() != ERROR_NO_MORE_FILES)
- @throw [OFReadFailedException
- exceptionWithObject: self
- requestedLength: 0
- errNo: retrieveError()];
- } @finally {
- FindClose(handle);
- }
- }
-#elif defined(OF_AMIGAOS)
- OFStringEncoding encoding = [OFLocale encoding];
- BPTR lock;
-
- if ((lock = Lock([path cStringWithEncoding: encoding],
- SHARED_LOCK)) == 0)
- @throw [OFOpenItemFailedException
- exceptionWithURL: URL
- mode: nil
- errNo: retrieveError()];
-
- @try {
-# ifdef OF_AMIGAOS4
- struct ExamineData *ed;
- APTR context;
-
- if ((context = ObtainDirContextTags(EX_FileLockInput, lock,
- EX_DoCurrentDir, TRUE, EX_DataFields, EXF_NAME,
- TAG_END)) == NULL)
- @throw [OFOpenItemFailedException
- exceptionWithURL: URL
- mode: nil
- errNo: retrieveError()];
-
- @try {
- while ((ed = ExamineDir(context)) != NULL) {
- OFString *file = [[OFString alloc]
- initWithCString: ed->Name
- encoding: encoding];
-
- @try {
- [URLs addObject: [URL
- URLByAppendingPathComponent: file]];
- } @finally {
- [file release];
- }
- }
- } @finally {
- ReleaseDirContext(context);
- }
-# else
- struct FileInfoBlock fib;
-
- if (!Examine(lock, &fib))
- @throw [OFOpenItemFailedException
- exceptionWithURL: URL
- mode: nil
- errNo: retrieveError()];
-
- while (ExNext(lock, &fib)) {
- OFString *file = [[OFString alloc]
- initWithCString: fib.fib_FileName
- encoding: encoding];
- @try {
- [URLs addObject:
- [URL URLByAppendingPathComponent: file]];
- } @finally {
- [file release];
- }
- }
-# endif
-
- if (IoErr() != ERROR_NO_MORE_ENTRIES)
- @throw [OFReadFailedException
- exceptionWithObject: self
- requestedLength: 0
- errNo: retrieveError()];
- } @finally {
- UnLock(lock);
- }
-#else
- OFStringEncoding encoding = [OFLocale encoding];
- DIR *dir;
- if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL)
- @throw [OFOpenItemFailedException exceptionWithURL: URL
- mode: nil
- errNo: errno];
-
-# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS)
- @try {
- [readdirMutex lock];
- } @catch (id e) {
- closedir(dir);
- @throw e;
- }
-# endif
-
- @try {
- for (;;) {
- struct dirent *dirent;
-# ifdef HAVE_READDIR_R
- struct dirent buffer;
-# endif
- OFString *file;
-
-# ifdef HAVE_READDIR_R
- if (readdir_r(dir, &buffer, &dirent) != 0)
- @throw [OFReadFailedException
- exceptionWithObject: self
- requestedLength: 0
- errNo: errno];
-
- if (dirent == NULL)
- break;
-# else
- errno = 0;
- if ((dirent = readdir(dir)) == NULL) {
- if (errno == 0)
- break;
- else
- @throw [OFReadFailedException
- exceptionWithObject: self
- requestedLength: 0
- errNo: errno];
- }
-# endif
-
- if (strcmp(dirent->d_name, ".") == 0 ||
- strcmp(dirent->d_name, "..") == 0)
- continue;
-
- file = [[OFString alloc] initWithCString: dirent->d_name
- encoding: encoding];
- @try {
- [URLs addObject:
- [URL URLByAppendingPathComponent: file]];
- } @finally {
- [file release];
- }
- }
- } @finally {
- closedir(dir);
-# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS)
- [readdirMutex unlock];
-# endif
- }
-#endif
-
- [URLs makeImmutable];
-
- objc_autoreleasePoolPop(pool);
-
- return URLs;
-}
-
-- (void)removeItemAtURL: (OFURL *)URL
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *path;
- int error;
- Stat s;
-
- if (URL == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![URL.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- path = URL.fileSystemRepresentation;
-
- if ((error = lstatWrapper(path, &s)) != 0)
- @throw [OFRemoveItemFailedException exceptionWithURL: URL
- errNo: error];
-
- if (S_ISDIR(s.st_mode)) {
- OFArray OF_GENERIC(OFURL *) *contents;
-
- @try {
- contents = [self contentsOfDirectoryAtURL: URL];
- } @catch (id e) {
- /*
- * Only convert exceptions to
- * OFRemoveItemFailedException that have an errNo
- * property. This covers all I/O related exceptions
- * from the operations used to remove an item, all
- * others should be left as is.
- */
- if ([e respondsToSelector: @selector(errNo)])
- @throw [OFRemoveItemFailedException
- exceptionWithURL: URL
- errNo: [e errNo]];
-
- @throw e;
- }
-
- for (OFURL *item in contents) {
- void *pool2 = objc_autoreleasePoolPush();
-
- [self removeItemAtURL: item];
-
- objc_autoreleasePoolPop(pool2);
- }
-
-#ifndef OF_AMIGAOS
- int status;
-
-# ifdef OF_WINDOWS
- if ([OFSystemInfo isWindowsNT])
- status = _wrmdir(path.UTF16String);
- else
-# endif
- status = rmdir(
- [path cStringWithEncoding: [OFLocale encoding]]);
-
- if (status != 0)
- @throw [OFRemoveItemFailedException
- exceptionWithURL: URL
- errNo: errno];
- } else {
- int status;
-
-# ifdef OF_WINDOWS
- if ([OFSystemInfo isWindowsNT])
- status = _wunlink(path.UTF16String);
- else
-# endif
- status = unlink(
- [path cStringWithEncoding: [OFLocale encoding]]);
-
- if (status != 0)
- @throw [OFRemoveItemFailedException
- exceptionWithURL: URL
- errNo: errno];
-#endif
- }
-
-#ifdef OF_AMIGAOS
- if (!DeleteFile([path cStringWithEncoding: [OFLocale encoding]]))
- @throw [OFRemoveItemFailedException
- exceptionWithURL: URL
- errNo: retrieveError()];
-#endif
-
- objc_autoreleasePoolPop(pool);
-}
-
-#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS
-- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *sourcePath, *destinationPath;
-
- if (source == nil || destination == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![source.scheme isEqual: _scheme] ||
- ![destination.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- sourcePath = source.fileSystemRepresentation;
- destinationPath = destination.fileSystemRepresentation;
-
-# ifndef OF_WINDOWS
- OFStringEncoding encoding = [OFLocale encoding];
-
- if (link([sourcePath cStringWithEncoding: encoding],
- [destinationPath cStringWithEncoding: encoding]) != 0)
- @throw [OFLinkFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
- errNo: errno];
-# else
- if (createHardLinkWFuncPtr == NULL)
- @throw [OFNotImplementedException exceptionWithSelector: _cmd
- object: self];
-
- if (!createHardLinkWFuncPtr(destinationPath.UTF16String,
- sourcePath.UTF16String, NULL))
- @throw [OFLinkFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
- errNo: retrieveError()];
-# endif
-
- objc_autoreleasePoolPop(pool);
-}
-#endif
-
-#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
-- (void)createSymbolicLinkAtURL: (OFURL *)URL
- withDestinationPath: (OFString *)target
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *path;
-
- if (URL == nil || target == nil)
- @throw [OFInvalidArgumentException exception];
-
- if (![URL.scheme isEqual: _scheme])
- @throw [OFInvalidArgumentException exception];
-
- path = URL.fileSystemRepresentation;
-
-# ifndef OF_WINDOWS
- OFStringEncoding encoding = [OFLocale encoding];
-
- if (symlink([target cStringWithEncoding: encoding],
- [path cStringWithEncoding: encoding]) != 0)
- @throw [OFCreateSymbolicLinkFailedException
- exceptionWithURL: URL
- target: target
- errNo: errno];
-# else
- if (createSymbolicLinkWFuncPtr == NULL)
- @throw [OFNotImplementedException exceptionWithSelector: _cmd
- object: self];
-
- if (!createSymbolicLinkWFuncPtr(path.UTF16String, target.UTF16String,
- 0))
- @throw [OFCreateSymbolicLinkFailedException
- exceptionWithURL: URL
- target: target
- errNo: retrieveError()];
-# endif
-
- objc_autoreleasePoolPop(pool);
-}
-#endif
-
-- (bool)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination
-{
- void *pool;
-
- if (![source.scheme isEqual: _scheme] ||
- ![destination.scheme isEqual: _scheme])
- return false;
-
- if ([self fileExistsAtURL: destination])
- @throw [OFMoveItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
- errNo: EEXIST];
-
- pool = objc_autoreleasePoolPush();
-
-#ifdef OF_AMIGAOS
- OFStringEncoding encoding = [OFLocale encoding];
-
- if (!Rename([source.fileSystemRepresentation
- cStringWithEncoding: encoding],
- [destination.fileSystemRepresentation
- cStringWithEncoding: encoding]))
- @throw [OFMoveItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
- errNo: retrieveError()];
-#else
- int status;
-
-# ifdef OF_WINDOWS
- if ([OFSystemInfo isWindowsNT])
- status = _wrename(source.fileSystemRepresentation.UTF16String,
- destination.fileSystemRepresentation.UTF16String);
- else {
-# endif
- OFStringEncoding encoding = [OFLocale encoding];
-
- status = rename([source.fileSystemRepresentation
- cStringWithEncoding: encoding],
- [destination.fileSystemRepresentation
- cStringWithEncoding: encoding]);
-# ifdef OF_WINDOWS
- }
-# endif
-
- if (status != 0)
- @throw [OFMoveItemFailedException
- exceptionWithSourceURL: source
- destinationURL: destination
- errNo: errno];
-#endif
-
- objc_autoreleasePoolPop(pool);
-
- return true;
-}
-@end
Index: src/OFHMAC.h
==================================================================
--- src/OFHMAC.h
+++ src/OFHMAC.h
@@ -46,10 +46,12 @@
/**
* @brief A buffer containing the HMAC.
*
* The size of the buffer depends on the hash used. The buffer is part of the
* receiver's memory pool.
+ *
+ * @throw OFHashNotCalculatedException The HMAC hasn't been calculated yet
*/
@property (readonly, nonatomic) const unsigned char *digest
OF_RETURNS_INNER_POINTER;
/**
@@ -98,15 +100,18 @@
/**
* @brief Adds a buffer to the HMAC to be calculated.
*
* @param buffer The buffer which should be included into the calculation
* @param length The length of the buffer
+ * @throw OFHashAlreadyCalculatedException The HMAC has already been calculated
*/
- (void)updateWithBuffer: (const void *)buffer length: (size_t)length;
/**
* @brief Performs the final calculation of the HMAC.
+ *
+ * @throw OFHashAlreadyCalculatedException The HMAC has already been calculated
*/
- (void)calculate;
/**
* @brief Resets the HMAC so that it can be calculated for a new message.
Index: src/OFHTTPClient.h
==================================================================
--- src/OFHTTPClient.h
+++ src/OFHTTPClient.h
@@ -26,11 +26,11 @@
@class OFHTTPRequest;
@class OFHTTPResponse;
@class OFStream;
@class OFTCPSocket;
@class OFTLSStream;
-@class OFURL;
+@class OFURI;
/**
* @protocol OFHTTPClientDelegate OFHTTPClient.h ObjFW/OFHTTPClient.h
*
* @brief A delegate for OFHTTPClient.
@@ -117,25 +117,25 @@
* This callback will only be called if the OFHTTPClient will follow a
* redirect. If the maximum number of redirects has been reached already, this
* callback will not be called.
*
* @param client The OFHTTPClient which wants to follow a redirect
- * @param URL The URL to which it will follow a redirect
+ * @param URI The URI to which it will follow a redirect
* @param statusCode The status code for the redirection
* @param request The request for which the OFHTTPClient wants to redirect.
* You are allowed to change the request's headers from this
* callback and they will be used when following the redirect
- * (e.g. to set the cookies for the new URL), however, keep in
+ * (e.g. to set the cookies for the new URI), however, keep in
* mind that this will change the request you originally passed.
* @param response The response indicating the redirect
* @return A boolean whether the OFHTTPClient should follow the redirect
*/
-- (bool)client: (OFHTTPClient *)client
- shouldFollowRedirect: (OFURL *)URL
- statusCode: (short)statusCode
- request: (OFHTTPRequest *)request
- response: (OFHTTPResponse *)response;
+- (bool)client: (OFHTTPClient *)client
+ shouldFollowRedirectToURI: (OFURI *)URI
+ statusCode: (short)statusCode
+ request: (OFHTTPRequest *)request
+ response: (OFHTTPResponse *)response;
@end
/**
* @class OFHTTPClient OFHTTPClient.h ObjFW/OFHTTPClient.h
*
@@ -142,17 +142,17 @@
* @brief A class for performing HTTP requests.
*/
OF_SUBCLASSING_RESTRICTED
@interface OFHTTPClient: OFObject
{
-#ifdef OF_HTTPCLIENT_M
+#ifdef OF_HTTP_CLIENT_M
@public
#endif
OFObject *_Nullable _delegate;
bool _allowsInsecureRedirects, _inProgress;
OFStream *_Nullable _stream;
- OFURL *_Nullable _lastURL;
+ OFURI *_Nullable _lastURI;
bool _lastWasHEAD;
OFHTTPResponse *_Nullable _lastResponse;
}
/**
@@ -180,10 +180,15 @@
* running! If you want to change the delegate during the request,
* perform an asynchronous request instead!
*
* @param request The request to perform
* @return The OFHTTPResponse for the request
+ * @throw OFHTTPRequestFailedException The HTTP request failed
+ * @throw OFInvalidServerResponseException The server sent an invalid response
+ * @throw OFUnsupportedVersionException The server responded in an unsupported
+ * version
+ * @throw OFAlreadyConnectedException The client is already performing a request
*/
- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request;
/**
* @brief Synchronously performs the specified HTTP request.
@@ -195,18 +200,24 @@
* @param request The request to perform
* @param redirects The maximum number of redirects after which no further
* attempt is done to follow the redirect, but instead the
* redirect is treated as an OFHTTPResponse
* @return The OFHTTPResponse for the request
+ * @throw OFHTTPRequestFailedException The HTTP request failed
+ * @throw OFInvalidServerResponseException The server sent an invalid response
+ * @throw OFUnsupportedVersionException The server responded in an unsupported
+ * version
+ * @throw OFAlreadyConnectedException The client is already performing a request
*/
- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
redirects: (unsigned int)redirects;
/**
* @brief Asynchronously performs the specified HTTP request.
*
* @param request The request to perform
+ * @throw OFAlreadyConnectedException The client is already performing a request
*/
- (void)asyncPerformRequest: (OFHTTPRequest *)request;
/**
* @brief Asynchronously performs the specified HTTP request.
@@ -213,10 +224,11 @@
*
* @param request The request to perform
* @param redirects The maximum number of redirects after which no further
* attempt is done to follow the redirect, but instead the
* redirect is treated as an OFHTTPResponse
+ * @throw OFAlreadyConnectedException The client is already performing a request
*/
- (void)asyncPerformRequest: (OFHTTPRequest *)request
redirects: (unsigned int)redirects;
/**
Index: src/OFHTTPClient.m
==================================================================
--- src/OFHTTPClient.m
+++ src/OFHTTPClient.m
@@ -11,11 +11,11 @@
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
-#define OF_HTTPCLIENT_M
+#define OF_HTTP_CLIENT_M
#include "config.h"
#include
#include
@@ -29,18 +29,18 @@
#import "OFNumber.h"
#import "OFRunLoop.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFTLSStream.h"
-#import "OFURL.h"
+#import "OFURI.h"
#import "OFAlreadyConnectedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
-#import "OFInvalidServerReplyException.h"
+#import "OFInvalidServerResponseException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
@@ -115,30 +115,30 @@
static OFString *
constructRequestString(OFHTTPRequest *request)
{
void *pool = objc_autoreleasePoolPush();
OFHTTPRequestMethod method = request.method;
- OFURL *URL = request.URL;
+ OFURI *URI = request.URI;
OFString *path;
- OFString *user = URL.user, *password = URL.password;
+ OFString *user = URI.user, *password = URI.password;
OFMutableString *requestString;
OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers;
bool hasContentLength, chunked;
OFEnumerator OF_GENERIC(OFString *) *keyEnumerator, *objectEnumerator;
OFString *key, *object;
- if (URL.path != nil)
- path = URL.URLEncodedPath;
+ if (URI.path.length > 0)
+ path = URI.percentEncodedPath;
else
path = @"/";
requestString = [OFMutableString stringWithFormat:
@"%s %@", OFHTTPRequestMethodName(method), path];
- if (URL.query != nil) {
+ if (URI.query != nil) {
[requestString appendString: @"?"];
- [requestString appendString: URL.URLEncodedQuery];
+ [requestString appendString: URI.percentEncodedQuery];
}
[requestString appendString: @" HTTP/"];
[requestString appendString: request.protocolVersionString];
[requestString appendString: @"\r\n"];
@@ -146,19 +146,20 @@
headers = [[request.headers mutableCopy] autorelease];
if (headers == nil)
headers = [OFMutableDictionary dictionary];
if ([headers objectForKey: @"Host"] == nil) {
- OFNumber *port = URL.port;
+ OFNumber *port = URI.port;
if (port != nil) {
OFString *host = [OFString stringWithFormat:
- @"%@:%@", URL.URLEncodedHost, port];
+ @"%@:%@", URI.percentEncodedHost, port];
[headers setObject: host forKey: @"Host"];
} else
- [headers setObject: URL.URLEncodedHost forKey: @"Host"];
+ [headers setObject: URI.percentEncodedHost
+ forKey: @"Host"];
}
if ((user.length > 0 || password.length > 0) &&
[headers objectForKey: @"Authorization"] == nil) {
OFMutableData *authorizationData = [OFMutableData data];
@@ -296,11 +297,11 @@
exception: exception];
}
- (void)createResponseWithStreamOrThrow: (OFStream *)stream
{
- OFURL *URL = _request.URL;
+ OFURI *URI = _request.URI;
OFHTTPClientResponse *response;
OFString *connectionHeader;
bool keepAlive;
OFString *location;
id exception;
@@ -327,44 +328,44 @@
if (keepAlive) {
response.of_keepAlive = true;
_client->_stream = [stream retain];
- _client->_lastURL = [URL copy];
+ _client->_lastURI = [URI copy];
_client->_lastWasHEAD =
(_request.method == OFHTTPRequestMethodHead);
_client->_lastResponse = [response retain];
}
if (_redirects > 0 && (_status == 301 || _status == 302 ||
_status == 303 || _status == 307) &&
(location = [_serverHeaders objectForKey: @"Location"]) != nil) {
bool follow = true;
- OFURL *newURL;
- OFString *newURLScheme;
-
- newURL = [OFURL URLWithString: location
- relativeToURL: URL];
- newURLScheme = newURL.scheme;
-
- if ([newURLScheme caseInsensitiveCompare: @"http"] !=
+ OFURI *newURI;
+ OFString *newURIScheme;
+
+ newURI = [OFURI URIWithString: location relativeToURI: URI];
+ newURIScheme = newURI.scheme;
+
+ if ([newURIScheme caseInsensitiveCompare: @"http"] !=
OFOrderedSame &&
- [newURLScheme caseInsensitiveCompare: @"https"] !=
+ [newURIScheme caseInsensitiveCompare: @"https"] !=
OFOrderedSame)
follow = false;
if (!_client->_allowsInsecureRedirects &&
- [URL.scheme caseInsensitiveCompare: @"https"] ==
+ [URI.scheme caseInsensitiveCompare: @"https"] ==
OFOrderedSame &&
- [newURLScheme caseInsensitiveCompare: @"http"] ==
+ [newURIScheme caseInsensitiveCompare: @"http"] ==
OFOrderedSame)
follow = false;
- if (follow && [_client->_delegate respondsToSelector: @selector(
- client:shouldFollowRedirect:statusCode:request:response:)])
+ if (follow && [_client->_delegate respondsToSelector:
+ @selector(client:shouldFollowRedirectToURI:statusCode:
+ request:response:)])
follow = [_client->_delegate client: _client
- shouldFollowRedirect: newURL
+ shouldFollowRedirectToURI: newURI
statusCode: _status
request: _request
response: response];
else if (follow)
follow = defaultShouldFollow(_request.method, _status);
@@ -375,11 +376,11 @@
OFHTTPRequest *newRequest =
[[_request copy] autorelease];
OFMutableDictionary *newHeaders =
[[headers mutableCopy] autorelease];
- if (![newURL.host isEqual: URL.host])
+ if (![newURI.host isEqual: URI.host])
[newHeaders removeObjectForKey: @"Host"];
/*
* 303 means the request should be converted to a GET
* request before redirection. This also means stripping
@@ -401,11 +402,11 @@
removeObjectForKey: key];
newRequest.method = OFHTTPRequestMethodGet;
}
- newRequest.URL = newURL;
+ newRequest.URI = newURI;
newRequest.headers = newHeaders;
_client->_inProgress = false;
[_client asyncPerformRequest: newRequest
@@ -455,21 +456,21 @@
return false;
}
if (![line hasPrefix: @"HTTP/"] || line.length < 9 ||
[line characterAtIndex: 8] != ' ')
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
- _version = [[line substringWithRange: OFRangeMake(5, 3)] copy];
+ _version = [[line substringWithRange: OFMakeRange(5, 3)] copy];
if (![_version isEqual: @"1.0"] && ![_version isEqual: @"1.1"])
@throw [OFUnsupportedVersionException
exceptionWithVersion: _version];
- status = [line substringWithRange: OFRangeMake(9, 3)].longLongValue;
+ status = [line substringWithRange: OFMakeRange(9, 3)].longLongValue;
if (status < 0 || status > 599)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
_status = (short)status;
return true;
}
@@ -479,11 +480,11 @@
OFString *key, *value, *old;
const char *lineC, *tmp;
char *keyC;
if (line == nil)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
if (line.length == 0) {
[_serverHeaders makeImmutable];
if ([_client->_delegate respondsToSelector: @selector(client:
@@ -503,11 +504,11 @@
}
lineC = line.UTF8String;
if ((tmp = strchr(lineC, ':')) == NULL)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
keyC = OFAllocMemory(tmp - lineC + 1, 1);
memcpy(keyC, lineC, tmp - lineC);
keyC[tmp - lineC] = '\0';
normalizeKey(keyC);
@@ -542,11 +543,12 @@
bool ret;
if (exception != nil) {
if ([exception isKindOfClass:
[OFInvalidEncodingException class]])
- exception = [OFInvalidServerReplyException exception];
+ exception =
+ [OFInvalidServerResponseException exception];
[self raiseException: exception];
return false;
}
@@ -644,19 +646,19 @@
@selector(client:didCreateTCPSocket:request:)])
[_client->_delegate client: _client
didCreateTCPSocket: sock
request: _request];
- if ([_request.URL.scheme caseInsensitiveCompare: @"https"] ==
+ if ([_request.URI.scheme caseInsensitiveCompare: @"https"] ==
OFOrderedSame) {
OFTLSStream *stream;
@try {
stream = [OFTLSStream streamWithStream: sock];
} @catch (OFNotImplementedException *e) {
[self raiseException:
[OFUnsupportedProtocolException
- exceptionWithURL: _request.URL]];
+ exceptionWithURI: _request.URI]];
return;
}
if ([_client->_delegate respondsToSelector:
@selector(client:didCreateTLSStream:request:)])
@@ -663,11 +665,11 @@
[_client->_delegate client: _client
didCreateTLSStream: stream
request: _request];
stream.delegate = self;
- [stream asyncPerformClientHandshakeWithHost: _request.URL.host];
+ [stream asyncPerformClientHandshakeWithHost: _request.URI.host];
} else {
sock.delegate = self;
[self performSelector: @selector(handleStream:)
withObject: sock
afterDelay: 0];
@@ -688,30 +690,30 @@
afterDelay: 0];
}
- (void)start
{
- OFURL *URL = _request.URL;
+ OFURI *URI = _request.URI;
OFStream *stream;
/* Can we reuse the last socket? */
if (_client->_stream != nil && !_client->_stream.atEndOfStream &&
- [_client->_lastURL.scheme isEqual: URL.scheme] &&
- [_client->_lastURL.host isEqual: URL.host] &&
- (_client->_lastURL.port == URL.port ||
- [_client->_lastURL.port isEqual: URL.port]) &&
+ [_client->_lastURI.scheme isEqual: URI.scheme] &&
+ [_client->_lastURI.host isEqual: URI.host] &&
+ (_client->_lastURI.port == URI.port ||
+ [_client->_lastURI.port isEqual: URI.port]) &&
(_client->_lastWasHEAD || _client->_lastResponse.atEndOfStream)) {
/*
* Set _stream to nil, so that in case of an error it won't be
* reused. If everything is successful, we set _stream again
* at the end.
*/
stream = [_client->_stream autorelease];
_client->_stream = nil;
- [_client->_lastURL release];
- _client->_lastURL = nil;
+ [_client->_lastURI release];
+ _client->_lastURI = nil;
[_client->_lastResponse release];
_client->_lastResponse = nil;
stream.delegate = self;
@@ -724,31 +726,31 @@
}
- (void)closeAndReconnect
{
@try {
- OFURL *URL = _request.URL;
+ OFURI *URI = _request.URI;
OFTCPSocket *sock;
uint16_t port;
- OFNumber *URLPort;
+ OFNumber *URIPort;
[_client close];
sock = [OFTCPSocket socket];
- if ([URL.scheme caseInsensitiveCompare: @"https"] ==
+ if ([URI.scheme caseInsensitiveCompare: @"https"] ==
OFOrderedSame)
port = 443;
else
port = 80;
- URLPort = URL.port;
- if (URLPort != nil)
- port = URLPort.unsignedShortValue;
+ URIPort = URI.port;
+ if (URIPort != nil)
+ port = URIPort.unsignedShortValue;
sock.delegate = self;
- [sock asyncConnectToHost: URL.host port: port];
+ [sock asyncConnectToHost: URI.host port: port];
} @catch (id e) {
[self raiseException: e];
}
}
@end
@@ -899,11 +901,11 @@
isEqual: @"chunked"];
contentLength = [headers objectForKey: @"Content-Length"];
if (contentLength != nil) {
if (_chunked || contentLength.length == 0)
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
_hasContentLength = true;
@try {
unsigned long long toRead =
@@ -912,11 +914,11 @@
if (toRead > LLONG_MAX)
@throw [OFOutOfRangeException exception];
_toRead = (long long)toRead;
} @catch (OFInvalidFormatException *e) {
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
}
}
}
- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
@@ -958,16 +960,16 @@
switch ([_stream readIntoBuffer: tmp length: 2]) {
case 2:
_toRead++;
if (tmp[1] != '\n')
- @throw [OFInvalidServerReplyException
+ @throw [OFInvalidServerResponseException
exception];
case 1:
_toRead++;
if (tmp[0] != '\r')
- @throw [OFInvalidServerReplyException
+ @throw [OFInvalidServerResponseException
exception];
}
if (_setAtEndOfStream && _toRead == 0)
_atEndOfStream = true;
@@ -977,11 +979,11 @@
char tmp;
if ([_stream readIntoBuffer: &tmp length: 1] == 1) {
_toRead++;
if (tmp != '\n')
- @throw [OFInvalidServerReplyException
+ @throw [OFInvalidServerResponseException
exception];
}
if (_setAtEndOfStream && _toRead == 0)
_atEndOfStream = true;
@@ -1004,11 +1006,11 @@
size_t pos;
@try {
line = [_stream tryReadLine];
} @catch (OFInvalidEncodingException *e) {
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
}
if (line == nil)
return 0;
@@ -1022,11 +1024,11 @@
* at end of stream.
*/
if (_stream.atEndOfStream && pos == OFNotFound)
@throw [OFTruncatedDataException exception];
else
- @throw [OFInvalidServerReplyException
+ @throw [OFInvalidServerResponseException
exception];
}
@try {
unsigned long long toRead =
@@ -1035,11 +1037,11 @@
if (toRead > LLONG_MAX)
@throw [OFOutOfRangeException exception];
_toRead = (long long)toRead;
} @catch (OFInvalidFormatException *e) {
- @throw [OFInvalidServerReplyException exception];
+ @throw [OFInvalidServerResponseException exception];
}
if (_toRead == 0) {
_setAtEndOfStream = true;
_toRead = -2;
@@ -1198,23 +1200,23 @@
didReceiveHeaders: headers
statusCode: statusCode
request: request];
}
-- (bool)client: (OFHTTPClient *)client
- shouldFollowRedirect: (OFURL *)URL
- statusCode: (short)statusCode
- request: (OFHTTPRequest *)request
- response: (OFHTTPResponse *)response
-{
- if ([_delegate respondsToSelector: @selector(client:
- shouldFollowRedirect:statusCode:request:response:)])
- return [_delegate client: client
- shouldFollowRedirect: URL
- statusCode: statusCode
- request: request
- response: response];
+- (bool)client: (OFHTTPClient *)client
+ shouldFollowRedirectToURI: (OFURI *)URI
+ statusCode: (short)statusCode
+ request: (OFHTTPRequest *)request
+ response: (OFHTTPResponse *)response
+{
+ if ([_delegate respondsToSelector: @selector(
+ client:shouldFollowRedirectToURI:statusCode:request:response:)])
+ return [_delegate client: client
+ shouldFollowRedirectToURI: URI
+ statusCode: statusCode
+ request: request
+ response: response];
else
return defaultShouldFollow(request.method, statusCode);
}
@end
@@ -1263,19 +1265,18 @@
- (void)asyncPerformRequest: (OFHTTPRequest *)request
redirects: (unsigned int)redirects
{
void *pool = objc_autoreleasePoolPush();
- OFURL *URL = request.URL;
- OFString *scheme = URL.scheme;
+ OFURI *URI = request.URI;
+ OFString *scheme = URI.scheme;
if ([scheme caseInsensitiveCompare: @"http"] != OFOrderedSame &&
[scheme caseInsensitiveCompare: @"https"] != OFOrderedSame)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
if (_inProgress)
- /* TODO: Find a better exception */
@throw [OFAlreadyConnectedException exception];
_inProgress = true;
[[[[OFHTTPClientRequestHandler alloc]
@@ -1289,12 +1290,12 @@
- (void)close
{
[_stream release];
_stream = nil;
- [_lastURL release];
- _lastURL = nil;
+ [_lastURI release];
+ _lastURI = nil;
[_lastResponse release];
_lastResponse = nil;
}
@end
Index: src/OFHTTPCookie.h
==================================================================
--- src/OFHTTPCookie.h
+++ src/OFHTTPCookie.h
@@ -20,24 +20,24 @@
@class OFArray OF_GENERIC(ObjectType);
@class OFDate;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
-@class OFURL;
+@class OFURI;
/**
* @class OFHTTPCookie OFHTTPCookie.h ObjFW/OFHTTPCookie.h
*
* @brief A class for storing and manipulating HTTP cookies.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFHTTPCookie: OFObject
{
OFString *_name, *_value, *_domain, *_path;
OFDate *_Nullable _expires;
bool _secure, _HTTPOnly;
OFMutableArray OF_GENERIC(OFString *) *_extensions;
- OF_RESERVE_IVARS(OFHTTPCookie, 4)
}
/**
* @brief The name of the cookie.
*/
@@ -78,20 +78,22 @@
*/
@property (readonly, nonatomic)
OFMutableArray OF_GENERIC(OFString *) *extensions;
/**
- * @brief Parses the specified response header fields for the specified URL and
+ * @brief Parses the specified response header fields for the specified URI and
* returns an array of cookies.
*
* @param headerFields The response header fields to parse
- * @param URL The URL for the response header fields to parse
+ * @param URI The URI for the response header fields to parse
* @return An array of cookies
+ * @throw OFInvalidFormatException The specified response header has an invalid
+ * format
*/
+ (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields:
(OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields
- forURL: (OFURL *)URL;
+ forURI: (OFURI *)URI;
/**
* @brief Returns the request header fields for the specified cookies.
*
* @param cookies The cookies to return the request header fields for
Index: src/OFHTTPCookie.m
==================================================================
--- src/OFHTTPCookie.m
+++ src/OFHTTPCookie.m
@@ -17,11 +17,11 @@
#import "OFHTTPCookie.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFDictionary.h"
-#import "OFURL.h"
+#import "OFURI.h"
#import "OFInvalidFormatException.h"
static void
handleAttribute(OFHTTPCookie *cookie, OFString *name, OFString *value)
@@ -60,16 +60,16 @@
@synthesize expires = _expires, secure = _secure, HTTPOnly = _HTTPOnly;
@synthesize extensions = _extensions;
+ (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields:
(OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields
- forURL: (OFURL *)URL
+ forURI: (OFURI *)URI
{
OFMutableArray OF_GENERIC(OFHTTPCookie *) *ret = [OFMutableArray array];
void *pool = objc_autoreleasePoolPush();
OFString *string = [headerFields objectForKey: @"Set-Cookie"];
- OFString *domain = URL.host;
+ OFString *domain = URI.host;
const OFUnichar *characters = string.characters;
size_t length = string.length, last = 0;
enum {
statePreName,
stateName,
@@ -93,11 +93,11 @@
}
break;
case stateName:
if (characters[i] == '=') {
name = [string substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
state = stateExpectValue;
}
break;
case stateExpectValue:
if (characters[i] == '"') {
@@ -111,11 +111,11 @@
i--;
break;
case stateValue:
if (characters[i] == ';' || characters[i] == ',') {
value = [string substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
[ret addObject:
[OFHTTPCookie cookieWithName: name
value: value
domain: domain]];
@@ -125,11 +125,11 @@
}
break;
case stateQuotedValue:
if (characters[i] == '"') {
value = [string substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
[ret addObject:
[OFHTTPCookie cookieWithName: name
value: value
domain: domain]];
@@ -153,18 +153,18 @@
}
break;
case stateAttrName:
if (characters[i] == '=') {
name = [string substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
state = stateAttrValue;
last = i + 1;
} else if (characters[i] == ';' ||
characters[i] == ',') {
name = [string substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
handleAttribute(ret.lastObject, name, nil);
state = (characters[i] == ';'
? statePreAttrName : statePreName);
@@ -172,11 +172,11 @@
break;
case stateAttrValue:
if (characters[i] == ';' || characters[i] == ',') {
value = [string substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
/*
* Expires often contains a comma, even though
* the comma is used as a separator for
* concatenating headers as per RFC 2616,
@@ -213,11 +213,11 @@
case stateQuotedValue:
@throw [OFInvalidFormatException exception];
break;
case stateValue:
value = [string substringWithRange:
- OFRangeMake(last, length - last)];
+ OFMakeRange(last, length - last)];
[ret addObject: [OFHTTPCookie cookieWithName: name
value: value
domain: domain]];
break;
/* We end up here if the cookie is just foo= */
@@ -227,18 +227,18 @@
domain: domain]];
break;
case stateAttrName:
if (last != length) {
name = [string substringWithRange:
- OFRangeMake(last, length - last)];
+ OFMakeRange(last, length - last)];
handleAttribute(ret.lastObject, name, nil);
}
break;
case stateAttrValue:
value = [string substringWithRange:
- OFRangeMake(last, length - last)];
+ OFMakeRange(last, length - last)];
handleAttribute(ret.lastObject, name, value);
break;
}
@@ -369,12 +369,12 @@
OFHashAddHash(&hash, _name.hash);
OFHashAddHash(&hash, _value.hash);
OFHashAddHash(&hash, _domain.hash);
OFHashAddHash(&hash, _path.hash);
OFHashAddHash(&hash, _expires.hash);
- OFHashAdd(&hash, _secure);
- OFHashAdd(&hash, _HTTPOnly);
+ OFHashAddByte(&hash, _secure);
+ OFHashAddByte(&hash, _HTTPOnly);
OFHashAddHash(&hash, _extensions.hash);
OFHashFinalize(&hash);
return hash;
}
Index: src/OFHTTPCookieManager.h
==================================================================
--- src/OFHTTPCookieManager.h
+++ src/OFHTTPCookieManager.h
@@ -18,11 +18,11 @@
OF_ASSUME_NONNULL_BEGIN
@class OFArray OF_GENERIC(ObjectType);
@class OFHTTPCookie;
@class OFMutableArray OF_GENERIC(ObjectType);
-@class OFURL;
+@class OFURI;
/**
* @class OFHTTPCookieManager OFHTTPCookieManager.h ObjFW/OFHTTPCookieManager.h
*
* @brief A class for managing cookies for multiple domains.
@@ -44,42 +44,42 @@
* @return A new, autoreleased OFHTTPCookieManager
*/
+ (instancetype)manager;
/**
- * @brief Adds the specified cookie for the specified URL.
+ * @brief Adds the specified cookie for the specified URI.
*
* @warning This modifies the cookie (e.g. it sets the domain if it is unset)!
* If you do not want this, pass a copy!
*
* @param cookie The cookie to add to the manager
- * @param URL The URL for which the cookie should be added
+ * @param URI The URI for which the cookie should be added
*/
-- (void)addCookie: (OFHTTPCookie *)cookie forURL: (OFURL *)URL;
+- (void)addCookie: (OFHTTPCookie *)cookie forURI: (OFURI *)URI;
/**
- * @brief Adds the specified cookies for the specified URL.
+ * @brief Adds the specified cookies for the specified URI.
*
* @warning This modifies the cookies (e.g. it sets the domain if it is unset)!
* If you do not want this, pass copies!
*
* @param cookies An array of cookies to add to the manager
- * @param URL The URL for which the cookies should be added
+ * @param URI The URI for which the cookies should be added
*/
- (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
- forURL: (OFURL *)URL;
+ forURI: (OFURI *)URI;
/**
- * @brief Returns the cookies for the specified URL.
+ * @brief Returns the cookies for the specified URI.
*
- * @param URL The URL for which the cookies should be returned
- * @return The cookies for the specified URL
+ * @param URI The URI for which the cookies should be returned
+ * @return The cookies for the specified URI
*/
-- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURL: (OFURL *)URL;
+- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURI: (OFURI *)URI;
/**
* @brief Purges all expired cookies.
*/
- (void)purgeExpiredCookies;
@end
OF_ASSUME_NONNULL_END
Index: src/OFHTTPCookieManager.m
==================================================================
--- src/OFHTTPCookieManager.m
+++ src/OFHTTPCookieManager.m
@@ -17,11 +17,11 @@
#import "OFHTTPCookieManager.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFHTTPCookie.h"
-#import "OFURL.h"
+#import "OFURI.h"
@implementation OFHTTPCookieManager
+ (instancetype)manager
{
return [[[self alloc] init] autorelease];
@@ -51,33 +51,33 @@
- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
{
return [[_cookies copy] autorelease];
}
-- (void)addCookie: (OFHTTPCookie *)cookie forURL: (OFURL *)URL
+- (void)addCookie: (OFHTTPCookie *)cookie forURI: (OFURI *)URI
{
void *pool = objc_autoreleasePoolPush();
- OFString *cookieDomain, *URLHost;
+ OFString *cookieDomain, *URIHost;
size_t i;
if (![cookie.path hasPrefix: @"/"])
cookie.path = @"/";
if (cookie.secure &&
- [URL.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) {
+ [URI.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) {
objc_autoreleasePoolPop(pool);
return;
}
cookieDomain = cookie.domain.lowercaseString;
cookie.domain = cookieDomain;
- URLHost = URL.host.lowercaseString;
- if (![cookieDomain isEqual: URLHost]) {
- URLHost = [@"." stringByAppendingString: URLHost];
+ URIHost = URI.host.lowercaseString;
+ if (![cookieDomain isEqual: URIHost]) {
+ URIHost = [@"." stringByAppendingString: URIHost];
- if (![cookieDomain hasSuffix: URLHost]) {
+ if (![cookieDomain hasSuffix: URIHost]) {
objc_autoreleasePoolPop(pool);
return;
}
}
@@ -98,66 +98,66 @@
objc_autoreleasePoolPop(pool);
}
- (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
- forURL: (OFURL *)URL
+ forURI: (OFURI *)URI
{
for (OFHTTPCookie *cookie in cookies)
- [self addCookie: cookie forURL: URL];
+ [self addCookie: cookie forURI: URI];
}
-- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURL: (OFURL *)URL
+- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURI: (OFURI *)URI
{
OFMutableArray *ret = [OFMutableArray array];
for (OFHTTPCookie *cookie in _cookies) {
void *pool;
OFDate *expires;
- OFString *cookieDomain, *URLHost, *cookiePath, *URLPath;
+ OFString *cookieDomain, *URIHost, *cookiePath, *URIPath;
bool match;
expires = cookie.expires;
if (expires != nil && expires.timeIntervalSinceNow <= 0)
continue;
- if (cookie.secure && [URL.scheme caseInsensitiveCompare:
+ if (cookie.secure && [URI.scheme caseInsensitiveCompare:
@"https"] != OFOrderedSame)
continue;
pool = objc_autoreleasePoolPush();
cookieDomain = cookie.domain.lowercaseString;
- URLHost = URL.host.lowercaseString;
+ URIHost = URI.host.lowercaseString;
if ([cookieDomain hasPrefix: @"."]) {
- if ([URLHost hasSuffix: cookieDomain])
+ if ([URIHost hasSuffix: cookieDomain])
match = true;
else {
cookieDomain =
[cookieDomain substringFromIndex: 1];
- match = [cookieDomain isEqual: URLHost];
+ match = [cookieDomain isEqual: URIHost];
}
} else
- match = [cookieDomain isEqual: URLHost];
+ match = [cookieDomain isEqual: URIHost];
if (!match) {
objc_autoreleasePoolPop(pool);
continue;
}
cookiePath = cookie.path;
- URLPath = URL.path;
+ URIPath = URI.path;
if (![cookiePath isEqual: @"/"]) {
- if ([cookiePath isEqual: URLPath])
+ if ([cookiePath isEqual: URIPath])
match = true;
else {
if (![cookiePath hasSuffix: @"/"])
cookiePath = [cookiePath
stringByAppendingString: @"/"];
- match = [URLPath hasPrefix: cookiePath];
+ match = [URIPath hasPrefix: cookiePath];
}
if (!match) {
objc_autoreleasePoolPop(pool);
continue;
Index: src/OFHTTPRequest.h
==================================================================
--- src/OFHTTPRequest.h
+++ src/OFHTTPRequest.h
@@ -17,11 +17,11 @@
#import "OFSocket.h"
#import "OFString.h"
OF_ASSUME_NONNULL_BEGIN
-@class OFURL;
+@class OFURI;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFData;
@class OFString;
/** @file */
@@ -63,33 +63,41 @@
/**
* @class OFHTTPRequest OFHTTPRequest.h ObjFW/OFHTTPRequest.h
*
* @brief A class for storing HTTP requests.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFHTTPRequest: OFObject
{
- OFURL *_URL;
+ OFURI *_URI;
OFHTTPRequestMethod _method;
OFHTTPRequestProtocolVersion _protocolVersion;
OFDictionary OF_GENERIC(OFString *, OFString *) *_Nullable _headers;
OFSocketAddress _remoteAddress;
bool _hasRemoteAddress;
- OF_RESERVE_IVARS(OFHTTPRequest, 4)
}
/**
- * @brief The URL of the HTTP request.
+ * @brief The URI of the HTTP request.
*/
-@property (copy, nonatomic) OFURL *URL;
+@property (copy, nonatomic) OFURI *URI;
/**
* @brief The protocol version of the HTTP request.
+ *
+ * @throw OFUnsupportedVersionException The specified version cannot be set
+ * because it is not supported
*/
@property (nonatomic) OFHTTPRequestProtocolVersion protocolVersion;
/**
* @brief The protocol version of the HTTP request as a string.
+ *
+ * @throw OFUnsupportedVersionException The specified version cannot be set
+ * because it is not supported
+ * @throw OFInvalidFormatException The specified version cannot be set because
+ * it is not in a valid format
*/
@property (copy, nonatomic) OFString *protocolVersionString;
/**
* @brief The request method of the HTTP request.
@@ -108,24 +116,24 @@
* @note The setter creates a copy of the remote address.
*/
@property OF_NULLABLE_PROPERTY (nonatomic) const OFSocketAddress *remoteAddress;
/**
- * @brief Creates a new OFHTTPRequest with the specified URL.
+ * @brief Creates a new OFHTTPRequest with the specified URI.
*
- * @param URL The URL for the request
+ * @param URI The URI for the request
* @return A new, autoreleased OFHTTPRequest
*/
-+ (instancetype)requestWithURL: (OFURL *)URL;
++ (instancetype)requestWithURI: (OFURI *)URI;
/**
- * @brief Initializes an already allocated OFHTTPRequest with the specified URL.
+ * @brief Initializes an already allocated OFHTTPRequest with the specified URI.
*
- * @param URL The URL for the request
+ * @param URI The URI for the request
* @return An initialized OFHTTPRequest
*/
-- (instancetype)initWithURL: (OFURL *)URL;
+- (instancetype)initWithURI: (OFURI *)URI;
- (instancetype)init OF_UNAVAILABLE;
@end
#ifdef __cplusplus
@@ -143,12 +151,14 @@
/**
* @brief Returns the request method for the specified string.
*
* @param string The string for which the request method should be returned
* @return The request method for the specified string
+ * @throw OFInvalidFormatException The specified string is not a valid HTTP
+ * request method
*/
extern OFHTTPRequestMethod OFHTTPRequestMethodParseName(OFString *string);
#ifdef __cplusplus
}
#endif
OF_ASSUME_NONNULL_END
Index: src/OFHTTPRequest.m
==================================================================
--- src/OFHTTPRequest.m
+++ src/OFHTTPRequest.m
@@ -17,11 +17,11 @@
#include
#import "OFHTTPRequest.h"
#import "OFString.h"
-#import "OFURL.h"
+#import "OFURI.h"
#import "OFDictionary.h"
#import "OFData.h"
#import "OFArray.h"
#import "OFInvalidArgumentException.h"
@@ -72,27 +72,27 @@
if ([string isEqual: @"TRACE"])
return OFHTTPRequestMethodTrace;
if ([string isEqual: @"CONNECT"])
return OFHTTPRequestMethodConnect;
- @throw [OFInvalidArgumentException exception];
+ @throw [OFInvalidFormatException exception];
}
@implementation OFHTTPRequest
-@synthesize URL = _URL, method = _method, headers = _headers;
+@synthesize URI = _URI, method = _method, headers = _headers;
-+ (instancetype)requestWithURL: (OFURL *)URL
++ (instancetype)requestWithURI: (OFURI *)URI
{
- return [[[self alloc] initWithURL: URL] autorelease];
+ return [[[self alloc] initWithURI: URI] autorelease];
}
-- (instancetype)initWithURL: (OFURL *)URL
+- (instancetype)initWithURI: (OFURI *)URI
{
self = [super init];
@try {
- _URL = [URL copy];
+ _URI = [URI copy];
_method = OFHTTPRequestMethodGet;
_protocolVersion.major = 1;
_protocolVersion.minor = 1;
} @catch (id e) {
[self release];
@@ -107,11 +107,11 @@
OF_INVALID_INIT_METHOD
}
- (void)dealloc
{
- [_URL release];
+ [_URI release];
[_headers release];
[super dealloc];
}
@@ -131,11 +131,11 @@
return NULL;
}
- (id)copy
{
- OFHTTPRequest *copy = [[OFHTTPRequest alloc] initWithURL: _URL];
+ OFHTTPRequest *copy = [[OFHTTPRequest alloc] initWithURI: _URI];
@try {
copy->_method = _method;
copy->_protocolVersion = _protocolVersion;
copy.headers = _headers;
@@ -161,11 +161,11 @@
request = object;
if (request->_method != _method ||
request->_protocolVersion.major != _protocolVersion.major ||
request->_protocolVersion.minor != _protocolVersion.minor ||
- ![request->_URL isEqual: _URL] ||
+ ![request->_URI isEqual: _URI] ||
![request->_headers isEqual: _headers])
return false;
if (request.remoteAddress != self.remoteAddress &&
!OFSocketAddressEqual(request.remoteAddress, self.remoteAddress))
@@ -178,14 +178,14 @@
{
unsigned long hash;
OFHashInit(&hash);
- OFHashAdd(&hash, _method);
- OFHashAdd(&hash, _protocolVersion.major);
- OFHashAdd(&hash, _protocolVersion.minor);
- OFHashAddHash(&hash, _URL.hash);
+ OFHashAddByte(&hash, _method);
+ OFHashAddByte(&hash, _protocolVersion.major);
+ OFHashAddByte(&hash, _protocolVersion.minor);
+ OFHashAddHash(&hash, _URI.hash);
OFHashAddHash(&hash, _headers.hash);
if (_hasRemoteAddress)
OFHashAddHash(&hash, OFSocketAddressHash(&_remoteAddress));
OFHashFinalize(&hash);
@@ -254,17 +254,17 @@
remoteAddress = OFSocketAddressString(&_remoteAddress);
else
remoteAddress = nil;
ret = [[OFString alloc] initWithFormat:
- @"<%@:\n\tURL = %@\n"
+ @"<%@:\n\tURI = %@\n"
@"\tMethod = %s\n"
@"\tHeaders = %@\n"
@"\tRemote address = %@\n"
@">",
- self.class, _URL, method, indentedHeaders, remoteAddress];
+ self.class, _URI, method, indentedHeaders, remoteAddress];
objc_autoreleasePoolPop(pool);
return [ret autorelease];
}
@end
Index: src/OFHTTPResponse.h
==================================================================
--- src/OFHTTPResponse.h
+++ src/OFHTTPResponse.h
@@ -24,25 +24,35 @@
/**
* @class OFHTTPResponse OFHTTPResponse.h ObjFW/OFHTTPResponse.h
*
* @brief A class for representing an HTTP request response as a stream.
*/
+#if !defined(OF_HTTP_CLIENT_M) && !defined(OF_HTTP_SERVER_M)
+OF_SUBCLASSING_RESTRICTED
+#endif
@interface OFHTTPResponse: OFStream
{
OFHTTPRequestProtocolVersion _protocolVersion;
short _statusCode;
OFDictionary OF_GENERIC(OFString *, OFString *) *_headers;
- OF_RESERVE_IVARS(OFHTTPResponse, 4)
}
/**
* @brief The protocol version of the HTTP request response.
+ *
+ * @throw OFUnsupportedVersionException The specified version cannot be set
+ * because it is not supported
*/
@property (nonatomic) OFHTTPRequestProtocolVersion protocolVersion;
/**
* @brief The protocol version of the HTTP request response as a string.
+ *
+ * @throw OFUnsupportedVersionException The specified version cannot be set
+ * because it is not supported
+ * @throw OFInvalidFormatException The specified version cannot be set because
+ * it is not in a valid format
*/
@property (copy, nonatomic) OFString *protocolVersionString;
/**
* @brief The status code of the response to the HTTP request.
Index: src/OFHTTPServer.h
==================================================================
--- src/OFHTTPServer.h
+++ src/OFHTTPServer.h
@@ -104,20 +104,20 @@
}
/**
* @brief The host on which the HTTP server will listen.
*
- * Setting this after @ref start has been called raises an
- * @ref OFAlreadyConnectedException.
+ * @throw OFAlreadyConnectedException The host could not be set because
+ * @ref start had already been called
*/
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *host;
/**
* @brief The port on which the HTTP server will listen.
*
- * Setting this after @ref start has been called raises an
- * @ref OFAlreadyConnectedException.
+ * @throw OFAlreadyConnectedException The port could not be set because
+ * @ref start had already been called
*/
@property (nonatomic) uint16_t port;
/**
* @brief The delegate for the HTTP server.
@@ -132,12 +132,12 @@
* If this is larger than 1 (the default), one thread will be used to accept
* incoming connections and all others will be used to handle connections.
*
* For maximum CPU utilization, set this to `[OFSystemInfo numberOfCPUs] + 1`.
*
- * Setting this after @ref start has been called raises an
- * @ref OFAlreadyConnectedException.
+ * @throw OFAlreadyConnectedException The number of threads could not be set
+ * because @ref start had already been called
*/
@property (nonatomic) size_t numberOfThreads;
#endif
/**
@@ -155,10 +155,12 @@
*/
+ (instancetype)server;
/**
* @brief Starts the HTTP server in the current thread's run loop.
+ *
+ * @throw OFAlreadyConnectedException The server had already been started
*/
- (void)start;
/**
* @brief Stops the HTTP server, meaning it will not accept any new incoming
Index: src/OFHTTPServer.m
==================================================================
--- src/OFHTTPServer.m
+++ src/OFHTTPServer.m
@@ -11,10 +11,12 @@
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
+#define OF_HTTP_SERVER_M
+
#include "config.h"
#include
#include
#include
@@ -29,11 +31,11 @@
#import "OFNumber.h"
#import "OFSocket+Private.h"
#import "OFTCPSocket.h"
#import "OFThread.h"
#import "OFTimer.h"
-#import "OFURL.h"
+#import "OFURI.h"
#import "OFAlreadyConnectedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
@@ -357,11 +359,11 @@
OFMutableString *path;
size_t pos;
@try {
OFString *version = [line
- substringWithRange: OFRangeMake(line.length - 9, 9)];
+ substringWithRange: OFMakeRange(line.length - 9, 9)];
OFUnichar tmp;
if (![version hasPrefix: @" HTTP/1."])
return [self sendErrorAndClose: 505];
@@ -384,11 +386,11 @@
} @catch (OFInvalidArgumentException *e) {
return [self sendErrorAndClose: 405];
}
@try {
- OFRange range = OFRangeMake(pos + 1, line.length - pos - 10);
+ OFRange range = OFMakeRange(pos + 1, line.length - pos - 10);
path = [[[line substringWithRange:
range] mutableCopy] autorelease];
} @catch (OFOutOfRangeException *e) {
return [self sendErrorAndClose: 400];
@@ -510,11 +512,11 @@
}
- (void)createResponse
{
void *pool = objc_autoreleasePoolPush();
- OFMutableURL *URL;
+ OFMutableURI *URI;
OFHTTPRequest *request;
OFHTTPServerResponse *response;
size_t pos;
[_timer invalidate];
@@ -530,37 +532,36 @@
[_host release];
_host = [_server.host copy];
_port = [_server port];
}
- URL = [OFMutableURL URL];
- URL.scheme = @"http";
- URL.host = _host;
+ URI = [OFMutableURI URIWithScheme: @"http"];
+ URI.host = _host;
if (_port != 80)
- URL.port = [OFNumber numberWithUnsignedShort: _port];
+ URI.port = [OFNumber numberWithUnsignedShort: _port];
@try {
if ((pos = [_path rangeOfString: @"?"].location) !=
OFNotFound) {
OFString *path, *query;
path = [_path substringToIndex: pos];
query = [_path substringFromIndex: pos + 1];
- URL.URLEncodedPath = path;
- URL.URLEncodedQuery = query;
+ URI.percentEncodedPath = path;
+ URI.percentEncodedQuery = query;
} else
- URL.URLEncodedPath = _path;
+ URI.percentEncodedPath = _path;
} @catch (OFInvalidFormatException *e) {
objc_autoreleasePoolPop(pool);
[self sendErrorAndClose: 400];
return;
}
- [URL makeImmutable];
+ [URI makeImmutable];
- request = [OFHTTPRequest requestWithURL: URL];
+ request = [OFHTTPRequest requestWithURI: URI];
request.method = _method;
request.protocolVersion =
(OFHTTPRequestProtocolVersion){ 1, _HTTPMinorVersion };
request.headers = _headers;
request.remoteAddress = _socket.remoteAddress;
ADDED src/OFHTTPURIHandler.h
Index: src/OFHTTPURIHandler.h
==================================================================
--- src/OFHTTPURIHandler.h
+++ src/OFHTTPURIHandler.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFURIHandler.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@interface OFHTTPURIHandler: OFURIHandler
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFHTTPURIHandler.m
Index: src/OFHTTPURIHandler.m
==================================================================
--- src/OFHTTPURIHandler.m
+++ src/OFHTTPURIHandler.m
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFHTTPURIHandler.h"
+#import "OFHTTPClient.h"
+#import "OFHTTPRequest.h"
+#import "OFHTTPResponse.h"
+
+@implementation OFHTTPURIHandler
+- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFHTTPClient *client = [OFHTTPClient client];
+ OFHTTPRequest *request = [OFHTTPRequest requestWithURI: URI];
+ OFHTTPResponse *response = [client performRequest: request];
+
+ [response retain];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [response autorelease];
+}
+@end
DELETED src/OFHTTPURLHandler.h
Index: src/OFHTTPURLHandler.h
==================================================================
--- src/OFHTTPURLHandler.h
+++ src/OFHTTPURLHandler.h
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFURLHandler.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-@interface OFHTTPURLHandler: OFURLHandler
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/OFHTTPURLHandler.m
Index: src/OFHTTPURLHandler.m
==================================================================
--- src/OFHTTPURLHandler.m
+++ src/OFHTTPURLHandler.m
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFHTTPURLHandler.h"
-#import "OFHTTPClient.h"
-#import "OFHTTPRequest.h"
-#import "OFHTTPResponse.h"
-
-@implementation OFHTTPURLHandler
-- (OFStream *)openItemAtURL: (OFURL *)URL
- mode: (OFString *)mode
-{
- void *pool = objc_autoreleasePoolPush();
- OFHTTPClient *client = [OFHTTPClient client];
- OFHTTPRequest *request = [OFHTTPRequest requestWithURL: URL];
- OFHTTPResponse *response = [client performRequest: request];
-
- [response retain];
-
- objc_autoreleasePoolPop(pool);
-
- return [response autorelease];
-}
-@end
Index: src/OFINICategory.h
==================================================================
--- src/OFINICategory.h
+++ src/OFINICategory.h
@@ -76,10 +76,12 @@
*
* @param key The key for which the long long should be returned
* @param defaultValue The value to return if the key does not exist
* @return The long long for the specified key or the specified default value
* if it does not exist
+ * @throw OFInvalidFormatException The specified key is not in the correct
+ * format for a long long
*/
- (long long)longLongForKey: (OFString *)key
defaultValue: (long long)defaultValue;
/**
@@ -91,10 +93,12 @@
*
* @param key The key for which the bool should be returned
* @param defaultValue The value to return if the key does not exist
* @return The bool for the specified key or the specified default value if it
* does not exist
+ * @throw OFInvalidFormatException The specified key is not in the correct
+ * format for a bool
*/
- (bool)boolForKey: (OFString *)key defaultValue: (bool)defaultValue;
/**
* @brief Returns the float for the specified key or the specified default
@@ -105,10 +109,12 @@
*
* @param key The key for which the float should be returned
* @param defaultValue The value to return if the key does not exist
* @return The float for the specified key or the specified default value if it
* does not exist
+ * @throw OFInvalidFormatException The specified key is not in the correct
+ * format for a float
*/
- (float)floatForKey: (OFString *)key defaultValue: (float)defaultValue;
/**
* @brief Returns the double for the specified key or the specified default
@@ -119,10 +125,12 @@
*
* @param key The key for which the double should be returned
* @param defaultValue The value to return if the key does not exist
* @return The double for the specified key or the specified default value if
* it does not exist
+ * @throw OFInvalidFormatException The specified key is not in the correct
+ * format for a double
*/
- (double)doubleForKey: (OFString *)key defaultValue: (double)defaultValue;
/**
* @brief Returns an array of strings for the specified multi-key, or an empty
Index: src/OFINICategory.m
==================================================================
--- src/OFINICategory.m
+++ src/OFINICategory.m
@@ -56,11 +56,11 @@
[mutableString replaceOccurrencesOfString: @"\f" withString: @"\\f"];
[mutableString replaceOccurrencesOfString: @"\r" withString: @"\\r"];
[mutableString replaceOccurrencesOfString: @"\n" withString: @"\\n"];
[mutableString replaceOccurrencesOfString: @"\"" withString: @"\\\""];
- [mutableString prependString: @"\""];
+ [mutableString insertString: @"\"" atIndex: 0];
[mutableString appendString: @"\""];
[mutableString makeImmutable];
return mutableString;
@@ -72,11 +72,11 @@
OFMutableString *mutableString;
if (![string hasPrefix: @"\""] || ![string hasSuffix: @"\""])
return string;
- string = [string substringWithRange: OFRangeMake(1, string.length - 2)];
+ string = [string substringWithRange: OFMakeRange(1, string.length - 2)];
mutableString = [[string mutableCopy] autorelease];
[mutableString replaceOccurrencesOfString: @"\\f" withString: @"\f"];
[mutableString replaceOccurrencesOfString: @"\\r" withString: @"\r"];
[mutableString replaceOccurrencesOfString: @"\\n" withString: @"\n"];
Index: src/OFINIFile.h
==================================================================
--- src/OFINIFile.h
+++ src/OFINIFile.h
@@ -18,11 +18,11 @@
#import "OFINICategory.h"
OF_ASSUME_NONNULL_BEGIN
@class OFMutableArray OF_GENERIC(ObjectType);
-@class OFURL;
+@class OFURI;
/**
* @class OFINIFile OFINIFile.h ObjFW/OFINIFile.h
*
* @brief A class for reading, creating and modifying INI files.
@@ -39,49 +39,63 @@
@property (readonly, nonatomic) OFArray OF_GENERIC(OFINICategory *) *categories;
/**
* @brief Creates a new OFINIFile with the contents of the specified file.
*
- * @param URL The URL to the file whose contents the OFINIFile should contain
+ * @param URI The URI to the file whose contents the OFINIFile should contain
*
* @return A new, autoreleased OFINIFile with the contents of the specified file
+ * @throw OFInvalidFormatException The format of the specified INI file is
+ * invalid
+ * @throw OFInvalidEncodingException The INI file is not in the specified
+ * encoding
*/
-+ (instancetype)fileWithURL: (OFURL *)URL;
++ (instancetype)fileWithURI: (OFURI *)URI;
/**
* @brief Creates a new OFINIFile with the contents of the specified file in
* the specified encoding.
*
- * @param URL The URL to the file whose contents the OFINIFile should contain
+ * @param URI The URI to the file whose contents the OFINIFile should contain
* @param encoding The encoding of the specified file
- *
* @return A new, autoreleased OFINIFile with the contents of the specified file
+ * @throw OFInvalidFormatException The format of the specified INI file is
+ * invalid
+ * @throw OFInvalidEncodingException The INI file is not in the specified
+ * encoding
*/
-+ (instancetype)fileWithURL: (OFURL *)URL encoding: (OFStringEncoding)encoding;
++ (instancetype)fileWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding;
- (instancetype)init OF_UNAVAILABLE;
/**
* @brief Initializes an already allocated OFINIFile with the contents of the
* specified file.
*
- * @param URL The URL to the file whose contents the OFINIFile should contain
+ * @param URI The URI to the file whose contents the OFINIFile should contain
*
* @return An initialized OFINIFile with the contents of the specified file
+ * @throw OFInvalidFormatException The format of the specified INI file is
+ * invalid
+ * @throw OFInvalidEncodingException The INI file is not in the specified
+ * encoding
*/
-- (instancetype)initWithURL: (OFURL *)URL;
+- (instancetype)initWithURI: (OFURI *)URI;
/**
* @brief Initializes an already allocated OFINIFile with the contents of the
* specified file in the specified encoding.
*
- * @param URL The URL to the file whose contents the OFINIFile should contain
+ * @param URI The URI to the file whose contents the OFINIFile should contain
* @param encoding The encoding of the specified file
- *
* @return An initialized OFINIFile with the contents of the specified file
+ * @throw OFInvalidFormatException The format of the specified INI file is
+ * invalid
+ * @throw OFInvalidEncodingException The INI file is not in the specified
+ * encoding
*/
-- (instancetype)initWithURL: (OFURL *)URL
+- (instancetype)initWithURI: (OFURI *)URI
encoding: (OFStringEncoding)encoding
OF_DESIGNATED_INITIALIZER;
/**
* @brief Returns an @ref OFINICategory for the category with the specified
@@ -95,20 +109,20 @@
- (OFINICategory *)categoryForName: (OFString *)name;
/**
* @brief Writes the contents of the OFINIFile to a file.
*
- * @param URL The URL of the file to write to
+ * @param URI The URI of the file to write to
*/
-- (void)writeToURL: (OFURL *)URL;
+- (void)writeToURI: (OFURI *)URI;
/**
* @brief Writes the contents of the OFINIFile to a file in the specified
* encoding.
*
- * @param URL The URL of the file to write to
+ * @param URI The URI of the file to write to
* @param encoding The encoding to use
*/
-- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding;
+- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding;
@end
OF_ASSUME_NONNULL_END
Index: src/OFINIFile.m
==================================================================
--- src/OFINIFile.m
+++ src/OFINIFile.m
@@ -21,19 +21,19 @@
#import "OFArray.h"
#import "OFINICategory+Private.h"
#import "OFINICategory.h"
#import "OFStream.h"
#import "OFString.h"
-#import "OFURL.h"
-#import "OFURLHandler.h"
+#import "OFURI.h"
+#import "OFURIHandler.h"
#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"
OF_DIRECT_MEMBERS
@interface OFINIFile ()
-- (void)of_parseURL: (OFURL *)URL encoding: (OFStringEncoding)encoding;
+- (void)of_parseURI: (OFURI *)URI encoding: (OFStringEncoding)encoding;
@end
static bool
isWhitespaceLine(OFString *line)
{
@@ -48,38 +48,38 @@
}
@implementation OFINIFile
@synthesize categories = _categories;
-+ (instancetype)fileWithURL: (OFURL *)URL
++ (instancetype)fileWithURI: (OFURI *)URI
{
- return [[[self alloc] initWithURL: URL] autorelease];
+ return [[[self alloc] initWithURI: URI] autorelease];
}
-+ (instancetype)fileWithURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
++ (instancetype)fileWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
- return [[[self alloc] initWithURL: URL encoding: encoding] autorelease];
+ return [[[self alloc] initWithURI: URI encoding: encoding] autorelease];
}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
-- (instancetype)initWithURL: (OFURL *)URL
+- (instancetype)initWithURI: (OFURI *)URI
{
- return [self initWithURL: URL encoding: OFStringEncodingAutodetect];
+ return [self initWithURI: URI encoding: OFStringEncodingAutodetect];
}
-- (instancetype)initWithURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
+- (instancetype)initWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
self = [super init];
@try {
_categories = [[OFMutableArray alloc] init];
- [self of_parseURL: URL encoding: encoding];
+ [self of_parseURI: URI encoding: encoding];
} @catch (id e) {
[self release];
@throw e;
}
@@ -108,20 +108,22 @@
objc_autoreleasePoolPop(pool);
return category;
}
-- (void)of_parseURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
+- (void)of_parseURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
void *pool = objc_autoreleasePoolPush();
OFStream *file;
OFINICategory *category = nil;
OFString *line;
+ if (encoding == OFStringEncodingAutodetect)
+ encoding = OFStringEncodingUTF8;
+
@try {
- file = [[OFURLHandler handlerForURL: URL] openItemAtURL: URL
- mode: @"r"];
+ file = [OFURIHandler openItemAtURI: URI mode: @"r"];
} @catch (OFOpenItemFailedException *e) {
/* Handle missing file like an empty file */
if (e.errNo == ENOENT)
return;
@@ -137,11 +139,11 @@
if (![line hasSuffix: @"]"])
@throw [OFInvalidFormatException exception];
categoryName = [line substringWithRange:
- OFRangeMake(1, line.length - 2)];
+ OFMakeRange(1, line.length - 2)];
category = [[[OFINICategory alloc]
of_initWithName: categoryName] autorelease];
[_categories addObject: category];
} else {
if (category == nil)
@@ -152,21 +154,19 @@
}
objc_autoreleasePoolPop(pool);
}
-- (void)writeToURL: (OFURL *)URL
+- (void)writeToURI: (OFURI *)URI
{
- [self writeToURL: URL encoding: OFStringEncodingUTF8];
+ [self writeToURI: URI encoding: OFStringEncodingUTF8];
}
-- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
+- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
void *pool = objc_autoreleasePoolPush();
- OFStream *file = [[OFURLHandler handlerForURL: URL]
- openItemAtURL: URL
- mode: @"w"];
+ OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"w"];
bool first = true;
for (OFINICategory *category in _categories)
if ([category of_writeToStream: file
encoding: encoding
Index: src/OFINIFileSettings.h
==================================================================
--- src/OFINIFileSettings.h
+++ src/OFINIFileSettings.h
@@ -17,15 +17,15 @@
OF_ASSUME_NONNULL_BEGIN
@class OFINIFile;
@class OFString;
-@class OFURL;
+@class OFURI;
@interface OFINIFileSettings: OFSettings
{
- OFURL *_fileURL;
+ OFURI *_fileURI;
OFINIFile *_INIFile;
}
@end
OF_ASSUME_NONNULL_END
Index: src/OFINIFileSettings.m
==================================================================
--- src/OFINIFileSettings.m
+++ src/OFINIFileSettings.m
@@ -18,11 +18,11 @@
#import "OFINIFileSettings.h"
#import "OFArray.h"
#import "OFINIFile.h"
#import "OFString.h"
#import "OFSystemInfo.h"
-#import "OFURL.h"
+#import "OFURI.h"
@implementation OFINIFileSettings
- (instancetype)initWithApplicationName: (OFString *)applicationName
{
self = [super initWithApplicationName: applicationName];
@@ -30,13 +30,13 @@
@try {
void *pool = objc_autoreleasePoolPush();
OFString *fileName;
fileName = [applicationName stringByAppendingString: @".ini"];
- _fileURL = [[[OFSystemInfo userConfigURL]
- URLByAppendingPathComponent: fileName] copy];
- _INIFile = [[OFINIFile alloc] initWithURL: _fileURL];
+ _fileURI = [[[OFSystemInfo userConfigURI]
+ URIByAppendingPathComponent: fileName] copy];
+ _INIFile = [[OFINIFile alloc] initWithURI: _fileURI];
objc_autoreleasePoolPop(pool);
} @catch (id e) {
[self release];
@throw e;
@@ -45,11 +45,11 @@
return self;
}
- (void)dealloc
{
- [_fileURL release];
+ [_fileURI release];
[_INIFile release];
[super dealloc];
}
@@ -241,8 +241,8 @@
objc_autoreleasePoolPop(pool);
}
- (void)save
{
- [_INIFile writeToURL: _fileURL];
+ [_INIFile writeToURI: _fileURI];
}
@end
DELETED src/OFIPSocketAsyncConnector.h
Index: src/OFIPSocketAsyncConnector.h
==================================================================
--- src/OFIPSocketAsyncConnector.h
+++ src/OFIPSocketAsyncConnector.h
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFDNSResolver.h"
-#import "OFRunLoop.h"
-#import "OFRunLoop+Private.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-@protocol OFIPSocketAsyncConnecting
-- (bool)of_createSocketForAddress: (const OFSocketAddress *)address
- errNo: (int *)errNo;
-- (bool)of_connectSocketToAddress: (const OFSocketAddress *)address
- errNo: (int *)errNo;
-- (void)of_closeSocket;
-@end
-
-@interface OFIPSocketAsyncConnector: OFObject
-{
- id _socket;
- OFString *_host;
- uint16_t _port;
- id _Nullable _delegate;
- id _Nullable _block;
- id _Nullable _exception;
- OFData *_Nullable _socketAddresses;
- size_t _socketAddressesIndex;
-}
-
-- (instancetype)initWithSocket: (id)sock
- host: (OFString *)host
- port: (uint16_t)port
- delegate: (nullable id)delegate
- block: (nullable id)block;
-- (void)didConnect;
-- (void)tryNextAddressWithRunLoopMode: (OFRunLoopMode)runLoopMode;
-- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/OFIPSocketAsyncConnector.m
Index: src/OFIPSocketAsyncConnector.m
==================================================================
--- src/OFIPSocketAsyncConnector.m
+++ src/OFIPSocketAsyncConnector.m
@@ -1,251 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#include
-
-#import "OFIPSocketAsyncConnector.h"
-#import "OFData.h"
-#import "OFTCPSocket.h"
-#import "OFThread.h"
-#import "OFTimer.h"
-
-#import "OFConnectionFailedException.h"
-#import "OFInvalidFormatException.h"
-
-@implementation OFIPSocketAsyncConnector
-- (instancetype)initWithSocket: (id)sock
- host: (OFString *)host
- port: (uint16_t)port
- delegate: (id)delegate
- block: (id)block
-{
- self = [super init];
-
- @try {
- _socket = [sock retain];
- _host = [host copy];
- _port = port;
- _delegate = [delegate retain];
- _block = [block copy];
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- [_socket release];
- [_host release];
- [_delegate release];
- [_block release];
- [_exception release];
- [_socketAddresses release];
-
- [super dealloc];
-}
-
-- (void)didConnect
-{
- if (_exception == nil)
- [_socket setCanBlock: true];
-
-#ifdef OF_HAVE_BLOCKS
- if (_block != NULL) {
- if ([_socket isKindOfClass: [OFTCPSocket class]])
- ((OFTCPSocketAsyncConnectBlock)_block)(_exception);
- else
- OFEnsure(0);
- } else {
-#endif
- if ([_delegate respondsToSelector:
- @selector(socket:didConnectToHost:port:exception:)])
- [_delegate socket: _socket
- didConnectToHost: _host
- port: _port
- exception: _exception];
-#ifdef OF_HAVE_BLOCKS
- }
-#endif
-}
-
-- (void)of_socketDidConnect: (id)sock exception: (id)exception
-{
- if (exception != nil) {
- /*
- * self might be retained only by the pending async requests,
- * which we're about to cancel.
- */
- [[self retain] autorelease];
-
- [sock cancelAsyncRequests];
- [sock of_closeSocket];
-
- if (_socketAddressesIndex >= _socketAddresses.count) {
- _exception = [exception retain];
- [self didConnect];
- } else {
- /*
- * We must not call it before returning, as otherwise
- * the new socket would be removed from the queue upon
- * return.
- */
- OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
- SEL selector =
- @selector(tryNextAddressWithRunLoopMode:);
- OFTimer *timer = [OFTimer
- timerWithTimeInterval: 0
- target: self
- selector: selector
- object: runLoop.currentMode
- repeats: false];
- [runLoop addTimer: timer forMode: runLoop.currentMode];
- }
-
- return;
- }
-
- [self didConnect];
-}
-
-- (id)of_connectionFailedExceptionForErrNo: (int)errNo
-{
- return [OFConnectionFailedException exceptionWithHost: _host
- port: _port
- socket: _socket
- errNo: errNo];
-}
-
-- (void)tryNextAddressWithRunLoopMode: (OFRunLoopMode)runLoopMode
-{
- OFSocketAddress address = *(const OFSocketAddress *)
- [_socketAddresses itemAtIndex: _socketAddressesIndex++];
- int errNo;
-
- OFSocketAddressSetPort(&address, _port);
-
- if (![_socket of_createSocketForAddress: &address errNo: &errNo]) {
- if (_socketAddressesIndex >= _socketAddresses.count) {
- _exception = [[OFConnectionFailedException alloc]
- initWithHost: _host
- port: _port
- socket: _socket
- errNo: errNo];
- [self didConnect];
- return;
- }
-
- [self tryNextAddressWithRunLoopMode: runLoopMode];
- return;
- }
-
-#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
- /*
- * On Wii and 3DS, connect() fails if non-blocking is enabled.
- *
- * Additionally, on Wii, there is no getsockopt(), so it would not be
- * possible to get the error (or success) after connecting anyway.
- *
- * So for now, connecting is blocking on Wii and 3DS.
- *
- * FIXME: Use a different thread as a work around.
- */
- [_socket setCanBlock: true];
-#else
- [_socket setCanBlock: false];
-#endif
-
- if (![_socket of_connectSocketToAddress: &address errNo: &errNo]) {
-#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
-# ifdef OF_WINDOWS
- if (errNo == EINPROGRESS || errNo == EWOULDBLOCK) {
-# else
- if (errNo == EINPROGRESS) {
-# endif
- [OFRunLoop of_addAsyncConnectForSocket: _socket
- mode: runLoopMode
- delegate: self];
- return;
- } else {
-#endif
- [_socket of_closeSocket];
-
- if (_socketAddressesIndex >= _socketAddresses.count) {
- _exception = [[OFConnectionFailedException
- alloc] initWithHost: _host
- port: _port
- socket: _socket
- errNo: errNo];
- [self didConnect];
- return;
- }
-
- [self tryNextAddressWithRunLoopMode: runLoopMode];
- return;
-#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
- }
-#endif
- }
-
-#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
- [_socket setCanBlock: false];
-#endif
-
- [self didConnect];
-}
-
-- (void)resolver: (OFDNSResolver *)resolver
- didResolveHost: (OFString *)host
- addresses: (OFData *)addresses
- exception: (id)exception
-{
- if (exception != nil) {
- _exception = [exception retain];
- [self didConnect];
- return;
- }
-
- _socketAddresses = [addresses copy];
-
- [self tryNextAddressWithRunLoopMode:
- [OFRunLoop currentRunLoop].currentMode];
-}
-
-- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode
-{
- @try {
- OFSocketAddress address = OFSocketAddressParseIP(_host, _port);
-
- _socketAddresses = [[OFData alloc]
- initWithItems: &address
- count: 1
- itemSize: sizeof(address)];
-
- [self tryNextAddressWithRunLoopMode: runLoopMode];
- return;
- } @catch (OFInvalidFormatException *e) {
- }
-
- [[OFThread DNSResolver]
- asyncResolveAddressesForHost: _host
- addressFamily: OFSocketAddressFamilyAny
- runLoopMode: runLoopMode
- delegate: self];
-}
-@end
Index: src/OFIPXSocket.h
==================================================================
--- src/OFIPXSocket.h
+++ src/OFIPXSocket.h
@@ -34,11 +34,11 @@
*
* Addresses are of type @ref OFSocketAddress. You can use
* @ref OFSocketAddressMakeIPX to create an address or
* @ref OFSocketAddressIPXNetwork to get the IPX network,
* @ref OFSocketAddressIPXNode to get the IPX node and
- * @ref OFSocketAddressPort to get the port (sometimes also called
+ * @ref OFSocketAddressIPXPort to get the port (sometimes also called
* socket number).
*
* @warning Even though the OFCopying protocol is implemented, it does *not*
* return an independent copy of the socket, but instead retains it.
* This is so that the socket can be used as a key for a dictionary,
@@ -65,14 +65,23 @@
/**
* @brief Bind the socket to the specified network, node and port with the
* specified packet type.
*
+ * @param network The IPX network to bind to. 0 means the current network.
+ * @param node The IPX network to bind to. An all zero node means the
+ * computer's node.
* @param port The port (sometimes called socket number) to bind to. 0 means to
- * pick one and return it.
+ * pick one and return via the returned socket address.
* @param packetType The packet type to use on the socket
* @return The address on which this socket can be reached
+ * @throw OFBindIPXSocketFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already bound
*/
-- (OFSocketAddress)bindToPort: (uint16_t)port packetType: (uint8_t)packetType;
+- (OFSocketAddress)
+ bindToNetwork: (uint32_t)network
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
+ port: (uint16_t)port
+ packetType: (uint8_t)packetType;
@end
OF_ASSUME_NONNULL_END
Index: src/OFIPXSocket.m
==================================================================
--- src/OFIPXSocket.m
+++ src/OFIPXSocket.m
@@ -24,42 +24,46 @@
#import "OFIPXSocket.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFAlreadyConnectedException.h"
-#import "OFBindFailedException.h"
+#import "OFBindIPXSocketFailedException.h"
@implementation OFIPXSocket
@dynamic delegate;
-- (OFSocketAddress)bindToPort: (uint16_t)port packetType: (uint8_t)packetType
+- (OFSocketAddress)bindToNetwork: (uint32_t)network
+ node: (const unsigned char [IPX_NODE_LEN])node
+ port: (uint16_t)port
+ packetType: (uint8_t)packetType
{
- const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
OFSocketAddress address;
int protocol = 0;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
int flags;
#endif
if (_socket != OFInvalidSocketHandle)
@throw [OFAlreadyConnectedException exceptionWithSocket: self];
- address = OFSocketAddressMakeIPX(0, zeroNode, port);
+ address = OFSocketAddressMakeIPX(network, node, port);
#ifdef OF_WINDOWS
protocol = NSPROTO_IPX + packetType;
#else
_packetType = address.sockaddr.ipx.sipx_type = packetType;
#endif
if ((_socket = socket(address.sockaddr.ipx.sipx_family,
SOCK_DGRAM | SOCK_CLOEXEC, protocol)) == OFInvalidSocketHandle)
- @throw [OFBindFailedException
- exceptionWithPort: port
- packetType: packetType
- socket: self
- errNo: OFSocketErrNo()];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: packetType
+ socket: self
+ errNo: OFSocketErrNo()];
_canBlock = true;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
@@ -71,14 +75,17 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: packetType
- socket: self
- errNo: errNo];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: packetType
+ socket: self
+ errNo: errNo];
}
memset(&address, 0, sizeof(address));
address.family = OFSocketAddressFamilyIPX;
address.length = (socklen_t)sizeof(address.sockaddr);
@@ -88,24 +95,30 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: packetType
- socket: self
- errNo: errNo];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: packetType
+ socket: self
+ errNo: errNo];
}
if (address.sockaddr.ipx.sipx_family != AF_IPX) {
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: packetType
- socket: self
- errNo: EAFNOSUPPORT];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: packetType
+ socket: self
+ errNo: EAFNOSUPPORT];
}
return address;
}
Index: src/OFInvocation.h
==================================================================
--- src/OFInvocation.h
+++ src/OFInvocation.h
@@ -24,16 +24,16 @@
/**
* @class OFInvocation OFInvocation.h ObjFW/OFInvocation.h
*
* @brief A class for storing and accessing invocations, and invoking them.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFInvocation: OFObject
{
OFMethodSignature *_methodSignature;
OFMutableArray OF_GENERIC(OFMutableData *) *_arguments;
OFMutableData *_returnValue;
- OF_RESERVE_IVARS(OFInvocation, 4)
}
/**
* @brief The method signature for the invocation.
*/
Index: src/OFKernelEventObserver.h
==================================================================
--- src/OFKernelEventObserver.h
+++ src/OFKernelEventObserver.h
@@ -43,12 +43,12 @@
@optional
/**
* @brief This callback is called when an object did get ready for reading.
*
* @note If the object is a subclass of @ref OFStream and
- * @ref OFStream::tryReadLine or @ref OFStream::tryReadTillDelimiter: has
- * been called on the stream, this callback will not be called again
+ * @ref OFStream::tryReadLine or @ref OFStream::tryReadUntilDelimiter:
+ * has been called on the stream, this callback will not be called again
* until new data has been received, even though there is still data in
* the cache. The reason for this is to prevent spinning in a loop when
* there is an incomplete string in the cache. Once the string has been
* completed, the callback will be called again as long there is data in
* the cache.
@@ -166,10 +166,12 @@
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent blocking even though the newly added object is ready.
*
* @param object The object to observe for reading
+ * @throw OFObserveKernelEventsFailedException Adding the object for observing
+ * failed
*/
- (void)addObjectForReading: (id )object;
/**
* @brief Adds an object to observe for writing.
@@ -176,10 +178,12 @@
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent blocking even though the newly added object is ready.
*
* @param object The object to observe for writing
+ * @throw OFObserveKernelEventsFailedException Adding the object for observing
+ * failed
*/
- (void)addObjectForWriting: (id )object;
/**
* @brief Removes an object to observe for reading.
@@ -186,10 +190,12 @@
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent the removed object from still being observed.
*
* @param object The object to remove from observing for reading
+ * @throw OFObserveKernelEventsFailedException Removing the object for observing
+ * failed
*/
- (void)removeObjectForReading: (id )object;
/**
* @brief Removes an object to observe for writing.
@@ -196,31 +202,40 @@
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent the removed object from still being observed.
*
* @param object The object to remove from observing for writing
+ * @throw OFObserveKernelEventsFailedException Removing the object for observing
+ * failed
*/
- (void)removeObjectForWriting: (id )object;
/**
* @brief Observes all objects and blocks until an event happens on an object.
+ *
+ * @throw OFObserveKernelEventsFailedException Observing for kernel events
+ * failed
*/
- (void)observe;
/**
* @brief Observes all objects until an event happens on an object or the
* timeout is reached.
*
* @param timeInterval The time to wait for an event, in seconds
+ * @throw OFObserveKernelEventsFailedException Observing for kernel events
+ * failed
*/
- (void)observeForTimeInterval: (OFTimeInterval)timeInterval;
/**
* @brief Observes all objects until an event happens on an object or the
* specified date is reached.
*
* @param date The until which to observe
+ * @throw OFObserveKernelEventsFailedException Observing for kernel events
+ * failed
*/
- (void)observeUntilDate: (OFDate *)date;
/**
* @brief Cancels the currently blocking observe call.
Index: src/OFKeyValueCoding.h
==================================================================
--- src/OFKeyValueCoding.h
+++ src/OFKeyValueCoding.h
@@ -31,18 +31,22 @@
/**
* @brief Returns the value for the specified key.
*
* @param key The key of the value to return
* @return The value for the specified key
+ * @throw OFUndefinedKeyException The specified key does not exist and
+ * @ref valueForUndefinedKey: was not overridden
*/
- (nullable id)valueForKey: (OFString *)key;
/**
* @brief Returns the value for the specified key path.
*
* @param keyPath The key path of the value to return
* @return The value for the specified key path
+ * @throw OFUndefinedKeyException The specified key does not exist and
+ * @ref valueForUndefinedKey: was not overridden
*/
- (nullable id)valueForKeyPath: (OFString *)keyPath;
/**
* @brief This is called by @ref valueForKey: if the specified key does not
@@ -50,26 +54,33 @@
*
* By default, this throws an @ref OFUndefinedKeyException.
*
* @param key The undefined key of the value to return
* @return The value for the specified undefined key
+ * @throw OFUndefinedKeyException The specified key does not exist
*/
- (nullable id)valueForUndefinedKey: (OFString *)key;
/**
* @brief Set the value for the specified key.
*
* @param value The value for the specified key
* @param key The key of the value to set
+ * @throw OFUndefinedKeyException The specified key does not exist and
+ * @ref setValue:forUndefinedKey: was not
+ * overridden
*/
- (void)setValue: (nullable id)value forKey: (OFString *)key;
/**
* @brief Set the value for the specified key path.
*
* @param value The value for the specified key path
* @param keyPath The key path of the value to set
+ * @throw OFUndefinedKeyException The specified key does not exist and
+ * @ref setValue:forUndefinedKey: was not
+ * overridden
*/
- (void)setValue: (nullable id)value forKeyPath: (OFString *)keyPath;
/**
* @brief This is called by @ref setValue:forKey: if the specified key does not
@@ -77,10 +88,11 @@
*
* By default, this throws an @ref OFUndefinedKeyException.
*
* @param value The value for the specified undefined key
* @param key The undefined key of the value to set
+ * @throw OFUndefinedKeyException The specified key does not exist
*/
- (void)setValue: (nullable id)value forUndefinedKey: (OFString *)key;
/**
* @brief This is called by @ref setValue:forKey: if the specified key is a
Index: src/OFKqueueKernelEventObserver.m
==================================================================
--- src/OFKqueueKernelEventObserver.m
+++ src/OFKqueueKernelEventObserver.m
@@ -29,11 +29,11 @@
#import "OFKqueueKernelEventObserver.h"
#import "OFArray.h"
#import "OFInitializationFailedException.h"
-#import "OFObserveFailedException.h"
+#import "OFObserveKernelEventsFailedException.h"
#import "OFOutOfRangeException.h"
#define eventListSize 64
@implementation OFKqueueKernelEventObserver
@@ -92,12 +92,13 @@
* switched this to `void *` in NetBSD 10.
*/
event.udata = (__typeof__(event.udata))object;
if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
[super addObjectForReading: object];
}
- (void)addObjectForWriting: (id )object
@@ -113,12 +114,13 @@
* switched this to `void *` in NetBSD 10.
*/
event.udata = (__typeof__(event.udata))object;
if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
[super addObjectForWriting: object];
}
- (void)removeObjectForReading: (id )object
@@ -129,12 +131,13 @@
event.ident = object.fileDescriptorForReading;
event.filter = EVFILT_READ;
event.flags = EV_DELETE;
if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
[super removeObjectForReading: object];
}
- (void)removeObjectForWriting: (id )object
@@ -145,12 +148,13 @@
event.ident = object.fileDescriptorForWriting;
event.filter = EVFILT_WRITE;
event.flags = EV_DELETE;
if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
[super removeObjectForWriting: object];
}
- (void)observeForTimeInterval: (OFTimeInterval)timeInterval
@@ -167,18 +171,19 @@
events = kevent(_kernelQueue, NULL, 0, eventList, eventListSize,
(timeInterval != -1 ? &timeout : NULL));
if (events < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
for (int i = 0; i < events; i++) {
void *pool;
if (eventList[i].flags & EV_ERROR)
- @throw [OFObserveFailedException
+ @throw [OFObserveKernelEventsFailedException
exceptionWithObserver: self
errNo: (int)eventList[i].data];
if (eventList[i].ident == (uintptr_t)_cancelFD[0]) {
char buffer;
Index: src/OFLHAArchive.h
==================================================================
--- src/OFLHAArchive.h
+++ src/OFLHAArchive.h
@@ -19,10 +19,11 @@
#import "OFString.h"
OF_ASSUME_NONNULL_BEGIN
@class OFStream;
+@class OFURI;
/**
* @class OFLHAArchive OFLHAArchive.h ObjFW/OFLHAArchive.h
*
* @brief A class for accessing and manipulating LHA files.
@@ -31,28 +32,22 @@
@interface OFLHAArchive: OFObject
{
OFStream *_stream;
uint_least8_t _mode;
OFStringEncoding _encoding;
+ OFLHAArchiveEntry *_Nullable _currentEntry;
+#ifdef OF_LHA_ARCHIVE_M
+@public
+#endif
OFStream *_Nullable _lastReturnedStream;
}
/**
* @brief The encoding to use for the archive. Defaults to ISO 8859-1.
*/
@property (nonatomic) OFStringEncoding encoding;
-/**
- * @brief A stream for reading the current entry.
- *
- * @note This is only available in read mode.
- *
- * @note The returned stream conforms to @ref OFReadyForReadingObserving if the
- * underlying stream does so, too.
- */
-@property (readonly, nonatomic) OFStream *streamForReadingCurrentEntry;
-
/**
* @brief Creates a new OFLHAArchive object with the specified stream.
*
* @param stream A stream from which the LHA archive will be read.
* For read and append mode, this needs to be an OFSeekableStream.
@@ -61,22 +56,31 @@
* archive.
* @return A new, autoreleased OFLHAArchive
*/
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode;
-#ifdef OF_HAVE_FILES
/**
* @brief Creates a new OFLHAArchive object with the specified file.
*
- * @param path The path to the LHA file
+ * @param URI The URI to the LHA file
* @param mode The mode for the LHA file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return A new, autoreleased OFLHAArchive
*/
-+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode;
-#endif
++ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode;
+
+/**
+ * @brief Creates a URI for accessing a the specified file within the specified
+ * LHA archive.
+ *
+ * @param path The path of the file within the archive
+ * @param URI The URI of the archive
+ * @return A URI for accessing the specified file within the specified LHA
+ * archive
+ */
++ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI;
- (instancetype)init OF_UNAVAILABLE;
/**
* @brief Initializes an already allocated OFLHAArchive object with the
@@ -90,23 +94,21 @@
* @return An initialized OFLHAArchive
*/
- (instancetype)initWithStream: (OFStream *)stream
mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;
-#ifdef OF_HAVE_FILES
/**
* @brief Initializes an already allocated OFLHAArchive object with the
* specified file.
*
- * @param path The path to the LHA file
+ * @param URI The URI to the LHA file
* @param mode The mode for the LHA file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return An initialized OFLHAArchive
*/
-- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode;
-#endif
+- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode;
/**
* @brief Returns the next entry from the LHA archive or `nil` if all entries
* have been read.
*
@@ -118,13 +120,29 @@
* invalidated stream will throw an @ref OFReadFailedException or
* @ref OFWriteFailedException!
*
* @return The next entry from the LHA archive or `nil` if all entries have
* been read
+ * @throw OFInvalidFormatException The archive's format is invalid
+ * @throw OFUnsupportedVersionException The archive's format is of an
+ * unsupported version
+ * @throw OFTruncatedDataException The archive was truncated
*/
- (nullable OFLHAArchiveEntry *)nextEntry;
+/**
+ * @brief Returns a stream for reading the current entry.
+ *
+ * @note This is only available in read mode.
+ *
+ * @note The returned stream conforms to @ref OFReadyForReadingObserving if the
+ * underlying stream does so, too.
+ *
+ * @return A stream for reading the current entry
+ */
+- (OFStream *)streamForReadingCurrentEntry;
+
/**
* @brief Returns a stream for writing the specified entry.
*
* @note This is only available in write and append mode.
*
@@ -145,10 +163,12 @@
*/
- (OFStream *)streamForWritingEntry: (OFLHAArchiveEntry *)entry;
/**
* @brief Closes the OFLHAArchive.
+ *
+ * @throw OFNotOpenException The archive is not open
*/
- (void)close;
@end
OF_ASSUME_NONNULL_END
Index: src/OFLHAArchive.m
==================================================================
--- src/OFLHAArchive.m
+++ src/OFLHAArchive.m
@@ -11,25 +11,27 @@
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
+#define OF_LHA_ARCHIVE_M
+
#include "config.h"
#include
#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"
+#import "OFArchiveURIHandler.h"
#import "OFCRC16.h"
-#ifdef OF_HAVE_FILES
-# import "OFFile.h"
-#endif
#import "OFLHADecompressingStream.h"
-#import "OFStream.h"
#import "OFSeekableStream.h"
+#import "OFStream.h"
#import "OFString.h"
+#import "OFURI.h"
+#import "OFURIHandler.h"
#import "OFChecksumMismatchException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
@@ -44,36 +46,40 @@
};
OF_DIRECT_MEMBERS
@interface OFLHAArchiveFileReadStream: OFStream
{
+ OFLHAArchive *_archive;
OFStream *_stream, *_decompressedStream;
OFLHAArchiveEntry *_entry;
- uint32_t _toRead, _bytesConsumed;
+ unsigned long long _toRead;
uint16_t _CRC16;
bool _atEndOfStream, _skipped;
}
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFLHAArchiveEntry *)entry;
+- (instancetype)of_initWithArchive: (OFLHAArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFLHAArchiveEntry *)entry;
- (void)of_skip;
@end
OF_DIRECT_MEMBERS
@interface OFLHAArchiveFileWriteStream: OFStream
{
+ OFLHAArchive *_archive;
OFMutableLHAArchiveEntry *_entry;
OFStringEncoding _encoding;
OFSeekableStream *_stream;
- OFFileOffset _headerOffset;
+ OFStreamOffset _headerOffset;
uint32_t _bytesWritten;
uint16_t _CRC16;
}
-- (instancetype)of_initWithStream: (OFSeekableStream *)stream
- entry: (OFLHAArchiveEntry *)entry
- encoding: (OFStringEncoding)encoding;
+- (instancetype)of_initWithArchive: (OFLHAArchive *)archive
+ stream: (OFSeekableStream *)stream
+ entry: (OFLHAArchiveEntry *)entry
+ encoding: (OFStringEncoding)encoding;
@end
@implementation OFLHAArchive
@synthesize encoding = _encoding;
@@ -80,16 +86,19 @@
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode
{
return [[[self alloc] initWithStream: stream mode: mode] autorelease];
}
-#ifdef OF_HAVE_FILES
-+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode
++ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode
{
- return [[[self alloc] initWithPath: path mode: mode] autorelease];
+ return [[[self alloc] initWithURI: URI mode: mode] autorelease];
}
-#endif
+
++ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI
+{
+ return OFArchiveURIHandlerURIForFileInArchive(@"lha", path, URI);
+}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
@@ -114,11 +123,11 @@
![_stream isKindOfClass: [OFSeekableStream class]])
@throw [OFInvalidArgumentException exception];
if (_mode == modeAppend)
[(OFSeekableStream *)_stream seekToOffset: 0
- whence: SEEK_END];
+ whence: OFSeekEnd];
_encoding = OFStringEncodingISO8859_1;
} @catch (id e) {
[self release];
@throw e;
@@ -125,54 +134,59 @@
}
return self;
}
-#ifdef OF_HAVE_FILES
-- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode
-{
- OFFile *file;
-
- if ([mode isEqual: @"a"])
- file = [[OFFile alloc] initWithPath: path mode: @"r+"];
- else
- file = [[OFFile alloc] initWithPath: path mode: mode];
+- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFStream *stream;
@try {
- self = [self initWithStream: file mode: mode];
- } @finally {
- [file release];
+ if ([mode isEqual: @"a"])
+ stream = [OFURIHandler openItemAtURI: URI mode: @"r+"];
+ else
+ stream = [OFURIHandler openItemAtURI: URI mode: mode];
+ } @catch (id e) {
+ [self release];
+ @throw e;
}
+
+ self = [self initWithStream: stream mode: mode];
+
+ objc_autoreleasePoolPop(pool);
return self;
}
-#endif
- (void)dealloc
{
if (_stream != nil)
[self close];
+ [_currentEntry release];
+
[super dealloc];
}
- (OFLHAArchiveEntry *)nextEntry
{
- OFLHAArchiveEntry *entry;
char header[21];
size_t headerLen;
if (_mode != modeRead)
@throw [OFInvalidArgumentException exception];
+
+ [_currentEntry release];
+ _currentEntry = nil;
[(OFLHAArchiveFileReadStream *)_lastReturnedStream of_skip];
@try {
[_lastReturnedStream close];
} @catch (OFNotOpenException *e) {
/* Might have already been closed by the user - that's fine. */
}
- [_lastReturnedStream release];
_lastReturnedStream = nil;
for (headerLen = 0; headerLen < 21;) {
if (_stream.atEndOfStream) {
if (headerLen == 0)
@@ -186,32 +200,34 @@
headerLen += [_stream readIntoBuffer: header + headerLen
length: 21 - headerLen];
}
- entry = [[[OFLHAArchiveEntry alloc]
+ _currentEntry= [[OFLHAArchiveEntry alloc]
of_initWithHeader: header
stream: _stream
- encoding: _encoding] autorelease];
+ encoding: _encoding];
- _lastReturnedStream = [[OFLHAArchiveFileReadStream alloc]
- of_initWithStream: _stream
- entry: entry];
-
- return entry;
+ return _currentEntry;
}
- (OFStream *)streamForReadingCurrentEntry
{
if (_mode != modeRead)
@throw [OFInvalidArgumentException exception];
- if (_lastReturnedStream == nil)
+ if (_currentEntry == nil)
@throw [OFInvalidArgumentException exception];
- return [[(OFLHAArchiveFileReadStream *)_lastReturnedStream
- retain] autorelease];
+ _lastReturnedStream = [[[OFLHAArchiveFileReadStream alloc]
+ of_initWithArchive: self
+ stream: _stream
+ entry: _currentEntry] autorelease];
+ [_currentEntry release];
+ _currentEntry = nil;
+
+ return _lastReturnedStream;
}
- (OFStream *)streamForWritingEntry: (OFLHAArchiveEntry *)entry
{
OFString *compressionMethod;
@@ -229,20 +245,19 @@
@try {
[_lastReturnedStream close];
} @catch (OFNotOpenException *e) {
/* Might have already been closed by the user - that's fine. */
}
- [_lastReturnedStream release];
_lastReturnedStream = nil;
- _lastReturnedStream = [[OFLHAArchiveFileWriteStream alloc]
- of_initWithStream: (OFSeekableStream *)_stream
- entry: entry
- encoding: _encoding];
+ _lastReturnedStream = [[[OFLHAArchiveFileWriteStream alloc]
+ of_initWithArchive: self
+ stream: (OFSeekableStream *)_stream
+ entry: entry
+ encoding: _encoding] autorelease];
- return [[(OFLHAArchiveFileWriteStream *)_lastReturnedStream
- retain] autorelease];
+ return _lastReturnedStream;
}
- (void)close
{
if (_stream == nil)
@@ -251,27 +266,28 @@
@try {
[_lastReturnedStream close];
} @catch (OFNotOpenException *e) {
/* Might have already been closed by the user - that's fine. */
}
- [_lastReturnedStream release];
_lastReturnedStream = nil;
[_stream release];
_stream = nil;
}
@end
@implementation OFLHAArchiveFileReadStream
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFLHAArchiveEntry *)entry
+- (instancetype)of_initWithArchive: (OFLHAArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFLHAArchiveEntry *)entry
{
self = [super init];
@try {
OFString *compressionMethod;
+ _archive = [archive retain];
_stream = [stream retain];
compressionMethod = entry.compressionMethod;
if ([compressionMethod isEqual: @"-lh4-"] ||
@@ -308,10 +324,15 @@
if (_stream != nil || _decompressedStream != nil)
[self close];
[_entry release];
+ if (_archive->_lastReturnedStream == self)
+ _archive->_lastReturnedStream = nil;
+
+ [_archive release];
+
[super dealloc];
}
- (bool)lowlevelIsAtEndOfStream
{
@@ -333,11 +354,11 @@
if (_stream.atEndOfStream && !_decompressedStream.hasDataInReadBuffer)
@throw [OFTruncatedDataException exception];
if (length > _toRead)
- length = _toRead;
+ length = (size_t)_toRead;
ret = [_decompressedStream readIntoBuffer: buffer length: length];
_toRead -= ret;
_CRC16 = OFCRC16(_CRC16, buffer, ret);
@@ -373,11 +394,11 @@
}
- (void)of_skip
{
OFStream *stream;
- uint32_t toRead;
+ unsigned long long toRead;
if (_stream == nil || _skipped)
return;
stream = _stream;
@@ -398,22 +419,23 @@
stream = _stream;
}
if ([stream isKindOfClass: [OFSeekableStream class]] &&
- (sizeof(OFFileOffset) > 4 || toRead < INT32_MAX))
- [(OFSeekableStream *)stream seekToOffset: (OFFileOffset)toRead
- whence: SEEK_CUR];
+ toRead < LLONG_MAX && (long long)toRead == (OFStreamOffset)toRead)
+ [(OFSeekableStream *)stream seekToOffset: (OFStreamOffset)toRead
+ whence: OFSeekCurrent];
else {
while (toRead > 0) {
char buffer[512];
- size_t min = toRead;
+ unsigned long long min = toRead;
if (min > 512)
min = 512;
- toRead -= [stream readIntoBuffer: buffer length: min];
+ toRead -= [stream readIntoBuffer: buffer
+ length: (size_t)min];
}
}
_toRead = 0;
_skipped = true;
@@ -435,21 +457,23 @@
[super close];
}
@end
@implementation OFLHAArchiveFileWriteStream
-- (instancetype)of_initWithStream: (OFSeekableStream *)stream
- entry: (OFLHAArchiveEntry *)entry
- encoding: (OFStringEncoding)encoding
+- (instancetype)of_initWithArchive: (OFLHAArchive *)archive
+ stream: (OFSeekableStream *)stream
+ entry: (OFLHAArchiveEntry *)entry
+ encoding: (OFStringEncoding)encoding
{
self = [super init];
@try {
+ _archive = [archive retain];
_entry = [entry mutableCopy];
_encoding = encoding;
- _headerOffset = [stream seekToOffset: 0 whence: SEEK_CUR];
+ _headerOffset = [stream seekToOffset: 0 whence: OFSeekCurrent];
[_entry of_writeToStream: stream encoding: _encoding];
/*
* Retain stream last, so that -[close] called by -[dealloc]
* doesn't write in case of an error.
@@ -467,10 +491,15 @@
{
if (_stream != nil)
[self close];
[_entry release];
+
+ if (_archive->_lastReturnedStream == self)
+ _archive->_lastReturnedStream = nil;
+
+ [_archive release];
[super dealloc];
}
- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
@@ -515,25 +544,25 @@
.fileDescriptorForWriting;
}
- (void)close
{
- OFFileOffset offset;
+ OFStreamOffset offset;
if (_stream == nil)
@throw [OFNotOpenException exceptionWithObject: self];
_entry.uncompressedSize = _bytesWritten;
_entry.compressedSize = _bytesWritten;
_entry.CRC16 = _CRC16;
- offset = [_stream seekToOffset: 0 whence: SEEK_CUR];
- [_stream seekToOffset: _headerOffset whence: SEEK_SET];
+ offset = [_stream seekToOffset: 0 whence: OFSeekCurrent];
+ [_stream seekToOffset: _headerOffset whence: OFSeekSet];
[_entry of_writeToStream: _stream encoding: _encoding];
- [_stream seekToOffset: offset whence: SEEK_SET];
+ [_stream seekToOffset: offset whence: OFSeekSet];
[_stream release];
_stream = nil;
[super close];
}
@end
Index: src/OFLHAArchiveEntry+Private.h
==================================================================
--- src/OFLHAArchiveEntry+Private.h
+++ src/OFLHAArchiveEntry+Private.h
@@ -15,16 +15,16 @@
#import "OFLHAArchive.h"
OF_ASSUME_NONNULL_BEGIN
-OF_DIRECT_MEMBERS
@interface OFLHAArchiveEntry ()
+- (instancetype)of_init OF_METHOD_FAMILY(init);
- (instancetype)of_initWithHeader: (char [_Nonnull 21])header
stream: (OFStream *)stream
encoding: (OFStringEncoding)encoding
- OF_METHOD_FAMILY(init);
+ OF_METHOD_FAMILY(init) OF_DIRECT;
- (void)of_writeToStream: (OFStream *)stream
- encoding: (OFStringEncoding)encoding;
+ encoding: (OFStringEncoding)encoding OF_DIRECT;
@end
OF_ASSUME_NONNULL_END
Index: src/OFLHAArchiveEntry.h
==================================================================
--- src/OFLHAArchiveEntry.h
+++ src/OFLHAArchiveEntry.h
@@ -12,10 +12,11 @@
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#import "OFObject.h"
+#import "OFArchiveEntry.h"
OF_ASSUME_NONNULL_BEGIN
@class OFArray OF_GENERIC(ObjectType);
@class OFData;
@@ -27,51 +28,33 @@
/**
* @class OFLHAArchiveEntry OFLHAArchiveEntry.h ObjFW/OFLHAArchiveEntry.h
*
* @brief A class which represents an entry in an LHA archive.
*/
-@interface OFLHAArchiveEntry: OFObject
+@interface OFLHAArchiveEntry: OFObject
{
OFString *_fileName, *_Nullable _directoryName, *_compressionMethod;
- uint32_t _compressedSize, _uncompressedSize;
- OFDate *_date;
+ unsigned long long _compressedSize, _uncompressedSize;
+ OFDate *_modificationDate;
uint8_t _headerLevel;
uint16_t _CRC16;
uint8_t _operatingSystemIdentifier;
OFString *_Nullable _fileComment;
- OFNumber *_Nullable _mode, *_Nullable _UID, *_Nullable _GID;
- OFString *_Nullable _owner, *_Nullable _group;
- OFDate *_Nullable _modificationDate;
+ OFNumber *_Nullable _POSIXPermissions, *_Nullable _ownerAccountID;
+ OFNumber *_Nullable _groupOwnerAccountID;
+ OFString *_Nullable _ownerAccountName;
+ OFString *_Nullable _groupOwnerAccountName;
OFMutableArray OF_GENERIC(OFData *) *_extensions;
OF_RESERVE_IVARS(OFLHAArchiveEntry, 4)
}
-/**
- * @brief The file name of the entry.
- */
-@property (readonly, copy, nonatomic) OFString *fileName;
-
/**
* @brief The compression method of the entry.
*/
@property (readonly, copy, nonatomic) OFString *compressionMethod;
-/**
- * @brief The compressed size of the entry's file.
- */
-@property (readonly, nonatomic) uint32_t compressedSize;
-
-/**
- * @brief The uncompressed size of the entry's file.
- */
-@property (readonly, nonatomic) uint32_t uncompressedSize;
-
-/**
- * @brief The date of the file.
- */
-@property (readonly, retain, nonatomic) OFDate *date;
-
/**
* @brief The LHA level of the file.
*/
@property (readonly, nonatomic) uint8_t headerLevel;
@@ -83,70 +66,16 @@
/**
* @brief The operating system identifier of the file.
*/
@property (readonly, nonatomic) uint8_t operatingSystemIdentifier;
-/**
- * @brief The comment of the file.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *fileComment;
-
-/**
- * @brief The mode of the entry.
- */
-@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic) OFNumber *mode;
-
-/**
- * @brief The UID of the owner.
- */
-@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic) OFNumber *UID;
-
-/**
- * @brief The GID of the group.
- */
-@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic) OFNumber *GID;
-
-/**
- * @brief The owner of the file.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *owner;
-
-/**
- * @brief The group of the file.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *group;
-
-/**
- * @brief The date of the last modification of the file.
- */
-@property OF_NULLABLE_PROPERTY (readonly, retain, nonatomic)
- OFDate *modificationDate;
-
/**
* @brief The LHA extensions of the file.
*/
@property (readonly, copy, nonatomic) OFArray OF_GENERIC(OFData *) *extensions;
-/**
- * @brief Creates a new OFLHAArchiveEntry with the specified file name.
- *
- * @param fileName The file name for the OFLHAArchiveEntry
- * @return A new, autoreleased OFLHAArchiveEntry
- */
-+ (instancetype)entryWithFileName: (OFString *)fileName;
-
- (instancetype)init OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated OFLHAArchiveEntry with the specified
- * file name.
- *
- * @param fileName The file name for the OFLHAArchiveEntry
- * @return An initialized OFLHAArchiveEntry
- */
-- (instancetype)initWithFileName: (OFString *)fileName;
@end
OF_ASSUME_NONNULL_END
#import "OFMutableLHAArchiveEntry.h"
Index: src/OFLHAArchiveEntry.m
==================================================================
--- src/OFLHAArchiveEntry.m
+++ src/OFLHAArchiveEntry.m
@@ -111,70 +111,73 @@
static void
parsePermissionsExtension(OFLHAArchiveEntry *entry, OFData *extension,
OFStringEncoding encoding)
{
- uint16_t mode;
+ uint16_t POSIXPermissions;
if (extension.count != 3)
@throw [OFInvalidFormatException exception];
- memcpy(&mode, (char *)extension.items + 1, 2);
- mode = OFFromLittleEndian16(mode);
+ memcpy(&POSIXPermissions, (char *)extension.items + 1, 2);
+ POSIXPermissions = OFFromLittleEndian16(POSIXPermissions);
- [entry->_mode release];
- entry->_mode = nil;
+ [entry->_POSIXPermissions release];
+ entry->_POSIXPermissions = nil;
- entry->_mode = [[OFNumber alloc] initWithUnsignedShort: mode];
+ entry->_POSIXPermissions =
+ [[OFNumber alloc] initWithUnsignedShort: POSIXPermissions];
}
static void
parseGIDUIDExtension(OFLHAArchiveEntry *entry, OFData *extension,
OFStringEncoding encoding)
{
- uint16_t UID, GID;
+ uint16_t ownerAccountID, groupOwnerAccountID;
if (extension.count != 5)
@throw [OFInvalidFormatException exception];
- memcpy(&GID, (char *)extension.items + 1, 2);
- GID = OFFromLittleEndian16(GID);
-
- memcpy(&UID, (char *)extension.items + 3, 2);
- UID = OFFromLittleEndian16(UID);
-
- [entry->_GID release];
- entry->_GID = nil;
-
- [entry->_UID release];
- entry->_UID = nil;
-
- entry->_GID = [[OFNumber alloc] initWithUnsignedShort: GID];
- entry->_UID = [[OFNumber alloc] initWithUnsignedShort: UID];
+ memcpy(&groupOwnerAccountID, (char *)extension.items + 1, 2);
+ groupOwnerAccountID = OFFromLittleEndian16(groupOwnerAccountID);
+
+ memcpy(&ownerAccountID, (char *)extension.items + 3, 2);
+ ownerAccountID = OFFromLittleEndian16(ownerAccountID);
+
+ [entry->_groupOwnerAccountID release];
+ entry->_groupOwnerAccountID = nil;
+
+ [entry->_ownerAccountID release];
+ entry->_ownerAccountID = nil;
+
+ entry->_groupOwnerAccountID =
+ [[OFNumber alloc] initWithUnsignedShort: groupOwnerAccountID];
+ entry->_ownerAccountID =
+ [[OFNumber alloc] initWithUnsignedShort: ownerAccountID];
}
static void
parseGroupExtension(OFLHAArchiveEntry *entry, OFData *extension,
OFStringEncoding encoding)
{
- [entry->_group release];
- entry->_group = nil;
+ [entry->_groupOwnerAccountName release];
+ entry->_groupOwnerAccountName = nil;
- entry->_group = [[OFString alloc]
+ entry->_groupOwnerAccountName = [[OFString alloc]
initWithCString: (char *)extension.items + 1
encoding: encoding
length: extension.count - 1];
}
static void
parseOwnerExtension(OFLHAArchiveEntry *entry, OFData *extension,
OFStringEncoding encoding)
{
- [entry->_owner release];
- entry->_owner = nil;
+ [entry->_ownerAccountName release];
+ entry->_ownerAccountName = nil;
- entry->_owner = [[OFString alloc]
+ entry->_ownerAccountName = [[OFString alloc]
initWithCString: (char *)extension.items + 1
encoding: encoding
length: extension.count - 1];
}
@@ -301,28 +304,22 @@
*fileNameLength = length - pos;
*directoryName = cString;
*directoryNameLength = pos;
}
-+ (instancetype)entryWithFileName: (OFString *)fileName
-{
- return [[[self alloc] initWithFileName: fileName] autorelease];
-}
-
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
-- (instancetype)initWithFileName: (OFString *)fileName
+- (instancetype)of_init
{
self = [super init];
@try {
- _fileName = [fileName copy];
_compressionMethod = @"-lh0-";
- _date = [[OFDate alloc] initWithTimeIntervalSince1970: 0];
+ _modificationDate = [[OFDate alloc] init];
} @catch (id e) {
[self release];
@throw e;
}
@@ -341,15 +338,21 @@
_compressionMethod = [[OFString alloc]
initWithCString: header + 2
encoding: OFStringEncodingASCII
length: 5];
+ if (_compressedSize > UINT32_MAX ||
+ _uncompressedSize > UINT32_MAX)
+ @throw [OFOutOfRangeException exception];
+
memcpy(&_compressedSize, header + 7, 4);
- _compressedSize = OFFromLittleEndian32(_compressedSize);
+ _compressedSize =
+ OFFromLittleEndian32((uint32_t)_compressedSize);
memcpy(&_uncompressedSize, header + 11, 4);
- _uncompressedSize = OFFromLittleEndian32(_uncompressedSize);
+ _uncompressedSize =
+ OFFromLittleEndian32((uint32_t)_uncompressedSize);
memcpy(&date, header + 15, 4);
date = OFFromLittleEndian32(date);
_headerLevel = header[20];
@@ -360,11 +363,11 @@
case 1:;
void *pool = objc_autoreleasePoolPush();
uint8_t fileNameLength;
OFString *tmp;
- _date = [parseMSDOSDate(date) retain];
+ _modificationDate = [parseMSDOSDate(date) retain];
fileNameLength = [stream readInt8];
tmp = [stream readStringWithLength: fileNameLength
encoding: encoding];
tmp = [tmp stringByReplacingOccurrencesOfString: @"\\"
@@ -380,11 +383,11 @@
}
objc_autoreleasePoolPop(pool);
break;
case 2:
- _date = [[OFDate alloc]
+ _modificationDate = [[OFDate alloc]
initWithTimeIntervalSince1970: date];
_CRC16 = [stream readLittleEndianInt16];
_operatingSystemIdentifier = [stream readInt8];
@@ -414,17 +417,17 @@
- (void)dealloc
{
[_compressionMethod release];
[_fileName release];
[_directoryName release];
- [_date release];
+ [_modificationDate release];
[_fileComment release];
- [_mode release];
- [_UID release];
- [_GID release];
- [_owner release];
- [_group release];
+ [_POSIXPermissions release];
+ [_ownerAccountID release];
+ [_groupOwnerAccountID release];
+ [_ownerAccountName release];
+ [_groupOwnerAccountName release];
[_extensions release];
[super dealloc];
}
@@ -440,28 +443,27 @@
@try {
[copy->_compressionMethod release];
copy->_compressionMethod = nil;
- [copy->_date release];
- copy->_date = nil;
+ [copy->_modificationDate release];
+ copy->_modificationDate = nil;
copy->_directoryName = [_directoryName copy];
copy->_compressionMethod = [_compressionMethod copy];
copy->_compressedSize = _compressedSize;
copy->_uncompressedSize = _uncompressedSize;
- copy->_date = [_date copy];
+ copy->_modificationDate = [_modificationDate copy];
copy->_headerLevel = _headerLevel;
copy->_CRC16 = _CRC16;
copy->_operatingSystemIdentifier = _operatingSystemIdentifier;
copy->_fileComment = [_fileComment copy];
- copy->_mode = [_mode retain];
- copy->_UID = [_UID retain];
- copy->_GID = [_GID retain];
- copy->_owner = [_owner copy];
- copy->_group = [_group copy];
- copy->_modificationDate = [_modificationDate retain];
+ copy->_POSIXPermissions = [_POSIXPermissions retain];
+ copy->_ownerAccountID = [_ownerAccountID retain];
+ copy->_groupOwnerAccountID = [_groupOwnerAccountID retain];
+ copy->_ownerAccountName = [_ownerAccountName copy];
+ copy->_groupOwnerAccountName = [_groupOwnerAccountName copy];
copy->_extensions = [_extensions copy];
} @catch (id e) {
[copy release];
@throw e;
}
@@ -480,23 +482,23 @@
- (OFString *)compressionMethod
{
return _compressionMethod;
}
-- (uint32_t)compressedSize
+- (unsigned long long)compressedSize
{
return _compressedSize;
}
-- (uint32_t)uncompressedSize
+- (unsigned long long)uncompressedSize
{
return _uncompressedSize;
}
-- (OFDate *)date
+- (OFDate *)modificationDate
{
- return _date;
+ return _modificationDate;
}
- (uint8_t)headerLevel
{
return _headerLevel;
@@ -515,38 +517,33 @@
- (OFString *)fileComment
{
return _fileComment;
}
-- (OFNumber *)mode
-{
- return _mode;
-}
-
-- (OFNumber *)UID
-{
- return _UID;
-}
-
-- (OFNumber *)GID
-{
- return _GID;
-}
-
-- (OFString *)owner
-{
- return _owner;
-}
-
-- (OFString *)group
-{
- return _group;
-}
-
-- (OFDate *)modificationDate
-{
- return _modificationDate;
+- (OFNumber *)POSIXPermissions
+{
+ return _POSIXPermissions;
+}
+
+- (OFNumber *)ownerAccountID
+{
+ return _ownerAccountID;
+}
+
+- (OFNumber *)groupOwnerAccountID
+{
+ return _groupOwnerAccountID;
+}
+
+- (OFString *)ownerAccountName
+{
+ return _ownerAccountName;
+}
+
+- (OFString *)groupOwnerAccountName
+{
+ return _groupOwnerAccountName;
}
- (OFArray OF_GENERIC(OFData *) *)extensions
{
return _extensions;
@@ -569,27 +566,29 @@
getFileNameAndDirectoryName(self, encoding, &fileName, &fileNameLength,
&directoryName, &directoryNameLength);
if (fileNameLength > UINT16_MAX - 3 ||
- directoryNameLength > UINT16_MAX - 3)
+ directoryNameLength > UINT16_MAX - 3 ||
+ _compressedSize > UINT32_MAX || _uncompressedSize > UINT32_MAX)
@throw [OFOutOfRangeException exception];
/* Length. Filled in after we're done. */
[data increaseCountBy: 2];
[data addItems: [_compressionMethod
cStringWithEncoding: OFStringEncodingASCII]
count: 5];
- tmp32 = OFToLittleEndian32(_compressedSize);
+ tmp32 = OFToLittleEndian32((uint32_t)_compressedSize);
[data addItems: &tmp32 count: sizeof(tmp32)];
- tmp32 = OFToLittleEndian32(_uncompressedSize);
+ tmp32 = OFToLittleEndian32((uint32_t)_uncompressedSize);
[data addItems: &tmp32 count: sizeof(tmp32)];
- tmp32 = OFToLittleEndian32((uint32_t)_date.timeIntervalSince1970);
+ tmp32 = OFToLittleEndian32(
+ (uint32_t)_modificationDate.timeIntervalSince1970);
[data addItems: &tmp32 count: sizeof(tmp32)];
/* Reserved */
[data increaseCountBy: 1];
@@ -633,70 +632,64 @@
[data addItem: "\x3F"];
[data addItems: [_fileComment cStringWithEncoding: encoding]
count: fileCommentLength];
}
- if (_mode != nil) {
+ if (_POSIXPermissions != nil) {
tmp16 = OFToLittleEndian16(5);
[data addItems: &tmp16 count: sizeof(tmp16)];
[data addItem: "\x50"];
- tmp16 = OFToLittleEndian16(_mode.unsignedShortValue);
+ tmp16 =
+ OFToLittleEndian16(_POSIXPermissions.unsignedShortValue);
[data addItems: &tmp16 count: sizeof(tmp16)];
}
- if (_UID != nil || _GID != nil) {
- if (_UID == nil || _GID == nil)
+ if (_ownerAccountID != nil || _groupOwnerAccountID != nil) {
+ if (_ownerAccountID == nil || _groupOwnerAccountID == nil)
@throw [OFInvalidArgumentException exception];
tmp16 = OFToLittleEndian16(7);
[data addItems: &tmp16 count: sizeof(tmp16)];
[data addItem: "\x51"];
- tmp16 = OFToLittleEndian16(_GID.unsignedShortValue);
- [data addItems: &tmp16 count: sizeof(tmp16)];
-
- tmp16 = OFToLittleEndian16(_UID.unsignedShortValue);
- [data addItems: &tmp16 count: sizeof(tmp16)];
- }
-
- if (_group != nil) {
- size_t groupLength =
- [_group cStringLengthWithEncoding: encoding];
-
- if (groupLength > UINT16_MAX - 3)
- @throw [OFOutOfRangeException exception];
-
- tmp16 = OFToLittleEndian16((uint16_t)groupLength + 3);
- [data addItems: &tmp16 count: sizeof(tmp16)];
- [data addItem: "\x52"];
- [data addItems: [_group cStringWithEncoding: encoding]
- count: groupLength];
- }
-
- if (_owner != nil) {
- size_t ownerLength =
- [_owner cStringLengthWithEncoding: encoding];
-
- if (ownerLength > UINT16_MAX - 3)
- @throw [OFOutOfRangeException exception];
-
- tmp16 = OFToLittleEndian16((uint16_t)ownerLength + 3);
- [data addItems: &tmp16 count: sizeof(tmp16)];
- [data addItem: "\x53"];
- [data addItems: [_owner cStringWithEncoding: encoding]
- count: ownerLength];
- }
-
- if (_modificationDate != nil) {
- tmp16 = OFToLittleEndian16(7);
- [data addItems: &tmp16 count: sizeof(tmp16)];
- [data addItem: "\x54"];
-
- tmp32 = OFToLittleEndian32(
- (uint32_t)_modificationDate.timeIntervalSince1970);
- [data addItems: &tmp32 count: sizeof(tmp32)];
+ tmp16 = OFToLittleEndian16(
+ _groupOwnerAccountID.unsignedShortValue);
+ [data addItems: &tmp16 count: sizeof(tmp16)];
+
+ tmp16 = OFToLittleEndian16(_ownerAccountID.unsignedShortValue);
+ [data addItems: &tmp16 count: sizeof(tmp16)];
+ }
+
+ if (_groupOwnerAccountName != nil) {
+ size_t length = [_groupOwnerAccountName
+ cStringLengthWithEncoding: encoding];
+
+ if (length > UINT16_MAX - 3)
+ @throw [OFOutOfRangeException exception];
+
+ tmp16 = OFToLittleEndian16((uint16_t)length + 3);
+ [data addItems: &tmp16 count: sizeof(tmp16)];
+ [data addItem: "\x52"];
+ [data addItems: [_groupOwnerAccountName
+ cStringWithEncoding: encoding]
+ count: length];
+ }
+
+ if (_ownerAccountName != nil) {
+ size_t length =
+ [_ownerAccountName cStringLengthWithEncoding: encoding];
+
+ if (length > UINT16_MAX - 3)
+ @throw [OFOutOfRangeException exception];
+
+ tmp16 = OFToLittleEndian16((uint16_t)length + 3);
+ [data addItems: &tmp16 count: sizeof(tmp16)];
+ [data addItem: "\x53"];
+ [data addItems: [_ownerAccountName
+ cStringWithEncoding: encoding]
+ count: length];
}
for (OFData *extension in _extensions) {
size_t extensionLength = extension.count;
@@ -733,41 +726,46 @@
}
- (OFString *)description
{
void *pool = objc_autoreleasePoolPush();
- OFString *mode = (_mode == nil ? nil
- : [OFString stringWithFormat: @"%ho", _mode.unsignedShortValue]);
+ OFString *POSIXPermissions = nil;
OFString *extensions = [_extensions.description
stringByReplacingOccurrencesOfString: @"\n"
withString: @"\n\t"];
- OFString *ret = [OFString stringWithFormat:
+ OFString *ret;
+
+ if (_POSIXPermissions != nil)
+ POSIXPermissions = [OFString stringWithFormat: @"%ho",
+ _POSIXPermissions.unsignedShortValue];
+
+ ret = [OFString stringWithFormat:
@"<%@:\n"
@"\tFile name = %@\n"
@"\tCompression method = %@\n"
- @"\tCompressed size = %" @PRIu32 "\n"
- @"\tUncompressed size = %" @PRIu32 "\n"
- @"\tDate = %@\n"
+ @"\tCompressed size = %llu\n"
+ @"\tUncompressed size = %llu\n"
+ @"\tModification date = %@\n"
@"\tHeader level = %u\n"
@"\tCRC16 = %04" @PRIX16 @"\n"
@"\tOperating system identifier = %c\n"
@"\tComment = %@\n"
- @"\tMode = %@\n"
- @"\tUID = %@\n"
- @"\tGID = %@\n"
- @"\tOwner = %@\n"
- @"\tGroup = %@\n"
- @"\tModification date = %@\n"
+ @"\tPOSIX permissions = %@\n"
+ @"\tOwner account ID = %@\n"
+ @"\tGroup owner account ID = %@\n"
+ @"\tOwner account name = %@\n"
+ @"\tGroup owner accounut name = %@\n"
@"\tExtensions: %@"
@">",
self.class, self.fileName, _compressionMethod, _compressedSize,
- _uncompressedSize, _date, _headerLevel, _CRC16,
- _operatingSystemIdentifier, _fileComment, mode, _UID, _GID, _owner,
- _group, _modificationDate, extensions];
+ _uncompressedSize, _modificationDate, _headerLevel, _CRC16,
+ _operatingSystemIdentifier, _fileComment, POSIXPermissions,
+ _ownerAccountID, _groupOwnerAccountID, _ownerAccountName,
+ _groupOwnerAccountName, extensions];
[ret retain];
objc_autoreleasePoolPop(pool);
return [ret autorelease];
}
@end
Index: src/OFLocale.h
==================================================================
--- src/OFLocale.h
+++ src/OFLocale.h
@@ -26,10 +26,12 @@
* @brief Returns the localized string for the specified ID with the specified
* arguments inserted.
*
* @param ID The ID of the localized string to retrieve
* @return The localized string with the specified arguments replaced
+ * @throw OFInvalidFormatException The string (either the fallback or the
+ * localized one) contains an invalid format
*/
#define OF_LOCALIZED(ID, ...) \
[[OFLocale currentLocale] localizedStringForID: ID \
fallback: __VA_ARGS__, nil]
@@ -42,38 +44,38 @@
* @brief A class for querying the locale and retrieving localized strings.
*/
OF_SUBCLASSING_RESTRICTED
@interface OFLocale: OFObject
{
- OFString *_Nullable _language, *_Nullable _territory;
+ OFString *_Nullable _languageCode, *_Nullable _countryCode;
OFStringEncoding _encoding;
- OFString *_decimalPoint;
+ OFString *_decimalSeparator;
OFMutableArray OF_GENERIC(OFDictionary OF_GENERIC(OFString *, id) *)
*_localizedStrings;
}
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nullable, nonatomic) OFLocale *currentLocale;
-@property (class, readonly, nullable, nonatomic) OFString *language;
-@property (class, readonly, nullable, nonatomic) OFString *territory;
+@property (class, readonly, nullable, nonatomic) OFString *languageCode;
+@property (class, readonly, nullable, nonatomic) OFString *countryCode;
@property (class, readonly, nonatomic) OFStringEncoding encoding;
-@property (class, readonly, nullable, nonatomic) OFString *decimalPoint;
+@property (class, readonly, nullable, nonatomic) OFString *decimalSeparator;
#endif
/**
- * @brief The language of the locale for messages.
+ * @brief The language code of the locale for messages.
*
* If the language is unknown, it is `nil`.
*/
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *language;
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *languageCode;
/**
- * @brief The territory of the locale for messages.
+ * @brief The country code of the locale for messages.
*
* If the territory is unknown, it is `nil`.
*/
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *territory;
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *countryCode;
/**
* @brief The native 8-bit string encoding of the locale for messages.
*
* This is useful to encode strings correctly for passing them to operating
@@ -82,13 +84,13 @@
* If the native 8-bit encoding is unknown, UTF-8 is assumed.
*/
@property (readonly, nonatomic) OFStringEncoding encoding;
/**
- * @brief The decimal point of the system's locale.
+ * @brief The decimal separator of the locale.
*/
-@property (readonly, nonatomic) OFString *decimalPoint;
+@property (readonly, nonatomic) OFString *decimalSeparator;
/**
* @brief Returns the current OFLocale.
*
* @warning If you don't use @ref OFApplication, this might be `nil`! In this
@@ -98,26 +100,26 @@
* @return The current OFLocale instance
*/
+ (nullable OFLocale *)currentLocale;
/**
- * @brief Returns the language of the locale.
+ * @brief Returns the language code of the locale.
*
* If the language is unknown, `nil` is returned.
*
- * @return The language of the locale.
+ * @return The language code of the locale.
*/
-+ (nullable OFString *)language;
++ (nullable OFString *)languageCode;
/**
- * @brief Returns the territory of the locale.
+ * @brief Returns the country code of the locale.
*
- * If the territory is unknown, `nil` is returned.
+ * If the country is unknown, `nil` is returned.
*
- * @return The territory of the locale.
+ * @return The country code of the locale.
*/
-+ (nullable OFString *)territory;
++ (nullable OFString *)countryCode;
/**
* @brief Returns the native 8-bit string encoding for the locale.
*
* This is useful to encode strings correctly for passing them to operating
@@ -132,19 +134,19 @@
/**
* @brief Returns the decimal point of the system's locale.
*
* @return The decimal point of the system's locale
*/
-+ (nullable OFString *)decimalPoint;
++ (nullable OFString *)decimalSeparator;
#ifdef OF_HAVE_FILES
/**
- * @brief Adds a directory to scan for language files.
+ * @brief Adds a directory to scan for localizations.
*
- * @param path The path to the directory to scan for language files
+ * @param path The path to the directory to scan for localizations
*/
-+ (void)addLanguageDirectory: (OFString *)path;
++ (void)addLocalizationDirectory: (OFString *)path;
#endif
/**
* @brief Initializes the current OFLocale.
*
@@ -157,15 +159,15 @@
*/
- (instancetype)init;
#ifdef OF_HAVE_FILES
/**
- * @brief Adds a directory to scan for language files.
+ * @brief Adds a directory to scan for localizations.
*
- * @param path The path to the directory to scan for language files
+ * @param path The path to the directory to scan for localizations
*/
-- (void)addLanguageDirectory: (OFString *)path;
+- (void)addLocalizationDirectory: (OFString *)path;
#endif
/**
* @brief Returns the localized string for the specified ID, using the fallback
* string if it cannot be looked up or is missing.
@@ -180,11 +182,11 @@
* care of the `nil` sentinel automatically.
*
* @param ID The ID for the localized string
* @param fallback The fallback to use in case the localized string cannot be
* looked up or is missing. This can also be an array and use
- * plural scripting, just like with the JSON language files.
+ * plural scripting, just like with the JSON localization files.
* @return The localized string
*/
- (OFString *)localizedStringForID: (OFConstantString *)ID
fallback: (id)fallback, ... OF_SENTINEL;
@@ -202,17 +204,19 @@
* care of the `nil` sentinel automatically.
*
* @param ID The ID for the localized string
* @param fallback The fallback to use in case the localized string cannot be
* looked up or is missing. This can also be an array and use
- * plural scripting, just like with the JSON language files.
+ * plural scripting, just like with the JSON localization files.
* @param arguments A va_list of arguments, consisting of pairs of variable
* names and values to replace in the localized string,
* terminated with `nil`
* @return The localized string
+ * @throw OFInvalidFormatException The string (either the fallback or the
+ * localized one) contains an invalid format
*/
- (OFString *)localizedStringForID: (OFConstantString *)ID
fallback: (id)fallback
arguments: (va_list)arguments;
@end
OF_ASSUME_NONNULL_END
Index: src/OFLocale.m
==================================================================
--- src/OFLocale.m
+++ src/OFLocale.m
@@ -38,11 +38,11 @@
static OFDictionary *operatorPrecedences = nil;
#ifndef OF_AMIGAOS
static void
parseLocale(char *locale, OFStringEncoding *encoding,
- OFString **language, OFString **territory)
+ OFString **languageCode, OFString **countryCode)
{
locale = OFStrDup(locale);
@try {
OFStringEncoding enc = OFStringEncodingASCII;
@@ -63,22 +63,23 @@
encoding: enc]);
} @catch (OFInvalidArgumentException *e) {
}
}
- /* Territory */
+ /* Country code */
if ((tmp = strrchr(locale, '_')) != NULL) {
*tmp++ = '\0';
- if (territory != NULL)
- *territory = [OFString stringWithCString: tmp
- encoding: enc];
+ if (countryCode != NULL)
+ *countryCode = [OFString
+ stringWithCString: tmp
+ encoding: enc];
}
- if (language != NULL)
- *language = [OFString stringWithCString: locale
- encoding: enc];
+ if (languageCode != NULL)
+ *languageCode = [OFString stringWithCString: locale
+ encoding: enc];
} @finally {
OFFreeMemory(locale);
}
}
#endif
@@ -301,12 +302,12 @@
return string;
}
@implementation OFLocale
-@synthesize language = _language, territory = _territory, encoding = _encoding;
-@synthesize decimalPoint = _decimalPoint;
+@synthesize languageCode = _languageCode, countryCode = _countryCode;
+@synthesize encoding = _encoding, decimalSeparator = _decimalSeparator;
+ (void)initialize
{
OFNumber *one, *two, *three, *four;
@@ -338,34 +339,34 @@
+ (OFLocale *)currentLocale
{
return currentLocale;
}
-+ (OFString *)language
++ (OFString *)languageCode
{
- return currentLocale.language;
+ return currentLocale.languageCode;
}
-+ (OFString *)territory
++ (OFString *)countryCode
{
- return currentLocale.territory;
+ return currentLocale.countryCode;
}
+ (OFStringEncoding)encoding
{
return currentLocale.encoding;
}
-+ (OFString *)decimalPoint
++ (OFString *)decimalSeparator
{
- return currentLocale.decimalPoint;
+ return currentLocale.decimalSeparator;
}
#ifdef OF_HAVE_FILES
-+ (void)addLanguageDirectory: (OFString *)path
++ (void)addLocalizationDirectory: (OFString *)path
{
- [currentLocale addLanguageDirectory: path];
+ [currentLocale addLocalizationDirectory: path];
}
#endif
- (instancetype)init
{
@@ -378,15 +379,15 @@
if (currentLocale != nil)
@throw [OFInitializationFailedException
exceptionWithClass: self.class];
_encoding = OFStringEncodingUTF8;
- _decimalPoint = @".";
+ _decimalSeparator = @".";
_localizedStrings = [[OFMutableArray alloc] init];
if ((locale = setlocale(LC_ALL, "")) != NULL)
- _decimalPoint = [[OFString alloc]
+ _decimalSeparator = [[OFString alloc]
initWithCString: localeconv()->decimal_point
encoding: _encoding];
# ifdef LC_MESSAGES
messagesLocale = setlocale(LC_MESSAGES, "");
@@ -396,14 +397,14 @@
if (messagesLocale != NULL) {
void *pool = objc_autoreleasePoolPush();
parseLocale(messagesLocale, &_encoding,
- &_language, &_territory);
+ &_languageCode, &_countryCode);
- [_language retain];
- [_territory retain];
+ [_languageCode retain];
+ [_countryCode retain];
objc_autoreleasePoolPop(pool);
}
#else
void *pool = objc_autoreleasePoolPush();
@@ -437,35 +438,35 @@
/*
* Get it via localeconv() instead of from the Locale struct,
* to make sure we and printf etc. have the same expectations.
*/
- _decimalPoint = [[OFString alloc]
+ _decimalSeparator = [[OFString alloc]
initWithCString: localeconv()->decimal_point
encoding: _encoding];
_localizedStrings = [[OFMutableArray alloc] init];
if (GetVar("Language", buffer, sizeof(buffer), 0) > 0)
- _language = [[OFString alloc]
+ _languageCode = [[OFString alloc]
initWithCString: buffer
encoding: _encoding];
if ((locale = OpenLocale(NULL)) != NULL) {
@try {
- uint32_t territory;
+ uint32_t countryCode;
size_t length;
- territory =
+ countryCode =
OFToBigEndian32(locale->loc_CountryCode);
for (length = 0; length < 4; length++)
- if (((char *)&territory)[length] == 0)
+ if (((char *)&countryCode)[length] == 0)
break;
- _territory = [[OFString alloc]
- initWithCString: (char *)&territory
+ _countryCode = [[OFString alloc]
+ initWithCString: (char *)&countryCode
encoding: _encoding
length: length];
} @finally {
CloseLocale(locale);
}
@@ -483,59 +484,61 @@
return self;
}
- (void)dealloc
{
- [_language release];
- [_territory release];
- [_decimalPoint release];
+ [_languageCode release];
+ [_countryCode release];
+ [_decimalSeparator release];
[_localizedStrings release];
[super dealloc];
}
#ifdef OF_HAVE_FILES
-- (void)addLanguageDirectory: (OFString *)path
+- (void)addLocalizationDirectory: (OFString *)path
{
void *pool;
- OFString *mapPath, *language, *territory, *languageFile;
+ OFString *mapPath, *languageCode, *countryCode, *localizationFile;
OFDictionary *map;
- if (_language == nil)
+ if (_languageCode == nil)
return;
pool = objc_autoreleasePoolPush();
- mapPath = [path stringByAppendingPathComponent: @"languages.json"];
+ mapPath = [path stringByAppendingPathComponent: @"localizations.json"];
@try {
map = [[OFString stringWithContentsOfFile: mapPath]
objectByParsingJSON];
} @catch (OFOpenItemFailedException *e) {
objc_autoreleasePoolPop(pool);
return;
}
- language = _language.lowercaseString;
- territory = _territory.lowercaseString;
-
- if (territory == nil)
- territory = @"";
-
- languageFile = [[map objectForKey: language] objectForKey: territory];
- if (languageFile == nil)
- languageFile = [[map objectForKey: language] objectForKey: @""];
-
- if (languageFile == nil) {
+ languageCode = _languageCode.lowercaseString;
+ countryCode = _countryCode.lowercaseString;
+
+ if (countryCode == nil)
+ countryCode = @"";
+
+ localizationFile = [[map objectForKey: languageCode]
+ objectForKey: countryCode];
+ if (localizationFile == nil)
+ localizationFile = [[map objectForKey: languageCode]
+ objectForKey: @""];
+
+ if (localizationFile == nil) {
objc_autoreleasePoolPop(pool);
return;
}
- languageFile = [path stringByAppendingPathComponent:
- [languageFile stringByAppendingString: @".json"]];
+ localizationFile = [path stringByAppendingPathComponent:
+ [localizationFile stringByAppendingString: @".json"]];
[_localizedStrings addObject: [[OFString stringWithContentsOfFile:
- languageFile] objectByParsingJSON]];
+ localizationFile] objectByParsingJSON]];
objc_autoreleasePoolPop(pool);
}
#endif
Index: src/OFLocking.h
==================================================================
--- src/OFLocking.h
+++ src/OFLocking.h
@@ -28,22 +28,29 @@
*/
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *name;
/**
* @brief Locks the lock.
+ *
+ * @throw OFLockFailedException Acquiring the lock failed
*/
- (void)lock;
/**
* @brief Tries to lock the lock.
*
* @return A boolean whether the lock could be locked
+ *
+ * @throw OFLockFailedException The lock could not be acquired for another
+ * reason than it already being held
*/
- (bool)tryLock;
/**
* @brief Unlocks the lock.
+ *
+ * @throw OFUnlockFailedException Releasing the lock failed
*/
- (void)unlock;
@end
OF_ASSUME_NONNULL_END
Index: src/OFMapTable.h
==================================================================
--- src/OFMapTable.h
+++ src/OFMapTable.h
@@ -229,10 +229,13 @@
* @class OFMapTableEnumerator OFMapTable.h ObjFW/OFMapTable.h
*
* @brief A class which provides methods to enumerate through an OFMapTable's
* keys or objects.
*/
+#ifndef OF_MAP_TABLE_M
+OF_SUBCLASSING_RESTRICTED
+#endif
@interface OFMapTableEnumerator: OFObject
{
OFMapTable *_mapTable;
struct OFMapTableBucket *_Nonnull *_Nullable _buckets;
uint32_t _capacity;
Index: src/OFMapTable.m
==================================================================
--- src/OFMapTable.m
+++ src/OFMapTable.m
@@ -10,10 +10,12 @@
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
+
+#define OF_MAP_TABLE_M
#include "config.h"
#include
#include
Index: src/OFMemoryStream.m
==================================================================
--- src/OFMemoryStream.m
+++ src/OFMemoryStream.m
@@ -44,11 +44,11 @@
writable: (bool)writable
{
self = [super init];
@try {
- if (size > SSIZE_MAX || (ssize_t)size != (OFFileOffset)size)
+ if (size > SSIZE_MAX || (ssize_t)size != (OFStreamOffset)size)
@throw [OFOutOfRangeException exception];
_address = address;
_size = size;
_writable = writable;
@@ -99,32 +99,33 @@
- (bool)lowlevelIsAtEndOfStream
{
return (_position == _size);
}
-- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence
+- (OFStreamOffset)lowlevelSeekToOffset: (OFStreamOffset)offset
+ whence: (OFSeekWhence)whence
{
- OFFileOffset new;
+ OFStreamOffset new;
switch (whence) {
- case SEEK_SET:
+ case OFSeekSet:
new = offset;
break;
- case SEEK_CUR:
- new = (OFFileOffset)_position + offset;
+ case OFSeekCurrent:
+ new = (OFStreamOffset)_position + offset;
break;
- case SEEK_END:
- new = (OFFileOffset)_size + offset;
+ case OFSeekEnd:
+ new = (OFStreamOffset)_size + offset;
break;
default:
@throw [OFInvalidArgumentException exception];
}
- if (new < 0 || new > (OFFileOffset)_size)
+ if (new < 0 || new > (OFStreamOffset)_size)
@throw [OFSeekFailedException exceptionWithStream: self
offset: offset
whence: whence
errNo: EINVAL];
return (_position = (size_t)new);
}
@end
Index: src/OFMessagePackExtension.h
==================================================================
--- src/OFMessagePackExtension.h
+++ src/OFMessagePackExtension.h
@@ -24,16 +24,16 @@
* @class OFMessagePackExtension \
* OFMessagePackExtension.h ObjFW/OFMessagePackExtension.h
*
* @brief A class for representing the MessagePack extension type.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFMessagePackExtension: OFObject
{
int8_t _type;
OFData *_data;
- OF_RESERVE_IVARS(OFMessagePackExtension, 4)
}
/**
* @brief The MessagePack extension type.
*/
Index: src/OFMessagePackExtension.m
==================================================================
--- src/OFMessagePackExtension.m
+++ src/OFMessagePackExtension.m
@@ -172,11 +172,11 @@
{
unsigned long hash;
OFHashInit(&hash);
- OFHashAdd(&hash, (uint8_t)_type);
+ OFHashAddByte(&hash, (uint8_t)_type);
OFHashAddHash(&hash, _data.hash);
OFHashFinalize(&hash);
return hash;
Index: src/OFMethodSignature.h
==================================================================
--- src/OFMethodSignature.h
+++ src/OFMethodSignature.h
@@ -22,15 +22,15 @@
/**
* @class OFMethodSignature OFMethodSignature.h ObjFW/OFMethodSignature.h
*
* @brief A class for parsing type encodings and accessing them.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFMethodSignature: OFObject
{
char *_types;
OFMutableData *_typesPointers, *_offsets;
- OF_RESERVE_IVARS(OFMethodSignature, 4)
}
/**
* @brief The number of arguments of the method.
*/
@@ -51,19 +51,21 @@
/**
* @brief Creates a new OFMethodSignature with the specified ObjC types.
*
* @param types The ObjC types of the method
* @return A new, autoreleased OFMethodSignature
+ * @throw OFInvalidFormatException The type encoding is invalid
*/
+ (instancetype)signatureWithObjCTypes: (const char *)types;
/**
* @brief Initializes an already allocated OFMethodSignature with the specified
* ObjC types.
*
* @param types The ObjC types of the method
* @return An Initialized OFMethodSignature
+ * @throw OFInvalidFormatException The type encoding is invalid
*/
- (instancetype)initWithObjCTypes: (const char *)types;
/**
* @brief Returns the ObjC type for the argument at the specified index.
@@ -91,20 +93,22 @@
/**
* @brief Returns the size for the specified type encoding.
*
* @param type The type encoding to return the size for
* @return The size for the specified type encoding
+ * @throw OFInvalidFormatException The type encoding is invalid
*/
extern size_t OFSizeOfTypeEncoding(const char *type);
/**
* @brief Returns the alignment for the specified type encoding.
*
* @param type The type encoding to return the alignment for
* @return The alignment for the specified type encoding
+ * @throw OFInvalidFormatException The type encoding is invalid
*/
extern size_t OFAlignmentOfTypeEncoding(const char *type);
#ifdef __cplusplus
}
#endif
OF_ASSUME_NONNULL_END
Index: src/OFMethodSignature.m
==================================================================
--- src/OFMethodSignature.m
+++ src/OFMethodSignature.m
@@ -144,10 +144,14 @@
return alignment;
}
static size_t
+#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 7
+/* Work around an ICE in Clang 3.7.0 on Windows/x86 */
+__attribute__((__optnone__))
+#endif
alignmentOfEncoding(const char **type, size_t *length, bool inStruct)
{
size_t alignment;
if (*length == 0)
ADDED src/OFMutableArchiveEntry.h
Index: src/OFMutableArchiveEntry.h
==================================================================
--- src/OFMutableArchiveEntry.h
+++ src/OFMutableArchiveEntry.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFArchiveEntry.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @protocol OFMutableArchiveEntry \
+ * OFMutableArchiveEntry.h ObjFW/OFMutableArchiveEntry.h
+ *
+ * @brief A class which represents a mutable entry in an archive.
+ */
+@protocol OFMutableArchiveEntry
+
+/**
+ * @brief The file name of the entry.
+ */
+@property (readwrite, copy, nonatomic) OFString *fileName;
+
+/**
+ * @brief The compressed size of the entry's file.
+ */
+@property (readwrite, nonatomic) unsigned long long compressedSize;
+
+/**
+ * @brief The uncompressed size of the entry's file.
+ */
+@property (readwrite, nonatomic) unsigned long long uncompressedSize;
+
+@optional
+/**
+ * @brief The modification date of the file.
+ */
+@property (readwrite, retain, nonatomic) OFDate *modificationDate;
+
+/**
+ * @brief The comment of the entry's file.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
+ OFString *fileComment;
+
+/**
+ * @brief The POSIX permissions of the file.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic)
+ OFNumber *POSIXPermissions;
+
+/**
+ * @brief The file owner's account ID.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic)
+ OFNumber *ownerAccountID;
+
+/**
+ * @brief The file owner's group account ID.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic)
+ OFNumber *groupOwnerAccountID;
+
+/**
+ * @brief The file owner's account name.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic)
+ OFString *ownerAccountName;
+
+/**
+ * @brief The file owner's group account name.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic)
+ OFString *groupOwnerAccountName;
+@end
+
+OF_ASSUME_NONNULL_END
+
+#import "OFMutableArchiveEntry.h"
Index: src/OFMutableArray.m
==================================================================
--- src/OFMutableArray.m
+++ src/OFMutableArray.m
@@ -379,11 +379,11 @@
[self removeObjectAtIndex: count - 1];
}
- (void)removeAllObjects
{
- [self removeObjectsInRange: OFRangeMake(0, self.count)];
+ [self removeObjectsInRange: OFMakeRange(0, self.count)];
}
#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (OFArrayReplaceBlock)block
{
Index: src/OFMutableData.h
==================================================================
--- src/OFMutableData.h
+++ src/OFMutableData.h
@@ -16,11 +16,11 @@
#import "OFData.h"
OF_ASSUME_NONNULL_BEGIN
@class OFString;
-@class OFURL;
+@class OFURI;
/**
* @class OFMutableData OFMutableData.h ObjFW/OFMutableData.h
*
* @brief A class for storing and manipulating arbitrary data in an array.
@@ -202,11 +202,11 @@
* @brief Removes all items.
*/
- (void)removeAllItems;
/**
- * @brief Converts the mutable URL to an immutable URL.
+ * @brief Converts the mutable data to an immutable data.
*/
- (void)makeImmutable;
@end
OF_ASSUME_NONNULL_END
Index: src/OFMutableData.m
==================================================================
--- src/OFMutableData.m
+++ src/OFMutableData.m
@@ -242,11 +242,11 @@
_count += count;
}
- (void)removeItemAtIndex: (size_t)idx
{
- [self removeItemsInRange: OFRangeMake(idx, 1)];
+ [self removeItemsInRange: OFMakeRange(idx, 1)];
}
- (void)removeItemsInRange: (OFRange)range
{
if (range.length > SIZE_MAX - range.location ||
Index: src/OFMutableLHAArchiveEntry.h
==================================================================
--- src/OFMutableLHAArchiveEntry.h
+++ src/OFMutableLHAArchiveEntry.h
@@ -12,49 +12,30 @@
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#import "OFLHAArchiveEntry.h"
+#import "OFMutableArchiveEntry.h"
OF_ASSUME_NONNULL_BEGIN
/**
* @class OFMutableLHAArchiveEntry \
* OFMutableLHAArchiveEntry.h ObjFW/OFMutableLHAArchiveEntry.h
*
* @brief A class which represents a mutable entry in an LHA archive.
*/
-@interface OFMutableLHAArchiveEntry: OFLHAArchiveEntry
+@interface OFMutableLHAArchiveEntry: OFLHAArchiveEntry
{
OF_RESERVE_IVARS(OFMutableLHAArchiveEntry, 4)
}
-/**
- * @brief The file name of the entry.
- */
-@property (readwrite, copy, nonatomic) OFString *fileName;
-
/**
* @brief The compression method of the entry.
*/
@property (readwrite, copy, nonatomic) OFString *compressionMethod;
-/**
- * @brief The compressed size of the entry's file.
- */
-@property (readwrite, nonatomic) uint32_t compressedSize;
-
-/**
- * @brief The uncompressed size of the entry's file.
- */
-@property (readwrite, nonatomic) uint32_t uncompressedSize;
-
-/**
- * @brief The date of the file.
- */
-@property (readwrite, retain, nonatomic) OFDate *date;
-
/**
* @brief The LHA level of the file.
*/
@property (readwrite, nonatomic) uint8_t headerLevel;
@@ -66,55 +47,35 @@
/**
* @brief The operating system identifier of the file.
*/
@property (readwrite, nonatomic) uint8_t operatingSystemIdentifier;
-/**
- * @brief The comment of the file.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *fileComment;
-
-/**
- * @brief The mode of the entry.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic) OFNumber *mode;
-
-/**
- * @brief The UID of the owner.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic) OFNumber *UID;
-
-/**
- * @brief The GID of the group.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic) OFNumber *GID;
-
-/**
- * @brief The owner of the file.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *owner;
-
-/**
- * @brief The group of the file.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *group;
-
-/**
- * @brief The date of the last modification of the file.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, retain, nonatomic)
- OFDate *modificationDate;
-
/**
* @brief The LHA extensions of the file.
*/
@property (readwrite, copy, nonatomic) OFArray OF_GENERIC(OFData *) *extensions;
+/**
+ * @brief Creates a new OFMutableLHAArchiveEntry with the specified file name.
+ *
+ * @param fileName The file name for the OFLHAArchiveEntry
+ * @return A new, autoreleased OFLHAArchiveEntry
+ */
++ (instancetype)entryWithFileName: (OFString *)fileName;
+
+/**
+ * @brief Initializes an already allocated OFMutableLHAArchiveEntry with the
+ * specified file name.
+ *
+ * @param fileName The file name for the OFLHAArchiveEntry
+ * @return An initialized OFLHAArchiveEntry
+ */
+- (instancetype)initWithFileName: (OFString *)fileName;
+
/**
* @brief Converts the OFMutableLHAArchiveEntry to an immutable
* OFLHAArchiveEntry.
*/
- (void)makeImmutable;
@end
OF_ASSUME_NONNULL_END
Index: src/OFMutableLHAArchiveEntry.m
==================================================================
--- src/OFMutableLHAArchiveEntry.m
+++ src/OFMutableLHAArchiveEntry.m
@@ -14,21 +14,42 @@
*/
#include "config.h"
#import "OFMutableLHAArchiveEntry.h"
+#import "OFLHAArchiveEntry+Private.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFNumber.h"
#import "OFString.h"
@implementation OFMutableLHAArchiveEntry
-@dynamic fileName, compressionMethod, compressedSize, uncompressedSize, date;
-@dynamic headerLevel, CRC16, operatingSystemIdentifier, fileComment, mode, UID;
-@dynamic GID, owner, group, modificationDate, extensions;
+@dynamic fileName, compressionMethod, compressedSize, uncompressedSize;
+@dynamic modificationDate, headerLevel, CRC16, operatingSystemIdentifier;
+@dynamic fileComment, POSIXPermissions, ownerAccountID, groupOwnerAccountID;
+@dynamic ownerAccountName, groupOwnerAccountName, extensions;
+
++ (instancetype)entryWithFileName: (OFString *)fileName
+{
+ return [[[self alloc] initWithFileName: fileName] autorelease];
+}
+
+- (instancetype)initWithFileName: (OFString *)fileName
+{
+ self = [super of_init];
+
+ @try {
+ _fileName = [fileName copy];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
- (id)copy
{
OFMutableLHAArchiveEntry *copy = [self mutableCopy];
@@ -52,24 +73,24 @@
OFString *old = _compressionMethod;
_compressionMethod = [compressionMethod copy];
[old release];
}
-- (void)setCompressedSize: (uint32_t)compressedSize
+- (void)setCompressedSize: (unsigned long long)compressedSize
{
_compressedSize = compressedSize;
}
-- (void)setUncompressedSize: (uint32_t)uncompressedSize
+- (void)setUncompressedSize: (unsigned long long)uncompressedSize
{
_uncompressedSize = uncompressedSize;
}
-- (void)setDate: (OFDate *)date
+- (void)setModificationDate: (OFDate *)modificationDate
{
- OFDate *old = _date;
- _date = [date retain];
+ OFDate *old = _modificationDate;
+ _modificationDate = [modificationDate retain];
[old release];
}
- (void)setHeaderLevel: (uint8_t)headerLevel
{
@@ -91,49 +112,42 @@
OFString *old = _fileComment;
_fileComment = [fileComment copy];
[old release];
}
-- (void)setMode: (OFNumber *)mode
-{
- OFNumber *old = _mode;
- _mode = [mode retain];
- [old release];
-}
-
-- (void)setUID: (OFNumber *)UID
-{
- OFNumber *old = _UID;
- _UID = [UID retain];
- [old release];
-}
-
-- (void)setGID: (OFNumber *)GID
-{
- OFNumber *old = _GID;
- _GID = [GID retain];
- [old release];
-}
-
-- (void)setOwner: (OFString *)owner
-{
- OFString *old = _owner;
- _owner = [owner copy];
- [old release];
-}
-
-- (void)setGroup: (OFString *)group
-{
- OFString *old = _group;
- _group = [group copy];
- [old release];
-}
-
-- (void)setModificationDate: (OFDate *)modificationDate
-{
- OFDate *old = _modificationDate;
- _modificationDate = [modificationDate retain];
+- (void)setPOSIXPermissions: (OFNumber *)POSIXPermissions
+{
+ OFNumber *old = _POSIXPermissions;
+ _POSIXPermissions = [POSIXPermissions retain];
+ [old release];
+}
+
+- (void)setOwnerAccountID: (OFNumber *)ownerAccountID
+{
+ OFNumber *old = _ownerAccountID;
+ _ownerAccountID = [ownerAccountID retain];
+ [old release];
+}
+
+- (void)setGroupOwnerAccountID: (OFNumber *)groupOwnerAccountID
+{
+ OFNumber *old = _groupOwnerAccountID;
+ _groupOwnerAccountID = [groupOwnerAccountID retain];
+ [old release];
+}
+
+- (void)setOwnerAccountName: (OFString *)ownerAccountName
+{
+ OFString *old = _ownerAccountName;
+ _ownerAccountName = [ownerAccountName copy];
+ [old release];
+}
+
+- (void)setGroupOwnerAccountName: (OFString *)groupOwnerAccountName
+{
+ OFString *old = _groupOwnerAccountName;
+ _groupOwnerAccountName = [groupOwnerAccountName copy];
[old release];
}
- (void)setExtensions: (OFArray OF_GENERIC(OFData *) *)extensions
{
Index: src/OFMutableString.h
==================================================================
--- src/OFMutableString.h
+++ src/OFMutableString.h
@@ -52,28 +52,34 @@
/**
* @brief Appends a UTF-8 encoded C string to the OFMutableString.
*
* @param UTF8String A UTF-8 encoded C string to append
+ * @throw OFInvalidEncodingException The C string is not in not in the correct
+ * encoding
*/
- (void)appendUTF8String: (const char *)UTF8String;
/**
* @brief Appends a UTF-8 encoded C string with the specified length to the
* OFMutableString.
*
* @param UTF8String A UTF-8 encoded C string to append
* @param UTF8StringLength The length of the UTF-8 encoded C string
+ * @throw OFInvalidEncodingException The C string is not in not in the correct
+ * encoding
*/
- (void)appendUTF8String: (const char *)UTF8String
length: (size_t)UTF8StringLength;
/**
* @brief Appends a C string with the specified encoding to the OFMutableString.
*
* @param cString A C string to append
* @param encoding The encoding of the C string
+ * @throw OFInvalidEncodingException The C string is not in not in the correct
+ * encoding
*/
- (void)appendCString: (const char *)cString
encoding: (OFStringEncoding)encoding;
/**
@@ -81,10 +87,12 @@
* OFMutableString.
*
* @param cString A C string to append
* @param encoding The encoding of the C string
* @param cStringLength The length of the UTF-8 encoded C string
+ * @throw OFInvalidEncodingException The C string is not in not in the correct
+ * encoding
*/
- (void)appendCString: (const char *)cString
encoding: (OFStringEncoding)encoding
length: (size_t)cStringLength;
@@ -94,10 +102,13 @@
* See `printf` for the format syntax. As an addition, `%@` is available as
* format specifier for objects, `%C` for `OFUnichar` and `%S` for
* `const OFUnichar *`.
*
* @param format A format string which generates the string to append
+ * @throw OFInvalidFormatException The specified format is invalid
+ * @throw OFInvalidEncodingException The resulting string is not in not in UTF-8
+ * encoding
*/
- (void)appendFormat: (OFConstantString *)format, ...;
/**
* @brief Appends a formatted string to the OFMutableString.
@@ -106,25 +117,14 @@
* format specifier for objects, `%C` for `OFUnichar` and `%S` for
* `const OFUnichar *`.
*
* @param format A format string which generates the string to append
* @param arguments The arguments used in the format string
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (void)appendFormat: (OFConstantString *)format arguments: (va_list)arguments;
-/**
- * @brief Prepends another OFString to the OFMutableString.
- *
- * @param string An OFString to prepend
- */
-- (void)prependString: (OFString *)string;
-
-/**
- * @brief Reverses the string.
- */
-- (void)reverse;
-
/**
* @brief Converts the string to uppercase.
*/
- (void)uppercase;
Index: src/OFMutableString.m
==================================================================
--- src/OFMutableString.m
+++ src/OFMutableString.m
@@ -172,20 +172,20 @@
initWithContentsOfFile: path
encoding: encoding];
}
#endif
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
{
- return (id)[[OFMutableUTF8String alloc] initWithContentsOfURL: URL];
+ return (id)[[OFMutableUTF8String alloc] initWithContentsOfURI: URI];
}
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
encoding: (OFStringEncoding)encoding
{
return (id)[[OFMutableUTF8String alloc]
- initWithContentsOfURL: URL
+ initWithContentsOfURI: URI
encoding: encoding];
}
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
@@ -287,11 +287,11 @@
- (void)setCharacter: (OFUnichar)character atIndex: (size_t)idx
{
void *pool = objc_autoreleasePoolPush();
OFString *string =
[OFString stringWithCharacters: &character length: 1];
- [self replaceCharactersInRange: OFRangeMake(idx, 1) withString: string];
+ [self replaceCharactersInRange: OFMakeRange(idx, 1) withString: string];
objc_autoreleasePoolPop(pool);
}
- (void)appendString: (OFString *)string
{
@@ -370,26 +370,10 @@
} @finally {
free(UTF8String);
}
}
-- (void)prependString: (OFString *)string
-{
- [self insertString: string atIndex: 0];
-}
-
-- (void)reverse
-{
- size_t i, j, length = self.length;
-
- for (i = 0, j = length - 1; i < length / 2; i++, j--) {
- OFUnichar tmp = [self characterAtIndex: j];
- [self setCharacter: [self characterAtIndex: i] atIndex: j];
- [self setCharacter: tmp atIndex: i];
- }
-}
-
#ifdef OF_HAVE_UNICODE_TABLES
- (void)uppercase
{
[self of_convertWithWordStartTable: OFUnicodeUppercaseTable
wordMiddleTable: OFUnicodeUppercaseTable
@@ -429,11 +413,11 @@
}
#endif
- (void)insertString: (OFString *)string atIndex: (size_t)idx
{
- [self replaceCharactersInRange: OFRangeMake(idx, 0) withString: string];
+ [self replaceCharactersInRange: OFMakeRange(idx, 0) withString: string];
}
- (void)deleteCharactersInRange: (OFRange)range
{
[self replaceCharactersInRange: range withString: @""];
@@ -449,11 +433,11 @@
withString: (OFString *)replacement
{
[self replaceOccurrencesOfString: string
withString: replacement
options: 0
- range: OFRangeMake(0, self.length)];
+ range: OFMakeRange(0, self.length)];
}
- (void)replaceOccurrencesOfString: (OFString *)string
withString: (OFString *)replacement
options: (int)options
@@ -483,11 +467,11 @@
for (size_t i = range.location; i <= range.length - searchLength; i++) {
if (memcmp(characters + i, searchCharacters,
searchLength * sizeof(OFUnichar)) != 0)
continue;
- [self replaceCharactersInRange: OFRangeMake(i, searchLength)
+ [self replaceCharactersInRange: OFMakeRange(i, searchLength)
withString: replacement];
range.length -= searchLength;
range.length += replacementLength;
@@ -515,11 +499,11 @@
break;
}
objc_autoreleasePoolPop(pool);
- [self deleteCharactersInRange: OFRangeMake(0, i)];
+ [self deleteCharactersInRange: OFMakeRange(0, i)];
}
- (void)deleteTrailingWhitespaces
{
void *pool;
@@ -542,11 +526,11 @@
d++;
}
objc_autoreleasePoolPop(pool);
- [self deleteCharactersInRange: OFRangeMake(length - d, d)];
+ [self deleteCharactersInRange: OFMakeRange(length - d, d)];
}
- (void)deleteEnclosingWhitespaces
{
[self deleteLeadingWhitespaces];
Index: src/OFMutableTarArchiveEntry.h
==================================================================
--- src/OFMutableTarArchiveEntry.h
+++ src/OFMutableTarArchiveEntry.h
@@ -12,54 +12,25 @@
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#import "OFTarArchiveEntry.h"
+#import "OFMutableArchiveEntry.h"
OF_ASSUME_NONNULL_BEGIN
/**
* @class OFMutableTarArchiveEntry \
* OFMutableTarArchiveEntry.h ObjFW/OFMutableTarArchiveEntry.h
*
* @brief A class which represents a mutable entry of a tar archive.
*/
-@interface OFMutableTarArchiveEntry: OFTarArchiveEntry
+@interface OFMutableTarArchiveEntry: OFTarArchiveEntry
{
OF_RESERVE_IVARS(OFMutableTarArchiveEntry, 4)
}
-/**
- * @brief The file name of the entry.
- */
-@property (readwrite, copy, nonatomic) OFString *fileName;
-
-/**
- * @brief The mode of the entry.
- */
-@property (readwrite, nonatomic) unsigned long mode;
-
-/**
- * @brief The UID of the owner.
- */
-@property (readwrite, nonatomic) unsigned long UID;
-
-/**
- * @brief The GID of the group.
- */
-@property (readwrite, nonatomic) unsigned long GID;
-
-/**
- * @brief The size of the file.
- */
-@property (readwrite, nonatomic) unsigned long long size;
-
-/**
- * @brief The date of the last modification of the file.
- */
-@property (readwrite, retain, nonatomic) OFDate *modificationDate;
-
/**
* @brief The type of the archive entry.
*
* See @ref OFTarArchiveEntryType.
*/
@@ -69,20 +40,10 @@
* @brief The file name of the target (for a hard link or symbolic link).
*/
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
OFString *targetFileName;
-/**
- * @brief The owner of the file.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *owner;
-
-/**
- * @brief The group of the file.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *group;
-
/**
* @brief The device major (if the file is a device).
*/
@property (readwrite, nonatomic) unsigned long deviceMajor;
@@ -89,13 +50,30 @@
/**
* @brief The device major (if the file is a device).
*/
@property (readwrite, nonatomic) unsigned long deviceMinor;
+/**
+ * @brief Creates a new OFMutableTarArchiveEntry with the specified file name.
+ *
+ * @param fileName The file name for the OFTarArchiveEntry
+ * @return A new, autoreleased OFTarArchiveEntry
+ */
++ (instancetype)entryWithFileName: (OFString *)fileName;
+
+/**
+ * @brief Initializes an already allocated OFMutableTarArchiveEntry with the
+ * specified file name.
+ *
+ * @param fileName The file name for the OFTarArchiveEntry
+ * @return An initialized OFTarArchiveEntry
+ */
+- (instancetype)initWithFileName: (OFString *)fileName;
+
/**
* @brief Converts the OFMutableTarArchiveEntry to an immutable
* OFTarArchiveEntry.
*/
- (void)makeImmutable;
@end
OF_ASSUME_NONNULL_END
Index: src/OFMutableTarArchiveEntry.m
==================================================================
--- src/OFMutableTarArchiveEntry.m
+++ src/OFMutableTarArchiveEntry.m
@@ -14,16 +14,44 @@
*/
#include "config.h"
#import "OFMutableTarArchiveEntry.h"
-#import "OFString.h"
+#import "OFTarArchiveEntry+Private.h"
#import "OFDate.h"
+#import "OFNumber.h"
+#import "OFString.h"
@implementation OFMutableTarArchiveEntry
-@dynamic fileName, mode, UID, GID, size, modificationDate, type, targetFileName;
-@dynamic owner, group, deviceMajor, deviceMinor;
+@dynamic fileName, POSIXPermissions, ownerAccountID, groupOwnerAccountID;
+@dynamic compressedSize, uncompressedSize, modificationDate, type;
+@dynamic targetFileName, ownerAccountName, groupOwnerAccountName, deviceMajor;
+@dynamic deviceMinor;
+/*
+ * The following is optional in OFMutableArchiveEntry, but Apple GCC 4.0.1 is
+ * buggy and needs this to stop complaining.
+ */
+@dynamic fileComment;
+
++ (instancetype)entryWithFileName: (OFString *)fileName
+{
+ return [[[self alloc] initWithFileName: fileName] autorelease];
+}
+
+- (instancetype)initWithFileName: (OFString *)fileName
+{
+ self = [super of_init];
+
+ @try {
+ _fileName = [fileName copy];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
- (id)copy
{
OFMutableTarArchiveEntry *copy = [self mutableCopy];
@@ -37,28 +65,39 @@
OFString *old = _fileName;
_fileName = [fileName copy];
[old release];
}
-- (void)setMode: (unsigned long)mode
-{
- _mode = mode;
-}
-
-- (void)setUID: (unsigned long)UID
-{
- _UID = UID;
-}
-
-- (void)setGID: (unsigned long)GID
-{
- _GID = GID;
-}
-
-- (void)setSize: (unsigned long long)size
-{
- _size = size;
+- (void)setPOSIXPermissions: (OFNumber *)POSIXPermissions
+{
+ OFNumber *old = _POSIXPermissions;
+ _POSIXPermissions = [POSIXPermissions retain];
+ [old release];
+}
+
+- (void)setOwnerAccountID: (OFNumber *)ownerAccountID
+{
+ OFNumber *old = _ownerAccountID;
+ _ownerAccountID = [ownerAccountID retain];
+ [old release];
+}
+
+- (void)setGroupOwnerAccountID: (OFNumber *)groupOwnerAccountID
+{
+ OFNumber *old = _groupOwnerAccountID;
+ _groupOwnerAccountID = [groupOwnerAccountID retain];
+ [old release];
+}
+
+- (void)setCompressedSize: (unsigned long long)compressedSize
+{
+ _compressedSize = compressedSize;
+}
+
+- (void)setUncompressedSize: (unsigned long long)uncompressedSize
+{
+ _uncompressedSize = uncompressedSize;
}
- (void)setModificationDate: (OFDate *)modificationDate
{
OFDate *old = _modificationDate;
@@ -76,21 +115,21 @@
OFString *old = _targetFileName;
_targetFileName = [targetFileName copy];
[old release];
}
-- (void)setOwner: (OFString *)owner
+- (void)setOwnerAccountName: (OFString *)ownerAccountName
{
- OFString *old = _owner;
- _owner = [owner copy];
+ OFString *old = _ownerAccountName;
+ _ownerAccountName = [ownerAccountName copy];
[old release];
}
-- (void)setGroup: (OFString *)group
+- (void)setGroupOwnerAccountName: (OFString *)groupOwnerAccountName
{
- OFString *old = _group;
- _group = [group copy];
+ OFString *old = _groupOwnerAccountName;
+ _groupOwnerAccountName = [groupOwnerAccountName copy];
[old release];
}
- (void)setDeviceMajor: (unsigned long)deviceMajor
{
ADDED src/OFMutableURI.h
Index: src/OFMutableURI.h
==================================================================
--- src/OFMutableURI.h
+++ src/OFMutableURI.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFURI.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFMutableURI OFMutableURI.h ObjFW/OFMutableURI.h
+ *
+ * @brief A class for parsing URIs as per RFC 3986 and accessing and modifying
+ * parts of it.
+ */
+@interface OFMutableURI: OFURI
+{
+ OF_RESERVE_IVARS(OFMutableURI, 4)
+}
+
+/**
+ * @brief The scheme part of the URI.
+ *
+ * @throw OFInvalidFormatException The scheme being set is not in the correct
+ * format
+ */
+@property (readwrite, copy, nonatomic) OFString *scheme;
+
+/**
+ * @brief The host part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *host;
+
+/**
+ * @brief The host part of the URI in percent-encoded form.
+ *
+ * Setting this retains the original percent-encoding used - if more characters
+ * than necessary are percent-encoded, it is kept this way.
+ *
+ * @throw OFInvalidFormatException The host being set is not in the correct
+ * format
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
+ OFString *percentEncodedHost;
+
+/**
+ * @brief The port part of the URI.
+ *
+ * @throw OFInvalidArgumentException The port is not valid (e.g. negative or
+ * too big)
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFNumber *port;
+
+/**
+ * @brief The user part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *user;
+
+/**
+ * @brief The user part of the URI in percent-encoded form.
+ *
+ * Setting this retains the original percent-encoding used - if more characters
+ * than necessary are percent-encoded, it is kept this way.
+ *
+ * @throw OFInvalidFormatException The user being set is not in the correct
+ * format
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
+ OFString *percentEncodedUser;
+
+/**
+ * @brief The password part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *password;
+
+/**
+ * @brief The password part of the URI in URI-encoded form.
+ *
+ * Setting this retains the original percent-encoding used - if more characters
+ * than necessary are percent-encoded, it is kept this way.
+ *
+ * @throw OFInvalidFormatException The password being set is not in the correct
+ * format
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
+ OFString *percentEncodedPassword;
+
+/**
+ * @brief The path part of the URI.
+ */
+@property (readwrite, copy, nonatomic) OFString *path;
+
+/**
+ * @brief The path part of the URI in percent-encoded form.
+ *
+ * Setting this retains the original percent-encoding used - if more characters
+ * than necessary are percent-encoded, it is kept this way.
+ *
+ * @throw OFInvalidFormatException The path being set is not in the correct
+ * format
+ */
+@property (readwrite, copy, nonatomic) OFString *percentEncodedPath;
+
+/**
+ * @brief The path of the URI split into components.
+ *
+ * The first component must always be empty to designate the root.
+ *
+ * @throw OFInvalidFormatException The path components being set are not in the
+ * correct format
+ */
+@property (readwrite, copy, nonatomic)
+ OFArray OF_GENERIC(OFString *) *pathComponents;
+
+/**
+ * @brief The query part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *query;
+
+/**
+ * @brief The query part of the URI in percent-encoded form.
+ *
+ * Setting this retains the original percent-encoding used - if more characters
+ * than necessary are percent-encoded, it is kept this way.
+ *
+ * @throw OFInvalidFormatException The query being set is not in the correct
+ * format
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
+ OFString *percentEncodedQuery;
+
+/**
+ * @brief The query part of the URI as an array.
+ *
+ * For example, a query like `key1=value1&key2=value2` would correspond to the
+ * following array:
+ *
+ * @[
+ * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"],
+ * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"],
+ * ]
+ *
+ * @throw OFInvalidFormatException The query is not in the correct format
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
+ OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems;
+
+/**
+ * @brief The fragment part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *fragment;
+
+/**
+ * @brief The fragment part of the URI in percent-encoded form.
+ *
+ * Setting this retains the original percent-encoding used - if more characters
+ * than necessary are percent-encoded, it is kept this way.
+ *
+ * @throw OFInvalidFormatException The fragment being set is not in the correct
+ * format
+ */
+@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
+ OFString *percentEncodedFragment;
+
+/**
+ * @brief Creates a new mutable URI with the specified schemed.
+ *
+ * @param scheme The scheme for the URI
+ * @return A new, autoreleased OFMutableURI
+ */
++ (instancetype)URIWithScheme: (OFString *)scheme;
+
+/**
+ * @brief Initializes an already allocated mutable URI with the specified
+ * schemed.
+ *
+ * @param scheme The scheme for the URI
+ * @return An initialized OFMutableURI
+ */
+- (instancetype)initWithScheme: (OFString *)scheme;
+
+/**
+ * @brief Appends the specified path component.
+ *
+ * @param component The component to append
+ */
+- (void)appendPathComponent: (OFString *)component;
+
+/**
+ * @brief Appends the specified path component.
+ *
+ * @param component The component to append
+ * @param isDirectory Whether the path is a directory, in which case a slash is
+ * appened if there is no slash yet
+ */
+- (void)appendPathComponent: (OFString *)component
+ isDirectory: (bool)isDirectory;
+
+/**
+ * @brief Resolves relative subpaths.
+ */
+- (void)standardizePath;
+
+/**
+ * @brief Converts the mutable URI to an immutable URI.
+ */
+- (void)makeImmutable;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFMutableURI.m
Index: src/OFMutableURI.m
==================================================================
--- src/OFMutableURI.m
+++ src/OFMutableURI.m
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFMutableURI.h"
+#import "OFURI+Private.h"
+#import "OFArray.h"
+#import "OFDictionary.h"
+#ifdef OF_HAVE_FILES
+# import "OFFileManager.h"
+#endif
+#import "OFNumber.h"
+#import "OFPair.h"
+#import "OFString.h"
+
+#import "OFInvalidArgumentException.h"
+#import "OFInvalidFormatException.h"
+
+@implementation OFMutableURI
+@dynamic scheme, host, percentEncodedHost, port, user, percentEncodedUser;
+@dynamic password, percentEncodedPassword, path, percentEncodedPath;
+@dynamic pathComponents, query, percentEncodedQuery, queryItems, fragment;
+@dynamic percentEncodedFragment;
+
++ (instancetype)URIWithScheme: (OFString *)scheme
+{
+ return [[[self alloc] initWithScheme: scheme] autorelease];
+}
+
+- (instancetype)initWithScheme: (OFString *)scheme
+{
+ self = [super of_init];
+
+ @try {
+ self.scheme = scheme;
+ _percentEncodedPath = @"";
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)setScheme: (OFString *)scheme
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *old = _scheme;
+
+ if (scheme.length < 1 || !OFASCIIIsAlpha(*scheme.UTF8String))
+ @throw [OFInvalidFormatException exception];
+
+ OFURIVerifyIsEscaped(scheme,
+ [OFCharacterSet URISchemeAllowedCharacterSet], false);
+
+ _scheme = [scheme.lowercaseString copy];
+
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setHost: (OFString *)host
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *old = _percentEncodedHost;
+
+ if (OFURIIsIPv6Host(host))
+ _percentEncodedHost = [[OFString alloc]
+ initWithFormat: @"[%@]", host];
+ else
+ _percentEncodedHost = [[host
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIHostAllowedCharacterSet]] copy];
+
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setPercentEncodedHost: (OFString *)percentEncodedHost
+{
+ OFString *old;
+
+ if ([percentEncodedHost hasPrefix: @"["] &&
+ [percentEncodedHost hasSuffix: @"]"]) {
+ if (!OFURIIsIPv6Host([percentEncodedHost substringWithRange:
+ OFMakeRange(1, percentEncodedHost.length - 2)]))
+ @throw [OFInvalidFormatException exception];
+ } else if (percentEncodedHost != nil)
+ OFURIVerifyIsEscaped(percentEncodedHost,
+ [OFCharacterSet URIHostAllowedCharacterSet], true);
+
+ old = _percentEncodedHost;
+ _percentEncodedHost = [percentEncodedHost copy];
+ [old release];
+}
+
+- (void)setPort: (OFNumber *)port
+{
+ OFNumber *old = _port;
+
+ if (port.longLongValue < 0 || port.longLongValue > 65535)
+ @throw [OFInvalidArgumentException exception];
+
+ _port = [port copy];
+ [old release];
+}
+
+- (void)setUser: (OFString *)user
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *old = _percentEncodedUser;
+
+ _percentEncodedUser = [[user
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIUserAllowedCharacterSet]] copy];
+
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setPercentEncodedUser: (OFString *)percentEncodedUser
+{
+ OFString *old;
+
+ if (percentEncodedUser != nil)
+ OFURIVerifyIsEscaped(percentEncodedUser,
+ [OFCharacterSet URIUserAllowedCharacterSet], true);
+
+ old = _percentEncodedUser;
+ _percentEncodedUser = [percentEncodedUser copy];
+ [old release];
+}
+
+- (void)setPassword: (OFString *)password
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *old = _percentEncodedPassword;
+
+ _percentEncodedPassword = [[password
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIPasswordAllowedCharacterSet]] copy];
+
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setPercentEncodedPassword: (OFString *)percentEncodedPassword
+{
+ OFString *old;
+
+ if (percentEncodedPassword != nil)
+ OFURIVerifyIsEscaped(percentEncodedPassword,
+ [OFCharacterSet URIPasswordAllowedCharacterSet], true);
+
+ old = _percentEncodedPassword;
+ _percentEncodedPassword = [percentEncodedPassword copy];
+ [old release];
+}
+
+- (void)setPath: (OFString *)path
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *old = _percentEncodedPath;
+
+ _percentEncodedPath = [[path
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIPathAllowedCharacterSet]] copy];
+
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setPercentEncodedPath: (OFString *)percentEncodedPath
+{
+ OFString *old;
+
+ OFURIVerifyIsEscaped(percentEncodedPath,
+ [OFCharacterSet URIPathAllowedCharacterSet], true);
+
+ old = _percentEncodedPath;
+ _percentEncodedPath = [percentEncodedPath copy];
+ [old release];
+}
+
+- (void)setPathComponents: (OFArray *)components
+{
+ void *pool = objc_autoreleasePoolPush();
+
+ if (components.count == 0)
+ @throw [OFInvalidFormatException exception];
+
+ if ([components.firstObject isEqual: @"/"]) {
+ OFMutableArray *mutComponents =
+ [[components mutableCopy] autorelease];
+ [mutComponents replaceObjectAtIndex: 0 withObject: @""];
+ components = mutComponents;
+ }
+
+ self.path = [components componentsJoinedByString: @"/"];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setQuery: (OFString *)query
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *old = _percentEncodedQuery;
+
+ _percentEncodedQuery = [[query
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIQueryAllowedCharacterSet]] copy];
+
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setPercentEncodedQuery: (OFString *)percentEncodedQuery
+{
+ OFString *old;
+
+ if (percentEncodedQuery != nil)
+ OFURIVerifyIsEscaped(percentEncodedQuery,
+ [OFCharacterSet URIQueryAllowedCharacterSet], true);
+
+ old = _percentEncodedQuery;
+ _percentEncodedQuery = [percentEncodedQuery copy];
+ [old release];
+}
+
+- (void)setQueryItems:
+ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *)
+ queryItems
+{
+ void *pool;
+ OFMutableString *percentEncodedQuery;
+ OFCharacterSet *characterSet;
+ OFString *old;
+
+ if (queryItems == nil) {
+ [_percentEncodedQuery release];
+ _percentEncodedQuery = nil;
+ return;
+ }
+
+ pool = objc_autoreleasePoolPush();
+ percentEncodedQuery = [OFMutableString string];
+ characterSet = [OFCharacterSet URIQueryKeyValueAllowedCharacterSet];
+
+ for (OFPair OF_GENERIC(OFString *, OFString *) *item in queryItems) {
+ OFString *key = [item.firstObject
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ characterSet];
+ OFString *value = [item.secondObject
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ characterSet];
+
+ if (percentEncodedQuery.length > 0)
+ [percentEncodedQuery appendString: @"&"];
+
+ [percentEncodedQuery appendFormat: @"%@=%@", key, value];
+ }
+
+ old = _percentEncodedQuery;
+ _percentEncodedQuery = [percentEncodedQuery copy];
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setFragment: (OFString *)fragment
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *old = _percentEncodedFragment;
+
+ _percentEncodedFragment = [[fragment
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIFragmentAllowedCharacterSet]] copy];
+
+ [old release];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)setPercentEncodedFragment: (OFString *)percentEncodedFragment
+{
+ OFString *old;
+
+ if (percentEncodedFragment != nil)
+ OFURIVerifyIsEscaped(percentEncodedFragment,
+ [OFCharacterSet URIFragmentAllowedCharacterSet], true);
+
+ old = _percentEncodedFragment;
+ _percentEncodedFragment = [percentEncodedFragment copy];
+ [old release];
+}
+
+- (id)copy
+{
+ OFMutableURI *copy = [self mutableCopy];
+
+ [copy makeImmutable];
+
+ return copy;
+}
+
+- (void)appendPathComponent: (OFString *)component
+{
+ [self appendPathComponent: component isDirectory: false];
+
+#ifdef OF_HAVE_FILES
+ if ([_scheme isEqual: @"file"] &&
+ ![_percentEncodedPath hasSuffix: @"/"] &&
+ [[OFFileManager defaultManager] directoryExistsAtURI: self]) {
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path = [_percentEncodedPath
+ stringByAppendingString: @"/"];
+
+ [_percentEncodedPath release];
+ _percentEncodedPath = [path retain];
+
+ objc_autoreleasePoolPop(pool);
+ }
+#endif
+}
+
+- (void)appendPathComponent: (OFString *)component
+ isDirectory: (bool)isDirectory
+{
+ void *pool;
+ OFString *path;
+
+ if ([component isEqual: @"/"] && [_percentEncodedPath hasSuffix: @"/"])
+ return;
+
+ pool = objc_autoreleasePoolPush();
+ component = [component
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIPathAllowedCharacterSet]];
+
+#if defined(OF_WINDOWS) || defined(OF_MSDOS)
+ if ([_percentEncodedPath hasSuffix: @"/"] ||
+ ([_scheme isEqual: @"file"] &&
+ [_percentEncodedPath hasSuffix: @":"]))
+#else
+ if ([_percentEncodedPath hasSuffix: @"/"])
+#endif
+ path = [_percentEncodedPath stringByAppendingString: component];
+ else
+ path = [_percentEncodedPath
+ stringByAppendingFormat: @"/%@", component];
+
+ if (isDirectory && ![path hasSuffix: @"/"])
+ path = [path stringByAppendingString: @"/"];
+
+ [_percentEncodedPath release];
+ _percentEncodedPath = [path retain];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)standardizePath
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFMutableArray OF_GENERIC(OFString *) *array;
+ bool done = false, startsWithEmpty, endsWithEmpty;
+ OFString *path;
+
+ array = [[[_percentEncodedPath
+ componentsSeparatedByString: @"/"] mutableCopy] autorelease];
+
+ endsWithEmpty = ([array.lastObject length] == 0);
+ startsWithEmpty = ([array.firstObject length] == 0);
+
+ while (!done) {
+ size_t length = array.count;
+
+ done = true;
+
+ for (size_t i = 0; i < length; i++) {
+ OFString *current = [array objectAtIndex: i];
+ OFString *parent =
+ (i > 0 ? [array objectAtIndex: i - 1] : nil);
+
+ if ([current isEqual: @"."] || current.length == 0) {
+ [array removeObjectAtIndex: i];
+
+ done = false;
+ break;
+ }
+
+ if ([current isEqual: @".."] && parent != nil &&
+ ![parent isEqual: @".."]) {
+ [array removeObjectsInRange:
+ OFMakeRange(i - 1, 2)];
+
+ done = false;
+ break;
+ }
+ }
+ }
+
+ if (startsWithEmpty)
+ [array insertObject: @"" atIndex: 0];
+ if (endsWithEmpty)
+ [array addObject: @""];
+
+ path = [array componentsJoinedByString: @"/"];
+ if (startsWithEmpty && path.length == 0)
+ path = @"/";
+
+ self.percentEncodedPath = path;
+
+ objc_autoreleasePoolPop(pool);
+}
+
+- (void)makeImmutable
+{
+ object_setClass(self, [OFURI class]);
+}
+@end
DELETED src/OFMutableURL.h
Index: src/OFMutableURL.h
==================================================================
--- src/OFMutableURL.h
+++ src/OFMutableURL.h
@@ -1,190 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFURL.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-/**
- * @class OFMutableURL OFMutableURL.h ObjFW/OFMutableURL.h
- *
- * @brief A class for parsing URLs and accessing parts of it.
- */
-@interface OFMutableURL: OFURL
-{
- OF_RESERVE_IVARS(OFMutableURL, 4)
-}
-
-/**
- * @brief The scheme part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *scheme;
-
-/**
- * @brief The scheme part of the URL in URL-encoded form.
- *
- * Setting this retains the original URL-encoding used - if more characters
- * than necessary are URL-encoded, it is kept this way.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *URLEncodedScheme;
-
-/**
- * @brief The host part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *host;
-
-/**
- * @brief The host part of the URL in URL-encoded form.
- *
- * Setting this retains the original URL-encoding used - if more characters
- * than necessary are URL-encoded, it is kept this way.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *URLEncodedHost;
-
-/**
- * @brief The port part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFNumber *port;
-
-/**
- * @brief The user part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *user;
-
-/**
- * @brief The user part of the URL in URL-encoded form.
- *
- * Setting this retains the original URL-encoding used - if more characters
- * than necessary are URL-encoded, it is kept this way.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *URLEncodedUser;
-
-/**
- * @brief The password part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *password;
-
-/**
- * @brief The password part of the URL in URL-encoded form.
- *
- * Setting this retains the original URL-encoding used - if more characters
- * than necessary are URL-encoded, it is kept this way.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *URLEncodedPassword;
-
-/**
- * @brief The path part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *path;
-
-/**
- * @brief The path part of the URL in URL-encoded form.
- *
- * Setting this retains the original URL-encoding used - if more characters
- * than necessary are URL-encoded, it is kept this way.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *URLEncodedPath;
-
-/**
- * @brief The path of the URL split into components.
- *
- * The first component must always be empty to designate the root.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFArray OF_GENERIC(OFString *) *pathComponents;
-
-/**
- * @brief The query part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *query;
-
-/**
- * @brief The query part of the URL in URL-encoded form.
- *
- * Setting this retains the original URL-encoding used - if more characters
- * than necessary are URL-encoded, it is kept this way.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *URLEncodedQuery;
-
-/**
- * @brief The query part of the URL as a dictionary.
- *
- * For example, a query like `key1=value1&key2=value2` would correspond to the
- * following dictionary:
- *
- * @{
- * @"key1": @"value1",
- * @"key2": @"value2"
- * }
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFDictionary OF_GENERIC(OFString *, OFString *) *queryDictionary;
-
-/**
- * @brief The fragment part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *fragment;
-
-/**
- * @brief The fragment part of the URL in URL-encoded form.
- *
- * Setting this retains the original URL-encoding used - if more characters
- * than necessary are URL-encoded, it is kept this way.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *URLEncodedFragment;
-
-/**
- * @brief Creates a new mutable URL.
- *
- * @return A new, autoreleased OFMutableURL
- */
-+ (instancetype)URL;
-
-/**
- * @brief Appends the specified path component.
- *
- * @param component The component to append
- */
-- (void)appendPathComponent: (OFString *)component;
-
-/**
- * @brief Appends the specified path component.
- *
- * @param component The component to append
- * @param isDirectory Whether the path is a directory, in which case a slash is
- * appened if there is no slash yet
- */
-- (void)appendPathComponent: (OFString *)component
- isDirectory: (bool)isDirectory;
-
-/**
- * @brief Resolves relative subpaths.
- */
-- (void)standardizePath;
-
-/**
- * @brief Converts the mutable URL to an immutable URL.
- */
-- (void)makeImmutable;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/OFMutableURL.m
Index: src/OFMutableURL.m
==================================================================
--- src/OFMutableURL.m
+++ src/OFMutableURL.m
@@ -1,430 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFMutableURL.h"
-#import "OFArray.h"
-#import "OFDictionary.h"
-#ifdef OF_HAVE_FILES
-# import "OFFileManager.h"
-#endif
-#import "OFNumber.h"
-#import "OFString.h"
-
-#import "OFInvalidFormatException.h"
-
-@implementation OFMutableURL
-@dynamic scheme, URLEncodedScheme, host, URLEncodedHost, port, user;
-@dynamic URLEncodedUser, password, URLEncodedPassword, path, URLEncodedPath;
-@dynamic pathComponents, query, URLEncodedQuery, queryDictionary, fragment;
-@dynamic URLEncodedFragment;
-
-+ (instancetype)URL
-{
- return [[[self alloc] init] autorelease];
-}
-
-- (void)setScheme: (OFString *)scheme
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *old = _URLEncodedScheme;
-
- _URLEncodedScheme = [[scheme stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLSchemeAllowedCharacterSet]] copy];
-
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setURLEncodedScheme: (OFString *)URLEncodedScheme
-{
- OFString *old;
-
- if (URLEncodedScheme != nil)
- OFURLVerifyIsEscaped(URLEncodedScheme,
- [OFCharacterSet URLSchemeAllowedCharacterSet]);
-
- old = _URLEncodedScheme;
- _URLEncodedScheme = [URLEncodedScheme copy];
- [old release];
-}
-
-- (void)setHost: (OFString *)host
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *old = _URLEncodedHost;
-
- if (OFURLIsIPv6Host(host))
- _URLEncodedHost = [[OFString alloc]
- initWithFormat: @"[%@]", host];
- else
- _URLEncodedHost = [[host
- stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLHostAllowedCharacterSet]] copy];
-
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setURLEncodedHost: (OFString *)URLEncodedHost
-{
- OFString *old;
-
- if ([URLEncodedHost hasPrefix: @"["] &&
- [URLEncodedHost hasSuffix: @"]"]) {
- if (!OFURLIsIPv6Host([URLEncodedHost substringWithRange:
- OFRangeMake(1, URLEncodedHost.length - 2)]))
- @throw [OFInvalidFormatException exception];
- } else if (URLEncodedHost != nil)
- OFURLVerifyIsEscaped(URLEncodedHost,
- [OFCharacterSet URLHostAllowedCharacterSet]);
-
- old = _URLEncodedHost;
- _URLEncodedHost = [URLEncodedHost copy];
- [old release];
-}
-
-- (void)setPort: (OFNumber *)port
-{
- OFNumber *old = _port;
- _port = [port copy];
- [old release];
-}
-
-- (void)setUser: (OFString *)user
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *old = _URLEncodedUser;
-
- _URLEncodedUser = [[user stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLUserAllowedCharacterSet]] copy];
-
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setURLEncodedUser: (OFString *)URLEncodedUser
-{
- OFString *old;
-
- if (URLEncodedUser != nil)
- OFURLVerifyIsEscaped(URLEncodedUser,
- [OFCharacterSet URLUserAllowedCharacterSet]);
-
- old = _URLEncodedUser;
- _URLEncodedUser = [URLEncodedUser copy];
- [old release];
-}
-
-- (void)setPassword: (OFString *)password
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *old = _URLEncodedPassword;
-
- _URLEncodedPassword = [[password
- stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLPasswordAllowedCharacterSet]] copy];
-
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setURLEncodedPassword: (OFString *)URLEncodedPassword
-{
- OFString *old;
-
- if (URLEncodedPassword != nil)
- OFURLVerifyIsEscaped(URLEncodedPassword,
- [OFCharacterSet URLPasswordAllowedCharacterSet]);
-
- old = _URLEncodedPassword;
- _URLEncodedPassword = [URLEncodedPassword copy];
- [old release];
-}
-
-- (void)setPath: (OFString *)path
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *old = _URLEncodedPath;
-
- _URLEncodedPath = [[path stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLPathAllowedCharacterSet]] copy];
-
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setURLEncodedPath: (OFString *)URLEncodedPath
-{
- OFString *old;
-
- if (URLEncodedPath != nil)
- OFURLVerifyIsEscaped(URLEncodedPath,
- [OFCharacterSet URLPathAllowedCharacterSet]);
-
- old = _URLEncodedPath;
- _URLEncodedPath = [URLEncodedPath copy];
- [old release];
-}
-
-- (void)setPathComponents: (OFArray *)components
-{
- void *pool = objc_autoreleasePoolPush();
-
- if (components == nil) {
- self.path = nil;
- return;
- }
-
- if (components.count == 0)
- @throw [OFInvalidFormatException exception];
-
- if ([components.firstObject length] != 0)
- @throw [OFInvalidFormatException exception];
-
- self.path = [components componentsJoinedByString: @"/"];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setQuery: (OFString *)query
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *old = _URLEncodedQuery;
-
- _URLEncodedQuery = [[query stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLQueryAllowedCharacterSet]] copy];
-
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setURLEncodedQuery: (OFString *)URLEncodedQuery
-{
- OFString *old;
-
- if (URLEncodedQuery != nil)
- OFURLVerifyIsEscaped(URLEncodedQuery,
- [OFCharacterSet URLQueryAllowedCharacterSet]);
-
- old = _URLEncodedQuery;
- _URLEncodedQuery = [URLEncodedQuery copy];
- [old release];
-}
-
-- (void)setQueryDictionary:
- (OFDictionary OF_GENERIC(OFString *, OFString *) *)dictionary
-{
- void *pool;
- OFMutableString *URLEncodedQuery;
- OFEnumerator OF_GENERIC(OFString *) *keyEnumerator, *objectEnumerator;
- OFCharacterSet *characterSet;
- OFString *key, *object, *old;
-
- if (dictionary == nil) {
- [_URLEncodedQuery release];
- _URLEncodedQuery = nil;
- return;
- }
-
- pool = objc_autoreleasePoolPush();
- URLEncodedQuery = [OFMutableString string];
- keyEnumerator = [dictionary keyEnumerator];
- objectEnumerator = [dictionary objectEnumerator];
- characterSet = [OFCharacterSet URLQueryKeyValueAllowedCharacterSet];
-
- while ((key = [keyEnumerator nextObject]) != nil &&
- (object = [objectEnumerator nextObject]) != nil) {
- key = [key
- stringByURLEncodingWithAllowedCharacters: characterSet];
- object = [object
- stringByURLEncodingWithAllowedCharacters: characterSet];
-
- if (URLEncodedQuery.length > 0)
- [URLEncodedQuery appendString: @"&"];
-
- [URLEncodedQuery appendFormat: @"%@=%@", key, object];
- }
-
- old = _URLEncodedQuery;
- _URLEncodedQuery = [URLEncodedQuery copy];
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setFragment: (OFString *)fragment
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *old = _URLEncodedFragment;
-
- _URLEncodedFragment = [[fragment
- stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLFragmentAllowedCharacterSet]] copy];
-
- [old release];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)setURLEncodedFragment: (OFString *)URLEncodedFragment
-{
- OFString *old;
-
- if (URLEncodedFragment != nil)
- OFURLVerifyIsEscaped(URLEncodedFragment,
- [OFCharacterSet URLFragmentAllowedCharacterSet]);
-
- old = _URLEncodedFragment;
- _URLEncodedFragment = [URLEncodedFragment copy];
- [old release];
-}
-
-- (id)copy
-{
- OFMutableURL *copy = [self mutableCopy];
-
- [copy makeImmutable];
-
- return copy;
-}
-
-- (void)appendPathComponent: (OFString *)component
-{
- [self appendPathComponent: component isDirectory: false];
-
-#ifdef OF_HAVE_FILES
- if ([_URLEncodedScheme isEqual: @"file"] &&
- ![_URLEncodedPath hasSuffix: @"/"] &&
- [[OFFileManager defaultManager] directoryExistsAtURL: self]) {
- void *pool = objc_autoreleasePoolPush();
- OFString *path = [_URLEncodedPath
- stringByAppendingString: @"/"];
-
- [_URLEncodedPath release];
- _URLEncodedPath = [path retain];
-
- objc_autoreleasePoolPop(pool);
- }
-#endif
-}
-
-- (void)appendPathComponent: (OFString *)component
- isDirectory: (bool)isDirectory
-{
- void *pool;
- OFString *path;
-
- if ([component isEqual: @"/"] && [_URLEncodedPath hasSuffix: @"/"])
- return;
-
- pool = objc_autoreleasePoolPush();
- component = [component stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLPathAllowedCharacterSet]];
-
-#if defined(OF_WINDOWS) || defined(OF_MSDOS)
- if ([_URLEncodedPath hasSuffix: @"/"] ||
- ([_URLEncodedScheme isEqual: @"file"] &&
- [_URLEncodedPath hasSuffix: @":"]))
-#else
- if ([_URLEncodedPath hasSuffix: @"/"])
-#endif
- path = [_URLEncodedPath stringByAppendingString: component];
- else
- path = [_URLEncodedPath
- stringByAppendingFormat: @"/%@", component];
-
- if (isDirectory && ![path hasSuffix: @"/"])
- path = [path stringByAppendingString: @"/"];
-
- [_URLEncodedPath release];
- _URLEncodedPath = [path retain];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)standardizePath
-{
- void *pool;
- OFMutableArray OF_GENERIC(OFString *) *array;
- bool done = false, endsWithEmpty;
- OFString *path;
-
- if (_URLEncodedPath == nil)
- return;
-
- pool = objc_autoreleasePoolPush();
-
- array = [[[_URLEncodedPath
- componentsSeparatedByString: @"/"] mutableCopy] autorelease];
-
- if ([array.firstObject length] != 0)
- @throw [OFInvalidFormatException exception];
-
- endsWithEmpty = ([array.lastObject length] == 0);
-
- while (!done) {
- size_t length = array.count;
-
- done = true;
-
- for (size_t i = 0; i < length; i++) {
- OFString *current = [array objectAtIndex: i];
- OFString *parent =
- (i > 0 ? [array objectAtIndex: i - 1] : nil);
-
- if ([current isEqual: @"."] || current.length == 0) {
- [array removeObjectAtIndex: i];
-
- done = false;
- break;
- }
-
- if ([current isEqual: @".."] && parent != nil &&
- ![parent isEqual: @".."]) {
- [array removeObjectsInRange:
- OFRangeMake(i - 1, 2)];
-
- done = false;
- break;
- }
- }
- }
-
- [array insertObject: @"" atIndex: 0];
- if (endsWithEmpty)
- [array addObject: @""];
-
- path = [array componentsJoinedByString: @"/"];
- if (path.length == 0)
- path = @"/";
-
- [self setURLEncodedPath: path];
-
- objc_autoreleasePoolPop(pool);
-}
-
-- (void)makeImmutable
-{
- object_setClass(self, [OFURL class]);
-}
-@end
Index: src/OFMutableUTF8String.m
==================================================================
--- src/OFMutableUTF8String.m
+++ src/OFMutableUTF8String.m
@@ -423,90 +423,10 @@
} @finally {
free(UTF8String);
}
}
-- (void)reverse
-{
- size_t i, j;
-
- _s->hasHash = false;
-
- /* We reverse all bytes and restore UTF-8 later, if necessary */
- for (i = 0, j = _s->cStringLength - 1; i < _s->cStringLength / 2;
- i++, j--) {
- _s->cString[i] ^= _s->cString[j];
- _s->cString[j] ^= _s->cString[i];
- _s->cString[i] ^= _s->cString[j];
- }
-
- if (!_s->isUTF8)
- return;
-
- for (i = 0; i < _s->cStringLength; i++) {
- /* ASCII */
- if OF_LIKELY (!(_s->cString[i] & 0x80))
- continue;
-
- /* A start byte can't happen first as we reversed everything */
- if OF_UNLIKELY (_s->cString[i] & 0x40)
- @throw [OFInvalidEncodingException exception];
-
- /* Next byte must not be ASCII */
- if OF_UNLIKELY (_s->cStringLength < i + 1 ||
- !(_s->cString[i + 1] & 0x80))
- @throw [OFInvalidEncodingException exception];
-
- /* Next byte is the start byte */
- if OF_LIKELY (_s->cString[i + 1] & 0x40) {
- _s->cString[i] ^= _s->cString[i + 1];
- _s->cString[i + 1] ^= _s->cString[i];
- _s->cString[i] ^= _s->cString[i + 1];
-
- i++;
- continue;
- }
-
- /* Second next byte must not be ASCII */
- if OF_UNLIKELY (_s->cStringLength < i + 2 ||
- !(_s->cString[i + 2] & 0x80))
- @throw [OFInvalidEncodingException exception];
-
- /* Second next byte is the start byte */
- if OF_LIKELY (_s->cString[i + 2] & 0x40) {
- _s->cString[i] ^= _s->cString[i + 2];
- _s->cString[i + 2] ^= _s->cString[i];
- _s->cString[i] ^= _s->cString[i + 2];
-
- i += 2;
- continue;
- }
-
- /* Third next byte must not be ASCII */
- if OF_UNLIKELY (_s->cStringLength < i + 3 ||
- !(_s->cString[i + 3] & 0x80))
- @throw [OFInvalidEncodingException exception];
-
- /* Third next byte is the start byte */
- if OF_LIKELY (_s->cString[i + 3] & 0x40) {
- _s->cString[i] ^= _s->cString[i + 3];
- _s->cString[i + 3] ^= _s->cString[i];
- _s->cString[i] ^= _s->cString[i + 3];
-
- _s->cString[i + 1] ^= _s->cString[i + 2];
- _s->cString[i + 2] ^= _s->cString[i + 1];
- _s->cString[i + 1] ^= _s->cString[i + 2];
-
- i += 3;
- continue;
- }
-
- /* UTF-8 does not allow more than 4 bytes per character */
- @throw [OFInvalidEncodingException exception];
- }
-}
-
- (void)insertString: (OFString *)string atIndex: (size_t)idx
{
size_t newCStringLength;
if (idx > _s->length)
Index: src/OFMutableZIPArchiveEntry.h
==================================================================
--- src/OFMutableZIPArchiveEntry.h
+++ src/OFMutableZIPArchiveEntry.h
@@ -12,10 +12,11 @@
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#import "OFZIPArchiveEntry.h"
+#import "OFMutableArchiveEntry.h"
OF_ASSUME_NONNULL_BEGIN
/**
* @class OFMutableZIPArchiveEntry \
@@ -22,26 +23,15 @@
* OFMutableZIPArchiveEntry.h ObjFW/OFMutableZIPArchiveEntry.h
*
* @brief A class which represents a mutable entry in the central directory of
* a ZIP archive.
*/
-@interface OFMutableZIPArchiveEntry: OFZIPArchiveEntry
+@interface OFMutableZIPArchiveEntry: OFZIPArchiveEntry
{
OF_RESERVE_IVARS(OFMutableZIPArchiveEntry, 4)
}
-/**
- * @brief The file name of the entry.
- */
-@property (readwrite, copy, nonatomic) OFString *fileName;
-
-/**
- * @brief The comment of the entry's file.
- */
-@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
- OFString *fileComment;
-
/**
* @brief The extra field of the entry.
*
* The item size *must* be 1!
*/
@@ -65,17 +55,10 @@
* See @ref OFZIPArchiveEntryAttributeCompatibility.
*/
@property (readwrite, nonatomic)
OFZIPArchiveEntryAttributeCompatibility minVersionNeeded;
-/**
- * @brief The last modification date of the entry's file.
- *
- * @note Due to limitations of the ZIP format, this has only 2 second precision.
- */
-@property (readwrite, retain, nonatomic) OFDate *modificationDate;
-
/**
* @brief The compression method of the entry.
*
* Supported values are:
* Value | Description
@@ -87,20 +70,10 @@
* Other values may be returned, but the file cannot be extracted then.
*/
@property (readwrite, nonatomic)
OFZIPArchiveEntryCompressionMethod compressionMethod;
-/**
- * @brief The compressed size of the entry's file.
- */
-@property (readwrite, nonatomic) uint64_t compressedSize;
-
-/**
- * @brief The uncompressed size of the entry's file.
- */
-@property (readwrite, nonatomic) uint64_t uncompressedSize;
-
/**
* @brief The CRC32 checksum of the entry's file.
*/
@property (readwrite, nonatomic) uint32_t CRC32;
@@ -117,13 +90,30 @@
*
* See the ZIP specification for details.
*/
@property (readwrite, nonatomic) uint16_t generalPurposeBitFlag;
+/**
+ * @brief Creates a new OFMutableZIPArchiveEntry with the specified file name.
+ *
+ * @param fileName The file name for the OFZIPArchiveEntry
+ * @return A new, autoreleased OFZIPArchiveEntry
+ */
++ (instancetype)entryWithFileName: (OFString *)fileName;
+
+/**
+ * @brief Initializes an already allocated OFMutableZIPArchiveEntry with the
+ * specified file name.
+ *
+ * @param fileName The file name for the OFZIPArchiveEntry
+ * @return An initialized OFZIPArchiveEntry
+ */
+- (instancetype)initWithFileName: (OFString *)fileName;
+
/**
* @brief Converts the OFMutableZIPArchiveEntry to an immutable
* OFZIPArchiveEntry.
*/
- (void)makeImmutable;
@end
OF_ASSUME_NONNULL_END
Index: src/OFMutableZIPArchiveEntry.m
==================================================================
--- src/OFMutableZIPArchiveEntry.m
+++ src/OFMutableZIPArchiveEntry.m
@@ -27,10 +27,42 @@
@implementation OFMutableZIPArchiveEntry
@dynamic fileName, fileComment, extraField, versionMadeBy, minVersionNeeded;
@dynamic modificationDate, compressionMethod, compressedSize, uncompressedSize;
@dynamic CRC32, versionSpecificAttributes, generalPurposeBitFlag;
@dynamic of_localFileHeaderOffset;
+/*
+ * The following are optional in OFMutableArchiveEntry, but Apple GCC 4.0.1 is
+ * buggy and needs this to stop complaining.
+ */
+@dynamic POSIXPermissions, ownerAccountID, groupOwnerAccountID;
+@dynamic ownerAccountName, groupOwnerAccountName;
+
++ (instancetype)entryWithFileName: (OFString *)fileName
+{
+ return [[[self alloc] initWithFileName: fileName] autorelease];
+}
+
+- (instancetype)initWithFileName: (OFString *)fileName
+{
+ self = [super of_init];
+
+ @try {
+ void *pool = objc_autoreleasePoolPush();
+
+ if (fileName.UTF8StringLength > UINT16_MAX)
+ @throw [OFOutOfRangeException exception];
+
+ _fileName = [fileName copy];
+
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
- (id)copy
{
OFMutableZIPArchiveEntry *copy = [self mutableCopy];
[copy makeImmutable];
@@ -114,16 +146,16 @@
(OFZIPArchiveEntryCompressionMethod)compressionMethod
{
_compressionMethod = compressionMethod;
}
-- (void)setCompressedSize: (uint64_t)compressedSize
+- (void)setCompressedSize: (unsigned long long)compressedSize
{
_compressedSize = compressedSize;
}
-- (void)setUncompressedSize: (uint64_t)uncompressedSize
+- (void)setUncompressedSize: (unsigned long long)uncompressedSize
{
_uncompressedSize = uncompressedSize;
}
- (void)setCRC32: (uint32_t)CRC32
Index: src/OFMutex.h
==================================================================
--- src/OFMutex.h
+++ src/OFMutex.h
@@ -21,10 +21,15 @@
/**
* @class OFMutex OFMutex.h ObjFW/OFMutex.h
*
* @brief A class for creating mutual exclusions.
+ *
+ * If the mutex is deallocated while being held, it throws an
+ * @ref OFStillLockedException. While this might break ARC's assumption that no
+ * object ever throws in dealloc, it is considered a fatal programmer error
+ * that should terminate the application.
*/
@interface OFMutex: OFObject
{
OFPlainMutex _mutex;
bool _initialized;
Index: src/OFNotification.h
==================================================================
--- src/OFNotification.h
+++ src/OFNotification.h
@@ -31,16 +31,16 @@
* @class OFNotification OFNotification.h ObjFW/OFNotification.h
*
* @brief A class to represent a notification for or from
* @ref OFNotificationCenter.
*/
-OF_SUBCLASSING_RESTRICTED
@interface OFNotification: OFObject
{
OFNotificationName _name;
id _Nullable _object;
OFDictionary *_Nullable _userInfo;
+ OF_RESERVE_IVARS(OFNotification, 4)
}
/**
* @brief The name of the notification.
*/
Index: src/OFNotificationCenter.h
==================================================================
--- src/OFNotificationCenter.h
+++ src/OFNotificationCenter.h
@@ -37,17 +37,19 @@
* @class OFNotificationCenter OFNotificationCenter.h \
* ObjFW/OFNotificationCenter.h
*
* @brief A class to send and register for notifications.
*/
+#ifndef OF_NOTIFICATION_CENTER_M
+OF_SUBCLASSING_RESTRICTED
+#endif
@interface OFNotificationCenter: OFObject
{
#ifdef OF_HAVE_THREADS
OFMutex *_mutex;
#endif
OFMutableDictionary *_handles;
- OF_RESERVE_IVARS(OFNotificationCenter, 4)
}
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) OFNotificationCenter *defaultCenter;
#endif
Index: src/OFNotificationCenter.m
==================================================================
--- src/OFNotificationCenter.m
+++ src/OFNotificationCenter.m
@@ -10,10 +10,12 @@
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
+
+#define OF_NOTIFICATION_CENTER_M
#include "config.h"
#import "OFNotificationCenter.h"
#import "OFArray.h"
Index: src/OFNumber.m
==================================================================
--- src/OFNumber.m
+++ src/OFNumber.m
@@ -1036,16 +1036,16 @@
return 0;
d = OFToLittleEndianDouble(self.doubleValue);
for (uint_fast8_t i = 0; i < sizeof(double); i++)
- OFHashAdd(&hash, ((char *)&d)[i]);
+ OFHashAddByte(&hash, ((char *)&d)[i]);
} else if (isSigned(self) || isUnsigned(self)) {
unsigned long long value = self.unsignedLongLongValue;
while (value != 0) {
- OFHashAdd(&hash, value & 0xFF);
+ OFHashAddByte(&hash, value & 0xFF);
value >>= 8;
}
} else
@throw [OFInvalidFormatException exception];
Index: src/OFObject.h
==================================================================
--- src/OFObject.h
+++ src/OFObject.h
@@ -108,11 +108,11 @@
* @param start The starting index of the range
* @param length The length of the range
* @return An OFRangeith the specified start and length
*/
static OF_INLINE OFRange OF_CONST_FUNC
-OFRangeMake(size_t start, size_t length)
+OFMakeRange(size_t start, size_t length)
{
OFRange range = { start, length };
return range;
}
@@ -123,11 +123,11 @@
* @param range1 The first range for the comparison
* @param range2 The second range for the comparison
* @return Whether the two ranges are equal
*/
static OF_INLINE bool
-OFRangeEqual(OFRange range1, OFRange range2)
+OFEqualRanges(OFRange range1, OFRange range2)
{
if (range1.location != range2.location)
return false;
if (range1.length != range2.length)
@@ -159,11 +159,11 @@
* @param x The x coordinate of the point
* @param y The x coordinate of the point
* @return An OFPoint with the specified coordinates
*/
static OF_INLINE OFPoint OF_CONST_FUNC
-OFPointMake(float x, float y)
+OFMakePoint(float x, float y)
{
OFPoint point = { x, y };
return point;
}
@@ -174,11 +174,11 @@
* @param point1 The first point for the comparison
* @param point2 The second point for the comparison
* @return Whether the two points are equal
*/
static OF_INLINE bool
-OFPointEqual(OFPoint point1, OFPoint point2)
+OFEqualPoints(OFPoint point1, OFPoint point2)
{
if (point1.x != point2.x)
return false;
if (point1.y != point2.y)
@@ -205,11 +205,11 @@
* @param width The width of the size
* @param height The height of the size
* @return An OFSize with the specified width and height
*/
static OF_INLINE OFSize OF_CONST_FUNC
-OFSizeMake(float width, float height)
+OFMakeSize(float width, float height)
{
OFSize size = { width, height };
return size;
}
@@ -220,11 +220,11 @@
* @param size1 The first size for the comparison
* @param size2 The second size for the comparison
* @return Whether the two sizes are equal
*/
static OF_INLINE bool
-OFSizeEqual(OFSize size1, OFSize size2)
+OFEqualSizes(OFSize size1, OFSize size2)
{
if (size1.width != size2.width)
return false;
if (size1.height != size2.height)
@@ -253,15 +253,15 @@
* @param width The width of the rectangle
* @param height The height of the rectangle
* @return An OFRect with the specified origin and size
*/
static OF_INLINE OFRect OF_CONST_FUNC
-OFRectMake(float x, float y, float width, float height)
+OFMakeRect(float x, float y, float width, float height)
{
OFRect rect = {
- OFPointMake(x, y),
- OFSizeMake(width, height)
+ OFMakePoint(x, y),
+ OFMakeSize(width, height)
};
return rect;
}
@@ -271,16 +271,16 @@
* @param rect1 The first rectangle for the comparison
* @param rect2 The second rectangle for the comparison
* @return Whether the two rectangles are equal
*/
static OF_INLINE bool
-OFRectEqual(OFRect rect1, OFRect rect2)
+OFEqualRects(OFRect rect1, OFRect rect2)
{
- if (!OFPointEqual(rect1.origin, rect2.origin))
+ if (!OFEqualPoints(rect1.origin, rect2.origin))
return false;
- if (!OFSizeEqual(rect1.size, rect2.size))
+ if (!OFEqualSizes(rect1.size, rect2.size))
return false;
return true;
}
@@ -289,11 +289,11 @@
*
* @param hash A pointer to a hash to add the byte to
* @param byte The byte to add to the hash
*/
static OF_INLINE void
-OFHashAdd(unsigned long *_Nonnull hash, unsigned char byte)
+OFHashAddByte(unsigned long *_Nonnull hash, unsigned char byte)
{
uint32_t tmp = (uint32_t)*hash;
tmp += byte;
tmp += tmp << 10;
@@ -309,14 +309,14 @@
* @param otherHash The hash to add to the hash
*/
static OF_INLINE void
OFHashAddHash(unsigned long *_Nonnull hash, unsigned long otherHash)
{
- OFHashAdd(hash, (otherHash >> 24) & 0xFF);
- OFHashAdd(hash, (otherHash >> 16) & 0xFF);
- OFHashAdd(hash, (otherHash >> 8) & 0xFF);
- OFHashAdd(hash, otherHash & 0xFF);
+ OFHashAddByte(hash, (otherHash >> 24) & 0xFF);
+ OFHashAddByte(hash, (otherHash >> 16) & 0xFF);
+ OFHashAddByte(hash, (otherHash >> 8) & 0xFF);
+ OFHashAddByte(hash, otherHash & 0xFF);
}
/**
* @brief Finalizes the specified hash.
*
@@ -360,20 +360,20 @@
* @return The superclass of the object
*/
- (nullable Class)superclass;
/**
- * @brief Returns a 32 bit hash for the object.
+ * @brief Returns a hash for the object.
*
* Classes containing data (like strings, arrays, lists etc.) should reimplement
* this!
*
* @warning If you reimplement this, you also need to reimplement @ref isEqual:
* to behave in a way compatible to your reimplementation of this
* method!
*
- * @return A 32 bit hash for the object
+ * @return A hash for the object
*/
- (unsigned long)hash;
/**
* @brief Returns the retain count.
@@ -388,20 +388,13 @@
* @return Whether the object is a proxy object
*/
- (bool)isProxy;
/**
- * @brief Returns whether the object allows weak references.
- *
- * @return Whether the object allows weak references
- */
-- (bool)allowsWeakReference;
-
-/**
- * @brief Returns a boolean whether the object of the specified kind.
- *
- * @param class_ The class whose kind is checked
+ * @brief Returns a boolean whether the object is of the specified kind.
+ *
+ * @param class_ The class for which the receiver is checked
* @return A boolean whether the object is of the specified kind
*/
- (bool)isKindOfClass: (Class)class_;
/**
@@ -550,10 +543,17 @@
*
* @return The receiver
*/
- (instancetype)self;
+/**
+ * @brief Returns whether the object allows a weak reference.
+ *
+ * @return Whether the object allows a weak references
+ */
+- (bool)allowsWeakReference;
+
/**
* @brief Retain a weak reference to this object.
*
* @return Whether a weak reference to this object has been retained
*/
@@ -648,14 +648,16 @@
/**
* @brief Allocates memory for an instance of the class and sets up the memory
* pool for the object.
*
- * This method will never return `nil`, instead, it will throw an
- * @ref OFAllocFailedException.
+ * This method will never return `nil`.
*
* @return The allocated object
+ * @throw OFAllocFailedException There was not enough memory to allocate the
+ * object
+ * @throw OFInitializationFailedException The instance could not be constructed
*/
+ (instancetype)alloc;
/**
* @brief Calls @ref alloc on `self` and then `init` on the returned object.
@@ -1220,10 +1222,11 @@
*
* @warning If you override this method, you must make sure that it never
* returns!
*
* @param selector The selector not understood by the receiver
+ * @throw OFNotImplementedException
*/
- (void)doesNotRecognizeSelector: (SEL)selector OF_NO_RETURN;
@end
#else
typedef void OFObject;
@@ -1290,17 +1293,17 @@
* @brief Allocates memory for the specified number of items of the specified
* size.
*
* To free the allocated memory, use @ref OFFreeMemory.
*
- * Throws @ref OFOutOfMemoryException if allocating failed and
- * @ref OFOutOfRangeException if the requested size exceeds the address space.
- *
* @param count The number of items to allocate
* @param size The size of each item to allocate
* @return A pointer to the allocated memory. May return NULL if the specified
* size or count is 0.
+ * @throw OFOutOfMemoryException The allocation failed due to not enough memory
+ * @throw OFOutOfRangeException The requested `count * size` exceeds the
+ * address space
*/
extern void *_Nullable OFAllocMemory(size_t count, size_t size)
OF_WARN_UNUSED_RESULT;
/**
@@ -1307,17 +1310,17 @@
* @brief Allocates memory for the specified number of items of the specified
* size and initializes it with zeros.
*
* To free the allocated memory, use @ref OFFreeMemory.
*
- * Throws @ref OFOutOfMemoryException if allocating failed and
- * @ref OFOutOfRangeException if the requested size exceeds the address space.
- *
* @param size The size of each item to allocate
* @param count The number of items to allocate
* @return A pointer to the allocated memory. May return NULL if the specified
* size or count is 0.
+ * @throw OFOutOfMemoryException The allocation failed due to not enough memory
+ * @throw OFOutOfRangeException The requested `count * size` exceeds the
+ * address space
*/
extern void *_Nullable OFAllocZeroedMemory(size_t count, size_t size)
OF_WARN_UNUSED_RESULT;
/**
@@ -1326,17 +1329,18 @@
* To free the allocated memory, use @ref OFFreeMemory.
*
* If the pointer is NULL, this is equivalent to allocating memory.
* If the size or number of items is 0, this is equivalent to freeing memory.
*
- * Throws @ref OFOutOfMemoryException if allocating failed and
- * @ref OFOutOfRangeException if the requested size exceeds the address space.
- *
* @param pointer A pointer to the already allocated memory
* @param size The size of each item to resize to
* @param count The number of items to resize to
* @return A pointer to the resized memory chunk
+ * @throw OFOutOfMemoryException The reallocation failed due to not enough
+ * memory
+ * @throw OFOutOfRangeException The requested `count * size` exceeds the
+ * address space
*/
extern void *_Nullable OFResizeMemory(void *_Nullable pointer, size_t count,
size_t size) OF_WARN_UNUSED_RESULT;
/**
Index: src/OFObject.m
==================================================================
--- src/OFObject.m
+++ src/OFObject.m
@@ -42,10 +42,11 @@
# import "OFPlainMutex.h" /* For OFSpinlock */
#endif
#import "OFString.h"
#import "OFThread.h"
#import "OFTimer.h"
+#import "OFValue.h"
#import "OFAllocFailedException.h"
#import "OFEnumerationMutationException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
@@ -250,23 +251,52 @@
#if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__)
static void
uncaughtExceptionHandler(id exception)
{
OFString *description = [exception description];
- OFArray *backtrace = nil;
+ OFArray OF_GENERIC(OFValue *) *stackTraceAddresses = nil;
+ OFArray OF_GENERIC(OFString *) *stackTraceSymbols = nil;
OFStringEncoding encoding = [OFLocale encoding];
fprintf(stderr, "\nRuntime error: Unhandled exception:\n%s\n",
[description cStringWithEncoding: encoding]);
- if ([exception respondsToSelector: @selector(backtrace)])
- backtrace = [exception backtrace];
+ if ([exception respondsToSelector: @selector(stackTraceAddresses)])
+ stackTraceAddresses = [exception stackTraceAddresses];
- if (backtrace != nil) {
- OFString *s = [backtrace componentsJoinedByString: @"\n "];
- fprintf(stderr, "\nBacktrace:\n %s\n\n",
- [s cStringWithEncoding: encoding]);
+ if (stackTraceAddresses != nil) {
+ size_t count = stackTraceAddresses.count;
+
+ if ([exception respondsToSelector:
+ @selector(stackTraceSymbols)])
+ stackTraceSymbols = [exception stackTraceSymbols];
+
+ if (stackTraceSymbols.count != count)
+ stackTraceSymbols = nil;
+
+ fputs("\nStack trace:\n", stderr);
+
+ if (stackTraceSymbols != nil) {
+ for (size_t i = 0; i < count; i++) {
+ void *address = [[stackTraceAddresses
+ objectAtIndex: i] pointerValue];
+ const char *symbol = [[stackTraceSymbols
+ objectAtIndex: i]
+ cStringWithEncoding: encoding];
+
+ fprintf(stderr, " %p %s\n", address, symbol);
+ }
+ } else {
+ for (size_t i = 0; i < count; i++) {
+ void *address = [[stackTraceAddresses
+ objectAtIndex: i] pointerValue];
+
+ fprintf(stderr, " %p\n", address);
+ }
+ }
+
+ fputs("\n", stderr);
}
abort();
}
#endif
@@ -330,10 +360,14 @@
#endif
instance = (OFObject *)(void *)((char *)instance + PRE_IVARS_ALIGN);
if (!objc_constructInstance(class, instance)) {
+#if !defined(OF_HAVE_ATOMIC_OPS) && !defined(OF_AMIGAOS)
+ OFSpinlockFree(&((struct PreIvars *)(void *)
+ ((char *)instance - PRE_IVARS_ALIGN))->retainCountSpinlock);
+#endif
free((char *)instance - PRE_IVARS_ALIGN);
@throw [OFInitializationFailedException
exceptionWithClass: class];
}
@@ -1080,11 +1114,11 @@
unsigned long hash;
OFHashInit(&hash);
for (size_t i = 0; i < sizeof(ptr); i++) {
- OFHashAdd(&hash, ptr & 0xFF);
+ OFHashAddByte(&hash, ptr & 0xFF);
ptr >>= 8;
}
OFHashFinalize(&hash);
@@ -1200,10 +1234,14 @@
}
- (void)dealloc
{
objc_destructInstance(self);
+
+#if !defined(OF_HAVE_ATOMIC_OPS) && !defined(OF_AMIGAOS)
+ OFSpinlockFree(&PRE_IVARS->retainCountSpinlock);
+#endif
free((char *)self - PRE_IVARS_ALIGN);
}
/* Required to use properties with the Apple runtime */
Index: src/OFOnce.h
==================================================================
--- src/OFOnce.h
+++ src/OFOnce.h
@@ -13,11 +13,11 @@
* file.
*/
#include "objfw-defs.h"
-#include "platform.h"
+#import "macros.h"
#if defined(OF_HAVE_PTHREADS)
# include
typedef pthread_once_t OFOnceControl;
# define OFOnceControlInitValue PTHREAD_ONCE_INIT
@@ -26,10 +26,12 @@
# define OFOnceControlInitValue 0
#elif defined(OF_AMIGAOS) || !defined(OF_HAVE_THREADS)
typedef int OFOnceControl;
# define OFOnceControlInitValue 0
#endif
+
+OF_ASSUME_NONNULL_BEGIN
#ifdef __cplusplus
extern "C" {
#endif
/**
@@ -42,5 +44,7 @@
*/
extern void OFOnce(OFOnceControl *control, void (*function)(void));
#ifdef __cplusplus
}
#endif
+
+OF_ASSUME_NONNULL_END
Index: src/OFOptionsParser.m
==================================================================
--- src/OFOptionsParser.m
+++ src/OFOptionsParser.m
@@ -197,11 +197,11 @@
substringFromIndex: pos + 1] copy];
else
pos = argument.length;
_lastLongOption = [[argument substringWithRange:
- OFRangeMake(2, pos - 2)] copy];
+ OFMakeRange(2, pos - 2)] copy];
objc_autoreleasePoolPop(pool);
option = [_longOptions objectForKey: _lastLongOption];
if (option == NULL)
@@ -269,8 +269,8 @@
}
- (OFArray *)remainingArguments
{
return [_arguments objectsInRange:
- OFRangeMake(_index, _arguments.count - _index)];
+ OFMakeRange(_index, _arguments.count - _index)];
}
@end
Index: src/OFPlugin.h
==================================================================
--- src/OFPlugin.h
+++ src/OFPlugin.h
@@ -54,10 +54,11 @@
* @brief Creates a new OFPlugin by loading the plugin with the specified path.
*
* @param path The path to the plugin file. The suffix is appended
* automatically.
* @return An new, autoreleased OFPlugin
+ * @throw OFLoadPluginFailedException The plugin could not be loaded
*/
+ (instancetype)pluginWithPath: (OFString *)path;
/**
* @brief Initializes an already allocated OFPlugin by loading the plugin with
@@ -64,10 +65,11 @@
* the specified path.
*
* @param path The path to the plugin file. The suffix is appended
* automatically.
* @return An initialized OFPlugin
+ * @throw OFLoadPluginFailedException The plugin could not be loaded
*/
- (instancetype)initWithPath: (OFString *)path;
/**
* @brief Returns the address for the specified symbol, or `nil` if not found.
Index: src/OFPollKernelEventObserver.m
==================================================================
--- src/OFPollKernelEventObserver.m
+++ src/OFPollKernelEventObserver.m
@@ -26,11 +26,11 @@
#import "OFPollKernelEventObserver.h"
#import "OFData.h"
#import "OFSocket+Private.h"
-#import "OFObserveFailedException.h"
+#import "OFObserveKernelEventsFailedException.h"
#import "OFOutOfRangeException.h"
#ifdef OF_WII
# define pollfd pollsd
# define fd socket
@@ -72,12 +72,13 @@
struct pollfd *FDs;
size_t count;
bool found;
if (fd < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: EBADF];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: EBADF];
FDs = self->_FDs.mutableItems;
count = self->_FDs.count;
found = false;
@@ -108,12 +109,13 @@
{
struct pollfd *FDs;
size_t nFDs;
if (fd < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: EBADF];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: EBADF];
FDs = self->_FDs.mutableItems;
nFDs = self->_FDs.count;
for (size_t i = 0; i < nFDs; i++) {
@@ -183,12 +185,13 @@
events = poll(FDs, (nfds_t)nFDs,
(int)(timeInterval != -1 ? timeInterval * 1000 : -1));
if (events < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: errno];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: errno];
for (size_t i = 0; i < nFDs; i++) {
assert(FDs[i].fd <= _maxFD);
if (FDs[i].revents & POLLIN) {
Index: src/OFRecursiveMutex.h
==================================================================
--- src/OFRecursiveMutex.h
+++ src/OFRecursiveMutex.h
@@ -22,17 +22,23 @@
/**
* @class OFRecursiveMutex OFRecursiveMutex.h ObjFW/OFRecursiveMutex.h
*
* @brief A class for creating mutual exclusions which can be entered
* recursively.
+ *
+ * If the mutex is deallocated while being held, it throws an
+ * @ref OFStillLockedException. While this might break ARC's assumption that no
+ * object ever throws in dealloc, it is considered a fatal programmer error
+ * that should terminate the application.
*/
OF_SUBCLASSING_RESTRICTED
@interface OFRecursiveMutex: OFObject
{
OFPlainRecursiveMutex _rmutex;
bool _initialized;
OFString *_Nullable _name;
+ OF_RESERVE_IVARS(OFRecursiveMutex, 4)
}
/**
* @brief Creates a new recursive mutex.
*
Index: src/OFRunLoop.m
==================================================================
--- src/OFRunLoop.m
+++ src/OFRunLoop.m
@@ -39,11 +39,11 @@
#import "OFSortedList.h"
#import "OFTimer.h"
#import "OFTimer+Private.h"
#import "OFDate.h"
-#import "OFObserveFailedException.h"
+#import "OFObserveKernelEventsFailedException.h"
#import "OFWriteFailedException.h"
#include "OFRunLoopConstants.inc"
static OFRunLoop *mainRunLoop = nil;
@@ -1649,11 +1649,11 @@
#if defined(OF_HAVE_SOCKETS)
@try {
[state->_kernelEventObserver
observeForTimeInterval: timeout];
- } @catch (OFObserveFailedException *e) {
+ } @catch (OFObserveKernelEventsFailedException *e) {
if (e.errNo != EINTR)
@throw e;
}
#elif defined(OF_HAVE_THREADS)
[state->_condition lock];
@@ -1677,11 +1677,11 @@
* another thread, it cancels the observe.
*/
#if defined(OF_HAVE_SOCKETS)
@try {
[state->_kernelEventObserver observe];
- } @catch (OFObserveFailedException *e) {
+ } @catch (OFObserveKernelEventsFailedException *e) {
if (e.errNo != EINTR)
@throw e;
}
#elif defined(OF_HAVE_THREADS)
[state->_condition lock];
Index: src/OFSPXSocket.h
==================================================================
--- src/OFSPXSocket.h
+++ src/OFSPXSocket.h
@@ -50,11 +50,11 @@
* @param exception An exception that occurred while connecting, or nil on
* success
*/
- (void)socket: (OFSPXSocket *)socket
didConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
exception: (nullable id)exception;
@end
/**
@@ -87,13 +87,15 @@
*
* @param network The network on which the node to connect to is
* @param node The node to connect to
* @param port The port (sometimes also called socket number) on the node to
* connect to
+ * @throw OFConnectSPXSocketFailedException Connecting failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
- (void)connectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port;
/**
* @brief Asynchronously connect the OFSPXSocket to the specified destination.
*
@@ -101,11 +103,11 @@
* @param node The node to connect to
* @param port The port (sometimes also called socket number) on the node to
* connect to
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port;
/**
* @brief Asynchronously connect the OFSPXSocket to the specified destination.
*
@@ -114,11 +116,11 @@
* @param port The port (sometimes also called socket number) on the node to
* connect to
* @param runLoopMode The run loop mode in which to perform the async connect
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode;
#ifdef OF_HAVE_BLOCKS
/**
@@ -129,11 +131,11 @@
* @param port The port (sometimes also called socket number) on the node to
* connect to
* @param block The block to execute once the connection has been established
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
block: (OFSPXSocketAsyncConnectBlock)block;
/**
* @brief Asynchronously connect the OFSPXSocket to the specified destination.
@@ -144,22 +146,30 @@
* connect to
* @param runLoopMode The run loop mode in which to perform the async connect
* @param block The block to execute once the connection has been established
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode
block: (OFSPXSocketAsyncConnectBlock)block;
#endif
/**
* @brief Bind the socket to the specified network, node and port.
*
+ * @param network The IPX network to bind to. 0 means the current network.
+ * @param node The IPX network to bind to. An all zero node means the
+ * computer's node.
* @param port The port (sometimes called socket number) to bind to. 0 means to
- * pick one and return it.
+ * pick one and return via the returned socket address.
* @return The address on which this socket can be reached
+ * @throw OFBindIPXSocketFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
-- (OFSocketAddress)bindToPort: (uint16_t)port;
+- (OFSocketAddress)
+ bindToNetwork: (uint32_t)network
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
+ port: (uint16_t)port;
@end
OF_ASSUME_NONNULL_END
Index: src/OFSPXSocket.m
==================================================================
--- src/OFSPXSocket.m
+++ src/OFSPXSocket.m
@@ -22,12 +22,12 @@
#import "OFRunLoop+Private.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFAlreadyConnectedException.h"
-#import "OFBindFailedException.h"
-#import "OFConnectionFailedException.h"
+#import "OFBindIPXSocketFailedException.h"
+#import "OFConnectSPXSocketFailedException.h"
#import "OFNotOpenException.h"
#ifndef NSPROTO_SPX
# define NSPROTO_SPX 0
#endif
@@ -54,11 +54,11 @@
#endif
}
- (instancetype)initWithSocket: (OFSPXSocket *)socket
network: (uint32_t)network
- node: (unsigned char [IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
block: (OFSPXSocketAsyncConnectBlock)block
#endif
;
@@ -66,11 +66,11 @@
@end
@implementation OFSPXSocketAsyncConnectDelegate
- (instancetype)initWithSocket: (OFSPXSocket *)sock
network: (uint32_t)network
- node: (unsigned char [IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
block: (OFSPXSocketAsyncConnectBlock)block
#endif
{
@@ -164,15 +164,15 @@
#endif
}
- (id)of_connectionFailedExceptionForErrNo: (int)errNo
{
- return [OFConnectionFailedException exceptionWithNetwork: _network
- node: _node
- port: _port
- socket: _socket
- errNo: errNo];
+ return [OFConnectSPXSocketFailedException exceptionWithNetwork: _network
+ node: _node
+ port: _port
+ socket: _socket
+ errNo: errNo];
}
@end
@implementation OFSPXSocket
@dynamic delegate;
@@ -222,48 +222,48 @@
closesocket(_socket);
_socket = OFInvalidSocketHandle;
}
- (void)connectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
{
OFSocketAddress address = OFSocketAddressMakeIPX(network, node, port);
int errNo;
if (![self of_createSocketForAddress: &address errNo: &errNo])
- @throw [OFConnectionFailedException
+ @throw [OFConnectSPXSocketFailedException
exceptionWithNetwork: network
node: node
port: port
socket: self
errNo: errNo];
if (![self of_connectSocketToAddress: &address errNo: &errNo]) {
[self of_closeSocket];
- @throw [OFConnectionFailedException
+ @throw [OFConnectSPXSocketFailedException
exceptionWithNetwork: network
node: node
port: port
socket: self
errNo: errNo];
}
}
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
{
[self asyncConnectToNetwork: network
node: node
port: port
runLoopMode: OFDefaultRunLoopMode];
}
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode
{
void *pool = objc_autoreleasePoolPush();
@@ -280,11 +280,11 @@
objc_autoreleasePoolPop(pool);
}
#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
block: (OFSPXSocketAsyncConnectBlock)block
{
[self asyncConnectToNetwork: network
node: node
@@ -292,11 +292,11 @@
runLoopMode: OFDefaultRunLoopMode
block: block];
}
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode
block: (OFSPXSocketAsyncConnectBlock)block
{
void *pool = objc_autoreleasePoolPush();
@@ -311,31 +311,34 @@
objc_autoreleasePoolPop(pool);
}
#endif
-- (OFSocketAddress)bindToPort: (uint16_t)port
+- (OFSocketAddress)bindToNetwork: (uint32_t)network
+ node: (const unsigned char [IPX_NODE_LEN])node
+ port: (uint16_t)port
{
- const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
int flags;
#endif
if (_socket != OFInvalidSocketHandle)
@throw [OFAlreadyConnectedException exceptionWithSocket: self];
- address = OFSocketAddressMakeIPX(0, zeroNode, port);
+ address = OFSocketAddressMakeIPX(network, node, port);
if ((_socket = socket(address.sockaddr.ipx.sipx_family,
SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) ==
OFInvalidSocketHandle)
- @throw [OFBindFailedException
- exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: OFSocketErrNo()];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: OFSocketErrNo()];
_canBlock = true;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
@@ -347,14 +350,17 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: errNo];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: errNo];
}
memset(&address, 0, sizeof(address));
address.family = OFSocketAddressFamilyIPX;
address.length = (socklen_t)sizeof(address.sockaddr);
@@ -364,24 +370,30 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: errNo];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: errNo];
}
if (address.sockaddr.ipx.sipx_family != AF_IPX) {
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: EAFNOSUPPORT];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: EAFNOSUPPORT];
}
return address;
}
@end
Index: src/OFSPXStreamSocket.h
==================================================================
--- src/OFSPXStreamSocket.h
+++ src/OFSPXStreamSocket.h
@@ -51,11 +51,11 @@
* @param exception An exception that occurred while connecting, or nil on
* success
*/
- (void)socket: (OFSPXStreamSocket *)socket
didConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
exception: (nullable id)exception;
@end
/**
@@ -88,13 +88,15 @@
*
* @param network The network on which the node to connect to is
* @param node The node to connect to
* @param port The port (sometimes also called socket number) on the node to
* connect to
+ * @throw OFConnectSPXSocketFailedException Connecting failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
- (void)connectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port;
/**
* @brief Asynchronously connect the OFSPXStreamSocket to the specified
* destination.
@@ -103,11 +105,11 @@
* @param node The node to connect to
* @param port The port (sometimes also called socket number) on the node to
* connect to
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port;
/**
* @brief Asynchronously connect the OFSPXStreamSocket to the specified
* destination.
@@ -117,11 +119,11 @@
* @param port The port (sometimes also called socket number) on the node to
* connect to
* @param runLoopMode The run loop mode in which to perform the async connect
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode;
#ifdef OF_HAVE_BLOCKS
/**
@@ -133,11 +135,11 @@
* @param port The port (sometimes also called socket number) on the node to
* connect to
* @param block The block to execute once the connection has been established
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
block: (OFSPXStreamSocketAsyncConnectBlock)block;
/**
* @brief Asynchronously connect the OFSPXStreamSocket to the specified
@@ -149,22 +151,30 @@
* connect to
* @param runLoopMode The run loop mode in which to perform the async connect
* @param block The block to execute once the connection has been established
*/
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode
block: (OFSPXStreamSocketAsyncConnectBlock)block;
#endif
/**
* @brief Bind the socket to the specified network, node and port.
*
+ * @param network The IPX network to bind to. 0 means the current network.
+ * @param node The IPX network to bind to. An all zero node means the
+ * computer's node.
* @param port The port (sometimes called socket number) to bind to. 0 means to
- * pick one and return it.
+ * pick one and return via the returned socket address.
* @return The address on which this socket can be reached
+ * @throw OFBindIPXSocketFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
-- (OFSocketAddress)bindToPort: (uint16_t)port;
+- (OFSocketAddress)
+ bindToNetwork: (uint32_t)network
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
+ port: (uint16_t)port;
@end
OF_ASSUME_NONNULL_END
Index: src/OFSPXStreamSocket.m
==================================================================
--- src/OFSPXStreamSocket.m
+++ src/OFSPXStreamSocket.m
@@ -22,12 +22,12 @@
#import "OFRunLoop+Private.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFAlreadyConnectedException.h"
-#import "OFBindFailedException.h"
-#import "OFConnectionFailedException.h"
+#import "OFBindIPXSocketFailedException.h"
+#import "OFConnectSPXSocketFailedException.h"
#import "OFNotOpenException.h"
#ifndef NSPROTO_SPX
# define NSPROTO_SPX 0
#endif
@@ -55,11 +55,11 @@
#endif
}
- (instancetype)initWithSocket: (OFSPXStreamSocket *)socket
network: (uint32_t)network
- node: (unsigned char [IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
block: (OFSPXStreamSocketAsyncConnectBlock)block
#endif
;
@@ -67,11 +67,11 @@
@end
@implementation OFSPXStreamSocketAsyncConnectDelegate
- (instancetype)initWithSocket: (OFSPXStreamSocket *)sock
network: (uint32_t)network
- node: (unsigned char [IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
block: (OFSPXStreamSocketAsyncConnectBlock)block
#endif
{
@@ -166,15 +166,15 @@
#endif
}
- (id)of_connectionFailedExceptionForErrNo: (int)errNo
{
- return [OFConnectionFailedException exceptionWithNetwork: _network
- node: _node
- port: _port
- socket: _socket
- errNo: errNo];
+ return [OFConnectSPXSocketFailedException exceptionWithNetwork: _network
+ node: _node
+ port: _port
+ socket: _socket
+ errNo: errNo];
}
@end
@implementation OFSPXStreamSocket
@dynamic delegate;
@@ -188,11 +188,11 @@
if (_socket != OFInvalidSocketHandle)
@throw [OFAlreadyConnectedException exceptionWithSocket: self];
if ((_socket = socket(address->sockaddr.ipx.sipx_family,
- SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) ==
+ SOCK_STREAM | SOCK_CLOEXEC, NSPROTO_SPX)) ==
OFInvalidSocketHandle) {
*errNo = OFSocketErrNo();
return false;
}
@@ -224,48 +224,48 @@
closesocket(_socket);
_socket = OFInvalidSocketHandle;
}
- (void)connectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
{
OFSocketAddress address = OFSocketAddressMakeIPX(network, node, port);
int errNo;
if (![self of_createSocketForAddress: &address errNo: &errNo])
- @throw [OFConnectionFailedException
+ @throw [OFConnectSPXSocketFailedException
exceptionWithNetwork: network
node: node
port: port
socket: self
errNo: errNo];
if (![self of_connectSocketToAddress: &address errNo: &errNo]) {
[self of_closeSocket];
- @throw [OFConnectionFailedException
+ @throw [OFConnectSPXSocketFailedException
exceptionWithNetwork: network
node: node
port: port
socket: self
errNo: errNo];
}
}
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
{
[self asyncConnectToNetwork: network
node: node
port: port
runLoopMode: OFDefaultRunLoopMode];
}
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode
{
void *pool = objc_autoreleasePoolPush();
@@ -282,11 +282,11 @@
objc_autoreleasePoolPop(pool);
}
#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
block: (OFSPXStreamSocketAsyncConnectBlock)block
{
[self asyncConnectToNetwork: network
node: node
@@ -294,11 +294,11 @@
runLoopMode: OFDefaultRunLoopMode
block: block];
}
- (void)asyncConnectToNetwork: (uint32_t)network
- node: (unsigned char [_Nonnull IPX_NODE_LEN])node
+ node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
runLoopMode: (OFRunLoopMode)runLoopMode
block: (OFSPXStreamSocketAsyncConnectBlock)block
{
void *pool = objc_autoreleasePoolPush();
@@ -313,30 +313,33 @@
objc_autoreleasePoolPop(pool);
}
#endif
-- (OFSocketAddress)bindToPort: (uint16_t)port
+- (OFSocketAddress)bindToNetwork: (uint32_t)network
+ node: (const unsigned char [IPX_NODE_LEN])node
+ port: (uint16_t)port
{
- const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
int flags;
#endif
if (_socket != OFInvalidSocketHandle)
@throw [OFAlreadyConnectedException exceptionWithSocket: self];
- address = OFSocketAddressMakeIPX(0, zeroNode, port);
+ address = OFSocketAddressMakeIPX(network, node, port);
if ((_socket = socket(address.sockaddr.ipx.sipx_family,
SOCK_STREAM | SOCK_CLOEXEC, NSPROTO_SPX)) == OFInvalidSocketHandle)
- @throw [OFBindFailedException
- exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: OFSocketErrNo()];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: OFSocketErrNo()];
_canBlock = true;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
@@ -348,14 +351,17 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: errNo];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: errNo];
}
memset(&address, 0, sizeof(address));
address.family = OFSocketAddressFamilyIPX;
address.length = (socklen_t)sizeof(address.sockaddr);
@@ -365,24 +371,30 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: errNo];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: errNo];
}
if (address.sockaddr.ipx.sipx_family != AF_IPX) {
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPort: port
- packetType: SPXPacketType
- socket: self
- errNo: EAFNOSUPPORT];
+ @throw [OFBindIPXSocketFailedException
+ exceptionWithNetwork: network
+ node: node
+ port: port
+ packetType: SPXPacketType
+ socket: self
+ errNo: EAFNOSUPPORT];
}
return address;
}
@end
Index: src/OFSandbox.h
==================================================================
--- src/OFSandbox.h
+++ src/OFSandbox.h
@@ -21,10 +21,11 @@
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFPair OF_GENERIC(FirstType, SecondType);
typedef OFPair OF_GENERIC(OFString *, OFString *) *OFSandboxUnveilPath;
+OF_SUBCLASSING_RESTRICTED
@interface OFSandbox: OFObject
{
unsigned int _allowsStdIO: 1;
unsigned int _allowsReadingFiles: 1;
unsigned int _allowsWritingFiles: 1;
@@ -56,11 +57,10 @@
unsigned int _allowsUnveil: 1;
unsigned int _returnsErrors: 1;
OFMutableArray OF_GENERIC(OFSandboxUnveilPath) *_unveiledPaths;
@public
size_t _unveiledPathsIndex;
- OF_RESERVE_IVARS(OFSandbox, 4)
}
@property (nonatomic) bool allowsStdIO;
@property (nonatomic) bool allowsReadingFiles;
@property (nonatomic) bool allowsWritingFiles;
Index: src/OFSandbox.m
==================================================================
--- src/OFSandbox.m
+++ src/OFSandbox.m
@@ -471,40 +471,40 @@
{
unsigned long hash;
OFHashInit(&hash);
- OFHashAdd(&hash, _allowsStdIO);
- OFHashAdd(&hash, _allowsReadingFiles);
- OFHashAdd(&hash, _allowsWritingFiles);
- OFHashAdd(&hash, _allowsCreatingFiles);
- OFHashAdd(&hash, _allowsCreatingSpecialFiles);
- OFHashAdd(&hash, _allowsTemporaryFiles);
- OFHashAdd(&hash, _allowsIPSockets);
- OFHashAdd(&hash, _allowsMulticastSockets);
- OFHashAdd(&hash, _allowsChangingFileAttributes);
- OFHashAdd(&hash, _allowsFileOwnerChanges);
- OFHashAdd(&hash, _allowsFileLocks);
- OFHashAdd(&hash, _allowsUNIXSockets);
- OFHashAdd(&hash, _allowsDNS);
- OFHashAdd(&hash, _allowsUserDatabaseReading);
- OFHashAdd(&hash, _allowsFileDescriptorSending);
- OFHashAdd(&hash, _allowsFileDescriptorReceiving);
- OFHashAdd(&hash, _allowsTape);
- OFHashAdd(&hash, _allowsTTY);
- OFHashAdd(&hash, _allowsProcessOperations);
- OFHashAdd(&hash, _allowsExec);
- OFHashAdd(&hash, _allowsProtExec);
- OFHashAdd(&hash, _allowsSetTime);
- OFHashAdd(&hash, _allowsPS);
- OFHashAdd(&hash, _allowsVMInfo);
- OFHashAdd(&hash, _allowsChangingProcessRights);
- OFHashAdd(&hash, _allowsPF);
- OFHashAdd(&hash, _allowsAudio);
- OFHashAdd(&hash, _allowsBPF);
- OFHashAdd(&hash, _allowsUnveil);
- OFHashAdd(&hash, _returnsErrors);
+ OFHashAddByte(&hash, _allowsStdIO);
+ OFHashAddByte(&hash, _allowsReadingFiles);
+ OFHashAddByte(&hash, _allowsWritingFiles);
+ OFHashAddByte(&hash, _allowsCreatingFiles);
+ OFHashAddByte(&hash, _allowsCreatingSpecialFiles);
+ OFHashAddByte(&hash, _allowsTemporaryFiles);
+ OFHashAddByte(&hash, _allowsIPSockets);
+ OFHashAddByte(&hash, _allowsMulticastSockets);
+ OFHashAddByte(&hash, _allowsChangingFileAttributes);
+ OFHashAddByte(&hash, _allowsFileOwnerChanges);
+ OFHashAddByte(&hash, _allowsFileLocks);
+ OFHashAddByte(&hash, _allowsUNIXSockets);
+ OFHashAddByte(&hash, _allowsDNS);
+ OFHashAddByte(&hash, _allowsUserDatabaseReading);
+ OFHashAddByte(&hash, _allowsFileDescriptorSending);
+ OFHashAddByte(&hash, _allowsFileDescriptorReceiving);
+ OFHashAddByte(&hash, _allowsTape);
+ OFHashAddByte(&hash, _allowsTTY);
+ OFHashAddByte(&hash, _allowsProcessOperations);
+ OFHashAddByte(&hash, _allowsExec);
+ OFHashAddByte(&hash, _allowsProtExec);
+ OFHashAddByte(&hash, _allowsSetTime);
+ OFHashAddByte(&hash, _allowsPS);
+ OFHashAddByte(&hash, _allowsVMInfo);
+ OFHashAddByte(&hash, _allowsChangingProcessRights);
+ OFHashAddByte(&hash, _allowsPF);
+ OFHashAddByte(&hash, _allowsAudio);
+ OFHashAddByte(&hash, _allowsBPF);
+ OFHashAddByte(&hash, _allowsUnveil);
+ OFHashAddByte(&hash, _returnsErrors);
OFHashFinalize(&hash);
return hash;
}
Index: src/OFSecureData.h
==================================================================
--- src/OFSecureData.h
+++ src/OFSecureData.h
@@ -62,11 +62,11 @@
* @param size The number of bytes of unswappable memory to preallocate
*/
+ (void)preallocateUnswappableMemoryWithSize: (size_t)size;
/**
- * @brief Creates a new, autoreleased OFSecureData with count items of item
+ * @brief Creates a new, autoreleased OFSecureData with `count` items of item
* size 1, all set to zero.
*
* @param count The number of zero items the OFSecureData should contain
* @param allowsSwappableMemory Whether the data may be stored in swappable
* memory
@@ -74,11 +74,11 @@
*/
+ (instancetype)dataWithCount: (size_t)count
allowsSwappableMemory: (bool)allowsSwappableMemory;
/**
- * @brief Creates a new, autoreleased OFSecureData with count items of the
+ * @brief Creates a new, autoreleased OFSecureData with `count` items of the
* specified item size, all set to zero.
*
* @param count The number of zero items the OFSecureData should contain
* @param itemSize The size of a single item in the OFSecureData in bytes
* @param allowsSwappableMemory Whether the data may be stored in swappable
@@ -102,16 +102,16 @@
itemSize: (size_t)itemSize
freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path OF_UNAVAILABLE;
#endif
-+ (instancetype)dataWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE;
++ (instancetype)dataWithContentsOfURI: (OFURI *)URI OF_UNAVAILABLE;
+ (instancetype)dataWithStringRepresentation: (OFString *)string OF_UNAVAILABLE;
+ (instancetype)dataWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE;
/**
- * @brief Initializes an already allocated OFSecureData with count items of
+ * @brief Initializes an already allocated OFSecureData with `count` items of
* item size 1, all set to zero.
*
* @param count The number of zero items the OFSecureData should contain
* @param allowsSwappableMemory Whether the data may be stored in swappable
* memory
@@ -119,12 +119,12 @@
*/
- (instancetype)initWithCount: (size_t)count
allowsSwappableMemory: (bool)allowsSwappableMemory;
/**
- * @brief Initializes an already allocated OFSecureData with count items of the
- * specified item size, all set to zero.
+ * @brief Initializes an already allocated OFSecureData with `count` items of
+ * the specified item size, all set to zero.
*
* @param itemSize The size of a single item in the OFSecureData in bytes
* @param count The number of zero items the OFSecureData should contain
* @param allowsSwappableMemory Whether the data may be stored in swappable
* memory
@@ -148,11 +148,11 @@
itemSize: (size_t)itemSize
freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path OF_UNAVAILABLE;
#endif
-- (instancetype)initWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE;
+- (instancetype)initWithContentsOfURI: (OFURI *)URI OF_UNAVAILABLE;
- (instancetype)initWithStringRepresentation: (OFString *)string OF_UNAVAILABLE;
- (instancetype)initWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE;
- (instancetype)initWithSerialization: (OFXMLElement *)element OF_UNAVAILABLE;
/**
@@ -185,11 +185,11 @@
- (OFString *)stringRepresentation OF_UNAVAILABLE;
- (OFString *)stringByBase64Encoding OF_UNAVAILABLE;
#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path OF_UNAVAILABLE;
#endif
-- (void)writeToURL: (OFURL *)URL OF_UNAVAILABLE;
+- (void)writeToURI: (OFURI *)URI OF_UNAVAILABLE;
- (OFXMLElement *)XMLElementBySerializing OF_UNAVAILABLE;
- (OFData *)messagePackRepresentation OF_UNAVAILABLE;
@end
OF_ASSUME_NONNULL_END
Index: src/OFSecureData.m
==================================================================
--- src/OFSecureData.m
+++ src/OFSecureData.m
@@ -373,11 +373,11 @@
{
OF_UNRECOGNIZED_SELECTOR
}
#endif
-+ (instancetype)dataWithContentsOfURL: (OFURL *)URL
++ (instancetype)dataWithContentsOfURI: (OFURI *)URI
{
OF_UNRECOGNIZED_SELECTOR
}
+ (instancetype)dataWithStringRepresentation: (OFString *)string
@@ -497,11 +497,11 @@
{
OF_INVALID_INIT_METHOD
}
#endif
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
{
OF_INVALID_INIT_METHOD
}
- (instancetype)initWithStringRepresentation: (OFString *)string
@@ -629,11 +629,11 @@
{
OF_UNRECOGNIZED_SELECTOR
}
#endif
-- (void)writeToURL: (OFURL *)URL
+- (void)writeToURI: (OFURI *)URI
{
OF_UNRECOGNIZED_SELECTOR
}
- (OFXMLElement *)XMLElementBySerializing
Index: src/OFSeekableStream.h
==================================================================
--- src/OFSeekableStream.h
+++ src/OFSeekableStream.h
@@ -27,23 +27,37 @@
#endif
#import "OFStream.h"
OF_ASSUME_NONNULL_BEGIN
+
+/** @file */
#if defined(OF_WINDOWS)
-typedef __int64 OFFileOffset;
+typedef __int64 OFStreamOffset;
#elif defined(OF_ANDROID)
-typedef long long OFFileOffset;
+typedef long long OFStreamOffset;
#elif defined(OF_MORPHOS)
-typedef signed long long OFFileOffset;
+typedef signed long long OFStreamOffset;
#elif defined(OF_HAVE_OFF64_T)
-typedef off64_t OFFileOffset;
+typedef off64_t OFStreamOffset;
#else
-typedef off_t OFFileOffset;
+typedef off_t OFStreamOffset;
#endif
+/**
+ * @brief From where to seek.
+ */
+typedef enum {
+ /** Seek to the end of the stream + offset. */
+ OFSeekSet,
+ /** Seek to the current location + offset. */
+ OFSeekCurrent,
+ /** Seek to the specified byte. */
+ OFSeekEnd
+} OFSeekWhence;
+
/**
* @class OFSeekableStream OFSeekableStream.h ObjFW/OFSeekableStream.h
*
* @brief A stream that supports seeking.
*
@@ -57,23 +71,20 @@
{
OF_RESERVE_IVARS(OFSeekableStream, 4)
}
/**
- * @brief Seeks to the specified absolute offset.
+ * @brief Seeks to the specified offset.
*
* @param offset The offset in bytes
- * @param whence From where to seek.@n
- * Possible values are:
- * Value | Description
- * -----------|---------------------------------------
- * `SEEK_SET` | Seek to the specified byte
- * `SEEK_CUR` | Seek to the current location + offset
- * `SEEK_END` | Seek to the end of the stream + offset
+ * @param whence From where to seek.
* @return The new offset form the start of the file
+ * @throw OFSeekFailedException Seeking failed
+ * @throw OFNotOpenException The stream is not open
*/
-- (OFFileOffset)seekToOffset: (OFFileOffset)offset whence: (int)whence;
+- (OFStreamOffset)seekToOffset: (OFStreamOffset)offset
+ whence: (OFSeekWhence)whence;
/**
* @brief Seek the stream on the lowlevel.
*
* @warning Do not call this directly!
@@ -80,18 +91,15 @@
*
* @note Override this method with your actual seek implementation when
* subclassing!
*
* @param offset The offset to seek to
- * @param whence From where to seek.@n
- * Possible values are:
- * Value | Description
- * -----------|---------------------------------------
- * `SEEK_SET` | Seek to the specified byte
- * `SEEK_CUR` | Seek to the current location + offset
- * `SEEK_END` | Seek to the end of the stream + offset
+ * @param whence From where to seek.
* @return The new offset from the start of the file
+ * @throw OFSeekFailedException Seeking failed
+ * @throw OFNotOpenException The stream is not open
*/
-- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence;
+- (OFStreamOffset)lowlevelSeekToOffset: (OFStreamOffset)offset
+ whence: (OFSeekWhence)whence;
@end
OF_ASSUME_NONNULL_END
Index: src/OFSeekableStream.m
==================================================================
--- src/OFSeekableStream.m
+++ src/OFSeekableStream.m
@@ -36,18 +36,20 @@
}
return [super init];
}
-- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence
+- (OFStreamOffset)lowlevelSeekToOffset: (OFStreamOffset)offset
+ whence: (OFSeekWhence)whence
{
OF_UNRECOGNIZED_SELECTOR
}
-- (OFFileOffset)seekToOffset: (OFFileOffset)offset whence: (int)whence
+- (OFStreamOffset)seekToOffset: (OFStreamOffset)offset
+ whence: (OFSeekWhence)whence
{
- if (whence == SEEK_CUR)
+ if (whence == OFSeekCurrent)
offset -= _readBufferLength;
offset = [self lowlevelSeekToOffset: offset whence: whence];
OFFreeMemory(_readBufferMemory);
Index: src/OFSelectKernelEventObserver.m
==================================================================
--- src/OFSelectKernelEventObserver.m
+++ src/OFSelectKernelEventObserver.m
@@ -32,11 +32,11 @@
#import "OFSelectKernelEventObserver.h"
#import "OFArray.h"
#import "OFSocket+Private.h"
#import "OFInitializationFailedException.h"
-#import "OFObserveFailedException.h"
+#import "OFObserveKernelEventsFailedException.h"
#import "OFOutOfRangeException.h"
#ifdef OF_AMIGAOS
# include
#endif
@@ -82,12 +82,13 @@
- (void)addObjectForReading: (id )object
{
int fd = object.fileDescriptorForReading;
if (fd < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: EBADF];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: EBADF];
if (fd > INT_MAX - 1)
@throw [OFOutOfRangeException exception];
#ifndef OF_WINDOWS
@@ -106,12 +107,13 @@
- (void)addObjectForWriting: (id )object
{
int fd = object.fileDescriptorForWriting;
if (fd < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: EBADF];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: EBADF];
if (fd > INT_MAX - 1)
@throw [OFOutOfRangeException exception];
#ifndef OF_WINDOWS
@@ -132,12 +134,13 @@
/* TODO: Adjust _maxFD */
int fd = object.fileDescriptorForReading;
if (fd < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: EBADF];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: EBADF];
#ifndef OF_WINDOWS
if (fd >= (int)FD_SETSIZE)
@throw [OFOutOfRangeException exception];
#endif
@@ -152,12 +155,13 @@
/* TODO: Adjust _maxFD */
int fd = object.fileDescriptorForWriting;
if (fd < 0)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: EBADF];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: EBADF];
#ifndef OF_WINDOWS
if (fd >= (int)FD_SETSIZE)
@throw [OFOutOfRangeException exception];
@@ -204,12 +208,13 @@
#endif
timeout.tv_usec = (int)((timeInterval - timeout.tv_sec) * 1000000);
#ifdef OF_AMIGAOS
if ((cancelSignal = AllocSignal(-1)) == (BYTE)-1)
- @throw [OFObserveFailedException exceptionWithObserver: self
- errNo: EAGAIN];
+ @throw [OFObserveKernelEventsFailedException
+ exceptionWithObserver: self
+ errNo: EAGAIN];
execSignalMask = _execSignalMask | (1ul << cancelSignal);
Forbid();
@@ -229,11 +234,11 @@
events = select(_maxFD + 1, &readFDs, &writeFDs, NULL,
(timeInterval != -1 ? &timeout : NULL));
#endif
if (events < 0)
- @throw [OFObserveFailedException
+ @throw [OFObserveKernelEventsFailedException
exceptionWithObserver: self
errNo: OFSocketErrNo()];
#ifdef OF_AMIGAOS
if (execSignalMask != 0 &&
Index: src/OFSequencedPacketSocket.h
==================================================================
--- src/OFSequencedPacketSocket.h
+++ src/OFSequencedPacketSocket.h
@@ -134,10 +134,12 @@
/**
* @brief Whether the socket can block.
*
* By default, a socket can block.
+ *
+ * @throw OFSetOptionFailedException The option could not be set
*/
@property (nonatomic) bool canBlock;
/**
* @brief Whether the socket is a listening socket.
@@ -146,10 +148,13 @@
/**
* @brief The remote address.
*
* @note This only works for accepted sockets!
+ *
+ * @throw OFNotOpenException The socket is not open
+ * @throw OFInvalidArgumentException The socket has no remote address
*/
@property (readonly, nonatomic) const OFSocketAddress *remoteAddress;
/**
* @brief The delegate for asynchronous operations on the socket.
@@ -173,10 +178,12 @@
* If the buffer is too small, the receive operation fails.
*
* @param buffer The buffer to write the packet to
* @param length The length of the buffer
* @return The length of the received packet
+ * @throw OFReadFailedException Receiving failed
+ * @throw OFNotOpenException The socket is not open
*/
- (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length;
/**
* @brief Asynchronously receives a packet and stores it into the specified
@@ -246,10 +253,12 @@
/**
* @brief Sends the specified packet.
*
* @param buffer The buffer to send as a packet
* @param length The length of the buffer
+ * @throw OFWriteFailedException Sending failed
+ * @throw OFNotOpenException The socket is not open
*/
- (void)sendBuffer: (const void *)buffer length: (size_t)length;
/**
* @brief Asynchronously sends the specified packet.
@@ -294,22 +303,29 @@
/**
* @brief Listen on the socket.
*
* @param backlog Maximum length for the queue of pending connections.
+ * @throw OFListenOnSocketFailedException Listening failed
+ * @throw OFNotOpenException The socket is not open
*/
- (void)listenWithBacklog: (int)backlog;
/**
* @brief Listen on the socket.
+ *
+ * @throw OFListenOnSocketFailedException Listening failed
+ * @throw OFNotOpenException The socket is not open
*/
- (void)listen;
/**
* @brief Accept an incoming connection.
*
* @return An autoreleased sequenced packet socket for the accepted connection.
+ * @throw OFAcceptSocketFailedException Accepting failed
+ * @throw OFNotOpenException The socket is not open
*/
- (instancetype)accept;
/**
* @brief Asynchronously accept an incoming connection.
@@ -352,10 +368,12 @@
- (void)cancelAsyncRequests;
/**
* @brief Closes the socket so that it can neither receive nor send any more
* datagrams.
+ *
+ * @throw OFNotOpenException The socket is not open
*/
- (void)close;
@end
OF_ASSUME_NONNULL_END
Index: src/OFSequencedPacketSocket.m
==================================================================
--- src/OFSequencedPacketSocket.m
+++ src/OFSequencedPacketSocket.m
@@ -33,14 +33,14 @@
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
-#import "OFAcceptFailedException.h"
+#import "OFAcceptSocketFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
-#import "OFListenFailedException.h"
+#import "OFListenOnSocketFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"
@@ -306,52 +306,55 @@
{
if (_socket == OFInvalidSocketHandle)
@throw [OFNotOpenException exceptionWithObject: self];
if (listen(_socket, backlog) == -1)
- @throw [OFListenFailedException
+ @throw [OFListenOnSocketFailedException
exceptionWithSocket: self
backlog: backlog
errNo: OFSocketErrNo()];
_listening = true;
}
- (instancetype)accept
{
- OFSequencedPacketSocket *client =
- [[[[self class] alloc] init] autorelease];
+ OFSequencedPacketSocket *client;
#if (!defined(HAVE_PACCEPT) && !defined(HAVE_ACCEPT4)) || !defined(SOCK_CLOEXEC)
# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
int flags;
# endif
#endif
+ if (_socket == OFInvalidSocketHandle)
+ @throw [OFNotOpenException exceptionWithObject: self];
+
+ client = [[[[self class] alloc] init] autorelease];
client->_remoteAddress.length =
(socklen_t)sizeof(client->_remoteAddress.sockaddr);
#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC)
if ((client->_socket = paccept(_socket,
(struct sockaddr *)&client->_remoteAddress.sockaddr,
&client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) ==
OFInvalidSocketHandle)
- @throw [OFAcceptFailedException
+ @throw [OFAcceptSocketFailedException
exceptionWithSocket: self
errNo: OFSocketErrNo()];
#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
if ((client->_socket = accept4(_socket,
(struct sockaddr *)&client->_remoteAddress.sockaddr,
&client->_remoteAddress.length, SOCK_CLOEXEC)) ==
OFInvalidSocketHandle)
- @throw [OFAcceptFailedException
+ @throw [OFAcceptSocketFailedException
exceptionWithSocket: self
errNo: OFSocketErrNo()];
#else
if ((client->_socket = accept(_socket,
(struct sockaddr *)&client->_remoteAddress.sockaddr,
&client->_remoteAddress.length)) == OFInvalidSocketHandle)
- @throw [OFAcceptFailedException
+ @throw [OFAcceptSocketFailedException
exceptionWithSocket: self
errNo: OFSocketErrNo()];
# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1)
Index: src/OFSerialization.h
==================================================================
--- src/OFSerialization.h
+++ src/OFSerialization.h
@@ -34,10 +34,12 @@
/**
* @brief Initializes the object with the specified XML element serialization.
*
* @param element An OFXMLElement with the serialized object
* @return An initialized object
+ * @throw OFInvalidFormatException The specified element is not of the correct
+ * serialization format
*/
- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end
#ifdef __cplusplus
Index: src/OFSocket.h
==================================================================
--- src/OFSocket.h
+++ src/OFSocket.h
@@ -33,25 +33,33 @@
# include
#endif
#ifdef OF_HAVE_NETINET_TCP_H
# include
#endif
-#ifdef OF_HAVE_NETIPX_IPX_H
-# include
-#endif
#ifdef OF_HAVE_SYS_UN_H
# include
#endif
#ifdef OF_HAVE_AFUNIX_H
# include
+#endif
+#ifdef OF_HAVE_NETIPX_IPX_H
+# include
+#endif
+#if defined(OF_HAVE_NETAT_APPLETALK_H)
+# include
+#elif defined(OF_HAVE_NETATALK_AT_H)
+# include
#endif
#ifdef OF_WINDOWS
# include
# include
# ifdef OF_HAVE_IPX
# include
+# endif
+# ifdef OF_HAVE_APPLETALK
+# include
# endif
#endif
/** @file */
@@ -101,10 +109,12 @@
OFSocketAddressFamilyIPv6,
/** UNIX */
OFSocketAddressFamilyUNIX,
/** IPX */
OFSocketAddressFamilyIPX,
+ /** AppleTalk */
+ OFSocketAddressFamilyAppleTalk,
/** Any address family */
OFSocketAddressFamilyAny = 255
} OFSocketAddressFamily;
#ifndef OF_HAVE_IPV6
@@ -142,10 +152,27 @@
# define sipx_network sa_netnum
# define sipx_node sa_nodenum
# define sipx_port sa_socket
#endif
+#ifndef OF_HAVE_APPLETALK
+struct sockaddr_at {
+ sa_family_t sat_family;
+ uint8_t sat_port;
+ struct at_addr {
+ uint16_t s_net;
+ uint8_t s_node;
+ } sat_addr;
+};
+#endif
+#ifdef OF_WINDOWS
+# define sat_port sat_socket
+#else
+# define sat_net sat_addr.s_net
+# define sat_node sat_addr.s_node
+#endif
+
/**
* @struct OFSocketAddress OFSocket.h ObjFW/OFSocket.h
*
* @brief A struct which represents a host / port pair for a socket.
*/
@@ -158,10 +185,11 @@
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
struct sockaddr_ipx ipx;
+ struct sockaddr_at at;
} sockaddr;
socklen_t length;
} OFSocketAddress;
#ifdef __cplusplus
@@ -172,28 +200,31 @@
* @ref OFSocketAddress.
*
* @param IP The IP to parse
* @param port The port to use
* @return The parsed IP and port as an OFSocketAddress
+ * @throw OFInvalidFormatException The specified string is not a valid IP
*/
extern OFSocketAddress OFSocketAddressParseIP(OFString *IP, uint16_t port);
/**
* @brief Parses the specified IPv4 and port into an @ref OFSocketAddress.
*
* @param IP The IPv4 to parse
* @param port The port to use
* @return The parsed IPv4 and port as an OFSocketAddress
+ * @throw OFInvalidFormatException The specified string is not a valid IPv4
*/
extern OFSocketAddress OFSocketAddressParseIPv4(OFString *IP, uint16_t port);
/**
* @brief Parses the specified IPv6 and port into an @ref OFSocketAddress.
*
* @param IP The IPv6 to parse
* @param port The port to use
* @return The parsed IPv6 and port as an OFSocketAddress
+ * @throw OFInvalidFormatException The specified string is not a valid IPv6
*/
extern OFSocketAddress OFSocketAddressParseIPv6(OFString *IP, uint16_t port);
/**
* @brief Creates a UNIX socket address from the specified path.
@@ -202,20 +233,32 @@
* @return A UNIX socket address with the specified path
*/
extern OFSocketAddress OFSocketAddressMakeUNIX(OFString *path);
/**
- * @brief Creates an IPX address for the specified node, network and port.
+ * @brief Creates an IPX address for the specified network, node and port.
*
* @param network The IPX network
* @param node The node in the IPX network
* @param port The IPX port (sometimes called socket number) on the node
* @return An IPX socket address with the specified node, network and port.
*/
extern OFSocketAddress OFSocketAddressMakeIPX(uint32_t network,
const unsigned char node[_Nonnull IPX_NODE_LEN], uint16_t port);
+/**
+ * @brief Creates an AppleTalk address for the specified network, node and port.
+ *
+ * @param network The AppleTalk network
+ * @param node The node in the AppleTalk network
+ * @param port The AppleTalk (sometimes called socket number) on the node
+ * @return An AppleTalk socket address with the specified node, network and
+ * port.
+ */
+extern OFSocketAddress OFSocketAddressMakeAppleTalk(uint16_t network,
+ uint8_t node, uint8_t port);
+
/**
* @brief Compares two OFSocketAddress for equality.
*
* @param address1 The address to compare with the second address
* @param address2 The second address
@@ -241,27 +284,25 @@
*/
extern OFString *_Nonnull OFSocketAddressString(
const OFSocketAddress *_Nonnull address);
/**
- * @brief Sets the port of the specified @ref OFSocketAddress, independent of
- * the address family used.
+ * @brief Sets the IP port of the specified @ref OFSocketAddress.
*
* @param address The address on which to set the port
* @param port The port to set on the address
*/
-extern void OFSocketAddressSetPort(OFSocketAddress *_Nonnull address,
+extern void OFSocketAddressSetIPPort(OFSocketAddress *_Nonnull address,
uint16_t port);
/**
- * @brief Returns the port of the specified @ref OFSocketAddress, independent of
- * the address family used.
+ * @brief Returns the IP port of the specified @ref OFSocketAddress.
*
* @param address The address on which to get the port
* @return The port of the address
*/
-extern uint16_t OFSocketAddressPort(const OFSocketAddress *_Nonnull address);
+extern uint16_t OFSocketAddressIPPort(const OFSocketAddress *_Nonnull address);
/**
* @brief Gets the UNIX socket path of the specified @ref OFSocketAddress.
*
* @param address The address on which to get the UNIX socket path
@@ -301,13 +342,84 @@
* @brief Gets the IPX node of the specified @ref OFSocketAddress.
*
* @param address The address on which to get the IPX node
* @param node A byte array to store the IPX node of the address
*/
-extern void OFSocketAddressIPXNode(const OFSocketAddress *_Nonnull address,
+extern void OFSocketAddressGetIPXNode(const OFSocketAddress *_Nonnull address,
unsigned char node[_Nonnull IPX_NODE_LEN]);
+/**
+ * @brief Sets the IPX port of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to set the port
+ * @param port The port to set on the address
+ */
+extern void OFSocketAddressSetIPXPort(OFSocketAddress *_Nonnull address,
+ uint16_t port);
+
+/**
+ * @brief Returns the IPX port of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to get the port
+ * @return The port of the address
+ */
+extern uint16_t OFSocketAddressIPXPort(const OFSocketAddress *_Nonnull address);
+
+/**
+ * @brief Sets the AppleTalk network of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to set the AppleTalk network
+ * @param network The AppleTalk network to set on the address
+ */
+extern void OFSocketAddressSetAppleTalkNetwork(
+ OFSocketAddress *_Nonnull address, uint16_t network);
+
+/**
+ * @brief Returns the AppleTalk network of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to get the AppleTalk network
+ * @return The AppleTalk network of the address
+ */
+extern uint16_t OFSocketAddressAppleTalkNetwork(
+ const OFSocketAddress *_Nonnull address);
+
+/**
+ * @brief Sets the AppleTalk node of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to set the AppleTalk node
+ * @param node The AppleTalk node to set on the address
+ */
+extern void OFSocketAddressSetAppleTalkNode(OFSocketAddress *_Nonnull address,
+ uint8_t node);
+
+/**
+ * @brief Gets the AppleTalk node of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to get the AppleTalk node
+ * @return The AppleTalk node of the address
+ */
+extern uint8_t OFSocketAddressAppleTalkNode(
+ const OFSocketAddress *_Nonnull address);
+
+/**
+ * @brief Sets the AppleTalk port of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to set the port
+ * @param port The port to set on the address
+ */
+extern void OFSocketAddressSetAppleTalkPort(OFSocketAddress *_Nonnull address,
+ uint8_t port);
+
+/**
+ * @brief Returns the AppleTalk port of the specified @ref OFSocketAddress.
+ *
+ * @param address The address on which to get the port
+ * @return The port of the address
+ */
+extern uint8_t OFSocketAddressAppleTalkPort(
+ const OFSocketAddress *_Nonnull address);
+
extern bool OFSocketInit(void);
#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
extern void OFSocketDeinit(void);
#endif
extern int OFSocketErrNo(void);
Index: src/OFSocket.m
==================================================================
--- src/OFSocket.m
+++ src/OFSocket.m
@@ -45,10 +45,14 @@
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFLockFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFUnlockFailedException.h"
+
+#ifdef HAVE_NET_IF_H
+# include
+#endif
#ifdef OF_AMIGAOS
# include
#endif
@@ -232,11 +236,11 @@
CloseLibrary(socketBase);
}
#endif
int
-OFSocketErrNo()
+OFSocketErrNo(void)
{
#if defined(OF_WINDOWS)
switch (WSAGetLastError()) {
case WSAEACCES:
return EACCES;
@@ -431,11 +435,11 @@
OFSocketAddressParseIPv6(OFString *IPv6, uint16_t port)
{
void *pool = objc_autoreleasePoolPush();
OFSocketAddress ret;
struct sockaddr_in6 *addrIn6 = &ret.sockaddr.in6;
- size_t doubleColon;
+ size_t doubleColon, percent;
memset(&ret, '\0', sizeof(ret));
ret.family = OFSocketAddressFamilyIPv6;
ret.length = sizeof(ret.sockaddr.in6);
@@ -443,13 +447,30 @@
addrIn6->sin6_family = AF_INET6;
#else
addrIn6->sin6_family = AF_UNSPEC;
#endif
addrIn6->sin6_port = OFToBigEndian16(port);
+
+ if ((percent = [IPv6 rangeOfString: @"%"].location) != OFNotFound) {
+ OFString *interface = [IPv6 substringFromIndex: percent + 1];
+ IPv6 = [IPv6 substringToIndex: percent];
+
+ @try {
+ addrIn6->sin6_scope_id = (uint32_t)[interface
+ unsignedLongLongValueWithBase: 10];
+ } @catch (OFInvalidFormatException *e) {
+#if defined(HAVE_IF_NAMETOINDEX) && !defined(OF_WINDOWS)
+ addrIn6->sin6_scope_id = if_nametoindex([interface
+ cStringWithEncoding: [OFLocale encoding]]);
+#endif
+ }
+
+ if (addrIn6->sin6_scope_id == 0)
+ @throw [OFInvalidArgumentException exception];
+ }
doubleColon = [IPv6 rangeOfString: @"::"].location;
-
if (doubleColon != OFNotFound) {
OFString *left = [IPv6 substringToIndex: doubleColon];
OFString *right = [IPv6 substringFromIndex: doubleColon + 2];
OFArray OF_GENERIC(OFString *) *leftComponents;
OFArray OF_GENERIC(OFString *) *rightComponents;
@@ -570,18 +591,44 @@
memcpy(ret.sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
ret.sockaddr.ipx.sipx_port = OFToBigEndian16(port);
return ret;
}
+
+OFSocketAddress
+OFSocketAddressMakeAppleTalk(uint16_t network, uint8_t node, uint8_t port)
+{
+ OFSocketAddress ret;
+
+ memset(&ret, '\0', sizeof(ret));
+ ret.family = OFSocketAddressFamilyAppleTalk;
+ ret.length = sizeof(ret.sockaddr.at);
+
+#ifdef AF_APPLETALK
+ ret.sockaddr.at.sat_family = AF_APPLETALK;
+#else
+ ret.sockaddr.at.sat_family = AF_UNSPEC;
+#endif
+#ifdef OF_WINDOWS
+ ret.sockaddr.at.sat_net = network;
+#else
+ ret.sockaddr.at.sat_net = OFToBigEndian16(network);
+#endif
+ ret.sockaddr.at.sat_node = node;
+ ret.sockaddr.at.sat_port = port;
+
+ return ret;
+}
bool
OFSocketAddressEqual(const OFSocketAddress *address1,
const OFSocketAddress *address2)
{
const struct sockaddr_in *addrIn1, *addrIn2;
const struct sockaddr_in6 *addrIn6_1, *addrIn6_2;
const struct sockaddr_ipx *addrIPX1, *addrIPX2;
+ const struct sockaddr_at *addrAT1, *addrAT2;
void *pool;
OFString *path1, *path2;
bool ret;
if (address1->family != address2->family)
@@ -655,10 +702,26 @@
return false;
if (memcmp(addrIPX1->sipx_node, addrIPX2->sipx_node,
IPX_NODE_LEN) != 0)
return false;
+ return true;
+ case OFSocketAddressFamilyAppleTalk:
+ if (address1->length < (socklen_t)sizeof(struct sockaddr_at) ||
+ address2->length < (socklen_t)sizeof(struct sockaddr_at))
+ @throw [OFInvalidArgumentException exception];
+
+ addrAT1 = &address1->sockaddr.at;
+ addrAT2 = &address2->sockaddr.at;
+
+ if (addrAT1->sat_net != addrAT2->sat_net)
+ return false;
+ if (addrAT1->sat_node != addrAT2->sat_node)
+ return false;
+ if (addrAT1->sat_port != addrAT2->sat_port)
+ return false;
+
return true;
default:
@throw [OFInvalidArgumentException exception];
}
}
@@ -667,11 +730,11 @@
OFSocketAddressHash(const OFSocketAddress *address)
{
unsigned long hash;
OFHashInit(&hash);
- OFHashAdd(&hash, address->family);
+ OFHashAddByte(&hash, address->family);
switch (address->family) {
case OFSocketAddressFamilyIPv4:
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
if (address->length < 8)
@@ -679,28 +742,30 @@
#else
if (address->length < (socklen_t)sizeof(struct sockaddr_in))
@throw [OFInvalidArgumentException exception];
#endif
- OFHashAdd(&hash, address->sockaddr.in.sin_port >> 8);
- OFHashAdd(&hash, address->sockaddr.in.sin_port);
- OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr >> 24);
- OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr >> 16);
- OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr >> 8);
- OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr);
+ OFHashAddByte(&hash, address->sockaddr.in.sin_port >> 8);
+ OFHashAddByte(&hash, address->sockaddr.in.sin_port);
+ OFHashAddByte(&hash,
+ address->sockaddr.in.sin_addr.s_addr >> 24);
+ OFHashAddByte(&hash,
+ address->sockaddr.in.sin_addr.s_addr >> 16);
+ OFHashAddByte(&hash, address->sockaddr.in.sin_addr.s_addr >> 8);
+ OFHashAddByte(&hash, address->sockaddr.in.sin_addr.s_addr);
break;
case OFSocketAddressFamilyIPv6:
if (address->length < (socklen_t)sizeof(struct sockaddr_in6))
@throw [OFInvalidArgumentException exception];
- OFHashAdd(&hash, address->sockaddr.in6.sin6_port >> 8);
- OFHashAdd(&hash, address->sockaddr.in6.sin6_port);
+ OFHashAddByte(&hash, address->sockaddr.in6.sin6_port >> 8);
+ OFHashAddByte(&hash, address->sockaddr.in6.sin6_port);
for (size_t i = 0;
i < sizeof(address->sockaddr.in6.sin6_addr.s6_addr); i++)
- OFHashAdd(&hash,
+ OFHashAddByte(&hash,
address->sockaddr.in6.sin6_addr.s6_addr[i]);
break;
case OFSocketAddressFamilyUNIX:;
void *pool = objc_autoreleasePoolPush();
@@ -716,21 +781,31 @@
sizeof(address->sockaddr.ipx.sipx_network)];
if (address->length < (socklen_t)sizeof(struct sockaddr_ipx))
@throw [OFInvalidArgumentException exception];
- OFHashAdd(&hash, address->sockaddr.ipx.sipx_port >> 8);
- OFHashAdd(&hash, address->sockaddr.ipx.sipx_port);
+ OFHashAddByte(&hash, address->sockaddr.ipx.sipx_port >> 8);
+ OFHashAddByte(&hash, address->sockaddr.ipx.sipx_port);
memcpy(network, &address->sockaddr.ipx.sipx_network,
sizeof(network));
for (size_t i = 0; i < sizeof(network); i++)
- OFHashAdd(&hash, network[i]);
+ OFHashAddByte(&hash, network[i]);
for (size_t i = 0; i < IPX_NODE_LEN; i++)
- OFHashAdd(&hash, address->sockaddr.ipx.sipx_node[i]);
+ OFHashAddByte(&hash,
+ address->sockaddr.ipx.sipx_node[i]);
+
+ break;
+ case OFSocketAddressFamilyAppleTalk:
+ if (address->length < (socklen_t)sizeof(struct sockaddr_at))
+ @throw [OFInvalidArgumentException exception];
+
+ OFHashAddByte(&hash, address->sockaddr.at.sat_net >> 8);
+ OFHashAddByte(&hash, address->sockaddr.at.sat_net);
+ OFHashAddByte(&hash, address->sockaddr.at.sat_port);
break;
default:
@throw [OFInvalidArgumentException exception];
}
@@ -813,10 +888,21 @@
(addrIn6->sin6_addr.s6_addr[i] << 8) |
addrIn6->sin6_addr.s6_addr[i + 1]];
first = false;
}
}
+
+ if (addrIn6->sin6_scope_id != 0) {
+#if defined(HAVE_IF_INDEXTONAME) && !defined(OF_WINDOWS)
+ char interface[IF_NAMESIZE];
+
+ if (if_indextoname(addrIn6->sin6_scope_id, interface) != NULL)
+ [string appendFormat: @"%%%s", interface];
+ else
+# endif
+ [string appendFormat: @"%%%u", addrIn6->sin6_scope_id];
+ }
[string makeImmutable];
return string;
}
@@ -833,37 +919,32 @@
@throw [OFInvalidArgumentException exception];
}
}
void
-OFSocketAddressSetPort(OFSocketAddress *address, uint16_t port)
+OFSocketAddressSetIPPort(OFSocketAddress *address, uint16_t port)
{
switch (address->family) {
case OFSocketAddressFamilyIPv4:
address->sockaddr.in.sin_port = OFToBigEndian16(port);
break;
case OFSocketAddressFamilyIPv6:
address->sockaddr.in6.sin6_port = OFToBigEndian16(port);
break;
- case OFSocketAddressFamilyIPX:
- address->sockaddr.ipx.sipx_port = OFToBigEndian16(port);
- break;
default:
@throw [OFInvalidArgumentException exception];
}
}
uint16_t
-OFSocketAddressPort(const OFSocketAddress *address)
+OFSocketAddressIPPort(const OFSocketAddress *address)
{
switch (address->family) {
case OFSocketAddressFamilyIPv4:
return OFFromBigEndian16(address->sockaddr.in.sin_port);
case OFSocketAddressFamilyIPv6:
return OFFromBigEndian16(address->sockaddr.in6.sin6_port);
- case OFSocketAddressFamilyIPX:
- return OFFromBigEndian16(address->sockaddr.ipx.sipx_port);
default:
@throw [OFInvalidArgumentException exception];
}
}
@@ -922,13 +1003,93 @@
memcpy(address->sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
}
void
-OFSocketAddressIPXNode(const OFSocketAddress *address,
+OFSocketAddressGetIPXNode(const OFSocketAddress *address,
unsigned char node[IPX_NODE_LEN])
{
if (address->family != OFSocketAddressFamilyIPX)
@throw [OFInvalidArgumentException exception];
memcpy(node, address->sockaddr.ipx.sipx_node, IPX_NODE_LEN);
}
+
+void
+OFSocketAddressSetIPXPort(OFSocketAddress *address, uint16_t port)
+{
+ if (address->family != OFSocketAddressFamilyIPX)
+ @throw [OFInvalidArgumentException exception];
+
+ address->sockaddr.ipx.sipx_port = OFToBigEndian16(port);
+}
+
+uint16_t
+OFSocketAddressIPXPort(const OFSocketAddress *address)
+{
+ if (address->family != OFSocketAddressFamilyIPX)
+ @throw [OFInvalidArgumentException exception];
+
+ return OFFromBigEndian16(address->sockaddr.ipx.sipx_port);
+}
+
+void
+OFSocketAddressSetAppleTalkNetwork(OFSocketAddress *address, uint16_t network)
+{
+ if (address->family != OFSocketAddressFamilyAppleTalk)
+ @throw [OFInvalidArgumentException exception];
+
+#ifdef OF_WINDOWS
+ address->sockaddr.at.sat_net = network;
+#else
+ address->sockaddr.at.sat_net = OFToBigEndian16(network);
+#endif
+}
+
+uint16_t
+OFSocketAddressAppleTalkNetwork(const OFSocketAddress *address)
+{
+ if (address->family != OFSocketAddressFamilyAppleTalk)
+ @throw [OFInvalidArgumentException exception];
+
+#ifdef OF_WINDOWS
+ return address->sockaddr.at.sat_net;
+#else
+ return OFFromBigEndian16(address->sockaddr.at.sat_net);
+#endif
+}
+
+void
+OFSocketAddressSetAppleTalkNode(OFSocketAddress *address, uint8_t node)
+{
+ if (address->family != OFSocketAddressFamilyAppleTalk)
+ @throw [OFInvalidArgumentException exception];
+
+ address->sockaddr.at.sat_node = node;
+}
+
+uint8_t
+OFSocketAddressAppleTalkNode(const OFSocketAddress *address)
+{
+ if (address->family != OFSocketAddressFamilyAppleTalk)
+ @throw [OFInvalidArgumentException exception];
+
+ return address->sockaddr.at.sat_node;
+}
+
+void
+OFSocketAddressSetAppleTalkPort(OFSocketAddress *address, uint8_t port)
+{
+ if (address->family != OFSocketAddressFamilyAppleTalk)
+ @throw [OFInvalidArgumentException exception];
+
+ address->sockaddr.at.sat_port = port;
+}
+
+uint8_t
+OFSocketAddressAppleTalkPort(const OFSocketAddress *address)
+{
+ if (address->family != OFSocketAddressFamilyAppleTalk)
+ @throw [OFInvalidArgumentException exception];
+
+ return address->sockaddr.at.sat_port;
+}
Index: src/OFStream.h
==================================================================
--- src/OFStream.h
+++ src/OFStream.h
@@ -215,10 +215,12 @@
/**
* @brief Whether the stream can block.
*
* By default, a stream can block.
* On Win32, setting this currently only works for sockets!
+ *
+ * @throw OFSetOptionFailedException The option could not be set
*/
@property (nonatomic) bool canBlock;
/**
* @brief The delegate for asynchronous operations on the stream.
@@ -228,11 +230,11 @@
*/
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
id delegate;
/**
- * @brief Reads *at most* size bytes from the stream into a buffer.
+ * @brief Reads *at most* `length` bytes from the stream into a buffer.
*
* On network streams, this might read less than the specified number of bytes.
* If you want to read exactly the specified number of bytes, use
* @ref readIntoBuffer:exactLength:. Note that a read can even return 0 bytes -
* this does not necessarily mean that the stream ended, so you still need to
@@ -242,15 +244,17 @@
*
* @param buffer The buffer into which the data is read
* @param length The length of the data that should be read at most.
* The buffer *must* be *at least* this big!
* @return The number of bytes read
+ * @throw OFReadFailedException Reading failed
+ * @throw OFNotOpenException The stream is not open
*/
- (size_t)readIntoBuffer: (void *)buffer length: (size_t)length;
/**
- * @brief Reads exactly the specified length bytes from the stream into a
+ * @brief Reads exactly the specified `length` bytes from the stream into a
* buffer.
*
* Unlike @ref readIntoBuffer:length:, this method does not return when less
* than the specified length has been read - instead, it waits until it got
* exactly the specified length.
@@ -259,16 +263,20 @@
* available! Otherwise you will get an exception!
*
* @param buffer The buffer into which the data is read
* @param length The length of the data that should be read.
* The buffer *must* be *at least* this big!
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading the specified amount
+ * @throw OFNotOpenException The stream is not open
*/
- (void)readIntoBuffer: (void *)buffer exactLength: (size_t)length;
#ifdef OF_HAVE_SOCKETS
/**
- * @brief Asynchronously reads *at most* size bytes from the stream into a
+ * @brief Asynchronously reads *at most* `length` bytes from the stream into a
* buffer.
*
* On network streams, this might read less than the specified number of bytes.
* If you want to read exactly the specified number of bytes, use
* @ref asyncReadIntoBuffer:exactLength:. Note that a read can even return 0
@@ -286,11 +294,11 @@
* The buffer *must* be *at least* this big!
*/
- (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length;
/**
- * @brief Asynchronously reads *at most* size bytes from the stream into a
+ * @brief Asynchronously reads *at most* `length` bytes from the stream into a
* buffer.
*
* On network streams, this might read less than the specified number of bytes.
* If you want to read exactly the specified number of bytes, use
* @ref asyncReadIntoBuffer:exactLength:. Note that a read can even return 0
@@ -311,11 +319,11 @@
- (void)asyncReadIntoBuffer: (void *)buffer
length: (size_t)length
runLoopMode: (OFRunLoopMode)runLoopMode;
/**
- * @brief Asynchronously reads exactly the specified length bytes from the
+ * @brief Asynchronously reads exactly the specified `length` bytes from the
* stream into a buffer.
*
* Unlike @ref asyncReadIntoBuffer:length:, this method does not call the
* method when less than the specified length has been read - instead, it waits
* until it got exactly the specified length, the stream has ended or an
@@ -329,11 +337,11 @@
* The buffer *must* be *at least* this big!
*/
- (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length;
/**
- * @brief Asynchronously reads exactly the specified length bytes from the
+ * @brief Asynchronously reads exactly the specified `length` bytes from the
* stream into a buffer.
*
* Unlike @ref asyncReadIntoBuffer:length:, this method does not call the
* method when less than the specified length has been read - instead, it waits
* until it got exactly the specified length, the stream has ended or an
@@ -351,12 +359,12 @@
exactLength: (size_t)length
runLoopMode: (OFRunLoopMode)runLoopMode;
# ifdef OF_HAVE_BLOCKS
/**
- * @brief Asynchronously reads *at most* ref size bytes from the stream into a
- * buffer.
+ * @brief Asynchronously reads *at most* ref `length` bytes from the stream
+ * into a buffer.
*
* On network streams, this might read less than the specified number of bytes.
* If you want to read exactly the specified number of bytes, use
* @ref asyncReadIntoBuffer:exactLength:block:. Note that a read can even
* return 0 bytes - this does not necessarily mean that the stream ended, so
@@ -380,11 +388,11 @@
- (void)asyncReadIntoBuffer: (void *)buffer
length: (size_t)length
block: (OFStreamAsyncReadBlock)block;
/**
- * @brief Asynchronously reads *at most* ref size bytes from the stream into a
+ * @brief Asynchronously reads *at most* `length` bytes from the stream into a
* buffer.
*
* On network streams, this might read less than the specified number of bytes.
* If you want to read exactly the specified number of bytes, use
* @ref asyncReadIntoBuffer:exactLength:block:. Note that a read can even
@@ -411,11 +419,11 @@
length: (size_t)length
runLoopMode: (OFRunLoopMode)runLoopMode
block: (OFStreamAsyncReadBlock)block;
/**
- * @brief Asynchronously reads exactly the specified length bytes from the
+ * @brief Asynchronously reads exactly the specified `length` bytes from the
* stream into a buffer.
*
* Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke
* the block when less than the specified length has been read - instead, it
* waits until it got exactly the specified length, the stream has ended or an
@@ -436,11 +444,11 @@
- (void)asyncReadIntoBuffer: (void *)buffer
exactLength: (size_t)length
block: (OFStreamAsyncReadBlock)block;
/**
- * @brief Asynchronously reads exactly the specified length bytes from the
+ * @brief Asynchronously reads exactly the specified `length` bytes from the
* stream into a buffer.
*
* Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke
* the block when less than the specified length has been read - instead, it
* waits until it got exactly the specified length, the stream has ended or an
@@ -471,10 +479,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A uint8_t from the stream
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (uint8_t)readInt8;
/**
* @brief Reads a uint16_t from the stream which is encoded in big endian.
@@ -481,10 +493,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A uint16_t from the stream in native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (uint16_t)readBigEndianInt16;
/**
* @brief Reads a uint32_t from the stream which is encoded in big endian.
@@ -491,10 +507,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A uint32_t from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (uint32_t)readBigEndianInt32;
/**
* @brief Reads a uint64_t from the stream which is encoded in big endian.
@@ -501,10 +521,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A uint64_t from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (uint64_t)readBigEndianInt64;
/**
* @brief Reads a float from the stream which is encoded in big endian.
@@ -511,10 +535,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A float from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (float)readBigEndianFloat;
/**
* @brief Reads a double from the stream which is encoded in big endian.
@@ -521,10 +549,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A double from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (double)readBigEndianDouble;
/**
* @brief Reads a uint16_t from the stream which is encoded in little endian.
@@ -531,10 +563,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A uint16_t from the stream in native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (uint16_t)readLittleEndianInt16;
/**
* @brief Reads a uint32_t from the stream which is encoded in little endian.
@@ -541,10 +577,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A uint32_t from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (uint32_t)readLittleEndianInt32;
/**
* @brief Reads a uint64_t from the stream which is encoded in little endian.
@@ -551,10 +591,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A uint64_t from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (uint64_t)readLittleEndianInt64;
/**
* @brief Reads a float from the stream which is encoded in little endian.
@@ -561,10 +605,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A float from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (float)readLittleEndianFloat;
/**
* @brief Reads a double from the stream which is encoded in little endian.
@@ -571,10 +619,14 @@
*
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @return A double from the stream in the native endianess
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (double)readLittleEndianDouble;
/**
* @brief Reads the specified number of items with an item size of 1 from the
@@ -583,10 +635,14 @@
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @param count The number of items to read
* @return OFData with count items.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (OFData *)readDataWithCount: (size_t)count;
/**
* @brief Reads the specified number of items with the specified item size from
@@ -596,18 +652,24 @@
* Otherwise you will get an exception!
*
* @param itemSize The size of each item
* @param count The number of items to read
* @return OFData with count items.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (OFData *)readDataWithItemSize: (size_t)itemSize count: (size_t)count;
/**
* @brief Returns OFData with all the remaining data of the stream.
*
* @return OFData with an item size of 1 with all the data of the stream until
* the end of the stream is reached.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFNotOpenException The stream is not open
*/
- (OFData *)readDataUntilEndOfStream;
/**
* @brief Reads a string with the specified length from the stream.
@@ -620,10 +682,16 @@
* @warning Only call this when you know that enough data is available!
* Otherwise you will get an exception!
*
* @param length The length (in bytes) of the string to read from the stream
* @return A string with the specified length
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (OFString *)readStringWithLength: (size_t)length;
/**
* @brief Reads a string with the specified encoding and length from the stream.
@@ -637,19 +705,29 @@
* Otherwise you will get an exception!
*
* @param encoding The encoding of the string to read from the stream
* @param length The length (in bytes) of the string to read from the stream
* @return A string with the specified length
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFTruncatedDataException The end of the stream was reached before
+ * reading enough bytes
+ * @throw OFNotOpenException The stream is not open
*/
- (OFString *)readStringWithLength: (size_t)length
encoding: (OFStringEncoding)encoding;
/**
* @brief Reads until a newline, `\0` or end of stream occurs.
*
* @return The line that was read, autoreleased, or `nil` if the end of the
* stream has been reached.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
- (nullable OFString *)readLine;
/**
* @brief Reads with the specified encoding until a newline, `\0` or end of
@@ -656,10 +734,14 @@
* stream occurs.
*
* @param encoding The encoding used by the stream
* @return The line that was read, autoreleased, or `nil` if the end of the
* stream has been reached.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
- (nullable OFString *)readLineWithEncoding: (OFStringEncoding)encoding;
#ifdef OF_HAVE_SOCKETS
/**
@@ -753,10 +835,14 @@
* @brief Tries to read a line from the stream (see @ref readLine) and returns
* `nil` if no complete line has been received yet.
*
* @return The line that was read, autoreleased, or `nil` if the line is not
* complete yet
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
- (nullable OFString *)tryReadLine;
/**
* @brief Tries to read a line from the stream with the specified encoding (see
@@ -764,10 +850,14 @@
* been received yet.
*
* @param encoding The encoding used by the stream
* @return The line that was read, autoreleased, or `nil` if the line is not
* complete yet
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
- (nullable OFString *)tryReadLineWithEncoding: (OFStringEncoding)encoding;
/**
* @brief Reads until the specified string or `\0` is found or the end of
@@ -774,48 +864,64 @@
* stream occurs.
*
* @param delimiter The delimiter
* @return The line that was read, autoreleased, or `nil` if the end of the
* stream has been reached.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
-- (nullable OFString *)readTillDelimiter: (OFString *)delimiter;
+- (nullable OFString *)readUntilDelimiter: (OFString *)delimiter;
/**
* @brief Reads until the specified string or `\0` is found or the end of
* stream occurs.
*
* @param delimiter The delimiter
* @param encoding The encoding used by the stream
* @return The line that was read, autoreleased, or `nil` if the end of the
* stream has been reached.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
-- (nullable OFString *)readTillDelimiter: (OFString *)delimiter
- encoding: (OFStringEncoding)encoding;
+- (nullable OFString *)readUntilDelimiter: (OFString *)delimiter
+ encoding: (OFStringEncoding)encoding;
/**
* @brief Tries to reads until the specified string or `\0` is found or the end
- * of stream (see @ref readTillDelimiter:) and returns `nil` if not
+ * of stream (see @ref readUntilDelimiter:) and returns `nil` if not
* enough data has been received yet.
*
* @param delimiter The delimiter
* @return The line that was read, autoreleased, or `nil` if the end of the
* stream has been reached.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
-- (nullable OFString *)tryReadTillDelimiter: (OFString *)delimiter;
+- (nullable OFString *)tryReadUntilDelimiter: (OFString *)delimiter;
/**
* @brief Tries to read until the specified string or `\0` is found or the end
- * of stream occurs (see @ref readTillDelimiter:encoding:) and returns
+ * of stream occurs (see @ref readUntilDelimiter:encoding:) and returns
* `nil` if not enough data has been received yet.
*
* @param delimiter The delimiter
* @param encoding The encoding used by the stream
* @return The line that was read, autoreleased, or `nil` if the end of the
* stream has been reached.
+ * @throw OFReadFailedException Reading failed
+ * @throw OFInvalidEncodingException The string read from the stream has
+ * invalid encoding
+ * @throw OFNotOpenException The stream is not open
*/
-- (nullable OFString *)tryReadTillDelimiter: (OFString *)delimiter
- encoding: (OFStringEncoding)encoding;
+- (nullable OFString *)tryReadUntilDelimiter: (OFString *)delimiter
+ encoding: (OFStringEncoding)encoding;
/**
* @brief Writes everything in the write buffer to the stream.
*
* @return Whether the write buffer was flushed entirely. On non-blocking
@@ -834,10 +940,12 @@
* @ref OFWriteFailedException#bytesWritten being set to the number of bytes
* that were written, if any.
*
* @param buffer The buffer from which the data is written into the stream
* @param length The length of the data that should be written
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeBuffer: (const void *)buffer length: (size_t)length;
#ifdef OF_HAVE_SOCKETS
/**
@@ -991,118 +1099,144 @@
* @brief Writes a uint8_t into the stream.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param int8 A uint8_t
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeInt8: (uint8_t)int8;
/**
* @brief Writes a uint16_t into the stream, encoded in big endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param int16 A uint16_t
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeBigEndianInt16: (uint16_t)int16;
/**
* @brief Writes a uint32_t into the stream, encoded in big endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param int32 A uint32_t
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeBigEndianInt32: (uint32_t)int32;
/**
* @brief Writes a uint64_t into the stream, encoded in big endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param int64 A uint64_t
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeBigEndianInt64: (uint64_t)int64;
/**
* @brief Writes a float into the stream, encoded in big endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param float_ A float
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeBigEndianFloat: (float)float_;
/**
* @brief Writes a double into the stream, encoded in big endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param double_ A double
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeBigEndianDouble: (double)double_;
/**
* @brief Writes a uint16_t into the stream, encoded in little endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param int16 A uint16_t
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeLittleEndianInt16: (uint16_t)int16;
/**
* @brief Writes a uint32_t into the stream, encoded in little endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param int32 A uint32_t
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeLittleEndianInt32: (uint32_t)int32;
/**
* @brief Writes a uint64_t into the stream, encoded in little endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param int64 A uint64_t
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeLittleEndianInt64: (uint64_t)int64;
/**
* @brief Writes a float into the stream, encoded in little endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param float_ A float
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeLittleEndianFloat: (float)float_;
/**
* @brief Writes a double into the stream, encoded in little endian.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param double_ A double
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeLittleEndianDouble: (double)double_;
/**
* @brief Writes OFData into the stream.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param data The OFData to write into the stream
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeData: (OFData *)data;
/**
* @brief Writes a string into the stream, without the trailing zero.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param string The string from which the data is written to the stream
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeString: (OFString *)string;
/**
* @brief Writes a string into the stream in the specified encoding, without
@@ -1110,19 +1244,23 @@
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param string The string from which the data is written to the stream
* @param encoding The encoding in which to write the string to the stream
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeString: (OFString *)string encoding: (OFStringEncoding)encoding;
/**
* @brief Writes a string into the stream with a trailing newline.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param string The string from which the data is written to the stream
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeLine: (OFString *)string;
/**
* @brief Writes a string into the stream in the specified encoding with a
@@ -1130,10 +1268,12 @@
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param string The string from which the data is written to the stream
* @param encoding The encoding in which to write the string to the stream
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (void)writeLine: (OFString *)string encoding: (OFStringEncoding)encoding;
/**
* @brief Writes a formatted string into the stream.
@@ -1143,10 +1283,13 @@
* `const OFUnichar *`.
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param format A string used as format
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (void)writeFormat: (OFConstantString *)format, ...;
/**
* @brief Writes a formatted string into the stream.
@@ -1157,10 +1300,13 @@
*
* In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
*
* @param format A string used as format
* @param arguments The arguments used in the format string
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (void)writeFormat: (OFConstantString *)format arguments: (va_list)arguments;
#ifdef OF_HAVE_SOCKETS
/**
@@ -1194,10 +1340,12 @@
/**
* @brief Closes the stream.
*
* @note If you override this, make sure to call `[super close]`!
+ *
+ * @throw OFNotOpenException The stream is not open
*/
- (void)close;
/**
* @brief Performs a lowlevel read.
@@ -1208,10 +1356,12 @@
* subclassing!
*
* @param buffer The buffer for the data to read
* @param length The length of the buffer
* @return The number of bytes read
+ * @throw OFReadFailedException Reading failed
+ * @throw OFNotOpenException The stream is not open
*/
- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length;
/**
* @brief Performs a lowlevel write.
@@ -1222,10 +1372,12 @@
* subclassing!
*
* @param buffer The buffer with the data to write
* @param length The length of the data to write
* @return The number of bytes written
+ * @throw OFWriteFailedException Writing failed
+ * @throw OFNotOpenException The stream is not open
*/
- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length;
/**
* @brief Returns whether the lowlevel is at the end of the stream.
Index: src/OFStream.m
==================================================================
--- src/OFStream.m
+++ src/OFStream.m
@@ -672,12 +672,12 @@
- (OFString *)tryReadLine
{
return [self tryReadLineWithEncoding: OFStringEncodingUTF8];
}
-- (OFString *)tryReadTillDelimiter: (OFString *)delimiter
- encoding: (OFStringEncoding)encoding
+- (OFString *)tryReadUntilDelimiter: (OFString *)delimiter
+ encoding: (OFStringEncoding)encoding
{
const char *delimiterCString;
size_t j, delimiterLength, pageSize, bufferLength;
char *buffer, *readBuffer;
OFString *ret;
@@ -834,33 +834,33 @@
_waitingForDelimiter = true;
return nil;
}
-- (OFString *)readTillDelimiter: (OFString *)delimiter
+- (OFString *)readUntilDelimiter: (OFString *)delimiter
{
- return [self readTillDelimiter: delimiter
- encoding: OFStringEncodingUTF8];
+ return [self readUntilDelimiter: delimiter
+ encoding: OFStringEncodingUTF8];
}
-- (OFString *)readTillDelimiter: (OFString *)delimiter
- encoding: (OFStringEncoding)encoding
+- (OFString *)readUntilDelimiter: (OFString *)delimiter
+ encoding: (OFStringEncoding)encoding
{
OFString *ret = nil;
- while ((ret = [self tryReadTillDelimiter: delimiter
- encoding: encoding]) == nil)
+ while ((ret = [self tryReadUntilDelimiter: delimiter
+ encoding: encoding]) == nil)
if (self.atEndOfStream)
return nil;
return ret;
}
-- (OFString *)tryReadTillDelimiter: (OFString *)delimiter
+- (OFString *)tryReadUntilDelimiter: (OFString *)delimiter
{
- return [self tryReadTillDelimiter: delimiter
- encoding: OFStringEncodingUTF8];
+ return [self tryReadUntilDelimiter: delimiter
+ encoding: OFStringEncodingUTF8];
}
- (bool)flushWriteBuffer
{
size_t bytesWritten;
Index: src/OFStreamSocket.h
==================================================================
--- src/OFStreamSocket.h
+++ src/OFStreamSocket.h
@@ -78,10 +78,13 @@
/**
* @brief The remote address.
*
* @note This only works for accepted sockets!
+ *
+ * @throw OFNotOpenException The socket is not open
+ * @throw OFInvalidArgumentException The socket has no remote address
*/
@property (readonly, nonatomic) const OFSocketAddress *remoteAddress;
/**
* @brief The delegate for asynchronous operations on the socket.
@@ -101,22 +104,29 @@
/**
* @brief Listen on the socket.
*
* @param backlog Maximum length for the queue of pending connections.
+ * @throw OFListenOnSocketFailedException Listening failed
+ * @throw OFNotOpenException The socket is not open
*/
- (void)listenWithBacklog: (int)backlog;
/**
* @brief Listen on the socket.
+ *
+ * @throw OFListenOnSocketFailedException Listening failed
+ * @throw OFNotOpenException The socket is not open
*/
- (void)listen;
/**
* @brief Accept an incoming connection.
*
* @return An autoreleased OFStreamSocket for the accepted connection.
+ * @throw OFAcceptSocketFailedException Accepting failed
+ * @throw OFNotOpenException The socket is not open
*/
- (instancetype)accept;
/**
* @brief Asynchronously accept an incoming connection.
Index: src/OFStreamSocket.m
==================================================================
--- src/OFStreamSocket.m
+++ src/OFStreamSocket.m
@@ -29,14 +29,14 @@
#import "OFStreamSocket+Private.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFSocket+Private.h"
-#import "OFAcceptFailedException.h"
+#import "OFAcceptSocketFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
-#import "OFListenFailedException.h"
+#import "OFListenOnSocketFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
@@ -231,11 +231,11 @@
{
if (_socket == OFInvalidSocketHandle)
@throw [OFNotOpenException exceptionWithObject: self];
if (listen(_socket, backlog) == -1)
- @throw [OFListenFailedException
+ @throw [OFListenOnSocketFailedException
exceptionWithSocket: self
backlog: backlog
errNo: OFSocketErrNo()];
_listening = true;
@@ -256,26 +256,26 @@
#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC)
if ((client->_socket = paccept(_socket,
(struct sockaddr *)&client->_remoteAddress.sockaddr,
&client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) ==
OFInvalidSocketHandle)
- @throw [OFAcceptFailedException
+ @throw [OFAcceptSocketFailedException
exceptionWithSocket: self
errNo: OFSocketErrNo()];
#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
if ((client->_socket = accept4(_socket,
(struct sockaddr * )&client->_remoteAddress.sockaddr,
&client->_remoteAddress.length, SOCK_CLOEXEC)) ==
OFInvalidSocketHandle)
- @throw [OFAcceptFailedException
+ @throw [OFAcceptSocketFailedException
exceptionWithSocket: self
errNo: OFSocketErrNo()];
#else
if ((client->_socket = accept(_socket,
(struct sockaddr *)&client->_remoteAddress.sockaddr,
&client->_remoteAddress.length)) == OFInvalidSocketHandle)
- @throw [OFAcceptFailedException
+ @throw [OFAcceptSocketFailedException
exceptionWithSocket: self
errNo: OFSocketErrNo()];
# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1)
Index: src/OFString+JSONParsing.h
==================================================================
--- src/OFString+JSONParsing.h
+++ src/OFString+JSONParsing.h
@@ -38,10 +38,12 @@
* single primitive, leading to real world JSON files sometimes only
* consisting of a single primitive. Therefore, you should not make any
* assumptions about the object returned by this method if you don't
* want your program to terminate due to a message not understood, but
* instead check the returned object using @ref isKindOfClass:.
+ *
+ * @throw OFInvalidJSONException The string contained invalid JSON
*/
@property (readonly, nonatomic) id objectByParsingJSON;
/**
* @brief Creates an object from the JSON value of the string.
@@ -58,12 +60,12 @@
* want your program to terminate due to a message not understood, but
* instead check the returned object using @ref isKindOfClass:.
*
* @param depthLimit The maximum depth the parser should accept (defaults to 32
* if not specified, 0 means no limit (insecure!))
- *
* @return An object
+ * @throw OFInvalidJSONException The string contained invalid JSON
*/
- (id)objectByParsingJSONWithDepthLimit: (size_t)depthLimit;
@end
OF_ASSUME_NONNULL_END
Index: src/OFString+PathAdditions.h
==================================================================
--- src/OFString+PathAdditions.h
+++ src/OFString+PathAdditions.h
@@ -84,13 +84,13 @@
* @return A new, autoreleased OFString with the path extension appended
*/
- (OFString *)stringByAppendingPathExtension: (OFString *)extension;
- (bool)of_isDirectoryPath;
-- (OFString *)of_pathToURLPathWithURLEncodedHost:
- (OFString *__autoreleasing _Nullable *_Nonnull)URLEncodedHost;
-- (OFString *)of_URLPathToPathWithURLEncodedHost:
- (nullable OFString *)URLEncodedHost;
-- (OFString *)of_pathComponentToURLPathComponent;
+- (OFString *)of_pathToURIPathWithPercentEncodedHost:
+ (OFString *__autoreleasing _Nullable *_Nonnull)percentEncodedHost;
+- (OFString *)of_URIPathToPathWithPercentEncodedHost:
+ (nullable OFString *)percentEncodedHost;
+- (OFString *)of_pathComponentToURIPathComponent;
@end
OF_ASSUME_NONNULL_END
ADDED src/OFString+PercentEncoding.h
Index: src/OFString+PercentEncoding.h
==================================================================
--- src/OFString+PercentEncoding.h
+++ src/OFString+PercentEncoding.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFString.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFCharacterSet;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int _OFString_PercentEncoding_reference;
+#ifdef __cplusplus
+}
+#endif
+
+@interface OFString (PercentEncoding)
+/**
+ * @brief The string with percent encoding removed.
+ *
+ * @throw OFInvalidFormatException The string is not in proper percent encoding
+ */
+@property (readonly, nonatomic) OFString *stringByRemovingPercentEncoding;
+
+/**
+ * @brief Percent-encodes a string for use in a URI, but does not escape the
+ * specified allowed characters.
+ *
+ * @param allowedCharacters A character set of characters that should not be
+ * escaped
+ *
+ * @return A new autoreleased string
+ */
+- (OFString *)stringByAddingPercentEncodingWithAllowedCharacters:
+ (OFCharacterSet *)allowedCharacters;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFString+PercentEncoding.m
Index: src/OFString+PercentEncoding.m
==================================================================
--- src/OFString+PercentEncoding.m
+++ src/OFString+PercentEncoding.m
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#import "OFString+PercentEncoding.h"
+#import "OFCharacterSet.h"
+
+#import "OFInvalidFormatException.h"
+#import "OFInvalidEncodingException.h"
+#import "OFOutOfMemoryException.h"
+
+/* Reference for static linking */
+int _OFString_PercentEncoding_reference;
+
+@implementation OFString (PercentEncoding)
+- (OFString *)stringByAddingPercentEncodingWithAllowedCharacters:
+ (OFCharacterSet *)allowedCharacters
+{
+ OFMutableString *ret = [OFMutableString string];
+ void *pool = objc_autoreleasePoolPush();
+ const OFUnichar *characters = self.characters;
+ size_t length = self.length;
+ bool (*characterIsMember)(id, SEL, OFUnichar) =
+ (bool (*)(id, SEL, OFUnichar))[allowedCharacters
+ methodForSelector: @selector(characterIsMember:)];
+
+ for (size_t i = 0; i < length; i++) {
+ OFUnichar c = characters[i];
+
+ if (characterIsMember(allowedCharacters,
+ @selector(characterIsMember:), c))
+ [ret appendCharacters: &c length: 1];
+ else {
+ char buffer[4];
+ size_t bufferLen;
+
+ if ((bufferLen = OFUTF8StringEncode(c, buffer)) == 0)
+ @throw [OFInvalidEncodingException exception];
+
+ for (size_t j = 0; j < bufferLen; j++) {
+ unsigned char byte = buffer[j];
+ unsigned char high = byte >> 4;
+ unsigned char low = byte & 0x0F;
+ char escaped[3];
+
+ escaped[0] = '%';
+ escaped[1] =
+ (high > 9 ? high - 10 + 'A' : high + '0');
+ escaped[2] =
+ (low > 9 ? low - 10 + 'A' : low + '0');
+
+ [ret appendUTF8String: escaped length: 3];
+ }
+ }
+ }
+
+ objc_autoreleasePoolPop(pool);
+
+ return ret;
+}
+
+- (OFString *)stringByRemovingPercentEncoding
+{
+ void *pool = objc_autoreleasePoolPush();
+ const char *string = self.UTF8String;
+ size_t length = self.UTF8StringLength;
+ char *retCString;
+ char byte = 0;
+ int state = 0;
+ size_t i = 0;
+ OFString *ret;
+
+ retCString = OFAllocMemory(length + 1, 1);
+
+ while (length--) {
+ char c = *string++;
+
+ switch (state) {
+ case 0:
+ if (c == '%')
+ state = 1;
+ else
+ retCString[i++] = c;
+ break;
+ case 1:
+ case 2:;
+ uint8_t shift = (state == 1 ? 4 : 0);
+
+ if (c >= '0' && c <= '9')
+ byte += (c - '0') << shift;
+ else if (c >= 'A' && c <= 'F')
+ byte += (c - 'A' + 10) << shift;
+ else if (c >= 'a' && c <= 'f')
+ byte += (c - 'a' + 10) << shift;
+ else {
+ OFFreeMemory(retCString);
+ @throw [OFInvalidFormatException exception];
+ }
+
+ if (++state == 3) {
+ retCString[i++] = byte;
+ state = 0;
+ byte = 0;
+ }
+
+ break;
+ }
+ }
+ retCString[i] = '\0';
+
+ objc_autoreleasePoolPop(pool);
+
+ if (state != 0) {
+ OFFreeMemory(retCString);
+ @throw [OFInvalidFormatException exception];
+ }
+
+ @try {
+ retCString = OFResizeMemory(retCString, 1, i + 1);
+ } @catch (OFOutOfMemoryException *e) {
+ /* We don't care if it fails, as we only made it smaller. */
+ }
+
+ @try {
+ ret = [OFString stringWithUTF8StringNoCopy: retCString
+ length: i
+ freeWhenDone: true];
+ } @catch (id e) {
+ OFFreeMemory(retCString);
+ @throw e;
+ }
+
+ return ret;
+}
+@end
Index: src/OFString+PropertyListParsing.h
==================================================================
--- src/OFString+PropertyListParsing.h
+++ src/OFString+PropertyListParsing.h
@@ -28,10 +28,15 @@
@interface OFString (PropertyListParsing)
/**
* @brief The string interpreted as a property list and parsed as an object.
*
* @note This only supports XML property lists!
+ *
+ * @throw OFInvalidFormatException The string is not in correct XML property
+ * list format
+ * @throw OFUnsupportedVersionException The property list is using a version
+ * that is not supported
*/
@property (readonly, nonatomic) id objectByParsingPropertyList;
@end
OF_ASSUME_NONNULL_END
Index: src/OFString+Serialization.h
==================================================================
--- src/OFString+Serialization.h
+++ src/OFString+Serialization.h
@@ -26,10 +26,17 @@
#endif
@interface OFString (Serialization)
/**
* @brief The string interpreted as serialization and parsed as an object.
+ *
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundNamespaceException A prefix was used that was not bound to
+ * any namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
+ * @throw OFUnsupportedVersionException The serialization is in an unsupported
+ * version
*/
@property (readonly, nonatomic) id objectByDeserializing;
@end
OF_ASSUME_NONNULL_END
Index: src/OFString+Serialization.m
==================================================================
--- src/OFString+Serialization.m
+++ src/OFString+Serialization.m
@@ -36,17 +36,11 @@
OFXMLElement *root;
OFString *version;
OFArray *elements;
id object;
- @try {
- root = [OFXMLElement elementWithXMLString: self];
- } @catch (OFMalformedXMLException *e) {
- @throw [OFInvalidArgumentException exception];
- } @catch (OFUnboundNamespaceException *e) {
- @throw [OFInvalidArgumentException exception];
- }
+ root = [OFXMLElement elementWithXMLString: self];
version = [root attributeForName: @"version"].stringValue;
if (version == nil)
@throw [OFInvalidArgumentException exception];
DELETED src/OFString+URLEncoding.h
Index: src/OFString+URLEncoding.h
==================================================================
--- src/OFString+URLEncoding.h
+++ src/OFString+URLEncoding.h
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFString.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-@class OFCharacterSet;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-extern int _OFString_URLEncoding_reference;
-#ifdef __cplusplus
-}
-#endif
-
-@interface OFString (URLEncoding)
-/**
- * @brief The string as an URL decoded string.
- */
-@property (readonly, nonatomic) OFString *stringByURLDecoding;
-
-/**
- * @brief Encodes a string for use in a URL, but does not escape the specified
- * allowed characters.
- *
- * @param allowedCharacters A character set of characters that should not be
- * escaped
- *
- * @return A new autoreleased string
- */
-- (OFString *)stringByURLEncodingWithAllowedCharacters:
- (OFCharacterSet *)allowedCharacters;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/OFString+URLEncoding.m
Index: src/OFString+URLEncoding.m
==================================================================
--- src/OFString+URLEncoding.m
+++ src/OFString+URLEncoding.m
@@ -1,151 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#include
-#include
-
-#import "OFString+URLEncoding.h"
-#import "OFCharacterSet.h"
-
-#import "OFInvalidFormatException.h"
-#import "OFInvalidEncodingException.h"
-#import "OFOutOfMemoryException.h"
-
-/* Reference for static linking */
-int _OFString_URLEncoding_reference;
-
-@implementation OFString (URLEncoding)
-- (OFString *)stringByURLEncodingWithAllowedCharacters:
- (OFCharacterSet *)allowedCharacters
-{
- OFMutableString *ret = [OFMutableString string];
- void *pool = objc_autoreleasePoolPush();
- const OFUnichar *characters = self.characters;
- size_t length = self.length;
- bool (*characterIsMember)(id, SEL, OFUnichar) =
- (bool (*)(id, SEL, OFUnichar))[allowedCharacters
- methodForSelector: @selector(characterIsMember:)];
-
- for (size_t i = 0; i < length; i++) {
- OFUnichar c = characters[i];
-
- if (characterIsMember(allowedCharacters,
- @selector(characterIsMember:), c))
- [ret appendCharacters: &c length: 1];
- else {
- char buffer[4];
- size_t bufferLen;
-
- if ((bufferLen = OFUTF8StringEncode(c, buffer)) == 0)
- @throw [OFInvalidEncodingException exception];
-
- for (size_t j = 0; j < bufferLen; j++) {
- unsigned char byte = buffer[j];
- unsigned char high = byte >> 4;
- unsigned char low = byte & 0x0F;
- char escaped[3];
-
- escaped[0] = '%';
- escaped[1] =
- (high > 9 ? high - 10 + 'A' : high + '0');
- escaped[2] =
- (low > 9 ? low - 10 + 'A' : low + '0');
-
- [ret appendUTF8String: escaped length: 3];
- }
- }
- }
-
- objc_autoreleasePoolPop(pool);
-
- return ret;
-}
-
-- (OFString *)stringByURLDecoding
-{
- void *pool = objc_autoreleasePoolPush();
- const char *string = self.UTF8String;
- size_t length = self.UTF8StringLength;
- char *retCString;
- char byte = 0;
- int state = 0;
- size_t i = 0;
- OFString *ret;
-
- retCString = OFAllocMemory(length + 1, 1);
-
- while (length--) {
- char c = *string++;
-
- switch (state) {
- case 0:
- if (c == '%')
- state = 1;
- else
- retCString[i++] = c;
- break;
- case 1:
- case 2:;
- uint8_t shift = (state == 1 ? 4 : 0);
-
- if (c >= '0' && c <= '9')
- byte += (c - '0') << shift;
- else if (c >= 'A' && c <= 'F')
- byte += (c - 'A' + 10) << shift;
- else if (c >= 'a' && c <= 'f')
- byte += (c - 'a' + 10) << shift;
- else {
- OFFreeMemory(retCString);
- @throw [OFInvalidFormatException exception];
- }
-
- if (++state == 3) {
- retCString[i++] = byte;
- state = 0;
- byte = 0;
- }
-
- break;
- }
- }
- retCString[i] = '\0';
-
- objc_autoreleasePoolPop(pool);
-
- if (state != 0) {
- OFFreeMemory(retCString);
- @throw [OFInvalidFormatException exception];
- }
-
- @try {
- retCString = OFResizeMemory(retCString, 1, i + 1);
- } @catch (OFOutOfMemoryException *e) {
- /* We don't care if it fails, as we only made it smaller. */
- }
-
- @try {
- ret = [OFString stringWithUTF8StringNoCopy: retCString
- length: i
- freeWhenDone: true];
- } @catch (id e) {
- OFFreeMemory(retCString);
- @throw e;
- }
-
- return ret;
-}
-@end
Index: src/OFString+XMLUnescaping.h
==================================================================
--- src/OFString+XMLUnescaping.h
+++ src/OFString+XMLUnescaping.h
@@ -72,10 +72,12 @@
/**
* @brief Unescapes XML in the string and uses the specified delegate for
* unknown entities.
*
* @param delegate An OFXMLUnescapingDelegate as a handler for unknown entities
+ * @throw OFInvalidFormatException The string is not a valid XML string
+ * @throw OFUnknownXMLEntityException The string contains unknown XML entities
*/
- (OFString *)stringByXMLUnescapingWithDelegate:
(nullable id )delegate;
#ifdef OF_HAVE_BLOCKS
@@ -82,11 +84,13 @@
/**
* @brief Unescapes XML in the string and uses the specified block for unknown
* entities.
*
* @param block A block which handles unknown entities
+ * @throw OFInvalidFormatException The string is not a valid XML string
+ * @throw OFUnknownXMLEntityException The string contains unknown XML entities
*/
- (OFString *)stringByXMLUnescapingWithBlock: (OFStringXMLUnescapingBlock)block;
#endif
@end
OF_ASSUME_NONNULL_END
Index: src/OFString.h
==================================================================
--- src/OFString.h
+++ src/OFString.h
@@ -97,11 +97,11 @@
/** KOI8-R */
OFStringEncodingKOI8R,
/** KOI8-U */
OFStringEncodingKOI8U,
/** Try to automatically detect the encoding */
- OFStringEncodingAutodetect = 0xFF
+ OFStringEncodingAutodetect = -1
} OFStringEncoding;
/**
* @brief Options for searching in strings.
*
@@ -134,11 +134,11 @@
#endif
#ifdef __OBJC__
@class OFArray OF_GENERIC(ObjectType);
@class OFCharacterSet;
-@class OFURL;
+@class OFURI;
/**
* @class OFString OFString.h ObjFW/OFString.h
*
* @brief A class for handling strings.
@@ -210,29 +210,31 @@
@property (readonly, nonatomic) unsigned long long unsignedLongLongValue;
/**
* @brief The float value of the string as a float.
*
- * If the string contains any non-number characters, an
- * @ref OFInvalidFormatException is thrown.
+ * @throw OFInvalidFormatException The string cannot be parsed as a `float`
+ * @throw OFOutOfRangeException The value cannot be represented as a `float`
*/
@property (readonly, nonatomic) float floatValue;
/**
* @brief The double value of the string as a double.
*
- * If the string contains any non-number characters, an
- * @ref OFInvalidFormatException is thrown.
+ * @throw OFInvalidFormatException The string cannot be parsed as a `double`
+ * @throw OFOutOfRangeException The value cannot be represented as a `double`
*/
@property (readonly, nonatomic) double doubleValue;
/**
* @brief The string as an array of Unicode characters.
*
* The result is valid until the autorelease pool is released. If you want to
* use the result outside the scope of the current autorelease pool, you have to
* copy it.
+ *
+ * The returned string is *not* null-terminated.
*/
@property (readonly, nonatomic) const OFUnichar *characters
OF_RETURNS_INNER_POINTER;
/**
@@ -239,10 +241,12 @@
* @brief The string in UTF-16 encoding with native byte order.
*
* The result is valid until the autorelease pool is released. If you want to
* use the result outside the scope of the current autorelease pool, you have to
* copy it.
+ *
+ * The returned string is null-terminated.
*/
@property (readonly, nonatomic) const OFChar16 *UTF16String
OF_RETURNS_INNER_POINTER;
/**
@@ -254,10 +258,12 @@
* @brief The string in UTF-32 encoding with native byte order.
*
* The result is valid until the autorelease pool is released. If you want to
* use the result outside the scope of the current autorelease pool, you have to
* copy it.
+ *
+ * The returned string is null-terminated.
*/
@property (readonly, nonatomic) const OFChar32 *UTF32String
OF_RETURNS_INNER_POINTER;
/**
@@ -306,10 +312,11 @@
/**
* @brief Creates a new OFString from a UTF-8 encoded C string.
*
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
+ (instancetype)stringWithUTF8String: (const char *)UTF8String;
/**
* @brief Creates a new OFString from a UTF-8 encoded C string with the
@@ -316,10 +323,11 @@
* specified length.
*
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @param UTF8StringLength The length of the UTF-8 encoded C string
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
+ (instancetype)stringWithUTF8String: (const char *)UTF8String
length: (size_t)UTF8StringLength;
/**
@@ -333,10 +341,11 @@
*
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @param freeWhenDone Whether to free the C string when the OFString gets
* deallocated
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
+ (instancetype)stringWithUTF8StringNoCopy: (char *)UTF8String
freeWhenDone: (bool)freeWhenDone;
/**
@@ -351,10 +360,11 @@
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @param UTF8StringLength The length of the UTF-8 encoded C string
* @param freeWhenDone Whether to free the C string when the OFString gets
* deallocated
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
+ (instancetype)stringWithUTF8StringNoCopy: (char *)UTF8String
length: (size_t)UTF8StringLength
freeWhenDone: (bool)freeWhenDone;
@@ -362,10 +372,11 @@
* @brief Creates a new OFString from a C string with the specified encoding.
*
* @param cString A C string to initialize the OFString with
* @param encoding The encoding of the C string
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
+ (instancetype)stringWithCString: (const char *)cString
encoding: (OFStringEncoding)encoding;
/**
@@ -374,10 +385,11 @@
*
* @param cString A C string to initialize the OFString with
* @param encoding The encoding of the C string
* @param cStringLength The length of the C string
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
+ (instancetype)stringWithCString: (const char *)cString
encoding: (OFStringEncoding)encoding
length: (size_t)cStringLength;
@@ -385,10 +397,11 @@
* @brief Creates a new OFString from OFData with the specified encoding.
*
* @param data OFData with the contents of the string
* @param encoding The encoding in which the string is stored in the OFData
* @return An new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
+ (instancetype)stringWithData: (OFData *)data
encoding: (OFStringEncoding)encoding;
/**
@@ -411,64 +424,68 @@
length: (size_t)length;
/**
* @brief Creates a new OFString from a UTF-16 encoded string.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string;
/**
* @brief Creates a new OFString from a UTF-16 encoded string with the
* specified length.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @param length The length of the UTF-16 string
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
length: (size_t)length;
/**
* @brief Creates a new OFString from a UTF-16 encoded string, assuming the
* specified byte order if no byte order mark is found.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
byteOrder: (OFByteOrder)byteOrder;
/**
* @brief Creates a new OFString from a UTF-16 encoded string with the
* specified length, assuming the specified byte order if no byte order
* mark is found.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @param length The length of the UTF-16 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
length: (size_t)length
byteOrder: (OFByteOrder)byteOrder;
/**
* @brief Creates a new OFString from a UTF-32 encoded string.
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @return A new autoreleased OFString
*/
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string;
/**
* @brief Creates a new OFString from a UTF-32 encoded string with the
* specified length.
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @param length The length of the UTF-32 string
* @return A new autoreleased OFString
*/
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
length: (size_t)length;
@@ -475,11 +492,11 @@
/**
* @brief Creates a new OFString from a UTF-32 encoded string, assuming the
* specified byte order if no byte order mark is found.
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return A new autoreleased OFString
*/
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
byteOrder: (OFByteOrder)byteOrder;
@@ -487,11 +504,11 @@
/**
* @brief Creates a new OFString from a UTF-32 encoded string with the
* specified length, assuming the specified byte order if no byte order
* mark is found.
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @param length The length of the UTF-32 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return A new autoreleased OFString
*/
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
@@ -505,10 +522,13 @@
* format specifier for objects, `%C` for `OFUnichar` and `%S` for
* `const OFUnichar *`.
*
* @param format A string used as format to initialize the OFString
* @return A new autoreleased OFString
+ * @throw OFInvalidFormatException The specified format is invalid
+ * @throw OFInvalidEncodingException The resulting string is not in not in UTF-8
+ * encoding
*/
+ (instancetype)stringWithFormat: (OFConstantString *)format, ...;
# ifdef OF_HAVE_FILES
/**
@@ -515,10 +535,11 @@
* @brief Creates a new OFString with the contents of the specified UTF-8
* encoded file.
*
* @param path The path to the file
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
+ (instancetype)stringWithContentsOfFile: (OFString *)path;
/**
* @brief Creates a new OFString with the contents of the specified file in the
@@ -525,46 +546,50 @@
* specified encoding.
*
* @param path The path to the file
* @param encoding The encoding of the file
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
+ (instancetype)stringWithContentsOfFile: (OFString *)path
encoding: (OFStringEncoding)encoding;
# endif
/**
- * @brief Creates a new OFString with the contents of the specified URL.
+ * @brief Creates a new OFString with the contents of the specified URI.
*
- * If the URL's scheme is file, it tries UTF-8 encoding.
+ * If the URI's scheme is file, it tries UTF-8 encoding.
*
- * If the URL's scheme is http(s), it tries to detect the encoding from the HTTP
+ * If the URI's scheme is http(s), it tries to detect the encoding from the HTTP
* headers. If it could not detect the encoding using the HTTP headers, it tries
* UTF-8.
*
- * @param URL The URL to the contents for the string
+ * @param URI The URI to the contents for the string
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not in the expected encoding
*/
-+ (instancetype)stringWithContentsOfURL: (OFURL *)URL;
++ (instancetype)stringWithContentsOfURI: (OFURI *)URI;
/**
- * @brief Creates a new OFString with the contents of the specified URL in the
+ * @brief Creates a new OFString with the contents of the specified URI in the
* specified encoding.
*
- * @param URL The URL to the contents for the string
+ * @param URI The URI to the contents for the string
* @param encoding The encoding to assume
* @return A new autoreleased OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
-+ (instancetype)stringWithContentsOfURL: (OFURL *)URL
++ (instancetype)stringWithContentsOfURI: (OFURI *)URI
encoding: (OFStringEncoding)encoding;
/**
* @brief Initializes an already allocated OFString from a UTF-8 encoded C
* string.
*
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
- (instancetype)initWithUTF8String: (const char *)UTF8String;
/**
* @brief Initializes an already allocated OFString from a UTF-8 encoded C
@@ -571,10 +596,11 @@
* string with the specified length.
*
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @param UTF8StringLength The length of the UTF-8 encoded C string
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
- (instancetype)initWithUTF8String: (const char *)UTF8String
length: (size_t)UTF8StringLength;
/**
@@ -588,10 +614,11 @@
*
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @param freeWhenDone Whether to free the C string when it is not needed
* anymore
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
freeWhenDone: (bool)freeWhenDone;
/**
@@ -607,10 +634,11 @@
* @param UTF8String A UTF-8 encoded C string to initialize the OFString with
* @param UTF8StringLength The length of the UTF-8 encoded C string
* @param freeWhenDone Whether to free the C string when it is not needed
* anymore
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
length: (size_t)UTF8StringLength
freeWhenDone: (bool)freeWhenDone;
@@ -619,10 +647,11 @@
* specified encoding.
*
* @param cString A C string to initialize the OFString with
* @param encoding The encoding of the C string
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
- (instancetype)initWithCString: (const char *)cString
encoding: (OFStringEncoding)encoding;
/**
@@ -631,10 +660,11 @@
*
* @param cString A C string to initialize the OFString with
* @param encoding The encoding of the C string
* @param cStringLength The length of the C string
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
- (instancetype)initWithCString: (const char *)cString
encoding: (OFStringEncoding)encoding
length: (size_t)cStringLength;
@@ -643,10 +673,11 @@
* specified encoding.
*
* @param data OFData with the contents of the string
* @param encoding The encoding in which the string is stored in the OFData
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
- (instancetype)initWithData: (OFData *)data
encoding: (OFStringEncoding)encoding;
/**
@@ -669,64 +700,68 @@
length: (size_t)length;
/**
* @brief Initializes an already allocated OFString with a UTF-16 string.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
- (instancetype)initWithUTF16String: (const OFChar16 *)string;
/**
* @brief Initializes an already allocated OFString with a UTF-16 string with
* the specified length.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @param length The length of the UTF-16 string
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
- (instancetype)initWithUTF16String: (const OFChar16 *)string
length: (size_t)length;
/**
* @brief Initializes an already allocated OFString with a UTF-16 string,
* assuming the specified byte order if no byte order mark is found.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
- (instancetype)initWithUTF16String: (const OFChar16 *)string
byteOrder: (OFByteOrder)byteOrder;
/**
* @brief Initializes an already allocated OFString with a UTF-16 string with
* the specified length, assuming the specified byte order if no byte
* order mark is found.
*
- * @param string The UTF-16 string
+ * @param string A zero-terminated UTF-16 string
* @param length The length of the UTF-16 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-16-encoded
*/
- (instancetype)initWithUTF16String: (const OFChar16 *)string
length: (size_t)length
byteOrder: (OFByteOrder)byteOrder;
/**
* @brief Initializes an already allocated OFString with a UTF-32 string.
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @return An initialized OFString
*/
- (instancetype)initWithUTF32String: (const OFChar32 *)string;
/**
* @brief Initializes an already allocated OFString with a UTF-32 string with
* the specified length
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @param length The length of the UTF-32 string
* @return An initialized OFString
*/
- (instancetype)initWithUTF32String: (const OFChar32 *)string
length: (size_t)length;
@@ -733,11 +768,11 @@
/**
* @brief Initializes an already allocated OFString with a UTF-32 string,
* assuming the specified byte order if no byte order mark is found.
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return An initialized OFString
*/
- (instancetype)initWithUTF32String: (const OFChar32 *)string
byteOrder: (OFByteOrder)byteOrder;
@@ -745,11 +780,11 @@
/**
* @brief Initializes an already allocated OFString with a UTF-32 string with
* the specified length, assuming the specified byte order if no byte
* order mark is found.
*
- * @param string The UTF-32 string
+ * @param string A zero-terminated UTF-32 string
* @param length The length of the UTF-32 string
* @param byteOrder The byte order to assume if there is no byte order mark
* @return An initialized OFString
*/
- (instancetype)initWithUTF32String: (const OFChar32 *)string
@@ -763,10 +798,13 @@
* format specifier for objects, `%C` for `OFUnichar` and `%S` for
* `const OFUnichar *`.
*
* @param format A string used as format to initialize the OFString
* @return An initialized OFString
+ * @throw OFInvalidFormatException The specified format is invalid
+ * @throw OFInvalidEncodingException The resulting string is not in not in UTF-8
+ * encoding
*/
- (instancetype)initWithFormat: (OFConstantString *)format, ...;
/**
* @brief Initializes an already allocated OFString with a format string.
@@ -776,10 +814,13 @@
* `const OFUnichar *`.
*
* @param format A string used as format to initialize the OFString
* @param arguments The arguments used in the format string
* @return An initialized OFString
+ * @throw OFInvalidFormatException The specified format is invalid
+ * @throw OFInvalidEncodingException The resulting string is not in not in UTF-8
+ * encoding
*/
- (instancetype)initWithFormat: (OFConstantString *)format
arguments: (va_list)arguments;
# ifdef OF_HAVE_FILES
@@ -787,10 +828,11 @@
* @brief Initializes an already allocated OFString with the contents of the
* specified file in the specified encoding.
*
* @param path The path to the file
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not properly UTF-8-encoded
*/
- (instancetype)initWithContentsOfFile: (OFString *)path;
/**
* @brief Initializes an already allocated OFString with the contents of the
@@ -797,39 +839,42 @@
* specified file in the specified encoding.
*
* @param path The path to the file
* @param encoding The encoding of the file
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
- (instancetype)initWithContentsOfFile: (OFString *)path
encoding: (OFStringEncoding)encoding;
# endif
/**
* @brief Initializes an already allocated OFString with the contents of the
- * specified URL.
+ * specified URI.
*
- * If the URL's scheme is file, it tries UTF-8 encoding.
+ * If the URI's scheme is file, it tries UTF-8 encoding.
*
- * If the URL's scheme is http(s), it tries to detect the encoding from the HTTP
+ * If the URI's scheme is http(s), it tries to detect the encoding from the HTTP
* headers. If it could not detect the encoding using the HTTP headers, it tries
* UTF-8.
*
- * @param URL The URL to the contents for the string
+ * @param URI The URI to the contents for the string
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not in the expected encoding
*/
-- (instancetype)initWithContentsOfURL: (OFURL *)URL;
+- (instancetype)initWithContentsOfURI: (OFURI *)URI;
/**
* @brief Initializes an already allocated OFString with the contents of the
- * specified URL in the specified encoding.
+ * specified URI in the specified encoding.
*
- * @param URL The URL to the contents for the string
+ * @param URI The URI to the contents for the string
* @param encoding The encoding to assume
* @return An initialized OFString
+ * @throw OFInvalidEncodingException The string is not in the specified encoding
*/
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
encoding: (OFStringEncoding)encoding;
/**
* @brief Writes the OFString into the specified C string with the specified
* encoding.
@@ -838,10 +883,12 @@
* @param maxLength The maximum number of bytes to write into the C string,
* including the terminating zero
* @param encoding The encoding to use for writing into the C string
* @return The number of bytes written into the C string, without the
* terminating zero
+ * @throw OFInvalidEncodingException The string cannot be represented in the
+ * specified encoding
*/
- (size_t)getCString: (char *)cString
maxLength: (size_t)maxLength
encoding: (OFStringEncoding)encoding;
@@ -868,10 +915,12 @@
* use the result outside the scope of the current autorelease pool, you have to
* copy it.
*
* @param encoding The encoding for the C string
* @return The OFString as a C string in the specified encoding
+ * @throw OFInvalidEncodingException The string cannot be represented in the
+ * specified encoding
*/
- (const char *)cStringWithEncoding: (OFStringEncoding)encoding
OF_RETURNS_INNER_POINTER;
/**
@@ -893,10 +942,12 @@
* @brief Returns the number of bytes the string needs in the specified
* encoding.
*
* @param encoding The encoding for the string
* @return The number of bytes the string needs in the specified encoding.
+ * @throw OFInvalidEncodingException The string cannot be represented in the
+ * specified encoding
*/
- (size_t)cStringLengthWithEncoding: (OFStringEncoding)encoding;
/**
* @brief Compares the string to another string.
@@ -1043,12 +1094,14 @@
* @param base The base to use. If the base is 0, base 16 is assumed if the
* string starts with 0x (after stripping white spaces). If the
* string starts with 0, base 8 is assumed. Otherwise, base 10 is
* assumed.
* @return The value of the string in the specified base
+ * @throw OFInvalidFormatException The string cannot be parsed as a `long long`
+ * @throw OFOutOfRangeException The value cannot be represented as a `long long`
*/
-- (long long)longLongValueWithBase: (int)base;
+- (long long)longLongValueWithBase: (unsigned char)base;
/**
* @brief The value of the string in the specified base as an
* `unsigned long long`.
*
@@ -1063,12 +1116,16 @@
* @param base The base to use. If the base is 0, base 16 is assumed if the
* string starts with 0x (after stripping white spaces). If the
* string starts with 0, base 8 is assumed. Otherwise, base 10 is
* assumed.
* @return The value of the string in the specified base
+ * @throw OFInvalidFormatException The string cannot be parsed as an
+ * `unsigned long long`
+ * @throw OFOutOfRangeException The value cannot be represented as an
+ * `unsigned long long`
*/
-- (unsigned long long)unsignedLongLongValueWithBase: (int)base;
+- (unsigned long long)unsignedLongLongValueWithBase: (unsigned char)base;
/**
* @brief Creates a new string by appending another string.
*
* @param string The string to append
@@ -1079,31 +1136,29 @@
/**
* @brief Creates a new string by appending the specified format.
*
* @param format A format string which generates the string to append
* @return A new, autoreleased OFString with the specified format appended
+ * @throw OFInvalidEncodingException The string was not properly UTF-8-encoded
+ * after formatting it
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (OFString *)stringByAppendingFormat: (OFConstantString *)format, ...;
/**
* @brief Creates a new string by appending the specified format.
*
* @param format A format string which generates the string to append
* @param arguments The arguments used in the format string
* @return A new, autoreleased OFString with the specified format appended
+ * @throw OFInvalidEncodingException The string was not properly UTF-8-encoded
+ * after formatting it
+ * @throw OFInvalidFormatException The specified format is invalid
*/
- (OFString *)stringByAppendingFormat: (OFConstantString *)format
arguments: (va_list)arguments;
-/**
- * @brief Creates a new string by prepending another string.
- *
- * @param string The string to prepend
- * @return A new autoreleased OFString with the specified string prepended
- */
-- (OFString *)stringByPrependingString: (OFString *)string;
-
/**
* @brief Creates a new string by replacing the occurrences of the specified
* string with the specified replacement.
*
* @param string The string to replace
@@ -1119,11 +1174,11 @@
*
* @param string The string to replace
* @param replacement The string with which it should be replaced
* @param options Options modifying search behaviour.
* Possible values are:
- * * None yet
+ * * None yet, pass 0
* @param range The range in which to replace the string
* @return A new string with the occurrences of the specified string replaced
*/
- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
withString: (OFString *)replacement
@@ -1194,13 +1249,16 @@
* @brief Returns the string in UTF-16 encoding with the specified byte order.
*
* The result is valid until the autorelease pool is released. If you want to
* use the result outside the scope of the current autorelease pool, you have to
* copy it.
+ *
+ * The returned string is null-terminated.
*
* @param byteOrder The byte order for the UTF-16 encoding
* @return The string in UTF-16 encoding with the specified byte order
+ * @throw OFInvalidEncodingException The string cannot be represented in UTF-16
*/
- (const OFChar16 *)UTF16StringWithByteOrder: (OFByteOrder)byteOrder
OF_RETURNS_INNER_POINTER;
/**
@@ -1207,10 +1265,12 @@
* @brief Returns the string in UTF-32 encoding with the specified byte order.
*
* The result is valid until the autorelease pool is released. If you want to
* use the result outside the scope of the current autorelease pool, you have to
* copy it.
+ *
+ * The returned string is null-terminated.
*
* @param byteOrder The byte order for the UTF-32 encoding
* @return The string in UTF-32 encoding with the specified byte order
*/
- (const OFChar32 *)UTF32StringWithByteOrder: (OFByteOrder)byteOrder
@@ -1219,10 +1279,12 @@
/**
* @brief Returns the string as OFData with the specified encoding.
*
* @param encoding The encoding to use for the returned OFData
* @return The string as OFData with the specified encoding
+ * @throw OFInvalidEncodingException The string cannot be represented in the
+ * specified encoding
*/
- (OFData *)dataWithEncoding: (OFStringEncoding)encoding;
# ifdef OF_HAVE_FILES
/**
@@ -1236,28 +1298,32 @@
* @brief Writes the string into the specified file using the specified
* encoding.
*
* @param path The path of the file to write to
* @param encoding The encoding to use to write the string into the file
+ * @throw OFInvalidEncodingException The string cannot be represented in the
+ * specified encoding
*/
- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding;
# endif
/**
- * @brief Writes the string to the specified URL using UTF-8 encoding.
+ * @brief Writes the string to the specified URI using UTF-8 encoding.
*
- * @param URL The URL to write to
+ * @param URI The URI to write to
*/
-- (void)writeToURL: (OFURL *)URL;
+- (void)writeToURI: (OFURI *)URI;
/**
- * @brief Writes the string to the specified URL using the specified encoding.
+ * @brief Writes the string to the specified URI using the specified encoding.
*
- * @param URL The URL to write to
- * @param encoding The encoding to use to write the string to the URL
+ * @param URI The URI to write to
+ * @param encoding The encoding to use to write the string to the URI
+ * @throw OFInvalidEncodingException The string cannot be represented in the
+ * specified encoding
*/
-- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding;
+- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding;
# ifdef OF_HAVE_BLOCKS
/**
* Enumerates all lines in the receiver using the specified block.
*
@@ -1308,13 +1374,13 @@
# import "OFString+CryptographicHashing.h"
# import "OFString+JSONParsing.h"
# ifdef OF_HAVE_FILES
# import "OFString+PathAdditions.h"
# endif
+# import "OFString+PercentEncoding.h"
# import "OFString+PropertyListParsing.h"
# import "OFString+Serialization.h"
-# import "OFString+URLEncoding.h"
# import "OFString+XMLEscaping.h"
# import "OFString+XMLUnescaping.h"
#endif
#if defined(__OBJC__) && !defined(NSINTEGER_DEFINED) && !__has_feature(modules)
Index: src/OFString.m
==================================================================
--- src/OFString.m
+++ src/OFString.m
@@ -40,25 +40,25 @@
# import "OFFileManager.h"
#endif
#import "OFLocale.h"
#import "OFStream.h"
#import "OFSystemInfo.h"
-#import "OFURL.h"
-#import "OFURLHandler.h"
+#import "OFURI.h"
+#import "OFURIHandler.h"
#import "OFUTF8String.h"
#import "OFUTF8String+Private.h"
#import "OFXMLElement.h"
+#import "OFGetItemAttributesFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOpenItemFailedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
-#import "OFRetrieveItemAttributesFailedException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "unicode.h"
@@ -131,13 +131,13 @@
_OFString_CryptographicHashing_reference = 1;
_OFString_JSONParsing_reference = 1;
#ifdef OF_HAVE_FILES
_OFString_PathAdditions_reference = 1;
#endif
+ _OFString_PercentEncoding_reference = 1;
_OFString_PropertyListParsing_reference = 1;
_OFString_Serialization_reference = 1;
- _OFString_URLEncoding_reference = 1;
_OFString_XMLEscaping_reference = 1;
_OFString_XMLUnescaping_reference = 1;
}
void
@@ -580,19 +580,19 @@
return (id)[[OFUTF8String alloc] initWithContentsOfFile: path
encoding: encoding];
}
#endif
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
{
- return (id)[[OFUTF8String alloc] initWithContentsOfURL: URL];
+ return (id)[[OFUTF8String alloc] initWithContentsOfURI: URI];
}
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
encoding: (OFStringEncoding)encoding
{
- return (id)[[OFUTF8String alloc] initWithContentsOfURL: URL
+ return (id)[[OFUTF8String alloc] initWithContentsOfURI: URI
encoding: encoding];
}
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
@@ -794,19 +794,19 @@
return [[[self alloc] initWithContentsOfFile: path
encoding: encoding] autorelease];
}
#endif
-+ (instancetype)stringWithContentsOfURL: (OFURL *)URL
++ (instancetype)stringWithContentsOfURI: (OFURI *)URI
{
- return [[[self alloc] initWithContentsOfURL: URL] autorelease];
+ return [[[self alloc] initWithContentsOfURI: URI] autorelease];
}
-+ (instancetype)stringWithContentsOfURL: (OFURL *)URL
++ (instancetype)stringWithContentsOfURI: (OFURI *)URI
encoding: (OFStringEncoding)encoding
{
- return [[[self alloc] initWithContentsOfURL: URL
+ return [[[self alloc] initWithContentsOfURI: URI
encoding: encoding] autorelease];
}
- (instancetype)init
{
@@ -992,96 +992,79 @@
}
- (instancetype)initWithContentsOfFile: (OFString *)path
encoding: (OFStringEncoding)encoding
{
- char *tmp;
- unsigned long long fileSize;
+ char *buffer = NULL;
+ OFStreamOffset fileSize;
@try {
void *pool = objc_autoreleasePoolPush();
- OFFile *file = nil;
-
- @try {
- fileSize = [[OFFileManager defaultManager]
- attributesOfItemAtPath: path].fileSize;
- } @catch (OFRetrieveItemAttributesFailedException *e) {
- @throw [OFOpenItemFailedException
- exceptionWithPath: path
- mode: @"r"
- errNo: e.errNo];
- }
-
- objc_autoreleasePoolPop(pool);
-
-# if ULLONG_MAX > SIZE_MAX
- if (fileSize > SIZE_MAX)
- @throw [OFOutOfRangeException exception];
-#endif
+ OFFile *file = [OFFile fileWithPath: path mode: @"r"];
+ fileSize = [file seekToOffset: 0 whence: OFSeekEnd];
+
+ if (fileSize < 0 || (unsigned long long)fileSize > SIZE_MAX)
+ @throw [OFOutOfRangeException exception];
/*
* We need one extra byte for the terminating zero if we want
* to use -[initWithUTF8StringNoCopy:length:freeWhenDone:].
*/
if (SIZE_MAX - (size_t)fileSize < 1)
@throw [OFOutOfRangeException exception];
- tmp = OFAllocMemory((size_t)fileSize + 1, 1);
- @try {
- file = [[OFFile alloc] initWithPath: path mode: @"r"];
- [file readIntoBuffer: tmp
- exactLength: (size_t)fileSize];
- } @catch (id e) {
- OFFreeMemory(tmp);
- @throw e;
- } @finally {
- [file release];
- }
-
- tmp[(size_t)fileSize] = '\0';
- } @catch (id e) {
+ [file seekToOffset: 0 whence: OFSeekSet];
+
+ buffer = OFAllocMemory((size_t)fileSize + 1, 1);
+ [file readIntoBuffer: buffer exactLength: (size_t)fileSize];
+ buffer[(size_t)fileSize] = '\0';
+
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ OFFreeMemory(buffer);
[self release];
+
@throw e;
}
if (encoding == OFStringEncodingUTF8) {
@try {
- self = [self initWithUTF8StringNoCopy: tmp
+ self = [self initWithUTF8StringNoCopy: buffer
length: (size_t)fileSize
freeWhenDone: true];
} @catch (id e) {
- OFFreeMemory(tmp);
+ OFFreeMemory(buffer);
@throw e;
}
} else {
@try {
- self = [self initWithCString: tmp
+ self = [self initWithCString: buffer
encoding: encoding
length: (size_t)fileSize];
} @finally {
- OFFreeMemory(tmp);
+ OFFreeMemory(buffer);
}
}
return self;
}
#endif
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
{
- return [self initWithContentsOfURL: URL
+ return [self initWithContentsOfURI: URI
encoding: OFStringEncodingAutodetect];
}
-- (instancetype)initWithContentsOfURL: (OFURL *)URL
+- (instancetype)initWithContentsOfURI: (OFURI *)URI
encoding: (OFStringEncoding)encoding
{
void *pool = objc_autoreleasePoolPush();
OFData *data;
@try {
- data = [OFData dataWithContentsOfURL: URL];
+ data = [OFData dataWithContentsOfURI: URI];
} @catch (id e) {
[self release];
@throw e;
}
@@ -1351,12 +1334,11 @@
cString[length] = '\0';
return length;
#endif
default:
- @throw [OFNotImplementedException exceptionWithSelector: _cmd
- object: self];
+ @throw [OFInvalidArgumentException exception];
}
}
- (size_t)getCString: (char *)cString
maxLength: (size_t)maxLength
@@ -1433,11 +1415,11 @@
@throw e;
}
break;
default:
- @throw [OFInvalidEncodingException exception];
+ @throw [OFInvalidArgumentException exception];
}
@try {
ret = [[OFData dataWithItemsNoCopy: cString
count: cStringLength + 1
@@ -1504,11 +1486,11 @@
case OFStringEncodingMacRoman:
case OFStringEncodingKOI8R:
case OFStringEncodingKOI8U:
return self.length;
default:
- @throw [OFInvalidEncodingException exception];
+ @throw [OFInvalidArgumentException exception];
}
}
- (size_t)UTF8StringLength
{
@@ -1683,13 +1665,13 @@
OFHashInit(&hash);
for (size_t i = 0; i < length; i++) {
const OFUnichar c = characters[i];
- OFHashAdd(&hash, (c & 0xFF0000) >> 16);
- OFHashAdd(&hash, (c & 0x00FF00) >> 8);
- OFHashAdd(&hash, c & 0x0000FF);
+ OFHashAddByte(&hash, (c & 0xFF0000) >> 16);
+ OFHashAddByte(&hash, (c & 0x00FF00) >> 8);
+ OFHashAddByte(&hash, c & 0x0000FF);
}
OFHashFinalize(&hash);
return hash;
@@ -1754,21 +1736,21 @@
const char *cString = self.UTF8String;
if ((!OFASCIIIsAlpha(cString[0]) &&
cString[0] != '_' && cString[0] != '$') ||
strpbrk(cString, " \n\r\t\b\f\\\"'") != NULL) {
- [JSON prependString: @"\""];
+ [JSON insertString: @"\"" atIndex: 0];
[JSON appendString: @"\""];
}
} else {
- [JSON prependString: @"\""];
+ [JSON insertString: @"\"" atIndex: 0];
[JSON appendString: @"\""];
}
} else {
[JSON replaceOccurrencesOfString: @"\n" withString: @"\\n"];
- [JSON prependString: @"\""];
+ [JSON insertString: @"\"" atIndex: 0];
[JSON appendString: @"\""];
}
[JSON makeImmutable];
@@ -1818,19 +1800,19 @@
- (OFRange)rangeOfString: (OFString *)string
{
return [self rangeOfString: string
options: 0
- range: OFRangeMake(0, self.length)];
+ range: OFMakeRange(0, self.length)];
}
- (OFRange)rangeOfString: (OFString *)string
options: (OFStringSearchOptions)options
{
return [self rangeOfString: string
options: options
- range: OFRangeMake(0, self.length)];
+ range: OFMakeRange(0, self.length)];
}
- (OFRange)rangeOfString: (OFString *)string
options: (OFStringSearchOptions)options
range: (OFRange)range
@@ -1839,14 +1821,14 @@
const OFUnichar *searchCharacters;
OFUnichar *characters;
size_t searchLength;
if ((searchLength = string.length) == 0)
- return OFRangeMake(0, 0);
+ return OFMakeRange(0, 0);
if (searchLength > range.length)
- return OFRangeMake(OFNotFound, 0);
+ return OFMakeRange(OFNotFound, 0);
if (range.length > SIZE_MAX / sizeof(OFUnichar))
@throw [OFOutOfRangeException exception];
pool = objc_autoreleasePoolPush();
@@ -1860,11 +1842,11 @@
if (options & OFStringSearchBackwards) {
for (size_t i = range.length - searchLength;; i--) {
if (memcmp(characters + i, searchCharacters,
searchLength * sizeof(OFUnichar)) == 0) {
objc_autoreleasePoolPop(pool);
- return OFRangeMake(range.location + i,
+ return OFMakeRange(range.location + i,
searchLength);
}
/* No match and we're at the last character */
if (i == 0)
@@ -1874,11 +1856,11 @@
for (size_t i = 0;
i <= range.length - searchLength; i++) {
if (memcmp(characters + i, searchCharacters,
searchLength * sizeof(OFUnichar)) == 0) {
objc_autoreleasePoolPop(pool);
- return OFRangeMake(range.location + i,
+ return OFMakeRange(range.location + i,
searchLength);
}
}
}
} @finally {
@@ -1885,26 +1867,26 @@
OFFreeMemory(characters);
}
objc_autoreleasePoolPop(pool);
- return OFRangeMake(OFNotFound, 0);
+ return OFMakeRange(OFNotFound, 0);
}
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
{
return [self indexOfCharacterFromSet: characterSet
options: 0
- range: OFRangeMake(0, self.length)];
+ range: OFMakeRange(0, self.length)];
}
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
options: (OFStringSearchOptions)options
{
return [self indexOfCharacterFromSet: characterSet
options: options
- range: OFRangeMake(0, self.length)];
+ range: OFMakeRange(0, self.length)];
}
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
options: (OFStringSearchOptions)options
range: (OFRange)range
@@ -1979,16 +1961,16 @@
return false;
}
- (OFString *)substringFromIndex: (size_t)idx
{
- return [self substringWithRange: OFRangeMake(idx, self.length - idx)];
+ return [self substringWithRange: OFMakeRange(idx, self.length - idx)];
}
- (OFString *)substringToIndex: (size_t)idx
{
- return [self substringWithRange: OFRangeMake(0, idx)];
+ return [self substringWithRange: OFMakeRange(0, idx)];
}
- (OFString *)substringWithRange: (OFRange)range
{
void *pool;
@@ -2038,18 +2020,10 @@
[new appendFormat: format arguments: arguments];
[new makeImmutable];
return new;
}
-- (OFString *)stringByPrependingString: (OFString *)string
-{
- OFMutableString *new = [[string mutableCopy] autorelease];
- [new appendString: self];
- [new makeImmutable];
- return new;
-}
-
- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
withString: (OFString *)replacement
{
OFMutableString *new = [[self mutableCopy] autorelease];
[new replaceOccurrencesOfString: string withString: replacement];
@@ -2130,11 +2104,11 @@
tmp = OFAllocMemory(prefixLength, sizeof(OFUnichar));
@try {
void *pool = objc_autoreleasePoolPush();
- [self getCharacters: tmp inRange: OFRangeMake(0, prefixLength)];
+ [self getCharacters: tmp inRange: OFMakeRange(0, prefixLength)];
hasPrefix = (memcmp(tmp, prefix.characters,
prefixLength * sizeof(OFUnichar)) == 0);
objc_autoreleasePoolPop(pool);
@@ -2160,11 +2134,11 @@
tmp = OFAllocMemory(suffixLength, sizeof(OFUnichar));
@try {
void *pool = objc_autoreleasePoolPush();
[self getCharacters: tmp
- inRange: OFRangeMake(length - suffixLength,
+ inRange: OFMakeRange(length - suffixLength,
suffixLength)];
suffixCharacters = suffix.characters;
hasSuffix = (memcmp(tmp, suffixCharacters,
suffixLength * sizeof(OFUnichar)) == 0);
@@ -2220,18 +2194,18 @@
if (memcmp(characters + i, delimiterCharacters,
delimiterLength * sizeof(OFUnichar)) != 0)
continue;
component = [self substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
if (!skipEmpty || component.length > 0)
[array addObject: component];
i += delimiterLength - 1;
last = i + 1;
}
- component = [self substringWithRange: OFRangeMake(last, length - last)];
+ component = [self substringWithRange: OFMakeRange(last, length - last)];
if (!skipEmpty || component.length > 0)
[array addObject: component];
[array makeImmutable];
@@ -2265,20 +2239,20 @@
for (size_t i = 0; i < length; i++) {
if (characterIsMember(characterSet,
@selector(characterIsMember:), characters[i])) {
if (!skipEmpty || i != last) {
OFString *component = [self substringWithRange:
- OFRangeMake(last, i - last)];
+ OFMakeRange(last, i - last)];
[array addObject: component];
}
last = i + 1;
}
}
if (!skipEmpty || length != last) {
OFString *component = [self substringWithRange:
- OFRangeMake(last, length - last)];
+ OFMakeRange(last, length - last)];
[array addObject: component];
}
[array makeImmutable];
@@ -2290,11 +2264,11 @@
- (long long)longLongValue
{
return [self longLongValueWithBase: 10];
}
-- (long long)longLongValueWithBase: (int)base
+- (long long)longLongValueWithBase: (unsigned char)base
{
void *pool = objc_autoreleasePoolPush();
const char *UTF8String = self.UTF8String;
bool negative = false;
long long value = 0;
@@ -2366,11 +2340,11 @@
- (unsigned long long)unsignedLongLongValue
{
return [self unsignedLongLongValueWithBase: 10];
}
-- (unsigned long long)unsignedLongLongValueWithBase: (int)base
+- (unsigned long long)unsignedLongLongValueWithBase: (unsigned char)base
{
void *pool = objc_autoreleasePoolPush();
const char *UTF8String = self.UTF8String;
unsigned long long value = 0;
@@ -2457,14 +2431,14 @@
#else
/*
* If we have no strtof_l, we have no other choice but to replace "."
* with the locale's decimal point.
*/
- OFString *decimalPoint = [OFLocale decimalPoint];
+ OFString *decimalSeparator = [OFLocale decimalSeparator];
const char *UTF8String = [self
stringByReplacingOccurrencesOfString: @"."
- withString: decimalPoint].UTF8String;
+ withString: decimalSeparator].UTF8String;
#endif
char *endPtr = NULL;
float value;
errno = 0;
@@ -2510,14 +2484,14 @@
#else
/*
* If we have no strtod_l, we have no other choice but to replace "."
* with the locale's decimal point.
*/
- OFString *decimalPoint = [OFLocale decimalPoint];
+ OFString *decimalSeparator = [OFLocale decimalSeparator];
const char *UTF8String = [self
stringByReplacingOccurrencesOfString: @"."
- withString: decimalPoint].UTF8String;
+ withString: decimalSeparator].UTF8String;
#endif
char *endPtr = NULL;
double value;
errno = 0;
@@ -2548,11 +2522,11 @@
OFUnichar *buffer;
const OFUnichar *ret;
buffer = OFAllocMemory(length, sizeof(OFUnichar));
@try {
- [self getCharacters: buffer inRange: OFRangeMake(0, length)];
+ [self getCharacters: buffer inRange: OFMakeRange(0, length)];
ret = [[OFData dataWithItemsNoCopy: buffer
count: length
itemSize: sizeof(OFUnichar)
freeWhenDone: true] items];
@@ -2656,11 +2630,11 @@
OFChar32 *buffer;
const OFChar32 *ret;
buffer = OFAllocMemory(length + 1, sizeof(OFChar32));
@try {
- [self getCharacters: buffer inRange: OFRangeMake(0, length)];
+ [self getCharacters: buffer inRange: OFMakeRange(0, length)];
buffer[length] = 0;
if (byteOrder != OFByteOrderNative)
for (size_t i = 0; i < length; i++)
buffer[i] = OFByteSwap32(buffer[i]);
@@ -2748,25 +2722,21 @@
[file writeString: self encoding: encoding];
objc_autoreleasePoolPop(pool);
}
#endif
-- (void)writeToURL: (OFURL *)URL
+- (void)writeToURI: (OFURI *)URI
{
- [self writeToURL: URL encoding: OFStringEncodingUTF8];
+ [self writeToURI: URI encoding: OFStringEncodingUTF8];
}
-- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
+- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
void *pool = objc_autoreleasePoolPush();
- OFURLHandler *URLHandler;
OFStream *stream;
- if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
- @throw [OFUnsupportedProtocolException exceptionWithURL: URL];
-
- stream = [URLHandler openItemAtURL: URL mode: @"w"];
+ stream = [OFURIHandler openItemAtURI: URI mode: @"w"];
[stream writeString: self encoding: encoding];
objc_autoreleasePoolPop(pool);
}
@@ -2788,22 +2758,22 @@
if (characters[i] == '\n' || characters[i] == '\r') {
void *pool2 = objc_autoreleasePoolPush();
block([self substringWithRange:
- OFRangeMake(last, i - last)], &stop);
+ OFMakeRange(last, i - last)], &stop);
last = i + 1;
objc_autoreleasePoolPop(pool2);
}
lastCarriageReturn = (characters[i] == '\r');
}
if (!stop)
- block([self substringWithRange: OFRangeMake(last, i - last)],
+ block([self substringWithRange: OFMakeRange(last, i - last)],
&stop);
objc_autoreleasePoolPop(pool);
}
#endif
@end
Index: src/OFSubprocess.h
==================================================================
--- src/OFSubprocess.h
+++ src/OFSubprocess.h
@@ -192,18 +192,23 @@
* @brief Closes the write direction of the subprocess.
*
* This method needs to be called for some programs before data can be read,
* since some programs don't start processing before the write direction is
* closed.
+ *
+ * @throw OFNotOpenException The subprocess was already closed
*/
- (void)closeForWriting;
/**
* @brief Waits for the subprocess to terminate and returns the exit status.
*
* If the subprocess has already exited, this returns the exit status
* immediately.
+ *
+ * @return The status code of the subprocess
+ * @throw OFNotOpenException The subprocess was already closed
*/
- (int)waitForTermination;
@end
OF_ASSUME_NONNULL_END
Index: src/OFSystemInfo.h
==================================================================
--- src/OFSystemInfo.h
+++ src/OFSystemInfo.h
@@ -16,11 +16,11 @@
#import "OFObject.h"
#import "OFString.h"
OF_ASSUME_NONNULL_BEGIN
-@class OFURL;
+@class OFURI;
/**
* @class OFSystemInfo OFSystemInfo.h ObjFW/OFSystemInfo.h
*
* @brief A class for querying information about the system.
@@ -34,13 +34,13 @@
@property (class, readonly, nonatomic) unsigned short ObjFWVersionMajor;
@property (class, readonly, nonatomic) unsigned short ObjFWVersionMinor;
@property (class, readonly, nullable, nonatomic) OFString *operatingSystemName;
@property (class, readonly, nullable, nonatomic)
OFString *operatingSystemVersion;
-@property (class, readonly, nullable, nonatomic) OFURL *userDataURL;
-@property (class, readonly, nullable, nonatomic) OFURL *userConfigURL;
-@property (class, readonly, nullable, nonatomic) OFURL *temporaryDirectoryURL;
+@property (class, readonly, nullable, nonatomic) OFURI *userDataURI;
+@property (class, readonly, nullable, nonatomic) OFURI *userConfigURI;
+@property (class, readonly, nullable, nonatomic) OFURI *temporaryDirectoryURI;
@property (class, readonly, nullable, nonatomic) OFString *CPUVendor;
@property (class, readonly, nullable, nonatomic) OFString *CPUModel;
# if defined(OF_X86_64) || defined(OF_X86) || defined(DOXYGEN)
@property (class, readonly, nonatomic) bool supportsMMX;
@property (class, readonly, nonatomic) bool supportsSSE;
@@ -124,11 +124,11 @@
* On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.@n
* On AmigaOS and MorphOS, it returns `PROGDIR:`.
*
* @return The path where user data for the application can be stored
*/
-+ (nullable OFURL *)userDataURL;
++ (nullable OFURI *)userDataURI;
/**
* @brief Returns the path where user configuration for the application can be
* stored.
*
@@ -139,11 +139,11 @@
* On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.
* On AmigaOS and MorphOS, it returns `PROGDIR:`.
*
* @return The path where user configuration for the application can be stored
*/
-+ (nullable OFURL *)userConfigURL;
++ (nullable OFURI *)userConfigURI;
/**
* @brief Returns a path where temporary files for can be stored.
*
* If possible, returns a temporary directory for the user, otherwise returns a
@@ -157,11 +157,11 @@
* On Haiku, it uses the `B_SYSTEM_TEMP_DIRECTORY` directory.
* On AmigaOS and MorphOS, it returns `T:`.
*
* @return A path where temporary files can be stored
*/
-+ (nullable OFURL *)temporaryDirectoryURL;
++ (nullable OFURI *)temporaryDirectoryURI;
/**
* @brief Returns the vendor of the CPU.
*
* If the vendor could not be determined, `nil` is returned instead.
Index: src/OFSystemInfo.m
==================================================================
--- src/OFSystemInfo.m
+++ src/OFSystemInfo.m
@@ -52,11 +52,11 @@
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFLocale.h"
#import "OFOnce.h"
#import "OFString.h"
-#import "OFURL.h"
+#import "OFURI.h"
#if defined(OF_MACOS) || defined(OF_IOS)
# ifdef HAVE_SYSDIR_H
# include
# endif
@@ -68,10 +68,14 @@
# include
#endif
#ifdef OF_QNX
# include
#endif
+
+#if !defined(PATH_MAX) && defined(MAX_PATH)
+# define PATH_MAX MAX_PATH
+#endif
#if defined(OF_MACOS) || defined(OF_IOS)
/*
* These have been dropped from newer iOS SDKs, however, their replacements are
* not available on iOS < 10. This means it's impossible to search for the
@@ -240,17 +244,17 @@
encoding: [OFLocale encoding]];
#endif
}
#ifdef OF_NINTENDO_SWITCH
-static OFURL *tmpFSURL = nil;
+static OFURI *tmpFSURI = nil;
static void
mountTmpFS(void)
{
if (R_SUCCEEDED(fsdevMountTemporaryStorage("tmpfs")))
- tmpFSURL = [[OFURL alloc] initFileURLWithPath: @"tmpfs:/"
+ tmpFSURI = [[OFURI alloc] initFileURIWithPath: @"tmpfs:/"
isDirectory: true];
}
#endif
#if defined(OF_X86_64) || defined(OF_X86)
@@ -362,11 +366,11 @@
OFOnce(&onceControl, initOperatingSystemVersion);
return operatingSystemVersion;
}
-+ (OFURL *)userDataURL
++ (OFURI *)userDataURI
{
#ifdef OF_HAVE_FILES
# if defined(OF_MACOS) || defined(OF_IOS)
char pathC[PATH_MAX];
OFMutableString *path;
@@ -398,65 +402,65 @@
OFString *home;
if ((home = [env objectForKey: @"HOME"]) == nil)
return nil;
- [path deleteCharactersInRange: OFRangeMake(0, 1)];
- [path prependString: home];
+ [path deleteCharactersInRange: OFMakeRange(0, 1)];
+ [path insertString: home atIndex: 0];
}
[path makeImmutable];
- return [OFURL fileURLWithPath: path isDirectory: true];
+ return [OFURI fileURIWithPath: path isDirectory: true];
# elif defined(OF_WINDOWS)
OFDictionary *env = [OFApplication environment];
OFString *appData;
if ((appData = [env objectForKey: @"APPDATA"]) == nil)
return nil;
- return [OFURL fileURLWithPath: appData isDirectory: true];
+ return [OFURI fileURIWithPath: appData isDirectory: true];
# elif defined(OF_HAIKU)
char pathC[PATH_MAX];
if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, false,
pathC, PATH_MAX) != B_OK)
return nil;
- return [OFURL fileURLWithPath: [OFString stringWithUTF8String: pathC]
+ return [OFURI fileURIWithPath: [OFString stringWithUTF8String: pathC]
isDirectory: true];
# elif defined(OF_AMIGAOS)
- return [OFURL fileURLWithPath: @"PROGDIR:" isDirectory: true];
+ return [OFURI fileURIWithPath: @"PROGDIR:" isDirectory: true];
# else
OFDictionary *env = [OFApplication environment];
OFString *var;
- OFURL *URL;
+ OFURI *URI;
void *pool;
if ((var = [env objectForKey: @"XDG_DATA_HOME"]) != nil &&
var.length > 0)
- return [OFURL fileURLWithPath: var isDirectory: true];
+ return [OFURI fileURIWithPath: var isDirectory: true];
if ((var = [env objectForKey: @"HOME"]) == nil)
return nil;
pool = objc_autoreleasePoolPush();
var = [OFString pathWithComponents: [OFArray arrayWithObjects:
var, @".local", @"share", nil]];
- URL = [[OFURL alloc] initFileURLWithPath: var isDirectory: true];
+ URI = [[OFURI alloc] initFileURIWithPath: var isDirectory: true];
objc_autoreleasePoolPop(pool);
- return [URL autorelease];
+ return [URI autorelease];
# endif
#else
return nil;
#endif
}
-+ (OFURL *)userConfigURL
++ (OFURI *)userConfigURI
{
#ifdef OF_HAVE_FILES
# if defined(OF_MACOS) || defined(OF_IOS)
char pathC[PATH_MAX];
OFMutableString *path;
@@ -487,73 +491,73 @@
OFString *home;
if ((home = [env objectForKey: @"HOME"]) == nil)
return nil;
- [path deleteCharactersInRange: OFRangeMake(0, 1)];
- [path prependString: home];
+ [path deleteCharactersInRange: OFMakeRange(0, 1)];
+ [path insertString: home atIndex: 0];
}
[path appendString: @"/Preferences"];
[path makeImmutable];
- return [OFURL fileURLWithPath: path isDirectory: true];
+ return [OFURI fileURIWithPath: path isDirectory: true];
# elif defined(OF_WINDOWS)
OFDictionary *env = [OFApplication environment];
OFString *appData;
if ((appData = [env objectForKey: @"APPDATA"]) == nil)
return nil;
- return [OFURL fileURLWithPath: appData isDirectory: true];
+ return [OFURI fileURIWithPath: appData isDirectory: true];
# elif defined(OF_HAIKU)
char pathC[PATH_MAX];
if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, false,
pathC, PATH_MAX) != B_OK)
return nil;
- return [OFURL fileURLWithPath: [OFString stringWithUTF8String: pathC]
+ return [OFURI fileURIWithPath: [OFString stringWithUTF8String: pathC]
isDirectory: true];
# elif defined(OF_AMIGAOS)
- return [OFURL fileURLWithPath: @"PROGDIR:" isDirectory: true];
+ return [OFURI fileURIWithPath: @"PROGDIR:" isDirectory: true];
# else
OFDictionary *env = [OFApplication environment];
OFString *var;
if ((var = [env objectForKey: @"XDG_CONFIG_HOME"]) != nil &&
var.length > 0)
- return [OFURL fileURLWithPath: var isDirectory: true];
+ return [OFURI fileURIWithPath: var isDirectory: true];
if ((var = [env objectForKey: @"HOME"]) == nil)
return nil;
var = [var stringByAppendingPathComponent: @".config"];
- return [OFURL fileURLWithPath: var isDirectory: true];
+ return [OFURI fileURIWithPath: var isDirectory: true];
# endif
#else
return nil;
#endif
}
-+ (OFURL *)temporaryDirectoryURL
++ (OFURI *)temporaryDirectoryURI
{
#ifdef OF_HAVE_FILES
# if defined(OF_MACOS) || defined(OF_IOS)
char buffer[PATH_MAX];
size_t length;
OFString *path;
if ((length = confstr(_CS_DARWIN_USER_TEMP_DIR, buffer, PATH_MAX)) == 0)
- return [OFURL fileURLWithPath: @"/tmp" isDirectory: true];
+ return [OFURI fileURIWithPath: @"/tmp" isDirectory: true];
path = [OFString stringWithCString: buffer
encoding: [OFLocale encoding]
length: length - 1];
- return [OFURL fileURLWithPath: path isDirectory: true];
+ return [OFURI fileURIWithPath: path isDirectory: true];
# elif defined(OF_WINDOWS)
OFString *path;
if ([self isWindowsNT]) {
wchar_t buffer[PATH_MAX];
@@ -570,44 +574,44 @@
path = [OFString stringWithCString: buffer
encoding: [OFLocale encoding]];
}
- return [OFURL fileURLWithPath: path isDirectory: true];
+ return [OFURI fileURIWithPath: path isDirectory: true];
# elif defined(OF_HAIKU)
char pathC[PATH_MAX];
if (find_directory(B_SYSTEM_TEMP_DIRECTORY, 0, false,
pathC, PATH_MAX) != B_OK)
return nil;
- return [OFURL fileURLWithPath: [OFString stringWithUTF8String: pathC]
+ return [OFURI fileURIWithPath: [OFString stringWithUTF8String: pathC]
isDirectory: true];
# elif defined(OF_AMIGAOS)
- return [OFURL fileURLWithPath: @"T:" isDirectory: true];
+ return [OFURI fileURIWithPath: @"T:" isDirectory: true];
# elif defined(OF_MSDOS)
OFString *path = [[OFApplication environment] objectForKey: @"TEMP"];
if (path == nil)
return nil;
- return [OFURL fileURLWithPath: path isDirectory: true];
+ return [OFURI fileURIWithPath: path isDirectory: true];
# elif defined(OF_MINT)
- return [OFURL fileURLWithPath: @"u:\\tmp" isDirectory: true];
+ return [OFURI fileURIWithPath: @"u:\\tmp" isDirectory: true];
# elif defined(OF_NINTENDO_SWITCH)
static OFOnceControl onceControl = OFOnceControlInitValue;
OFOnce(&onceControl, mountTmpFS);
- return tmpFSURL;
+ return tmpFSURI;
# else
OFString *path =
[[OFApplication environment] objectForKey: @"XDG_RUNTIME_DIR"];
if (path != nil)
- return [OFURL fileURLWithPath: path];
+ return [OFURI fileURIWithPath: path];
- return [OFURL fileURLWithPath: @"/tmp"];
+ return [OFURI fileURIWithPath: @"/tmp"];
# endif
#else
return nil;
#endif
}
Index: src/OFTCPSocket.h
==================================================================
--- src/OFTCPSocket.h
+++ src/OFTCPSocket.h
@@ -81,20 +81,26 @@
#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
/**
* @brief Whether the socket sends keep alives for the connection.
*
* @warning This is not available on the Wii or Nintendo 3DS!
+ *
+ * @throw OFSetOptionFailedException The option could not be set
+ * @throw OFGetOptionFailedException The option could not be gotten
*/
@property (nonatomic) bool sendsKeepAlives;
#endif
#ifndef OF_WII
/**
- * @brief Whether sending segments can be delayed. Setting this to NO sets
+ * @brief Whether sending segments can be delayed. Setting this to `false` sets
* TCP_NODELAY on the socket.
*
* @warning This is not available on the Wii!
+ *
+ * @throw OFSetOptionFailedException The option could not be set
+ * @throw OFGetOptionFailedException The option could not be gotten
*/
@property (nonatomic) bool canDelaySendingSegments;
#endif
/**
@@ -148,10 +154,12 @@
/**
* @brief Connects the OFTCPSocket to the specified destination.
*
* @param host The host to connect to
* @param port The port on the host to connect to
+ * @throw OFConnectIPSocketFailedException Connecting failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
- (void)connectToHost: (OFString *)host port: (uint16_t)port;
/**
* @brief Asynchronously connects the OFTCPSocket to the specified destination.
@@ -204,10 +212,12 @@
* @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for
* IPv6 to bind to all.
* @param port The port to bind to. If the port is 0, an unused port will be
* chosen, which can be obtained using the return value.
* @return The port the socket was bound to
+ * @throw OFBindIPSocketFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port;
@end
OF_ASSUME_NONNULL_END
Index: src/OFTCPSocket.m
==================================================================
--- src/OFTCPSocket.m
+++ src/OFTCPSocket.m
@@ -29,24 +29,24 @@
#ifdef HAVE_FCNTL_H
# include
#endif
#import "OFTCPSocket.h"
+#import "OFAsyncIPSocketConnector.h"
#import "OFDNSResolver.h"
#import "OFData.h"
#import "OFDate.h"
-#import "OFIPSocketAsyncConnector.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#import "OFTCPSocketSOCKS5Connector.h"
#import "OFThread.h"
#import "OFAlreadyConnectedException.h"
-#import "OFBindFailedException.h"
+#import "OFBindIPSocketFailedException.h"
#import "OFGetOptionFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFSetOptionFailedException.h"
@@ -54,11 +54,11 @@
@"OFTCPSocketConnectRunLoopMode";
static OFString *defaultSOCKS5Host = nil;
static uint16_t defaultSOCKS5Port = 1080;
-@interface OFTCPSocket ()
+@interface OFTCPSocket ()
@end
@interface OFTCPSocketConnectDelegate: OFObject
{
@public
@@ -219,13 +219,10 @@
runLoopMode: (OFRunLoopMode)runLoopMode
{
void *pool = objc_autoreleasePoolPush();
id delegate;
- if (_socket != OFInvalidSocketHandle)
- @throw [OFAlreadyConnectedException exceptionWithSocket: self];
-
if (_SOCKS5Host != nil) {
delegate = [[[OFTCPSocketSOCKS5Connector alloc]
initWithSocket: self
host: host
port: port
@@ -237,11 +234,11 @@
host = _SOCKS5Host;
port = _SOCKS5Port;
} else
delegate = _delegate;
- [[[[OFIPSocketAsyncConnector alloc]
+ [[[[OFAsyncIPSocketConnector alloc]
initWithSocket: self
host: host
port: port
delegate: delegate
block: NULL
@@ -267,13 +264,10 @@
block: (OFTCPSocketAsyncConnectBlock)block
{
void *pool = objc_autoreleasePoolPush();
id delegate = nil;
- if (_socket != OFInvalidSocketHandle)
- @throw [OFAlreadyConnectedException exceptionWithSocket: self];
-
if (_SOCKS5Host != nil) {
delegate = [[[OFTCPSocketSOCKS5Connector alloc]
initWithSocket: self
host: host
port: port
@@ -281,11 +275,11 @@
block: block] autorelease];
host = _SOCKS5Host;
port = _SOCKS5Port;
}
- [[[[OFIPSocketAsyncConnector alloc]
+ [[[[OFAsyncIPSocketConnector alloc]
initWithSocket: self
host: host
port: port
delegate: delegate
block: (delegate == nil ? block : NULL)] autorelease]
@@ -315,16 +309,16 @@
socketAddresses = [[OFThread DNSResolver]
resolveAddressesForHost: host
addressFamily: OFSocketAddressFamilyAny];
address = *(OFSocketAddress *)[socketAddresses itemAtIndex: 0];
- OFSocketAddressSetPort(&address, port);
+ OFSocketAddressSetIPPort(&address, port);
if ((_socket = socket(
((struct sockaddr *)&address.sockaddr)->sa_family,
SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: host
port: port
socket: self
errNo: OFSocketErrNo()];
@@ -346,14 +340,15 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithHost: host
- port: port
- socket: self
- errNo: errNo];
+ @throw [OFBindIPSocketFailedException
+ exceptionWithHost: host
+ port: port
+ socket: self
+ errNo: errNo];
}
#if defined(OF_HPUX) || defined(OF_WII) || defined(OF_NINTENDO_3DS)
} else {
for (;;) {
uint16_t rnd = 0;
@@ -360,11 +355,11 @@
int ret;
while (rnd < 1024)
rnd = (uint16_t)rand();
- OFSocketAddressSetPort(&address, rnd);
+ OFSocketAddressSetIPPort(&address, rnd);
if ((ret = bind(_socket,
(struct sockaddr *)&address.sockaddr,
address.length)) == 0) {
port = rnd;
@@ -375,11 +370,11 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: host
port: port
socket: self
errNo: errNo];
}
@@ -401,14 +396,14 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithHost: host
- port: port
- socket: self
- errNo: errNo];
+ @throw [OFBindIPSocketFailedException exceptionWithHost: host
+ port: port
+ socket: self
+ errNo: errNo];
}
switch (((struct sockaddr *)&address.sockaddr)->sa_family) {
case AF_INET:
return OFFromBigEndian16(address.sockaddr.in.sin_port);
@@ -418,22 +413,23 @@
# endif
default:
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithHost: host
- port: port
- socket: self
- errNo: EAFNOSUPPORT];
+ @throw [OFBindIPSocketFailedException
+ exceptionWithHost: host
+ port: port
+ socket: self
+ errNo: EAFNOSUPPORT];
}
#else
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithHost: host
- port: port
- socket: self
- errNo: EADDRNOTAVAIL];
+ @throw [OFBindIPSocketFailedException exceptionWithHost: host
+ port: port
+ socket: self
+ errNo: EADDRNOTAVAIL];
#endif
}
#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
- (void)setSendsKeepAlives: (bool)sendsKeepAlives
Index: src/OFTCPSocketSOCKS5Connector.m
==================================================================
--- src/OFTCPSocketSOCKS5Connector.m
+++ src/OFTCPSocketSOCKS5Connector.m
@@ -21,11 +21,11 @@
#import "OFTCPSocketSOCKS5Connector.h"
#import "OFData.h"
#import "OFRunLoop.h"
#import "OFString.h"
-#import "OFConnectionFailedException.h"
+#import "OFConnectIPSocketFailedException.h"
enum {
stateSendAuthentication = 1,
stateReadVersion,
stateSendRequest,
@@ -142,11 +142,11 @@
switch (_SOCKS5State) {
case stateReadVersion:
SOCKSVersion = buffer;
if (SOCKSVersion[0] != 5 || SOCKSVersion[1] != 0) {
- _exception = [[OFConnectionFailedException alloc]
+ _exception = [[OFConnectIPSocketFailedException alloc]
initWithHost: _host
port: _port
socket: self
errNo: EPROTONOSUPPORT];
[self didConnect];
@@ -171,11 +171,11 @@
return false;
case stateReadResponse:
response = buffer;
if (response[0] != 5 || response[2] != 0) {
- _exception = [[OFConnectionFailedException alloc]
+ _exception = [[OFConnectIPSocketFailedException alloc]
initWithHost: _host
port: _port
socket: self
errNo: EPROTONOSUPPORT];
[self didConnect];
@@ -214,11 +214,11 @@
errNo = 0;
#endif
break;
}
- _exception = [[OFConnectionFailedException alloc]
+ _exception = [[OFConnectIPSocketFailedException alloc]
initWithHost: _host
port: _port
socket: _socket
errNo: errNo];
[self didConnect];
@@ -244,11 +244,11 @@
[_socket asyncReadIntoBuffer: _buffer
exactLength: 16 + 2
runLoopMode: runLoopMode];
return false;
default:
- _exception = [[OFConnectionFailedException alloc]
+ _exception = [[OFConnectIPSocketFailedException alloc]
initWithHost: _host
port: _port
socket: self
errNo: EPROTONOSUPPORT];
[self didConnect];
Index: src/OFTLSStream.h
==================================================================
--- src/OFTLSStream.h
+++ src/OFTLSStream.h
@@ -126,27 +126,33 @@
/**
* @brief Asynchronously performs the TLS client handshake for the specified
* host and calls the delegate afterwards.
*
* @param host The host to perform the handshake with
+ * @throw OFTLSHandshakeFailedException The TLS handshake failed
+ * @throw OFAlreadyConnectedException The handshake was already performed
*/
- (void)asyncPerformClientHandshakeWithHost: (OFString *)host;
/**
* @brief Asynchronously performs the TLS client handshake for the specified
* host and calls the delegate afterwards.
*
* @param host The host to perform the handshake with
* @param runLoopMode The run loop mode in which to perform the async handshake
+ * @throw OFTLSHandshakeFailedException The TLS handshake failed
+ * @throw OFAlreadyConnectedException The handshake was already performed
*/
- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
runLoopMode: (OFRunLoopMode)runLoopMode;
/**
* @brief Performs the TLS client handshake for the specified host.
*
* @param host The host to perform the handshake with
+ * @throw OFTLSHandshakeFailedException The TLS handshake failed
+ * @throw OFAlreadyConnectedException The handshake was already performed
*/
- (void)performClientHandshakeWithHost: (OFString *)host;
@end
#ifdef __cplusplus
Index: src/OFTarArchive.h
==================================================================
--- src/OFTarArchive.h
+++ src/OFTarArchive.h
@@ -19,10 +19,11 @@
#import "OFTarArchiveEntry.h"
OF_ASSUME_NONNULL_BEGIN
@class OFStream;
+@class OFURI;
/**
* @class OFTarArchive OFTarArchive.h ObjFW/OFTarArchive.h
*
* @brief A class for accessing and manipulating tar archives.
@@ -35,52 +36,61 @@
OFTarArchiveModeRead,
OFTarArchiveModeWrite,
OFTarArchiveModeAppend
} _mode;
OFStringEncoding _encoding;
+ OFTarArchiveEntry *_Nullable _currentEntry;
+#ifdef OF_TAR_ARCHIVE_M
+@public
+#endif
OFStream *_Nullable _lastReturnedStream;
}
/**
* @brief The encoding to use for the archive. Defaults to UTF-8.
*/
@property (nonatomic) OFStringEncoding encoding;
-/**
- * @brief A stream for reading the current entry.
- *
- * @note This is only available in read mode.
- *
- * @note The returned stream conforms to @ref OFReadyForReadingObserving if the
- * underlying stream does so, too.
- */
-@property (readonly, nonatomic) OFStream *streamForReadingCurrentEntry;
-
/**
* @brief Creates a new OFTarArchive object with the specified stream.
*
* @param stream A stream from which the tar archive will be read.
* For append mode, this needs to be an OFSeekableStream.
* @param mode The mode for the tar file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return A new, autoreleased OFTarArchive
+ * @throw OFInvalidFormatException The archive has an invalid format
+ * @throw OFSeekFailedException The archive was open in append mode and seeking
+ * failed
*/
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode;
-#ifdef OF_HAVE_FILES
/**
* @brief Creates a new OFTarArchive object with the specified file.
*
- * @param path The path to the tar archive
+ * @param URI The URI to the tar archive
* @param mode The mode for the tar file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return A new, autoreleased OFTarArchive
+ * @throw OFInvalidFormatException The archive has an invalid format
+ * @throw OFSeekFailedException The archive was open in append mode and seeking
+ * failed
+ */
++ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode;
+
+/**
+ * @brief Creates a URI for accessing a the specified file within the specified
+ * tar archive.
+ *
+ * @param path The path of the file within the archive
+ * @param URI The URI of the archive
+ * @return A URI for accessing the specified file within the specified tar
+ * archive
*/
-+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode;
-#endif
++ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI;
- (instancetype)init OF_UNAVAILABLE;
/**
* @brief Initializes an already allocated OFTarArchive object with the
@@ -90,27 +100,31 @@
* For append mode, this needs to be an OFSeekableStream.
* @param mode The mode for the tar file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return An initialized OFTarArchive
+ * @throw OFInvalidFormatException The archive has an invalid format
+ * @throw OFSeekFailedException The archive was open in append mode and seeking
+ * failed
*/
- (instancetype)initWithStream: (OFStream *)stream
mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;
-#ifdef OF_HAVE_FILES
/**
* @brief Initializes an already allocated OFTarArchive object with the
* specified file.
*
- * @param path The path to the tar archive
+ * @param URI The URI to the tar archive
* @param mode The mode for the tar file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return An initialized OFTarArchive
+ * @throw OFInvalidFormatException The archive has an invalid format
+ * @throw OFSeekFailedException The archive was open in append mode and seeking
+ * failed
*/
-- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode;
-#endif
+- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode;
/**
* @brief Returns the next entry from the tar archive or `nil` if all entries
* have been read.
*
@@ -122,13 +136,26 @@
* invalidated stream will throw an @ref OFReadFailedException or
* @ref OFWriteFailedException!
*
* @return The next entry from the tar archive or `nil` if all entries have
* been read
+ * @throw OFInvalidFormatException The archive has an invalid format
*/
- (nullable OFTarArchiveEntry *)nextEntry;
+/**
+ * @brief Returns a stream for reading the current entry.
+ *
+ * @note This is only available in read mode.
+ *
+ * @note The returned stream conforms to @ref OFReadyForReadingObserving if the
+ * underlying stream does so, too.
+ *
+ * @return A stream for reading the current entry
+ */
+- (OFStream *)streamForReadingCurrentEntry;
+
/**
* @brief Returns a stream for writing the specified entry.
*
* @note This is only available in write and append mode.
*
@@ -146,10 +173,12 @@
*/
- (OFStream *)streamForWritingEntry: (OFTarArchiveEntry *)entry;
/**
* @brief Closes the OFTarArchive.
+ *
+ * @throw OFNotOpenException The archive is not open
*/
- (void)close;
@end
OF_ASSUME_NONNULL_END
Index: src/OFTarArchive.m
==================================================================
--- src/OFTarArchive.m
+++ src/OFTarArchive.m
@@ -11,23 +11,25 @@
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
+#define OF_TAR_ARCHIVE_M
+
#include "config.h"
#include
#import "OFTarArchive.h"
#import "OFTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
+#import "OFArchiveURIHandler.h"
#import "OFDate.h"
#import "OFSeekableStream.h"
#import "OFStream.h"
-#ifdef OF_HAVE_FILES
-# import "OFFile.h"
-#endif
+#import "OFURI.h"
+#import "OFURIHandler.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
@@ -35,31 +37,35 @@
#import "OFWriteFailedException.h"
OF_DIRECT_MEMBERS
@interface OFTarArchiveFileReadStream: OFStream
{
+ OFTarArchive *_archive;
OFTarArchiveEntry *_entry;
OFStream *_stream;
- uint64_t _toRead;
+ unsigned long long _toRead;
bool _atEndOfStream, _skipped;
}
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFTarArchiveEntry *)entry;
+- (instancetype)of_initWithArchive: (OFTarArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFTarArchiveEntry *)entry;
- (void)of_skip;
@end
OF_DIRECT_MEMBERS
@interface OFTarArchiveFileWriteStream: OFStream
{
+ OFTarArchive *_archive;
OFTarArchiveEntry *_entry;
OFStream *_stream;
- uint64_t _toWrite;
+ unsigned long long _toWrite;
}
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFTarArchiveEntry *)entry;
+- (instancetype)of_initWithArchive: (OFTarArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFTarArchiveEntry *)entry;
@end
@implementation OFTarArchive: OFObject
@synthesize encoding = _encoding;
@@ -66,16 +72,19 @@
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode
{
return [[[self alloc] initWithStream: stream mode: mode] autorelease];
}
-#ifdef OF_HAVE_FILES
-+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode
++ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode
{
- return [[[self alloc] initWithPath: path mode: mode] autorelease];
+ return [[[self alloc] initWithURI: URI mode: mode] autorelease];
}
-#endif
+
++ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI
+{
+ return OFArchiveURIHandlerURIForFileInArchive(@"tar", path, URI);
+}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
@@ -102,11 +111,11 @@
if (![_stream isKindOfClass: [OFSeekableStream class]])
@throw [OFInvalidArgumentException exception];
[(OFSeekableStream *)_stream seekToOffset: -1024
- whence: SEEK_END];
+ whence: OFSeekEnd];
[_stream readIntoBuffer: buffer exactLength: 1024];
for (size_t i = 0; i < 1024 / sizeof(uint32_t); i++)
if (buffer[i] != 0)
empty = false;
@@ -113,11 +122,11 @@
if (!empty)
@throw [OFInvalidFormatException exception];
[(OFSeekableStream *)stream seekToOffset: -1024
- whence: SEEK_END];
+ whence: OFSeekEnd];
}
_encoding = OFStringEncodingUTF8;
} @catch (id e) {
[self release];
@@ -125,53 +134,58 @@
}
return self;
}
-#ifdef OF_HAVE_FILES
-- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode
-{
- OFFile *file;
-
- if ([mode isEqual: @"a"])
- file = [[OFFile alloc] initWithPath: path mode: @"r+"];
- else
- file = [[OFFile alloc] initWithPath: path mode: mode];
+- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFStream *stream;
@try {
- self = [self initWithStream: file mode: mode];
- } @finally {
- [file release];
+ if ([mode isEqual: @"a"])
+ stream = [OFURIHandler openItemAtURI: URI mode: @"r+"];
+ else
+ stream = [OFURIHandler openItemAtURI: URI mode: mode];
+ } @catch (id e) {
+ [self release];
+ @throw e;
}
+
+ self = [self initWithStream: stream mode: mode];
+
+ objc_autoreleasePoolPop(pool);
return self;
}
-#endif
- (void)dealloc
{
[self close];
+
+ [_currentEntry release];
[super dealloc];
}
- (OFTarArchiveEntry *)nextEntry
{
- OFTarArchiveEntry *entry;
uint32_t buffer[512 / sizeof(uint32_t)];
bool empty = true;
if (_mode != OFTarArchiveModeRead)
@throw [OFInvalidArgumentException exception];
+
+ [_currentEntry release];
+ _currentEntry = nil;
[(OFTarArchiveFileReadStream *)_lastReturnedStream of_skip];
@try {
[_lastReturnedStream close];
} @catch (OFNotOpenException *e) {
/* Might have already been closed by the user - that's fine. */
}
- [_lastReturnedStream release];
_lastReturnedStream = nil;
if (_stream.atEndOfStream)
return nil;
@@ -189,60 +203,55 @@
@throw [OFInvalidFormatException exception];
return nil;
}
- entry = [[[OFTarArchiveEntry alloc]
+ _currentEntry = [[OFTarArchiveEntry alloc]
of_initWithHeader: (unsigned char *)buffer
- encoding: _encoding] autorelease];
+ encoding: _encoding];
- _lastReturnedStream = [[OFTarArchiveFileReadStream alloc]
- of_initWithStream: _stream
- entry: entry];
-
- return entry;
+ return _currentEntry;
}
- (OFStream *)streamForReadingCurrentEntry
{
if (_mode != OFTarArchiveModeRead)
@throw [OFInvalidArgumentException exception];
- if (_lastReturnedStream == nil)
+ if (_currentEntry == nil)
@throw [OFInvalidArgumentException exception];
- return [[(OFTarArchiveFileReadStream *)_lastReturnedStream
- retain] autorelease];
+ _lastReturnedStream = [[[OFTarArchiveFileReadStream alloc]
+ of_initWithArchive: self
+ stream: _stream
+ entry: _currentEntry] autorelease];
+ [_currentEntry release];
+ _currentEntry = nil;
+
+ return _lastReturnedStream;
}
- (OFStream *)streamForWritingEntry: (OFTarArchiveEntry *)entry
{
- void *pool;
-
if (_mode != OFTarArchiveModeWrite && _mode != OFTarArchiveModeAppend)
@throw [OFInvalidArgumentException exception];
- pool = objc_autoreleasePoolPush();
-
@try {
[_lastReturnedStream close];
} @catch (OFNotOpenException *e) {
/* Might have already been closed by the user - that's fine. */
}
- [_lastReturnedStream release];
_lastReturnedStream = nil;
[entry of_writeToStream: _stream encoding: _encoding];
- _lastReturnedStream = [[OFTarArchiveFileWriteStream alloc]
- of_initWithStream: _stream
- entry: entry];
-
- objc_autoreleasePoolPop(pool);
-
- return [[(OFTarArchiveFileWriteStream *)_lastReturnedStream
- retain] autorelease];
+ _lastReturnedStream = [[[OFTarArchiveFileWriteStream alloc]
+ of_initWithArchive: self
+ stream: _stream
+ entry: entry] autorelease];
+
+ return _lastReturnedStream;
}
- (void)close
{
if (_stream == nil)
@@ -251,11 +260,10 @@
@try {
[_lastReturnedStream close];
} @catch (OFNotOpenException *e) {
/* Might have already been closed by the user - that's fine. */
}
- [_lastReturnedStream release];
_lastReturnedStream = nil;
if (_mode == OFTarArchiveModeWrite || _mode == OFTarArchiveModeAppend) {
char buffer[1024];
memset(buffer, '\0', 1024);
@@ -266,19 +274,21 @@
_stream = nil;
}
@end
@implementation OFTarArchiveFileReadStream
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFTarArchiveEntry *)entry
+- (instancetype)of_initWithArchive: (OFTarArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFTarArchiveEntry *)entry
{
self = [super init];
@try {
+ _archive = [archive retain];
_entry = [entry copy];
_stream = [stream retain];
- _toRead = entry.size;
+ _toRead = entry.uncompressedSize;
} @catch (id e) {
[self release];
@throw e;
}
@@ -290,30 +300,32 @@
if (_stream != nil)
[self close];
[_entry release];
+ if (_archive->_lastReturnedStream == self)
+ _archive->_lastReturnedStream = nil;
+
[super dealloc];
}
-- (size_t)lowlevelReadIntoBuffer: (void *)buffer
- length: (size_t)length
+- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
size_t ret;
if (_stream == nil)
@throw [OFNotOpenException exceptionWithObject: self];
if (_atEndOfStream)
return 0;
-#if SIZE_MAX >= UINT64_MAX
- if (length > UINT64_MAX)
+#if SIZE_MAX >= ULLONG_MAX
+ if (length > ULLONG_MAX)
@throw [OFOutOfRangeException exception];
#endif
- if ((uint64_t)length > _toRead)
+ if ((unsigned long long)length > _toRead)
length = (size_t)_toRead;
ret = [_stream readIntoBuffer: buffer length: length];
if (ret == 0)
_atEndOfStream = true;
@@ -359,27 +371,29 @@
{
if (_stream == nil || _skipped)
return;
if ([_stream isKindOfClass: [OFSeekableStream class]] &&
- _toRead <= INT64_MAX && (OFFileOffset)_toRead == (int64_t)_toRead) {
- uint64_t size;
+ _toRead <= LLONG_MAX &&
+ (OFStreamOffset)_toRead == (long long)_toRead) {
+ unsigned long long size;
- [(OFSeekableStream *)_stream seekToOffset: (OFFileOffset)_toRead
- whence: SEEK_CUR];
+ [(OFSeekableStream *)_stream
+ seekToOffset: (OFStreamOffset)_toRead
+ whence: OFSeekCurrent];
_toRead = 0;
- size = _entry.size;
+ size = _entry.uncompressedSize;
if (size % 512 != 0)
[(OFSeekableStream *)_stream
seekToOffset: 512 - (size % 512)
- whence: SEEK_CUR];
+ whence: OFSeekCurrent];
} else {
char buffer[512];
- uint64_t size;
+ unsigned long long size;
while (_toRead >= 512) {
[_stream readIntoBuffer: buffer exactLength: 512];
_toRead -= 512;
}
@@ -388,11 +402,11 @@
[_stream readIntoBuffer: buffer
exactLength: (size_t)_toRead];
_toRead = 0;
}
- size = _entry.size;
+ size = _entry.uncompressedSize;
if (size % 512 != 0)
[_stream readIntoBuffer: buffer
exactLength: (size_t)(512 - (size % 512))];
}
@@ -400,19 +414,21 @@
_skipped = true;
}
@end
@implementation OFTarArchiveFileWriteStream
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFTarArchiveEntry *)entry
+- (instancetype)of_initWithArchive: (OFTarArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFTarArchiveEntry *)entry
{
self = [super init];
@try {
+ _archive = [archive retain];
_entry = [entry copy];
_stream = [stream retain];
- _toWrite = entry.size;
+ _toWrite = entry.uncompressedSize;
} @catch (id e) {
[self release];
@throw e;
}
@@ -424,19 +440,22 @@
if (_stream != nil)
[self close];
[_entry release];
+ if (_archive->_lastReturnedStream == self)
+ _archive->_lastReturnedStream = nil;
+
[super dealloc];
}
- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
if (_stream == nil)
@throw [OFNotOpenException exceptionWithObject: self];
- if ((uint64_t)length > _toWrite)
+ if (length > _toWrite)
@throw [OFOutOfRangeException exception];
@try {
[_stream writeBuffer: buffer length: length];
} @catch (OFWriteFailedException *e) {
@@ -469,19 +488,19 @@
.fileDescriptorForWriting;
}
- (void)close
{
- uint64_t remainder;
+ unsigned long long remainder;
if (_stream == nil)
@throw [OFNotOpenException exceptionWithObject: self];
if (_toWrite > 0)
@throw [OFTruncatedDataException exception];
- remainder = 512 - _entry.size % 512;
+ remainder = 512 - _entry.uncompressedSize % 512;
if (remainder != 512) {
bool didBufferWrites = _stream.buffersWrites;
_stream.buffersWrites = true;
Index: src/OFTarArchiveEntry+Private.h
==================================================================
--- src/OFTarArchiveEntry+Private.h
+++ src/OFTarArchiveEntry+Private.h
@@ -18,15 +18,15 @@
OF_ASSUME_NONNULL_BEGIN
@class OFStream;
-OF_DIRECT_MEMBERS
@interface OFTarArchiveEntry ()
+- (instancetype)of_init OF_METHOD_FAMILY(init);
- (instancetype)of_initWithHeader: (unsigned char [_Nonnull 512])header
encoding: (OFStringEncoding)encoding
- OF_METHOD_FAMILY(init);
+ OF_METHOD_FAMILY(init) OF_DIRECT;
- (void)of_writeToStream: (OFStream *)stream
- encoding: (OFStringEncoding)encoding;
+ encoding: (OFStringEncoding)encoding OF_DIRECT;
@end
OF_ASSUME_NONNULL_END
Index: src/OFTarArchiveEntry.h
==================================================================
--- src/OFTarArchiveEntry.h
+++ src/OFTarArchiveEntry.h
@@ -12,16 +12,18 @@
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#import "OFObject.h"
+#import "OFArchiveEntry.h"
OF_ASSUME_NONNULL_BEGIN
/** @file */
@class OFDate;
+@class OFNumber;
/**
* @brief The type of the archive entry.
*/
typedef enum {
@@ -46,54 +48,25 @@
/**
* @class OFTarArchiveEntry OFTarArchiveEntry.h ObjFW/OFTarArchiveEntry.h
*
* @brief A class which represents an entry of a tar archive.
*/
-@interface OFTarArchiveEntry: OFObject
+@interface OFTarArchiveEntry: OFObject
{
OFString *_fileName;
- unsigned long _mode;
- unsigned long long _size;
- unsigned long _UID, _GID;
+ OFNumber *_POSIXPermissions, *_ownerAccountID, *_groupOwnerAccountID;
+ unsigned long long _compressedSize, _uncompressedSize;
OFDate *_modificationDate;
OFTarArchiveEntryType _type;
OFString *_Nullable _targetFileName;
- OFString *_Nullable _owner, *_Nullable _group;
+ OFString *_Nullable _ownerAccountName;
+ OFString *_Nullable _groupOwnerAccountName;
unsigned long _deviceMajor, _deviceMinor;
OF_RESERVE_IVARS(OFTarArchiveEntry, 4)
}
-/**
- * @brief The file name of the entry.
- */
-@property (readonly, copy, nonatomic) OFString *fileName;
-
-/**
- * @brief The mode of the entry.
- */
-@property (readonly, nonatomic) unsigned long mode;
-
-/**
- * @brief The UID of the owner.
- */
-@property (readonly, nonatomic) unsigned long UID;
-
-/**
- * @brief The GID of the group.
- */
-@property (readonly, nonatomic) unsigned long GID;
-
-/**
- * @brief The size of the file.
- */
-@property (readonly, nonatomic) unsigned long long size;
-
-/**
- * @brief The date of the last modification of the file.
- */
-@property (readonly, retain, nonatomic) OFDate *modificationDate;
-
/**
* @brief The type of the archive entry.
*
* See @ref OFTarArchiveEntryType.
*/
@@ -103,20 +76,10 @@
* @brief The file name of the target (for a hard link or symbolic link).
*/
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
OFString *targetFileName;
-/**
- * @brief The owner of the file.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *owner;
-
-/**
- * @brief The group of the file.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *group;
-
/**
* @brief The device major (if the file is a device).
*/
@property (readonly, nonatomic) unsigned long deviceMajor;
@@ -123,28 +86,11 @@
/**
* @brief The device major (if the file is a device).
*/
@property (readonly, nonatomic) unsigned long deviceMinor;
-/**
- * @brief Creates a new OFTarArchiveEntry with the specified file name.
- *
- * @param fileName The file name for the OFTarArchiveEntry
- * @return A new, autoreleased OFTarArchiveEntry
- */
-+ (instancetype)entryWithFileName: (OFString *)fileName;
-
- (instancetype)init OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated OFTarArchiveEntry with the specified
- * file name.
- *
- * @param fileName The file name for the OFTarArchiveEntry
- * @return An initialized OFTarArchiveEntry
- */
-- (instancetype)initWithFileName: (OFString *)fileName;
@end
OF_ASSUME_NONNULL_END
#import "OFMutableTarArchiveEntry.h"
Index: src/OFTarArchiveEntry.m
==================================================================
--- src/OFTarArchiveEntry.m
+++ src/OFTarArchiveEntry.m
@@ -16,10 +16,11 @@
#include "config.h"
#import "OFTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
#import "OFDate.h"
+#import "OFNumber.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFOutOfRangeException.h"
@@ -72,19 +73,36 @@
return value;
}
@implementation OFTarArchiveEntry
-+ (instancetype)entryWithFileName: (OFString *)fileName
-{
- return [[[self alloc] initWithFileName: fileName] autorelease];
-}
+/*
+ * The following is optional in OFArchiveEntry, but Apple GCC 4.0.1 is buggy
+ * and needs this to stop complaining.
+ */
+@dynamic fileComment;
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
+
+- (instancetype)of_init
+{
+ self = [super init];
+
+ @try {
+ _type = OFTarArchiveEntryTypeFile;
+ _POSIXPermissions =
+ [[OFNumber alloc] initWithUnsignedShort: 0644];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
- (instancetype)of_initWithHeader: (unsigned char [512])header
encoding: (OFStringEncoding)encoding
{
self = [super init];
@@ -92,18 +110,21 @@
@try {
void *pool = objc_autoreleasePoolPush();
OFString *targetFileName;
_fileName = [stringFromBuffer(header, 100, encoding) copy];
- _mode = (unsigned long)octalValueFromBuffer(
- header + 100, 8, ULONG_MAX);
- _UID = (unsigned long)octalValueFromBuffer(
- header + 108, 8, ULONG_MAX);
- _GID = (unsigned long)octalValueFromBuffer(
- header + 116, 8, ULONG_MAX);
- _size = (unsigned long long)octalValueFromBuffer(
+ _POSIXPermissions = [[OFNumber alloc] initWithUnsignedLongLong:
+ octalValueFromBuffer(header + 100, 8, ULONG_MAX)];
+ _ownerAccountID = [[OFNumber alloc] initWithUnsignedLongLong:
+ octalValueFromBuffer(header + 108, 8, ULONG_MAX)];
+ _groupOwnerAccountID = [[OFNumber alloc]
+ initWithUnsignedLongLong:
+ octalValueFromBuffer(header + 116, 8, ULONG_MAX)];
+ _uncompressedSize = (unsigned long long)octalValueFromBuffer(
header + 124, 12, ULLONG_MAX);
+ _compressedSize =
+ _uncompressedSize + (512 - _uncompressedSize % 512);
_modificationDate = [[OFDate alloc]
initWithTimeIntervalSince1970:
(OFTimeInterval)octalValueFromBuffer(
header + 136, 12, ULLONG_MAX)];
_type = header[156];
@@ -116,14 +137,14 @@
_type = OFTarArchiveEntryTypeFile;
if (memcmp(header + 257, "ustar\0" "00", 8) == 0) {
OFString *prefix;
- _owner = [stringFromBuffer(header + 265, 32, encoding)
- copy];
- _group = [stringFromBuffer(header + 297, 32, encoding)
- copy];
+ _ownerAccountName =
+ [stringFromBuffer(header + 265, 32, encoding) copy];
+ _groupOwnerAccountName = [stringFromBuffer(header + 297,
+ 32, encoding) copy];
_deviceMajor = (unsigned long)octalValueFromBuffer(
header + 329, 8, ULONG_MAX);
_deviceMinor = (unsigned long)octalValueFromBuffer(
header + 337, 8, ULONG_MAX);
@@ -145,33 +166,20 @@
}
return self;
}
-- (instancetype)initWithFileName: (OFString *)fileName
-{
- self = [super init];
-
- @try {
- _fileName = [fileName copy];
- _type = OFTarArchiveEntryTypeFile;
- _mode = 0644;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
- (void)dealloc
{
[_fileName release];
+ [_POSIXPermissions release];
+ [_ownerAccountID release];
+ [_groupOwnerAccountID release];
[_modificationDate release];
[_targetFileName release];
- [_owner release];
- [_group release];
+ [_ownerAccountName release];
+ [_groupOwnerAccountName release];
[super dealloc];
}
- (id)copy
@@ -183,17 +191,20 @@
{
OFTarArchiveEntry *copy = [[OFMutableTarArchiveEntry alloc]
initWithFileName: _fileName];
@try {
- copy->_mode = _mode;
- copy->_size = _size;
+ copy->_POSIXPermissions = [_POSIXPermissions retain];
+ copy->_ownerAccountID = [_ownerAccountID retain];
+ copy->_groupOwnerAccountID = [_groupOwnerAccountID retain];
+ copy->_compressedSize = _compressedSize;
+ copy->_uncompressedSize = _uncompressedSize;
copy->_modificationDate = [_modificationDate copy];
copy->_type = _type;
copy->_targetFileName = [_targetFileName copy];
- copy->_owner = [_owner copy];
- copy->_group = [_group copy];
+ copy->_ownerAccountName = [_ownerAccountName copy];
+ copy->_groupOwnerAccountName = [_groupOwnerAccountName copy];
copy->_deviceMajor = _deviceMajor;
copy->_deviceMinor = _deviceMinor;
} @catch (id e) {
[copy release];
@throw e;
@@ -205,28 +216,33 @@
- (OFString *)fileName
{
return _fileName;
}
-- (unsigned long)mode
-{
- return _mode;
-}
-
-- (unsigned long)UID
-{
- return _UID;
-}
-
-- (unsigned long)GID
-{
- return _GID;
-}
-
-- (unsigned long long)size
-{
- return _size;
+- (OFNumber *)POSIXPermissions
+{
+ return _POSIXPermissions;
+}
+
+- (OFNumber *)ownerAccountID
+{
+ return _ownerAccountID;
+}
+
+- (OFNumber *)groupOwnerAccountID
+{
+ return _groupOwnerAccountID;
+}
+
+- (unsigned long long)compressedSize
+{
+ return _compressedSize;
+}
+
+- (unsigned long long)uncompressedSize
+{
+ return _uncompressedSize;
}
- (OFDate *)modificationDate
{
return _modificationDate;
@@ -240,18 +256,18 @@
- (OFString *)targetFileName
{
return _targetFileName;
}
-- (OFString *)owner
+- (OFString *)ownerAccountName
{
- return _owner;
+ return _ownerAccountName;
}
-- (OFString *)group
+- (OFString *)groupOwnerAccountName
{
- return _group;
+ return _groupOwnerAccountName;
}
- (unsigned long)deviceMajor
{
return _deviceMajor;
@@ -263,26 +279,36 @@
}
- (OFString *)description
{
void *pool = objc_autoreleasePoolPush();
- OFString *ret = [OFString stringWithFormat: @"<%@:\n"
+ OFString *POSIXPermissions = nil, *ret;
+
+ if (_POSIXPermissions != nil)
+ POSIXPermissions = [OFString stringWithFormat: @"%ho",
+ _POSIXPermissions.unsignedShortValue];
+
+ ret = [OFString stringWithFormat: @"<%@:\n"
@"\tFile name = %@\n"
- @"\tMode = %06o\n"
- @"\tUID = %u\n"
- @"\tGID = %u\n"
- @"\tSize = %" PRIu64 @"\n"
+ @"\tPOSIX permissions = %@\n"
+ @"\tOwner account ID = %@\n"
+ @"\tGroup owner account ID = %@\n"
+ @"\tCompressed size = %llu\n"
+ @"\tUncompressed size = %llu\n"
@"\tModification date = %@\n"
@"\tType = %u\n"
@"\tTarget file name = %@\n"
- @"\tOwner = %@\n"
- @"\tGroup = %@\n"
+ @"\tOwner account name = %@\n"
+ @"\tGroup owner account name = %@\n"
@"\tDevice major = %" PRIu32 @"\n"
@"\tDevice minor = %" PRIu32 @"\n"
@">",
- self.class, _fileName, _mode, _UID, _GID, _size, _modificationDate,
- _type, _targetFileName, _owner, _group, _deviceMajor, _deviceMinor];
+ self.class, _fileName, POSIXPermissions, _ownerAccountID,
+ _groupOwnerAccountID, _compressedSize, _uncompressedSize,
+ _modificationDate, _type, _targetFileName,
+ _ownerAccountName, _groupOwnerAccountName, _deviceMajor,
+ _deviceMinor];
[ret retain];
objc_autoreleasePoolPop(pool);
@@ -290,26 +316,27 @@
}
- (void)of_writeToStream: (OFStream *)stream
encoding: (OFStringEncoding)encoding
{
+ void *pool = objc_autoreleasePoolPush();
unsigned char buffer[512];
unsigned long long modificationDate;
uint16_t checksum = 0;
stringToBuffer(buffer, _fileName, 100, encoding);
stringToBuffer(buffer + 100,
- [OFString stringWithFormat: @"%06" PRIo16 " ", _mode], 8,
- OFStringEncodingASCII);
+ [OFString stringWithFormat: @"%06o ",
+ _POSIXPermissions.unsignedShortValue], 8, OFStringEncodingASCII);
stringToBuffer(buffer + 108,
- [OFString stringWithFormat: @"%06" PRIo16 " ", _UID], 8,
- OFStringEncodingASCII);
+ [OFString stringWithFormat: @"%06o ",
+ _ownerAccountID.unsignedShortValue], 8, OFStringEncodingASCII);
stringToBuffer(buffer + 116,
- [OFString stringWithFormat: @"%06" PRIo16 " ", _GID], 8,
- OFStringEncodingASCII);
+ [OFString stringWithFormat: @"%06o ",
+ _groupOwnerAccountID.unsignedShortValue], 8, OFStringEncodingASCII);
stringToBuffer(buffer + 124,
- [OFString stringWithFormat: @"%011" PRIo64 " ", _size], 12,
+ [OFString stringWithFormat: @"%011llo ", _uncompressedSize], 12,
OFStringEncodingASCII);
modificationDate = _modificationDate.timeIntervalSince1970;
stringToBuffer(buffer + 136,
[OFString stringWithFormat: @"%011llo", modificationDate],
12, OFStringEncodingASCII);
@@ -323,12 +350,12 @@
buffer[156] = _type;
stringToBuffer(buffer + 157, _targetFileName, 100, encoding);
/* ustar */
memcpy(buffer + 257, "ustar\0" "00", 8);
- stringToBuffer(buffer + 265, _owner, 32, encoding);
- stringToBuffer(buffer + 297, _group, 32, encoding);
+ stringToBuffer(buffer + 265, _ownerAccountName, 32, encoding);
+ stringToBuffer(buffer + 297, _groupOwnerAccountName, 32, encoding);
stringToBuffer(buffer + 329,
[OFString stringWithFormat: @"%06" PRIo32 " ", _deviceMajor], 8,
OFStringEncodingASCII);
stringToBuffer(buffer + 337,
[OFString stringWithFormat: @"%06" PRIo32 " ", _deviceMinor], 8,
@@ -341,7 +368,9 @@
stringToBuffer(buffer + 148,
[OFString stringWithFormat: @"%06" PRIo16, checksum], 7,
OFStringEncodingASCII);
[stream writeBuffer: buffer length: sizeof(buffer)];
+
+ objc_autoreleasePoolPop(pool);
}
@end
Index: src/OFThread.h
==================================================================
--- src/OFThread.h
+++ src/OFThread.h
@@ -72,11 +72,11 @@
} _running;
# ifndef OF_OBJFW_RUNTIME
void *_pool;
# endif
# ifdef OF_HAVE_BLOCKS
- OFThreadBlock _Nullable _threadBlock;
+ OFThreadBlock _Nullable _block;
# endif
jmp_buf _exitEnv;
id _returnValue;
bool _supportsSockets;
OFRunLoop *_Nullable _runLoop;
@@ -116,11 +116,11 @@
# ifdef OF_HAVE_BLOCKS
/**
* @brief The block to execute in the thread.
*/
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThreadBlock threadBlock;
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThreadBlock block;
# endif
/**
* @brief The run loop for the thread.
*/
@@ -132,26 +132,36 @@
* @note This has to be set before the thread is started!
*
* This is a value between -1.0 (meaning lowest priority that still schedules)
* and +1.0 (meaning highest priority that still allows getting preempted)
* with normal priority being 0.0 (meaning being the same as the main thread).
+ *
+ * @throw OFThreadStillRunningException The thread is already/still running and
+ * thus the priority cannot be changed
*/
@property (nonatomic) float priority;
/**
* @brief The stack size of the thread.
*
* @note This has to be set before the thread is started!
+ *
+ * @throw OFThreadStillRunningException The thread is already/still running and
+ * thus the stack size cannot be changed
*/
@property (nonatomic) size_t stackSize;
/**
* @brief Whether the thread supports sockets.
*
* Some operating systems such as AmigaOS need special per-thread
* initialization of sockets. If you intend to use sockets in the thread, set
* this property to true before starting the thread.
+ *
+ * @throw OFThreadStillRunningException The thread is already/still running and
+ * thus the sockets support cannot be
+ * enabled/disabled
*/
@property (nonatomic) bool supportsSockets;
/**
* @brief Creates a new thread.
@@ -162,14 +172,14 @@
# ifdef OF_HAVE_BLOCKS
/**
* @brief Creates a new thread with the specified block.
*
- * @param threadBlock A block which is executed by the thread
+ * @param block A block which is executed by the thread
* @return A new, autoreleased thread
*/
-+ (instancetype)threadWithThreadBlock: (OFThreadBlock)threadBlock;
++ (instancetype)threadWithBlock: (OFThreadBlock)block;
# endif
/**
* @brief Returns the current thread.
*
@@ -201,14 +211,17 @@
#endif
#ifdef OF_HAVE_SOCKETS
/**
* @brief Returns the DNS resolver for the current thread.
+ *
+ * Constructs the DNS resolver is there is none yet, unless @ref currentThread
+ * is `nil`, in which case it returns `nil`.
*
* @return The DNS resolver for the current thread
*/
-+ (OFDNSResolver *)DNSResolver;
++ (nullable OFDNSResolver *)DNSResolver;
#endif
/**
* @brief Suspends execution of the current thread for the specified time
* interval.
@@ -238,10 +251,11 @@
/**
* @brief Terminates the current thread, letting it return the specified object.
*
* @param object The object which the terminated thread will return
+ * @throw OFInvalidArgumentException The method was called from the main thread
*/
+ (void)terminateWithObject: (nullable id)object OF_NO_RETURN;
/**
* @brief Sets the name of the current thread.
@@ -262,14 +276,14 @@
# ifdef OF_HAVE_BLOCKS
/**
* @brief Initializes an already allocated thread with the specified block.
*
- * @param threadBlock A block which is executed by the thread
+ * @param block A block which is executed by the thread
* @return An initialized OFThread.
*/
-- (instancetype)initWithThreadBlock: (OFThreadBlock)threadBlock;
+- (instancetype)initWithBlock: (OFThreadBlock)block;
# endif
/**
* @brief The main routine of the thread. You need to reimplement this!
*
@@ -285,20 +299,24 @@
*/
- (void)handleTermination OF_REQUIRES_SUPER;
/**
* @brief Starts the thread.
+ *
+ * @throw OFStartThreadFailedException Starting the thread failed
+ * @throw OFThreadStillRunningException The thread is still running
*/
- (void)start;
/**
* @brief Joins a thread.
*
* @return The object returned by the main method of the thread.
+ * @throw OFJoinThreadFailedException Joining the thread failed
*/
- (id)join;
#else
- (instancetype)init OF_UNAVAILABLE;
#endif
@end
OF_ASSUME_NONNULL_END
Index: src/OFThread.m
==================================================================
--- src/OFThread.m
+++ src/OFThread.m
@@ -73,12 +73,12 @@
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfRangeException.h"
#ifdef OF_HAVE_THREADS
-# import "OFThreadJoinFailedException.h"
-# import "OFThreadStartFailedException.h"
+# import "OFJoinThreadFailedException.h"
+# import "OFStartThreadFailedException.h"
# import "OFThreadStillRunningException.h"
#endif
#ifdef OF_MINT
/* freemint-gcc does not have trunc() */
@@ -131,12 +131,12 @@
* Nasty workaround for thread implementations which can't return a
* pointer on join, or don't have a way to exit a thread.
*/
if (setjmp(thread->_exitEnv) == 0) {
# ifdef OF_HAVE_BLOCKS
- if (thread->_threadBlock != NULL)
- thread->_returnValue = [thread->_threadBlock() retain];
+ if (thread->_block != NULL)
+ thread->_returnValue = [thread->_block() retain];
else
# endif
thread->_returnValue = [[thread main] retain];
}
@@ -158,11 +158,11 @@
[thread release];
}
@synthesize name = _name;
# ifdef OF_HAVE_BLOCKS
-@synthesize threadBlock = _threadBlock;
+@synthesize block = _block;
# endif
+ (void)initialize
{
if (self != [OFThread class])
@@ -177,13 +177,13 @@
{
return [[[self alloc] init] autorelease];
}
# ifdef OF_HAVE_BLOCKS
-+ (instancetype)threadWithThreadBlock: (OFThreadBlock)threadBlock
++ (instancetype)threadWithBlock: (OFThreadBlock)block
{
- return [[[self alloc] initWithThreadBlock: threadBlock] autorelease];
+ return [[[self alloc] initWithBlock: block] autorelease];
}
# endif
+ (OFThread *)currentThread
{
@@ -378,16 +378,16 @@
return self;
}
# ifdef OF_HAVE_BLOCKS
-- (instancetype)initWithThreadBlock: (OFThreadBlock)threadBlock
+- (instancetype)initWithBlock: (OFThreadBlock)block
{
self = [self init];
@try {
- _threadBlock = [threadBlock copy];
+ _block = [block copy];
} @catch (id e) {
[self release];
@throw e;
}
@@ -435,11 +435,11 @@
_running = OFThreadStateRunning;
if ((error = OFPlainThreadNew(&_thread, [_name cStringWithEncoding:
[OFLocale encoding]], callMain, self, &_attr)) != 0) {
[self release];
- @throw [OFThreadStartFailedException
+ @throw [OFStartThreadFailedException
exceptionWithThread: self
errNo: error];
}
}
@@ -446,16 +446,16 @@
- (id)join
{
int error;
if (_running == OFThreadStateNotRunning)
- @throw [OFThreadJoinFailedException
+ @throw [OFJoinThreadFailedException
exceptionWithThread: self
errNo: EINVAL];
if ((error = OFPlainThreadJoin(_thread)) != 0)
- @throw [OFThreadJoinFailedException exceptionWithThread: self
+ @throw [OFJoinThreadFailedException exceptionWithThread: self
errNo: error];
_running = OFThreadStateNotRunning;
return _returnValue;
@@ -541,11 +541,11 @@
if (_running == OFThreadStateWaitingForJoin)
OFPlainThreadDetach(_thread);
[_returnValue release];
# ifdef OF_HAVE_BLOCKS
- [_threadBlock release];
+ [_block release];
# endif
[super dealloc];
}
#else
Index: src/OFUDPSocket.h
==================================================================
--- src/OFUDPSocket.h
+++ src/OFUDPSocket.h
@@ -33,11 +33,11 @@
* @brief A class which provides methods to create and use UDP sockets.
*
* Addresses are of type @ref OFSocketAddress. You can use the current thread's
* @ref OFDNSResolver to create an address for a host / port pair,
* @ref OFSocketAddressString to get the IP address string for an address and
- * @ref OFSocketAddressPort to get the port for an address. If you want to
+ * @ref OFSocketAddressIPPort to get the port for an address. If you want to
* compare two addresses, you can use
* @ref OFSocketAddressEqual and you can use @ref OFSocketAddressHash to get a
* hash to use in e.g. @ref OFMapTable.
*
* @warning Even though the OFCopying protocol is implemented, it does *not*
@@ -70,10 +70,12 @@
* @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for
* IPv6 to bind to all.
* @param port The port to bind to. If the port is 0, an unused port will be
* chosen, which can be obtained using the return value.
* @return The port the socket was bound to
+ * @throw OFBindIPSocketFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already bound
*/
- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port;
@end
OF_ASSUME_NONNULL_END
Index: src/OFUDPSocket.m
==================================================================
--- src/OFUDPSocket.m
+++ src/OFUDPSocket.m
@@ -33,11 +33,11 @@
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFThread.h"
#import "OFAlreadyConnectedException.h"
-#import "OFBindFailedException.h"
+#import "OFBindIPSocketFailedException.h"
@implementation OFUDPSocket
@dynamic delegate;
- (uint16_t)of_bindToAddress: (OFSocketAddress *)address
@@ -50,13 +50,13 @@
#endif
if ((_socket = socket(
((struct sockaddr *)&address->sockaddr)->sa_family,
SOCK_DGRAM | SOCK_CLOEXEC | extraType, 0)) == OFInvalidSocketHandle)
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: OFSocketAddressString(address)
- port: OFSocketAddressPort(address)
+ port: OFSocketAddressIPPort(address)
socket: self
errNo: OFSocketErrNo()];
_canBlock = true;
@@ -66,22 +66,22 @@
fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
}
#endif
#if defined(OF_HPUX) || defined(OF_WII) || defined(OF_NINTENDO_3DS)
- if (OFSocketAddressPort(address) != 0) {
+ if (OFSocketAddressIPPort(address) != 0) {
#endif
if (bind(_socket, (struct sockaddr *)&address->sockaddr,
address->length) != 0) {
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: OFSocketAddressString(address)
- port: OFSocketAddressPort(address)
+ port: OFSocketAddressIPPort(address)
socket: self
errNo: errNo];
}
#if defined(OF_HPUX) || defined(OF_WII) || defined(OF_NINTENDO_3DS)
} else {
@@ -90,26 +90,26 @@
int ret;
while (rnd < 1024)
rnd = (uint16_t)rand();
- OFSocketAddressSetPort(address, rnd);
+ OFSocketAddressSetIPPort(address, rnd);
if ((ret = bind(_socket,
(struct sockaddr *)&address->sockaddr,
address->length)) == 0)
break;
if (OFSocketErrNo() != EADDRINUSE) {
int errNo = OFSocketErrNo();
OFString *host = OFSocketAddressString(address);
- port = OFSocketAddressPort(address);
+ port = OFSocketAddressIPPort(address);
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: host
port: port
socket: self
errNo: errNo];
}
@@ -117,11 +117,11 @@
}
#endif
objc_autoreleasePoolPop(pool);
- if ((port = OFSocketAddressPort(address)) > 0)
+ if ((port = OFSocketAddressIPPort(address)) > 0)
return port;
#if !defined(OF_HPUX) && !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
memset(address, 0, sizeof(*address));
@@ -131,13 +131,13 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: OFSocketAddressString(address)
- port: OFSocketAddressPort(address)
+ port: OFSocketAddressIPPort(address)
socket: self
errNo: errNo];
}
switch (((struct sockaddr *)&address->sockaddr)->sa_family) {
@@ -149,23 +149,23 @@
# endif
default:
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: OFSocketAddressString(address)
- port: OFSocketAddressPort(address)
+ port: OFSocketAddressIPPort(address)
socket: self
errNo: EAFNOSUPPORT];
}
#else
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException
+ @throw [OFBindIPSocketFailedException
exceptionWithHost: OFSocketAddressString(address)
- port: OFSocketAddressPort(address)
+ port: OFSocketAddressIPPort(address)
socket: self
errNo: EADDRNOTAVAIL];
#endif
}
@@ -181,14 +181,14 @@
socketAddresses = [[OFThread DNSResolver]
resolveAddressesForHost: host
addressFamily: OFSocketAddressFamilyAny];
address = *(OFSocketAddress *)[socketAddresses itemAtIndex: 0];
- OFSocketAddressSetPort(&address, port);
+ OFSocketAddressSetIPPort(&address, port);
port = [self of_bindToAddress: &address extraType: 0];
objc_autoreleasePoolPop(pool);
return port;
}
@end
Index: src/OFUNIXDatagramSocket.h
==================================================================
--- src/OFUNIXDatagramSocket.h
+++ src/OFUNIXDatagramSocket.h
@@ -63,10 +63,12 @@
/**
* @brief Bind the socket to the specified path.
*
* @param path The path to bind to
* @return The address on which this socket can be reached
+ * @throw OFBindUNIXSocketFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already bound
*/
- (OFSocketAddress)bindToPath: (OFString *)path;
@end
OF_ASSUME_NONNULL_END
Index: src/OFUNIXDatagramSocket.m
==================================================================
--- src/OFUNIXDatagramSocket.m
+++ src/OFUNIXDatagramSocket.m
@@ -23,11 +23,11 @@
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#import "OFAlreadyConnectedException.h"
-#import "OFBindFailedException.h"
+#import "OFBindUNIXSocketFailedException.h"
@implementation OFUNIXDatagramSocket
@dynamic delegate;
- (OFSocketAddress)bindToPath: (OFString *)path
@@ -42,11 +42,11 @@
address = OFSocketAddressMakeUNIX(path);
if ((_socket = socket(address.sockaddr.un.sun_family,
SOCK_DGRAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
- @throw [OFBindFailedException
+ @throw [OFBindUNIXSocketFailedException
exceptionWithPath: path
socket: self
errNo: OFSocketErrNo()];
_canBlock = true;
@@ -61,13 +61,14 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPath: path
- socket: self
- errNo: errNo];
+ @throw [OFBindUNIXSocketFailedException
+ exceptionWithPath: path
+ socket: self
+ errNo: errNo];
}
return address;
}
@end
Index: src/OFUNIXStreamSocket.h
==================================================================
--- src/OFUNIXStreamSocket.h
+++ src/OFUNIXStreamSocket.h
@@ -52,17 +52,21 @@
/**
* @brief Connects the OFUNIXStreamSocket to the specified destination.
*
* @param path The path to connect to
+ * @throw OFConnectUNIXSocketFailedException Connecting failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
- (void)connectToPath: (OFString *)path;
/**
* @brief Binds the socket to the specified host and port.
*
* @param path The path to bind to
+ * @throw OFBindUNIXSocketFailedException Binding failed
+ * @throw OFAlreadyConnectedException The socket is already connected or bound
*/
- (void)bindToPath: (OFString *)path;
@end
OF_ASSUME_NONNULL_END
Index: src/OFUNIXStreamSocket.m
==================================================================
--- src/OFUNIXStreamSocket.m
+++ src/OFUNIXStreamSocket.m
@@ -23,12 +23,12 @@
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#import "OFAlreadyConnectedException.h"
-#import "OFBindFailedException.h"
-#import "OFConnectionFailedException.h"
+#import "OFBindUNIXSocketFailedException.h"
+#import "OFConnectUNIXSocketFailedException.h"
@implementation OFUNIXStreamSocket
@dynamic delegate;
- (void)connectToPath: (OFString *)path
@@ -43,11 +43,11 @@
address = OFSocketAddressMakeUNIX(path);
if ((_socket = socket(address.sockaddr.un.sun_family,
SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
- @throw [OFConnectionFailedException
+ @throw [OFConnectUNIXSocketFailedException
exceptionWithPath: path
socket: self
errNo: OFSocketErrNo()];
_canBlock = true;
@@ -62,13 +62,14 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFConnectionFailedException exceptionWithPath: path
- socket: self
- errNo: errNo];
+ @throw [OFConnectUNIXSocketFailedException
+ exceptionWithPath: path
+ socket: self
+ errNo: errNo];
}
}
- (void)bindToPath: (OFString *)path
{
@@ -82,11 +83,11 @@
address = OFSocketAddressMakeUNIX(path);
if ((_socket = socket(address.sockaddr.un.sun_family,
SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
- @throw [OFBindFailedException
+ @throw [OFBindUNIXSocketFailedException
exceptionWithPath: path
socket: self
errNo: OFSocketErrNo()];
_canBlock = true;
@@ -101,11 +102,12 @@
int errNo = OFSocketErrNo();
closesocket(_socket);
_socket = OFInvalidSocketHandle;
- @throw [OFBindFailedException exceptionWithPath: path
- socket: self
- errNo: errNo];
+ @throw [OFBindUNIXSocketFailedException
+ exceptionWithPath: path
+ socket: self
+ errNo: errNo];
}
}
@end
ADDED src/OFURI+Private.h
Index: src/OFURI+Private.h
==================================================================
--- src/OFURI+Private.h
+++ src/OFURI+Private.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFURI.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@interface OFURI ()
+- (instancetype)of_init OF_METHOD_FAMILY(init);
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFURI.h
Index: src/OFURI.h
==================================================================
--- src/OFURI.h
+++ src/OFURI.h
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFObject.h"
+#import "OFCharacterSet.h"
+#import "OFSerialization.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFArray OF_GENERIC(ObjectType);
+@class OFDictionary OF_GENERIC(KeyType, ObjectType);
+@class OFNumber;
+@class OFPair OF_GENERIC(FirstType, SecondType);
+@class OFString;
+
+/**
+ * @class OFURI OFURI.h ObjFW/OFURI.h
+ *
+ * @brief A class for parsing URIs as per RFC 3986 and accessing parts of it.
+ */
+@interface OFURI: OFObject
+{
+ OFString *_scheme;
+ OFString *_Nullable _percentEncodedHost;
+ OFNumber *_Nullable _port;
+ OFString *_Nullable _percentEncodedUser;
+ OFString *_Nullable _percentEncodedPassword;
+ OFString *_percentEncodedPath;
+ OFString *_Nullable _percentEncodedQuery;
+ OFString *_Nullable _percentEncodedFragment;
+ OF_RESERVE_IVARS(OFURI, 4)
+}
+
+/**
+ * @brief The scheme part of the URI.
+ */
+@property (readonly, copy, nonatomic) OFString *scheme;
+
+/**
+ * @brief The host part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *host;
+
+/**
+ * @brief The host part of the URI in percent-encoded form.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
+ OFString *percentEncodedHost;
+
+/**
+ * @brief The port part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFNumber *port;
+
+/**
+ * @brief The user part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *user;
+
+/**
+ * @brief The user part of the URI in percent-encoded form.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
+ OFString *percentEncodedUser;
+
+/**
+ * @brief The password part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *password;
+
+/**
+ * @brief The password part of the URI in percent-encoded form.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
+ OFString *percentEncodedPassword;
+
+/**
+ * @brief The path part of the URI.
+ */
+@property (readonly, copy, nonatomic) OFString *path;
+
+/**
+ * @brief The path part of the URI in percent-encoded form.
+ */
+@property (readonly, copy, nonatomic) OFString *percentEncodedPath;
+
+/**
+ * @brief The path of the URI split into components.
+ *
+ * The first component must always be `/` to designate the root.
+ */
+@property (readonly, copy, nonatomic)
+ OFArray OF_GENERIC(OFString *) *pathComponents;
+
+/**
+ * @brief The last path component of the URI.
+ *
+ * Returns the empty string if the path is the root.
+ */
+@property (readonly, copy, nonatomic) OFString *lastPathComponent;
+
+/**
+ * @brief The query part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *query;
+
+/**
+ * @brief The query part of the URI in percent-encoded form.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
+ OFString *percentEncodedQuery;
+
+/**
+ * @brief The query part of the URI as an array.
+ *
+ * For example, a query like `key1=value1&key2=value2` would correspond to the
+ * following array:
+ *
+ * @[
+ * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"],
+ * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"],
+ * ]
+ *
+ * @throw OFInvalidFormatException The query is not in the correct format
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
+ OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems;
+
+/**
+ * @brief The fragment part of the URI.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *fragment;
+
+/**
+ * @brief The fragment part of the URI in URI-encoded form.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
+ OFString *percentEncodedFragment;
+
+/**
+ * @brief The URI as a string.
+ */
+@property (readonly, nonatomic) OFString *string;
+
+/**
+ * @brief The URI with relative subpaths resolved.
+ */
+@property (readonly, nonatomic) OFURI *URIByStandardizingPath;
+
+#ifdef OF_HAVE_FILES
+/**
+ * @brief The local file system representation for a file URI.
+ *
+ * @note This only exists for URIs with the file scheme and throws an exception
+ * otherwise.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
+ OFString *fileSystemRepresentation;
+#endif
+
+/**
+ * @brief Creates a new URI with the specified string.
+ *
+ * @param string A string describing a URI
+ * @return A new, autoreleased OFURI
+ * @throw OFInvalidFormatException The specified string is not a valid URI
+ * string
+ */
++ (instancetype)URIWithString: (OFString *)string;
+
+/**
+ * @brief Creates a new URI with the specified string relative to the
+ * specified URI.
+ *
+ * @param string A string describing a relative or absolute URI
+ * @param URI An URI to which the string is relative
+ * @return A new, autoreleased OFURI
+ * @throw OFInvalidFormatException The specified string is not a valid URI
+ * string
+ */
++ (instancetype)URIWithString: (OFString *)string relativeToURI: (OFURI *)URI;
+
+#ifdef OF_HAVE_FILES
+/**
+ * @brief Creates a new URI with the specified local file path.
+ *
+ * If a directory exists at the specified path, a slash is appended if there is
+ * no slash yet.
+ *
+ * @param path The local file path
+ * @return A new, autoreleased OFURI
+ * @throw OFInvalidFormatException The specified path is not a valid path
+ */
++ (instancetype)fileURIWithPath: (OFString *)path;
+
+/**
+ * @brief Creates a new URI with the specified local file path.
+ *
+ * @param path The local file path
+ * @param isDirectory Whether the path is a directory, in which case a slash is
+ * appened if there is no slash yet
+ * @return An initialized OFURI
+ */
++ (instancetype)fileURIWithPath: (OFString *)path
+ isDirectory: (bool)isDirectory;
+#endif
+
+/**
+ * @brief Initializes an already allocated OFURI with the specified string.
+ *
+ * @param string A string describing a URI
+ * @return An initialized OFURI
+ * @throw OFInvalidFormatException The specified string is not a valid URI
+ * string
+ */
+- (instancetype)initWithString: (OFString *)string;
+
+/**
+ * @brief Initializes an already allocated OFURI with the specified string and
+ * relative URI.
+ *
+ * @param string A string describing a relative or absolute URI
+ * @param URI A URI to which the string is relative
+ * @return An initialized OFURI
+ * @throw OFInvalidFormatException The specified string is not a valid URI
+ * string
+ */
+- (instancetype)initWithString: (OFString *)string relativeToURI: (OFURI *)URI;
+
+#ifdef OF_HAVE_FILES
+/**
+ * @brief Initializes an already allocated OFURI with the specified local file
+ * path.
+ *
+ * If a directory exists at the specified path, a slash is appended if there is
+ * no slash yet.
+ *
+ * @param path The local file path
+ * @return An initialized OFURI
+ * @throw OFInvalidFormatException The specified path is not a valid path
+ */
+- (instancetype)initFileURIWithPath: (OFString *)path;
+
+/**
+ * @brief Initializes an already allocated OFURI with the specified local file
+ * path.
+ *
+ * @param path The local file path
+ * @param isDirectory Whether the path is a directory, in which case a slash is
+ * appened if there is no slash yet
+ * @return An initialized OFURI
+ */
+- (instancetype)initFileURIWithPath: (OFString *)path
+ isDirectory: (bool)isDirectory;
+#endif
+
+- (instancetype)init OF_UNAVAILABLE;
+
+/**
+ * @brief Returns a new URI with the specified path component appended.
+ *
+ * If the URI is a file URI, the file system is queried whether the appended
+ * component is a directory.
+ *
+ * @param component The path component to append. If it starts with the slash,
+ * the component is not appended, but replaces the path
+ * instead.
+ * @return A new URI with the specified path component appended
+ */
+- (OFURI *)URIByAppendingPathComponent: (OFString *)component;
+
+/**
+ * @brief Returns a new URI with the specified path component appended.
+ *
+ * @param component The path component to append. If it starts with the slash,
+ * the component is not appended, but replaces the path
+ * instead.
+ * @param isDirectory Whether the appended component is a directory, meaning
+ * that the URI path should have a trailing slash
+ * @return A new URI with the specified path component appended
+ */
+- (OFURI *)URIByAppendingPathComponent: (OFString *)component
+ isDirectory: (bool)isDirectory;
+@end
+
+@interface OFCharacterSet (URICharacterSets)
+#ifdef OF_HAVE_CLASS_PROPERTIES
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URISchemeAllowedCharacterSet;
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URIHostAllowedCharacterSet;
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URIUserAllowedCharacterSet;
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URIPasswordAllowedCharacterSet;
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URIPathAllowedCharacterSet;
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URIQueryAllowedCharacterSet;
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URIQueryKeyValueAllowedCharacterSet;
+@property (class, readonly, nonatomic)
+ OFCharacterSet *URIFragmentAllowedCharacterSet;
+#endif
+
+/**
+ * @brief Returns the characters allowed in the scheme part of a URI.
+ *
+ * @return The characters allowed in the scheme part of a URI.
+ */
++ (OFCharacterSet *)URISchemeAllowedCharacterSet;
+
+/**
+ * @brief Returns the characters allowed in the host part of a URI.
+ *
+ * @return The characters allowed in the host part of a URI.
+ */
++ (OFCharacterSet *)URIHostAllowedCharacterSet;
+
+/**
+ * @brief Returns the characters allowed in the user part of a URI.
+ *
+ * @return The characters allowed in the user part of a URI.
+ */
++ (OFCharacterSet *)URIUserAllowedCharacterSet;
+
+/**
+ * @brief Returns the characters allowed in the password part of a URI.
+ *
+ * @return The characters allowed in the password part of a URI.
+ */
++ (OFCharacterSet *)URIPasswordAllowedCharacterSet;
+
+/**
+ * @brief Returns the characters allowed in the path part of a URI.
+ *
+ * @return The characters allowed in the path part of a URI.
+ */
++ (OFCharacterSet *)URIPathAllowedCharacterSet;
+
+/**
+ * @brief Returns the characters allowed in the query part of a URI.
+ *
+ * @return The characters allowed in the query part of a URI.
+ */
++ (OFCharacterSet *)URIQueryAllowedCharacterSet;
+
+/**
+ * @brief Returns the characters allowed in a key/value in the query part of a
+ * URI.
+ *
+ * @return The characters allowed in a key/value in the query part of a URI.
+ */
++ (OFCharacterSet *)URIQueryKeyValueAllowedCharacterSet;
+
+/**
+ * @brief Returns the characters allowed in the fragment part of a URI.
+ *
+ * @return The characters allowed in the fragment part of a URI.
+ */
++ (OFCharacterSet *)URIFragmentAllowedCharacterSet;
+@end
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern bool OFURIIsIPv6Host(OFString *host);
+extern void OFURIVerifyIsEscaped(OFString *, OFCharacterSet *, bool);
+#ifdef __cplusplus
+}
+#endif
+
+OF_ASSUME_NONNULL_END
+
+#import "OFMutableURI.h"
ADDED src/OFURI.m
Index: src/OFURI.m
==================================================================
--- src/OFURI.m
+++ src/OFURI.m
@@ -0,0 +1,1277 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#import "OFURI.h"
+#import "OFArray.h"
+#import "OFDictionary.h"
+#ifdef OF_HAVE_FILES
+# import "OFFileManager.h"
+# import "OFFileURIHandler.h"
+#endif
+#import "OFNumber.h"
+#import "OFOnce.h"
+#import "OFPair.h"
+#import "OFString.h"
+#import "OFXMLElement.h"
+
+#import "OFInvalidArgumentException.h"
+#import "OFInvalidFormatException.h"
+#import "OFOutOfMemoryException.h"
+
+@interface OFURIAllowedCharacterSetBase: OFCharacterSet
+@end
+
+@interface OFURIAllowedCharacterSet: OFURIAllowedCharacterSetBase
+@end
+
+@interface OFURISchemeAllowedCharacterSet: OFURIAllowedCharacterSetBase
+@end
+
+@interface OFURIPathAllowedCharacterSet: OFURIAllowedCharacterSetBase
+@end
+
+@interface OFURIQueryOrFragmentAllowedCharacterSet: OFURIAllowedCharacterSetBase
+@end
+
+@interface OFURIQueryKeyValueAllowedCharacterSet: OFURIAllowedCharacterSetBase
+@end
+
+OF_DIRECT_MEMBERS
+@interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet
+{
+ OFCharacterSet *_characterSet;
+ bool (*_characterIsMember)(id, SEL, OFUnichar);
+}
+
+- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
+@end
+
+static OFCharacterSet *URIAllowedCharacterSet = nil;
+static OFCharacterSet *URISchemeAllowedCharacterSet = nil;
+static OFCharacterSet *URIPathAllowedCharacterSet = nil;
+static OFCharacterSet *URIQueryOrFragmentAllowedCharacterSet = nil;
+static OFCharacterSet *URIQueryKeyValueAllowedCharacterSet = nil;
+
+static OFOnceControl URIAllowedCharacterSetOnce = OFOnceControlInitValue;
+static OFOnceControl URIQueryOrFragmentAllowedCharacterSetOnce =
+ OFOnceControlInitValue;
+
+static void
+initURIAllowedCharacterSet(void)
+{
+ URIAllowedCharacterSet = [[OFURIAllowedCharacterSet alloc] init];
+}
+
+static void
+initURISchemeAllowedCharacterSet(void)
+{
+ URISchemeAllowedCharacterSet =
+ [[OFURISchemeAllowedCharacterSet alloc] init];
+}
+
+static void
+initURIPathAllowedCharacterSet(void)
+{
+ URIPathAllowedCharacterSet =
+ [[OFURIPathAllowedCharacterSet alloc] init];
+}
+
+static void
+initURIQueryOrFragmentAllowedCharacterSet(void)
+{
+ URIQueryOrFragmentAllowedCharacterSet =
+ [[OFURIQueryOrFragmentAllowedCharacterSet alloc] init];
+}
+
+static void
+initURIQueryKeyValueAllowedCharacterSet(void)
+{
+ URIQueryKeyValueAllowedCharacterSet =
+ [[OFURIQueryKeyValueAllowedCharacterSet alloc] init];
+}
+
+bool
+OFURIIsIPv6Host(OFString *host)
+{
+ const char *UTF8String = host.UTF8String;
+ bool hasColon = false;
+
+ while (*UTF8String != '\0') {
+ if (!OFASCIIIsDigit(*UTF8String) && *UTF8String != ':' &&
+ (*UTF8String < 'a' || *UTF8String > 'f') &&
+ (*UTF8String < 'A' || *UTF8String > 'F'))
+ return false;
+
+ if (*UTF8String == ':')
+ hasColon = true;
+
+ UTF8String++;
+ }
+
+ return hasColon;
+}
+
+@implementation OFURIAllowedCharacterSetBase
+- (instancetype)autorelease
+{
+ return self;
+}
+
+- (instancetype)retain
+{
+ return self;
+}
+
+- (void)release
+{
+}
+
+- (unsigned int)retainCount
+{
+ return OFMaxRetainCount;
+}
+@end
+
+@implementation OFURIAllowedCharacterSet
+- (bool)characterIsMember: (OFUnichar)character
+{
+ if (character < CHAR_MAX && OFASCIIIsAlnum(character))
+ return true;
+
+ switch (character) {
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ return true;
+ default:
+ return false;
+ }
+}
+@end
+
+@implementation OFURISchemeAllowedCharacterSet
+- (bool)characterIsMember: (OFUnichar)character
+{
+ if (character < CHAR_MAX && OFASCIIIsAlnum(character))
+ return true;
+
+ switch (character) {
+ case '+':
+ case '-':
+ case '.':
+ return true;
+ default:
+ return false;
+ }
+}
+@end
+
+@implementation OFURIPathAllowedCharacterSet
+- (bool)characterIsMember: (OFUnichar)character
+{
+ if (character < CHAR_MAX && OFASCIIIsAlnum(character))
+ return true;
+
+ switch (character) {
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ case ':':
+ case '@':
+ case '/':
+ return true;
+ default:
+ return false;
+ }
+}
+@end
+
+@implementation OFURIQueryOrFragmentAllowedCharacterSet
+- (bool)characterIsMember: (OFUnichar)character
+{
+ if (character < CHAR_MAX && OFASCIIIsAlnum(character))
+ return true;
+
+ switch (character) {
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ case ':':
+ case '@':
+ case '/':
+ case '?':
+ return true;
+ default:
+ return false;
+ }
+}
+@end
+
+@implementation OFURIQueryKeyValueAllowedCharacterSet
+- (bool)characterIsMember: (OFUnichar)character
+{
+ if (character < CHAR_MAX && OFASCIIIsAlnum(character))
+ return true;
+
+ switch (character) {
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '!':
+ case '$':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case ':':
+ case '@':
+ case '/':
+ case '?':
+ return true;
+ default:
+ return false;
+ }
+}
+@end
+
+@implementation OFInvertedCharacterSetWithoutPercent
+- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
+{
+ self = [super init];
+
+ @try {
+ _characterSet = [characterSet retain];
+ _characterIsMember = (bool (*)(id, SEL, OFUnichar))
+ [_characterSet methodForSelector:
+ @selector(characterIsMember:)];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_characterSet release];
+
+ [super dealloc];
+}
+
+- (bool)characterIsMember: (OFUnichar)character
+{
+ return (character != '%' && !_characterIsMember(_characterSet,
+ @selector(characterIsMember:), character));
+}
+@end
+
+void
+OFURIVerifyIsEscaped(OFString *string, OFCharacterSet *characterSet,
+ bool allowPercent)
+{
+ void *pool = objc_autoreleasePoolPush();
+
+ if (allowPercent)
+ characterSet = [[[OFInvertedCharacterSetWithoutPercent alloc]
+ initWithCharacterSet: characterSet] autorelease];
+ else
+ characterSet = characterSet.invertedSet;
+
+ if ([string indexOfCharacterFromSet: characterSet] != OFNotFound)
+ @throw [OFInvalidFormatException exception];
+
+ objc_autoreleasePoolPop(pool);
+}
+
+@implementation OFCharacterSet (URICharacterSets)
++ (OFCharacterSet *)URISchemeAllowedCharacterSet
+{
+ static OFOnceControl onceControl = OFOnceControlInitValue;
+ OFOnce(&onceControl, initURISchemeAllowedCharacterSet);
+
+ return URISchemeAllowedCharacterSet;
+}
+
++ (OFCharacterSet *)URIHostAllowedCharacterSet
+{
+ OFOnce(&URIAllowedCharacterSetOnce, initURIAllowedCharacterSet);
+
+ return URIAllowedCharacterSet;
+}
+
++ (OFCharacterSet *)URIUserAllowedCharacterSet
+{
+ OFOnce(&URIAllowedCharacterSetOnce, initURIAllowedCharacterSet);
+
+ return URIAllowedCharacterSet;
+}
+
++ (OFCharacterSet *)URIPasswordAllowedCharacterSet
+{
+ OFOnce(&URIAllowedCharacterSetOnce, initURIAllowedCharacterSet);
+
+ return URIAllowedCharacterSet;
+}
+
++ (OFCharacterSet *)URIPathAllowedCharacterSet
+{
+ static OFOnceControl onceControl = OFOnceControlInitValue;
+ OFOnce(&onceControl, initURIPathAllowedCharacterSet);
+
+ return URIPathAllowedCharacterSet;
+}
+
++ (OFCharacterSet *)URIQueryAllowedCharacterSet
+{
+ OFOnce(&URIQueryOrFragmentAllowedCharacterSetOnce,
+ initURIQueryOrFragmentAllowedCharacterSet);
+
+ return URIQueryOrFragmentAllowedCharacterSet;
+}
+
++ (OFCharacterSet *)URIQueryKeyValueAllowedCharacterSet
+{
+ static OFOnceControl onceControl = OFOnceControlInitValue;
+ OFOnce(&onceControl, initURIQueryKeyValueAllowedCharacterSet);
+
+ return URIQueryKeyValueAllowedCharacterSet;
+}
+
++ (OFCharacterSet *)URIFragmentAllowedCharacterSet
+{
+ OFOnce(&URIQueryOrFragmentAllowedCharacterSetOnce,
+ initURIQueryOrFragmentAllowedCharacterSet);
+
+ return URIQueryOrFragmentAllowedCharacterSet;
+}
+@end
+
+@implementation OFURI
++ (instancetype)URI
+{
+ return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)URIWithString: (OFString *)string
+{
+ return [[[self alloc] initWithString: string] autorelease];
+}
+
++ (instancetype)URIWithString: (OFString *)string
+ relativeToURI: (OFURI *)URI
+{
+ return [[[self alloc] initWithString: string
+ relativeToURI: URI] autorelease];
+}
+
+#ifdef OF_HAVE_FILES
++ (instancetype)fileURIWithPath: (OFString *)path
+{
+ return [[[self alloc] initFileURIWithPath: path] autorelease];
+}
+
++ (instancetype)fileURIWithPath: (OFString *)path
+ isDirectory: (bool)isDirectory
+{
+ return [[[self alloc] initFileURIWithPath: path
+ isDirectory: isDirectory] autorelease];
+}
+#endif
+
+static void
+parseUserInfo(OFURI *self, const char *UTF8String, size_t length)
+{
+ const char *colon;
+
+ if ((colon = memchr(UTF8String, ':', length)) != NULL) {
+ self->_percentEncodedUser = [[OFString alloc]
+ initWithUTF8String: UTF8String
+ length: colon - UTF8String];
+ self->_percentEncodedPassword = [[OFString alloc]
+ initWithUTF8String: colon + 1
+ length: length - (colon - UTF8String) - 1];
+
+ OFURIVerifyIsEscaped(self->_percentEncodedPassword,
+ [OFCharacterSet URIPasswordAllowedCharacterSet], true);
+ } else
+ self->_percentEncodedUser = [[OFString alloc]
+ initWithUTF8String: UTF8String
+ length: length];
+
+ OFURIVerifyIsEscaped(self->_percentEncodedUser,
+ [OFCharacterSet URIUserAllowedCharacterSet], true);
+}
+
+static void
+parseHostPort(OFURI *self, const char *UTF8String, size_t length)
+{
+ OFString *portString;
+
+ if (*UTF8String == '[') {
+ const char *end = memchr(UTF8String, ']', length);
+
+ if (end == NULL)
+ @throw [OFInvalidFormatException exception];
+
+ for (const char *iter = UTF8String + 1; iter < end; iter++)
+ if (!OFASCIIIsDigit(*iter) && *iter != ':' &&
+ (*iter < 'a' || *iter > 'f') &&
+ (*iter < 'A' || *iter > 'F'))
+ @throw [OFInvalidFormatException exception];
+
+ self->_percentEncodedHost = [[OFString alloc]
+ initWithUTF8String: UTF8String
+ length: end - UTF8String + 1];
+
+ length -= (end - UTF8String) + 1;
+ UTF8String = end + 1;
+ } else {
+ const char *colon = memchr(UTF8String, ':', length);
+
+ if (colon != NULL) {
+ self->_percentEncodedHost = [[OFString alloc]
+ initWithUTF8String: UTF8String
+ length: colon - UTF8String];
+
+ length -= colon - UTF8String;
+ UTF8String = colon;
+ } else {
+ self->_percentEncodedHost = [[OFString alloc]
+ initWithUTF8String: UTF8String
+ length: length];
+
+ UTF8String += length;
+ length = 0;
+ }
+
+ OFURIVerifyIsEscaped(self->_percentEncodedHost,
+ [OFCharacterSet URIHostAllowedCharacterSet], true);
+ }
+
+ if (length == 0)
+ return;
+
+ if (length <= 1 || *UTF8String != ':')
+ @throw [OFInvalidFormatException exception];
+
+ UTF8String++;
+ length--;
+
+ for (size_t i = 0; i < length; i++)
+ if (!OFASCIIIsDigit(UTF8String[i]))
+ @throw [OFInvalidFormatException exception];
+
+ portString = [OFString stringWithUTF8String: UTF8String length: length];
+
+ if (portString.unsignedLongLongValue > 65535)
+ @throw [OFInvalidFormatException exception];
+
+ self->_port = [[OFNumber alloc] initWithUnsignedShort:
+ (unsigned short)portString.unsignedLongLongValue];
+}
+
+static size_t
+parseAuthority(OFURI *self, const char *UTF8String, size_t length)
+{
+ size_t ret;
+ const char *slash, *at;
+
+ if ((slash = memchr(UTF8String, '/', length)) != NULL)
+ length = slash - UTF8String;
+
+ ret = length;
+
+ if ((at = memchr(UTF8String, '@', length)) != NULL) {
+ parseUserInfo(self, UTF8String, at - UTF8String);
+
+ length -= at - UTF8String + 1;
+ UTF8String = at + 1;
+ }
+
+ parseHostPort(self, UTF8String, length);
+
+ return ret;
+}
+
+static void
+parsePathQueryFragment(const char *UTF8String, size_t length,
+ OFString **pathString, OFString **queryString, OFString **fragmentString)
+{
+ const char *fragment, *query;
+
+ if ((fragment = memchr(UTF8String, '#', length)) != NULL) {
+ *fragmentString = [OFString
+ stringWithUTF8String: fragment + 1
+ length: length - (fragment - UTF8String) - 1];
+
+ OFURIVerifyIsEscaped(*fragmentString,
+ [OFCharacterSet URIQueryAllowedCharacterSet], true);
+
+ length = fragment - UTF8String;
+ }
+
+ if ((query = memchr(UTF8String, '?', length)) != NULL) {
+ *queryString = [OFString
+ stringWithUTF8String: query + 1
+ length: length - (query - UTF8String) - 1];
+
+ OFURIVerifyIsEscaped(*queryString,
+ [OFCharacterSet URIFragmentAllowedCharacterSet], true);
+
+ length = query - UTF8String;
+ }
+
+ *pathString = [OFString stringWithUTF8String: UTF8String
+ length: length];
+
+ OFURIVerifyIsEscaped(*pathString,
+ [OFCharacterSet URIQueryAllowedCharacterSet], true);
+}
+
+- (instancetype)initWithString: (OFString *)string
+{
+ self = [super init];
+
+ @try {
+ void *pool = objc_autoreleasePoolPush();
+ const char *UTF8String = string.UTF8String;
+ size_t length = string.UTF8StringLength;
+ const char *colon;
+ OFString *path, *query = nil, *fragment = nil;
+
+ if ((colon = strchr(UTF8String, ':')) == NULL ||
+ colon - UTF8String < 1 || !OFASCIIIsAlpha(UTF8String[0]))
+ @throw [OFInvalidFormatException exception];
+
+ _scheme = [[[OFString stringWithUTF8String: UTF8String
+ length: colon - UTF8String]
+ lowercaseString] copy];
+
+ OFURIVerifyIsEscaped(_scheme,
+ [OFCharacterSet URISchemeAllowedCharacterSet], false);
+
+ length -= colon - UTF8String + 1;
+ UTF8String = colon + 1;
+
+ if (length >= 2 && UTF8String[0] == '/' &&
+ UTF8String[1] == '/') {
+ size_t authorityLength;
+
+ UTF8String += 2;
+ length -= 2;
+
+ authorityLength = parseAuthority(self,
+ UTF8String, length);
+
+ UTF8String += authorityLength;
+ length -= authorityLength;
+
+ if (length > 0)
+ OFEnsure(UTF8String[0] == '/');
+ }
+
+ parsePathQueryFragment(UTF8String, length,
+ &path, &query, &fragment);
+ _percentEncodedPath = [path copy];
+ _percentEncodedQuery = [query copy];
+ _percentEncodedFragment = [fragment copy];
+
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+static bool
+isAbsolute(OFString *string)
+{
+ void *pool = objc_autoreleasePoolPush();
+
+ @try {
+ const char *UTF8String = string.UTF8String;
+ size_t length = string.UTF8StringLength;
+
+ if (length < 1)
+ return false;
+
+ if (!OFASCIIIsAlpha(UTF8String[0]))
+ return false;
+
+ for (size_t i = 1; i < length; i++) {
+ if (UTF8String[i] == ':')
+ return true;
+
+ if (!OFASCIIIsAlnum(UTF8String[i]) &&
+ UTF8String[i] != '+' && UTF8String[i] != '-' &&
+ UTF8String[i] != '.')
+ return false;
+ }
+ } @finally {
+ objc_autoreleasePoolPop(pool);
+ }
+
+ return false;
+}
+
+static OFString *
+merge(OFString *base, OFString *path)
+{
+ OFMutableArray *components;
+
+ if (base.length == 0)
+ base = @"/";
+
+ components = [[[base componentsSeparatedByString: @"/"]
+ mutableCopy] autorelease];
+
+ if (components.count == 1)
+ [components addObject: path];
+ else
+ [components replaceObjectAtIndex: components.count - 1
+ withObject: path];
+
+ return [components componentsJoinedByString: @"/"];
+}
+
+- (instancetype)initWithString: (OFString *)string relativeToURI: (OFURI *)URI
+{
+ bool absolute;
+
+ @try {
+ absolute = isAbsolute(string);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ if (absolute)
+ return [self initWithString: string];
+
+ self = [super init];
+
+ @try {
+ void *pool = objc_autoreleasePoolPush();
+ const char *UTF8String = string.UTF8String;
+ size_t length = string.UTF8StringLength;
+ bool hasAuthority = false;
+ OFString *path, *query = nil, *fragment = nil;
+
+ _scheme = [URI->_scheme copy];
+
+ if (length >= 2 && UTF8String[0] == '/' &&
+ UTF8String[1] == '/') {
+ size_t authorityLength;
+
+ hasAuthority = true;
+
+ UTF8String += 2;
+ length -= 2;
+
+ authorityLength = parseAuthority(self,
+ UTF8String, length);
+
+ UTF8String += authorityLength;
+ length -= authorityLength;
+
+ if (length > 0)
+ OFEnsure(UTF8String[0] == '/');
+ } else {
+ _percentEncodedHost = [URI->_percentEncodedHost copy];
+ _port = [URI->_port copy];
+ _percentEncodedUser = [URI->_percentEncodedUser copy];
+ _percentEncodedPassword =
+ [URI->_percentEncodedPassword copy];
+ }
+
+ parsePathQueryFragment(UTF8String, length,
+ &path, &query, &fragment);
+ _percentEncodedFragment = [fragment copy];
+
+ if (hasAuthority) {
+ _percentEncodedPath = [path copy];
+ _percentEncodedQuery = [query copy];
+ } else {
+ if (path.length == 0) {
+ _percentEncodedPath =
+ [URI->_percentEncodedPath copy];
+ _percentEncodedQuery = (query != nil
+ ? [query copy]
+ : [URI->_percentEncodedQuery copy]);
+ } else {
+ if ([path hasPrefix: @"/"])
+ _percentEncodedPath = [path copy];
+ else
+ _percentEncodedPath = [merge(
+ URI->_percentEncodedPath, path)
+ copy];
+
+ _percentEncodedQuery = [query copy];
+ }
+ }
+
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+#ifdef OF_HAVE_FILES
+- (instancetype)initFileURIWithPath: (OFString *)path
+{
+ bool isDirectory;
+
+ @try {
+ void *pool = objc_autoreleasePoolPush();
+ isDirectory = [path of_isDirectoryPath];
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ self = [self initFileURIWithPath: path isDirectory: isDirectory];
+
+ return self;
+}
+
+- (instancetype)initFileURIWithPath: (OFString *)path
+ isDirectory: (bool)isDirectory
+{
+ self = [super init];
+
+ @try {
+ void *pool = objc_autoreleasePoolPush();
+ OFString *percentEncodedHost = nil;
+
+ if (!path.absolutePath) {
+ OFString *currentDirectoryPath = [OFFileManager
+ defaultManager].currentDirectoryPath;
+
+ path = [currentDirectoryPath
+ stringByAppendingPathComponent: path];
+ path = path.stringByStandardizingPath;
+ }
+
+ path = [path of_pathToURIPathWithPercentEncodedHost:
+ &percentEncodedHost];
+ _percentEncodedHost = [percentEncodedHost copy];
+
+ if (isDirectory && ![path hasSuffix: @"/"])
+ path = [path stringByAppendingString: @"/"];
+
+ _scheme = @"file";
+ _percentEncodedPath = [[path
+ stringByAddingPercentEncodingWithAllowedCharacters:
+ [OFCharacterSet URIPathAllowedCharacterSet]] copy];
+
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+#endif
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)of_init
+{
+ return [super init];
+}
+
+- (instancetype)initWithSerialization: (OFXMLElement *)element
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *stringValue;
+
+ @try {
+ if (![element.name isEqual: self.className] ||
+ ![element.namespace isEqual: OFSerializationNS])
+ @throw [OFInvalidArgumentException exception];
+
+ stringValue = element.stringValue;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ self = [self initWithString: stringValue];
+
+ objc_autoreleasePoolPop(pool);
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_scheme release];
+ [_percentEncodedHost release];
+ [_port release];
+ [_percentEncodedUser release];
+ [_percentEncodedPassword release];
+ [_percentEncodedPath release];
+ [_percentEncodedQuery release];
+ [_percentEncodedFragment release];
+
+ [super dealloc];
+}
+
+- (bool)isEqual: (id)object
+{
+ OFURI *URI;
+
+ if (object == self)
+ return true;
+
+ if (![object isKindOfClass: [OFURI class]])
+ return false;
+
+ URI = object;
+
+ if (![URI->_scheme isEqual: _scheme])
+ return false;
+ if (URI->_percentEncodedHost != _percentEncodedHost &&
+ ![URI->_percentEncodedHost isEqual: _percentEncodedHost])
+ return false;
+ if (URI->_port != _port && ![URI->_port isEqual: _port])
+ return false;
+ if (URI->_percentEncodedUser != _percentEncodedUser &&
+ ![URI->_percentEncodedUser isEqual: _percentEncodedUser])
+ return false;
+ if (URI->_percentEncodedPassword != _percentEncodedPassword &&
+ ![URI->_percentEncodedPassword isEqual: _percentEncodedPassword])
+ return false;
+ if (![URI->_percentEncodedPath isEqual: _percentEncodedPath])
+ return false;
+ if (URI->_percentEncodedQuery != _percentEncodedQuery &&
+ ![URI->_percentEncodedQuery isEqual: _percentEncodedQuery])
+ return false;
+ if (URI->_percentEncodedFragment != _percentEncodedFragment &&
+ ![URI->_percentEncodedFragment isEqual: _percentEncodedFragment])
+ return false;
+
+ return true;
+}
+
+- (unsigned long)hash
+{
+ unsigned long hash;
+
+ OFHashInit(&hash);
+
+ OFHashAddHash(&hash, _scheme.hash);
+ OFHashAddHash(&hash, _percentEncodedHost.hash);
+ OFHashAddHash(&hash, _port.hash);
+ OFHashAddHash(&hash, _percentEncodedUser.hash);
+ OFHashAddHash(&hash, _percentEncodedPassword.hash);
+ OFHashAddHash(&hash, _percentEncodedPath.hash);
+ OFHashAddHash(&hash, _percentEncodedQuery.hash);
+ OFHashAddHash(&hash, _percentEncodedFragment.hash);
+
+ OFHashFinalize(&hash);
+
+ return hash;
+}
+
+- (OFString *)scheme
+{
+ return _scheme;
+}
+
+- (OFString *)host
+{
+ if ([_percentEncodedHost hasPrefix: @"["] &&
+ [_percentEncodedHost hasSuffix: @"]"]) {
+ OFString *host = [_percentEncodedHost substringWithRange:
+ OFMakeRange(1, _percentEncodedHost.length - 2)];
+
+ if (!OFURIIsIPv6Host(host))
+ @throw [OFInvalidArgumentException exception];
+
+ return host;
+ }
+
+ return _percentEncodedHost.stringByRemovingPercentEncoding;
+}
+
+- (OFString *)percentEncodedHost
+{
+ return _percentEncodedHost;
+}
+
+- (OFNumber *)port
+{
+ return _port;
+}
+
+- (OFString *)user
+{
+ return _percentEncodedUser.stringByRemovingPercentEncoding;
+}
+
+- (OFString *)percentEncodedUser
+{
+ return _percentEncodedUser;
+}
+
+- (OFString *)password
+{
+ return _percentEncodedPassword.stringByRemovingPercentEncoding;
+}
+
+- (OFString *)percentEncodedPassword
+{
+ return _percentEncodedPassword;
+}
+
+- (OFString *)path
+{
+ return _percentEncodedPath.stringByRemovingPercentEncoding;
+}
+
+- (OFString *)percentEncodedPath
+{
+ return _percentEncodedPath;
+}
+
+- (OFArray *)pathComponents
+{
+ void *pool = objc_autoreleasePoolPush();
+#ifdef OF_HAVE_FILES
+ bool isFile = [_scheme isEqual: @"file"];
+#endif
+ OFMutableArray *ret;
+ size_t count;
+
+#ifdef OF_HAVE_FILES
+ if (isFile) {
+ OFString *path = [_percentEncodedPath
+ of_URIPathToPathWithPercentEncodedHost: nil];
+ ret = [[path.pathComponents mutableCopy] autorelease];
+
+ if (![ret.firstObject isEqual: @"/"])
+ [ret insertObject: @"/" atIndex: 0];
+ } else
+#endif
+ ret = [[[_percentEncodedPath componentsSeparatedByString: @"/"]
+ mutableCopy] autorelease];
+
+ count = ret.count;
+
+ if (count > 0 && [ret.firstObject length] == 0)
+ [ret replaceObjectAtIndex: 0 withObject: @"/"];
+
+ for (size_t i = 0; i < count; i++) {
+ OFString *component = [ret objectAtIndex: i];
+
+#ifdef OF_HAVE_FILES
+ if (isFile)
+ component =
+ [component of_pathComponentToURIPathComponent];
+#endif
+
+ component = component.stringByRemovingPercentEncoding;
+ [ret replaceObjectAtIndex: i withObject: component];
+ }
+
+ [ret makeImmutable];
+ [ret retain];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [ret autorelease];
+}
+
+- (OFString *)lastPathComponent
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path = _percentEncodedPath;
+ const char *UTF8String, *lastComponent;
+ size_t length;
+ OFString *ret;
+
+ if ([path isEqual: @"/"]) {
+ objc_autoreleasePoolPop(pool);
+ return @"/";
+ }
+
+ if ([path hasSuffix: @"/"])
+ path = [path substringToIndex: path.length - 1];
+
+ UTF8String = lastComponent = path.UTF8String;
+ length = path.UTF8StringLength;
+
+ for (size_t i = 1; i <= length; i++) {
+ if (UTF8String[length - i] == '/') {
+ lastComponent = UTF8String + (length - i) + 1;
+ break;
+ }
+ }
+
+ ret = [OFString
+ stringWithUTF8String: lastComponent
+ length: length - (lastComponent - UTF8String)];
+ ret = [ret.stringByRemovingPercentEncoding retain];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [ret autorelease];
+}
+
+- (OFString *)query
+{
+ return _percentEncodedQuery.stringByRemovingPercentEncoding;
+}
+
+- (OFString *)percentEncodedQuery
+{
+ return _percentEncodedQuery;
+}
+
+- (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *)queryItems
+{
+ void *pool;
+ OFArray OF_GENERIC(OFString *) *pairs;
+ OFMutableArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *)
+ *ret;
+
+ if (_percentEncodedQuery == nil)
+ return nil;
+
+ pool = objc_autoreleasePoolPush();
+ pairs = [_percentEncodedQuery componentsSeparatedByString: @"&"];
+ ret = [OFMutableArray arrayWithCapacity: pairs.count];
+
+ for (OFString *pair in pairs) {
+ OFArray *parts = [pair componentsSeparatedByString: @"="];
+ OFString *name, *value;
+
+ if (parts.count != 2)
+ @throw [OFInvalidFormatException exception];
+
+ name = [[parts objectAtIndex: 0]
+ stringByRemovingPercentEncoding];
+ value = [[parts objectAtIndex: 1]
+ stringByRemovingPercentEncoding];
+
+ [ret addObject: [OFPair pairWithFirstObject: name
+ secondObject: value]];
+ }
+
+ [ret makeImmutable];
+ [ret retain];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [ret autorelease];
+}
+
+- (OFString *)fragment
+{
+ return _percentEncodedFragment.stringByRemovingPercentEncoding;
+}
+
+- (OFString *)percentEncodedFragment
+{
+ return _percentEncodedFragment;
+}
+
+- (id)copy
+{
+ return [self retain];
+}
+
+- (id)mutableCopy
+{
+ OFURI *copy = [[OFMutableURI alloc] initWithScheme: _scheme];
+
+ @try {
+ copy->_percentEncodedHost = [_percentEncodedHost copy];
+ copy->_port = [_port copy];
+ copy->_percentEncodedUser = [_percentEncodedUser copy];
+ copy->_percentEncodedPassword = [_percentEncodedPassword copy];
+ copy->_percentEncodedPath = [_percentEncodedPath copy];
+ copy->_percentEncodedQuery = [_percentEncodedQuery copy];
+ copy->_percentEncodedFragment = [_percentEncodedFragment copy];
+ } @catch (id e) {
+ [copy release];
+ @throw e;
+ }
+
+ return copy;
+}
+
+- (OFString *)string
+{
+ OFMutableString *ret = [OFMutableString string];
+
+ [ret appendFormat: @"%@:", _scheme];
+
+ if (_percentEncodedHost != nil || _port != nil ||
+ _percentEncodedUser != nil || _percentEncodedPassword != nil)
+ [ret appendString: @"//"];
+
+ if (_percentEncodedUser != nil && _percentEncodedPassword != nil)
+ [ret appendFormat: @"%@:%@@",
+ _percentEncodedUser,
+ _percentEncodedPassword];
+ else if (_percentEncodedUser != nil)
+ [ret appendFormat: @"%@@", _percentEncodedUser];
+
+ if (_percentEncodedHost != nil)
+ [ret appendString: _percentEncodedHost];
+ if (_port != nil)
+ [ret appendFormat: @":%@", _port];
+
+ [ret appendString: _percentEncodedPath];
+
+ if (_percentEncodedQuery != nil)
+ [ret appendFormat: @"?%@", _percentEncodedQuery];
+
+ if (_percentEncodedFragment != nil)
+ [ret appendFormat: @"#%@", _percentEncodedFragment];
+
+ [ret makeImmutable];
+
+ return ret;
+}
+
+#ifdef OF_HAVE_FILES
+- (OFString *)fileSystemRepresentation
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFString *path;
+
+ if (![_scheme isEqual: @"file"])
+ @throw [OFInvalidArgumentException exception];
+
+ if (![_percentEncodedPath hasPrefix: @"/"])
+ @throw [OFInvalidFormatException exception];
+
+ path = [self.path
+ of_URIPathToPathWithPercentEncodedHost: _percentEncodedHost];
+
+ [path retain];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [path autorelease];
+}
+#endif
+
+- (OFURI *)URIByAppendingPathComponent: (OFString *)component
+{
+ OFMutableURI *URI = [[self mutableCopy] autorelease];
+ [URI appendPathComponent: component];
+ [URI makeImmutable];
+ return URI;
+}
+
+- (OFURI *)URIByAppendingPathComponent: (OFString *)component
+ isDirectory: (bool)isDirectory
+{
+ OFMutableURI *URI = [[self mutableCopy] autorelease];
+ [URI appendPathComponent: component isDirectory: isDirectory];
+ [URI makeImmutable];
+ return URI;
+}
+
+- (OFURI *)URIByStandardizingPath
+{
+ OFMutableURI *URI = [[self mutableCopy] autorelease];
+ [URI standardizePath];
+ [URI makeImmutable];
+ return URI;
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat: @"<%@: %@>",
+ self.class, self.string];
+}
+
+- (OFXMLElement *)XMLElementBySerializing
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFXMLElement *element;
+
+ element = [OFXMLElement elementWithName: self.className
+ namespace: OFSerializationNS
+ stringValue: self.string];
+
+ [element retain];
+
+ objc_autoreleasePoolPop(pool);
+
+ return [element autorelease];
+}
+@end
ADDED src/OFURIHandler.h
Index: src/OFURIHandler.h
==================================================================
--- src/OFURIHandler.h
+++ src/OFURIHandler.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFFileManager.h"
+#import "OFObject.h"
+#import "OFString.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFArray OF_GENERIC(ObjectType);
+@class OFDate;
+@class OFStream;
+@class OFURI;
+
+/**
+ * @class OFURIHandler OFURIHandler.h ObjFW/OFURIHandler.h
+ *
+ * @brief A handler for a URI scheme.
+ */
+@interface OFURIHandler: OFObject
+{
+ OFString *_scheme;
+ OF_RESERVE_IVARS(OFURIHandler, 4)
+}
+
+/**
+ * @brief The scheme this OFURIHandler handles.
+ */
+@property (readonly, nonatomic) OFString *scheme;
+
+/**
+ * @brief Registers the specified class as the handler for the specified scheme.
+ *
+ * If the same class is specified for two schemes, one instance of it is
+ * created per scheme.
+ *
+ * @param class_ The class to register as the handler for the specified scheme
+ * @param scheme The scheme for which to register the handler
+ * @return Whether the class was successfully registered. If a handler for the
+ * same scheme is already registered, registration fails.
+ */
++ (bool)registerClass: (Class)class_ forScheme: (OFString *)scheme;
+
+/**
+ * @brief Returns the handler for the specified URI.
+ *
+ * @return The handler for the specified URI.
+ * @throw OFUnsupportedProtocolException The specified URI is not supported
+ */
++ (OFURIHandler *)handlerForURI: (OFURI *)URI;
+
+/**
+ * @brief Opens the item at the specified URI.
+ *
+ * @param URI The URI of the item which should be opened
+ * @param mode The mode in which the file should be opened.@n
+ * Possible modes are:
+ * @n
+ * Mode | Description
+ * ---------------|-------------------------------------
+ * `r` | Read-only
+ * `r+` | Read-write
+ * `w` | Write-only, create or truncate
+ * `wx` | Write-only, create or fail, exclusive
+ * `w+` | Read-write, create or truncate
+ * `w+x` | Read-write, create or fail, exclusive
+ * `a` | Write-only, create or append
+ * `a+` | Read-write, create or append
+ * @n
+ * The handler is allowed to not implement all modes and is also
+ * allowed to implement additional, scheme-specific modes.
+ * @return The opened stream if it was successfully opened
+ * @throw OFOpenItemFailedException Opening the item failed
+ * @throw OFUnsupportedProtocolException The specified URI is not supported
+ */
++ (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode;
+
+- (instancetype)init OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes the handler for the specified scheme.
+ *
+ * @param scheme The scheme to initialize for
+ * @return An initialized URI handler
+ */
+- (instancetype)initWithScheme: (OFString *)scheme OF_DESIGNATED_INITIALIZER;
+
+/**
+ * @brief Opens the item at the specified URI.
+ *
+ * @param URI The URI of the item which should be opened
+ * @param mode The mode in which the file should be opened.@n
+ * Possible modes are:
+ * @n
+ * Mode | Description
+ * ---------------|-------------------------------------
+ * `r` | Read-only
+ * `r+` | Read-write
+ * `w` | Write-only, create or truncate
+ * `wx` | Write-only, create or fail, exclusive
+ * `w+` | Read-write, create or truncate
+ * `w+x` | Read-write, create or fail, exclusive
+ * `a` | Write-only, create or append
+ * `a+` | Read-write, create or append
+ * @n
+ * The handler is allowed to not implement all modes and is also
+ * allowed to implement additional, scheme-specific modes.
+ * @return The opened stream if it was successfully opened
+ * @throw OFOpenItemFailedException Opening the item failed
+ * @throw OFUnsupportedProtocolException The specified URI is not supported by
+ * the handler
+ */
+- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode;
+
+/**
+ * @brief Returns the attributes for the item at the specified URI.
+ *
+ * @param URI The URI to return the attributes for
+ * @return A dictionary of attributes for the specified URI, with the keys of
+ * type @ref OFFileAttributeKey
+ */
+- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI;
+
+/**
+ * @brief Sets the attributes for the item at the specified URI.
+ *
+ * All attributes not part of the dictionary are left unchanged.
+ *
+ * @param attributes The attributes to set for the specified URI
+ * @param URI The URI of the item to set the attributes for
+ */
+- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI;
+
+/**
+ * @brief Checks whether a file exists at the specified URI.
+ *
+ * @param URI The URI to check
+ * @return A boolean whether there is a file at the specified URI
+ */
+- (bool)fileExistsAtURI: (OFURI *)URI;
+
+/**
+ * @brief Checks whether a directory exists at the specified URI.
+ *
+ * @param URI The URI to check
+ * @return A boolean whether there is a directory at the specified URI
+ */
+- (bool)directoryExistsAtURI: (OFURI *)URI;
+
+/**
+ * @brief Creates a directory at the specified URI.
+ *
+ * @param URI The URI of the directory to create
+ */
+- (void)createDirectoryAtURI: (OFURI *)URI;
+
+/**
+ * @brief Returns an array with the URIs of the items in the specified
+ * directory.
+ *
+ * @note `.` and `..` are not part of the returned array.
+ *
+ * @param URI The URI to the directory whose items should be returned
+ * @return An array with the URIs of the items in the specified directory
+ */
+- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI;
+
+/**
+ * @brief Removes the item at the specified URI.
+ *
+ * If the item at the specified URI is a directory, it is removed recursively.
+ *
+ * @param URI The URI to the item which should be removed
+ */
+- (void)removeItemAtURI: (OFURI *)URI;
+
+/**
+ * @brief Creates a hard link for the specified item.
+ *
+ * The destination URI must have a full path, which means it must include the
+ * name of the item.
+ *
+ * This method is not available for all URIs.
+ *
+ * @param source The URI to the item for which a link should be created
+ * @param destination The URI to the item which should link to the source
+ */
+- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination;
+
+/**
+ * @brief Creates a symbolic link for an item.
+ *
+ * The destination URI must have a full path, which means it must include the
+ * name of the item.
+ *
+ * This method is not available for all URIs.
+ *
+ * @note On Windows, this requires at least Windows Vista and administrator
+ * privileges!
+ *
+ * @param URI The URI to the item which should symbolically link to the target
+ * @param target The target of the symbolic link
+ */
+- (void)createSymbolicLinkAtURI: (OFURI *)URI
+ withDestinationPath: (OFString *)target;
+
+/**
+ * @brief Tries to efficiently copy an item. If a copy would only be possible
+ * by reading the entire item and then writing it, it returns false.
+ *
+ * The destination URI must have a full path, which means it must include the
+ * name of the item.
+ *
+ * If an item already exists, the copy operation fails. This is also the case
+ * if a directory is copied and an item already exists in the destination
+ * directory.
+ *
+ * @param source The file, directory or symbolic link to copy
+ * @param destination The destination URI
+ * @return True if an efficient copy was performed, false if an efficient copy
+ * was not possible. Note that errors while performing a copy are
+ * reported via exceptions and not by returning false!
+ */
+- (bool)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination;
+
+/**
+ * @brief Tries to efficiently move an item. If a move would only be possible
+ * by copying the source and deleting it, it returns false.
+ *
+ * The destination URI must have a full path, which means it must include the
+ * name of the item.
+ *
+ * If the destination is on a different logical device or uses a different
+ * scheme, an efficient move is not possible and false is returned.
+ *
+ * @param source The item to rename
+ * @param destination The new name for the item
+ * @return True if an efficient move was performed, false if an efficient move
+ * was not possible. Note that errors while performing a move are
+ * reported via exceptions and not by returning false!
+ */
+- (bool)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/OFURIHandler.m
Index: src/OFURIHandler.m
==================================================================
--- src/OFURIHandler.m
+++ src/OFURIHandler.m
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFURIHandler.h"
+#import "OFDictionary.h"
+#import "OFNumber.h"
+#import "OFURI.h"
+
+#ifdef OF_HAVE_THREADS
+# import "OFMutex.h"
+#endif
+
+#import "OFArchiveURIHandler.h"
+#import "OFEmbeddedURIHandler.h"
+#ifdef OF_HAVE_FILES
+# import "OFFileURIHandler.h"
+#endif
+#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
+# import "OFHTTPURIHandler.h"
+#endif
+
+#import "OFUnsupportedProtocolException.h"
+
+static OFMutableDictionary OF_GENERIC(OFString *, OFURIHandler *) *handlers;
+#ifdef OF_HAVE_THREADS
+static OFMutex *mutex;
+
+static void
+releaseMutex(void)
+{
+ [mutex release];
+}
+#endif
+
+@implementation OFURIHandler
+@synthesize scheme = _scheme;
+
++ (void)initialize
+{
+ if (self != [OFURIHandler class])
+ return;
+
+ handlers = [[OFMutableDictionary alloc] init];
+#ifdef OF_HAVE_THREADS
+ mutex = [[OFMutex alloc] init];
+ atexit(releaseMutex);
+#endif
+
+ [self registerClass: [OFEmbeddedURIHandler class]
+ forScheme: @"embedded"];
+#ifdef OF_HAVE_FILES
+ [self registerClass: [OFFileURIHandler class] forScheme: @"file"];
+#endif
+#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
+ [self registerClass: [OFHTTPURIHandler class] forScheme: @"http"];
+ [self registerClass: [OFHTTPURIHandler class] forScheme: @"https"];
+#endif
+ [self registerClass: [OFArchiveURIHandler class] forScheme: @"gzip"];
+ [self registerClass: [OFArchiveURIHandler class] forScheme: @"lha"];
+ [self registerClass: [OFArchiveURIHandler class] forScheme: @"tar"];
+ [self registerClass: [OFArchiveURIHandler class] forScheme: @"zip"];
+}
+
++ (bool)registerClass: (Class)class forScheme: (OFString *)scheme
+{
+#ifdef OF_HAVE_THREADS
+ [mutex lock];
+ @try {
+#endif
+ OFURIHandler *handler;
+
+ if ([handlers objectForKey: scheme] != nil)
+ return false;
+
+ handler = [[class alloc] initWithScheme: scheme];
+ @try {
+ [handlers setObject: handler forKey: scheme];
+ } @finally {
+ [handler release];
+ }
+#ifdef OF_HAVE_THREADS
+ } @finally {
+ [mutex unlock];
+ }
+#endif
+
+ return true;
+}
+
++ (OFURIHandler *)handlerForURI: (OFURI *)URI
+{
+ OF_KINDOF(OFURIHandler *) handler;
+
+#ifdef OF_HAVE_THREADS
+ [mutex lock];
+ @try {
+#endif
+ handler = [handlers objectForKey: URI.scheme];
+#ifdef OF_HAVE_THREADS
+ } @finally {
+ [mutex unlock];
+ }
+#endif
+
+ if (handler == nil)
+ @throw [OFUnsupportedProtocolException exceptionWithURI: URI];
+
+ return handler;
+}
+
++ (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode
+{
+ return [[self handlerForURI: URI] openItemAtURI: URI mode: mode];
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithScheme: (OFString *)scheme
+{
+ self = [super init];
+
+ @try {
+ _scheme = [scheme copy];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_scheme release];
+
+ [super dealloc];
+}
+
+- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (bool)fileExistsAtURI: (OFURI *)URI
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (bool)directoryExistsAtURI: (OFURI *)URI
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (void)createDirectoryAtURI: (OFURI *)URI
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (void)removeItemAtURI: (OFURI *)URI
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (void)createSymbolicLinkAtURI: (OFURI *)destination
+ withDestinationPath: (OFString *)source
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (bool)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination
+{
+ return false;
+}
+
+- (bool)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination
+{
+ return false;
+}
+@end
DELETED src/OFURL.h
Index: src/OFURL.h
==================================================================
--- src/OFURL.h
+++ src/OFURL.h
@@ -1,378 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFObject.h"
-#import "OFCharacterSet.h"
-#import "OFSerialization.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-@class OFArray OF_GENERIC(ObjectType);
-@class OFDictionary OF_GENERIC(KeyType, ObjectType);
-@class OFNumber;
-@class OFString;
-
-/**
- * @class OFURL OFURL.h ObjFW/OFURL.h
- *
- * @brief A class for parsing URLs and accessing parts of it.
- */
-@interface OFURL: OFObject
-{
- OFString *_Nullable _URLEncodedScheme, *_Nullable _URLEncodedHost;
- OFNumber *_Nullable _port;
- OFString *_Nullable _URLEncodedUser, *_Nullable _URLEncodedPassword;
- OFString *_Nullable _URLEncodedPath;
- OFString *_Nullable _URLEncodedQuery, *_Nullable _URLEncodedFragment;
- OF_RESERVE_IVARS(OFURL, 4)
-}
-
-/**
- * @brief The scheme part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *scheme;
-
-/**
- * @brief The scheme part of the URL in URL-encoded form.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *URLEncodedScheme;
-
-/**
- * @brief The host part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *host;
-
-/**
- * @brief The host part of the URL in URL-encoded form.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *URLEncodedHost;
-
-/**
- * @brief The port part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFNumber *port;
-
-/**
- * @brief The user part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *user;
-
-/**
- * @brief The user part of the URL in URL-encoded form.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *URLEncodedUser;
-
-/**
- * @brief The password part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *password;
-
-/**
- * @brief The password part of the URL in URL-encoded form.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *URLEncodedPassword;
-
-/**
- * @brief The path part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *path;
-
-/**
- * @brief The path part of the URL in URL-encoded form.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *URLEncodedPath;
-
-/**
- * @brief The path of the URL split into components.
- *
- * The first component must always be `/` to designate the root.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFArray OF_GENERIC(OFString *) *pathComponents;
-
-/**
- * @brief The last path component of the URL.
- *
- * Returns the empty string if the path is the root.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *lastPathComponent;
-
-/**
- * @brief The query part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *query;
-
-/**
- * @brief The query part of the URL in URL-encoded form.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *URLEncodedQuery;
-
-/**
- * @brief The query part of the URL as a dictionary.
- *
- * For example, a query like `key1=value1&key2=value2` would correspond to the
- * following dictionary:
- *
- * @{
- * @"key1": @"value1",
- * @"key2": @"value2"
- * }
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFDictionary OF_GENERIC(OFString *, OFString *) *queryDictionary;
-
-/**
- * @brief The fragment part of the URL.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *fragment;
-
-/**
- * @brief The fragment part of the URL in URL-encoded form.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *URLEncodedFragment;
-
-/**
- * @brief The URL as a string.
- */
-@property (readonly, nonatomic) OFString *string;
-
-/**
- * @brief The URL with relative subpaths resolved.
- */
-@property (readonly, nonatomic) OFURL *URLByStandardizingPath;
-
-#ifdef OF_HAVE_FILES
-/**
- * @brief The local file system representation for a file URL.
- *
- * @note This only exists for URLs with the file scheme and throws an exception
- * otherwise.
- *
- */
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
- OFString *fileSystemRepresentation;
-#endif
-
-/**
- * @brief Creates a new URL with the specified string.
- *
- * @param string A string describing a URL
- * @return A new, autoreleased OFURL
- */
-+ (instancetype)URLWithString: (OFString *)string;
-
-/**
- * @brief Creates a new URL with the specified string relative to the
- * specified URL.
- *
- * @param string A string describing a URL
- * @param URL An URL to which the string is relative
- * @return A new, autoreleased OFURL
- */
-+ (instancetype)URLWithString: (OFString *)string relativeToURL: (OFURL *)URL;
-
-#ifdef OF_HAVE_FILES
-/**
- * @brief Creates a new URL with the specified local file path.
- *
- * If a directory exists at the specified path, a slash is appended if there is
- * no slash yet.
- *
- * @param path The local file path
- * @return A new, autoreleased OFURL
- */
-+ (instancetype)fileURLWithPath: (OFString *)path;
-
-/**
- * @brief Creates a new URL with the specified local file path.
- *
- * @param path The local file path
- * @param isDirectory Whether the path is a directory, in which case a slash is
- * appened if there is no slash yet
- * @return An Initialized OFURL
- */
-+ (instancetype)fileURLWithPath: (OFString *)path
- isDirectory: (bool)isDirectory;
-#endif
-
-/**
- * @brief Initializes an already allocated OFURL with the specified string.
- *
- * @param string A string describing a URL
- * @return An initialized OFURL
- */
-- (instancetype)initWithString: (OFString *)string;
-
-/**
- * @brief Initializes an already allocated OFURL with the specified string and
- * relative URL.
- *
- * @param string A string describing a URL
- * @param URL A URL to which the string is relative
- * @return An initialized OFURL
- */
-- (instancetype)initWithString: (OFString *)string relativeToURL: (OFURL *)URL;
-
-#ifdef OF_HAVE_FILES
-/**
- * @brief Initializes an already allocated OFURL with the specified local file
- * path.
- *
- * If a directory exists at the specified path, a slash is appended if there is
- * no slash yet.
- *
- * @param path The local file path
- * @return An initialized OFURL
- */
-- (instancetype)initFileURLWithPath: (OFString *)path;
-
-/**
- * @brief Initializes an already allocated OFURL with the specified local file
- * path.
- *
- * @param path The local file path
- * @param isDirectory Whether the path is a directory, in which case a slash is
- * appened if there is no slash yet
- * @return An Initialized OFURL
- */
-- (instancetype)initFileURLWithPath: (OFString *)path
- isDirectory: (bool)isDirectory;
-#endif
-
-/**
- * @brief Returns a new URL with the specified path component appended.
- *
- * If the URL is a file URL, the file system is queried whether the appended
- * component is a directory.
- *
- * @param component The path component to append. If it starts with the slash,
- * the component is not appended, but replaces the path
- * instead.
- * @return A new URL with the specified path component appended
- */
-- (OFURL *)URLByAppendingPathComponent: (OFString *)component;
-
-/**
- * @brief Returns a new URL with the specified path component appended.
- *
- * @param component The path component to append. If it starts with the slash,
- * the component is not appended, but replaces the path
- * instead.
- * @param isDirectory Whether the appended component is a directory, meaning
- * that the URL path should have a trailing slash
- * @return A new URL with the specified path component appended
- */
-- (OFURL *)URLByAppendingPathComponent: (OFString *)component
- isDirectory: (bool)isDirectory;
-@end
-
-@interface OFCharacterSet (URLCharacterSets)
-#ifdef OF_HAVE_CLASS_PROPERTIES
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLSchemeAllowedCharacterSet;
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLHostAllowedCharacterSet;
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLUserAllowedCharacterSet;
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLPasswordAllowedCharacterSet;
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLPathAllowedCharacterSet;
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLQueryAllowedCharacterSet;
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLQueryKeyValueAllowedCharacterSet;
-@property (class, readonly, nonatomic)
- OFCharacterSet *URLFragmentAllowedCharacterSet;
-#endif
-
-/**
- * @brief Returns the characters allowed in the scheme part of a URL.
- *
- * @return The characters allowed in the scheme part of a URL.
- */
-+ (OFCharacterSet *)URLSchemeAllowedCharacterSet;
-
-/**
- * @brief Returns the characters allowed in the host part of a URL.
- *
- * @return The characters allowed in the host part of a URL.
- */
-+ (OFCharacterSet *)URLHostAllowedCharacterSet;
-
-/**
- * @brief Returns the characters allowed in the user part of a URL.
- *
- * @return The characters allowed in the user part of a URL.
- */
-+ (OFCharacterSet *)URLUserAllowedCharacterSet;
-
-/**
- * @brief Returns the characters allowed in the password part of a URL.
- *
- * @return The characters allowed in the password part of a URL.
- */
-+ (OFCharacterSet *)URLPasswordAllowedCharacterSet;
-
-/**
- * @brief Returns the characters allowed in the path part of a URL.
- *
- * @return The characters allowed in the path part of a URL.
- */
-+ (OFCharacterSet *)URLPathAllowedCharacterSet;
-
-/**
- * @brief Returns the characters allowed in the query part of a URL.
- *
- * @return The characters allowed in the query part of a URL.
- */
-+ (OFCharacterSet *)URLQueryAllowedCharacterSet;
-
-/**
- * @brief Returns the characters allowed in a key/value in the query part of a
- * URL.
- *
- * @return The characters allowed in a key/value in the query part of a URL.
- */
-+ (OFCharacterSet *)URLQueryKeyValueAllowedCharacterSet;
-
-/**
- * @brief Returns the characters allowed in the fragment part of a URL.
- *
- * @return The characters allowed in the fragment part of a URL.
- */
-+ (OFCharacterSet *)URLFragmentAllowedCharacterSet;
-@end
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-extern bool OFURLIsIPv6Host(OFString *host);
-extern void OFURLVerifyIsEscaped(OFString *, OFCharacterSet *);
-#ifdef __cplusplus
-}
-#endif
-
-OF_ASSUME_NONNULL_END
-
-#import "OFMutableURL.h"
DELETED src/OFURL.m
Index: src/OFURL.m
==================================================================
--- src/OFURL.m
+++ src/OFURL.m
@@ -1,1214 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#include
-#include
-
-#import "OFURL.h"
-#import "OFArray.h"
-#import "OFDictionary.h"
-#ifdef OF_HAVE_FILES
-# import "OFFileManager.h"
-# import "OFFileURLHandler.h"
-#endif
-#import "OFNumber.h"
-#import "OFOnce.h"
-#import "OFString.h"
-#import "OFXMLElement.h"
-
-#import "OFInvalidArgumentException.h"
-#import "OFInvalidFormatException.h"
-#import "OFOutOfMemoryException.h"
-
-@interface OFURLAllowedCharacterSetBase: OFCharacterSet
-@end
-
-@interface OFURLAllowedCharacterSet: OFURLAllowedCharacterSetBase
-@end
-
-@interface OFURLSchemeAllowedCharacterSet: OFURLAllowedCharacterSetBase
-@end
-
-@interface OFURLPathAllowedCharacterSet: OFURLAllowedCharacterSetBase
-@end
-
-@interface OFURLQueryOrFragmentAllowedCharacterSet: OFURLAllowedCharacterSetBase
-@end
-
-@interface OFURLQueryKeyValueAllowedCharacterSet: OFURLAllowedCharacterSetBase
-@end
-
-static OFCharacterSet *URLAllowedCharacterSet = nil;
-static OFCharacterSet *URLSchemeAllowedCharacterSet = nil;
-static OFCharacterSet *URLPathAllowedCharacterSet = nil;
-static OFCharacterSet *URLQueryOrFragmentAllowedCharacterSet = nil;
-static OFCharacterSet *URLQueryKeyValueAllowedCharacterSet = nil;
-
-static OFOnceControl URLAllowedCharacterSetOnce = OFOnceControlInitValue;
-static OFOnceControl URLQueryOrFragmentAllowedCharacterSetOnce =
- OFOnceControlInitValue;
-
-static void
-initURLAllowedCharacterSet(void)
-{
- URLAllowedCharacterSet = [[OFURLAllowedCharacterSet alloc] init];
-}
-
-static void
-initURLSchemeAllowedCharacterSet(void)
-{
- URLSchemeAllowedCharacterSet =
- [[OFURLSchemeAllowedCharacterSet alloc] init];
-}
-
-static void
-initURLPathAllowedCharacterSet(void)
-{
- URLPathAllowedCharacterSet =
- [[OFURLPathAllowedCharacterSet alloc] init];
-}
-
-static void
-initURLQueryOrFragmentAllowedCharacterSet(void)
-{
- URLQueryOrFragmentAllowedCharacterSet =
- [[OFURLQueryOrFragmentAllowedCharacterSet alloc] init];
-}
-
-static void
-initURLQueryKeyValueAllowedCharacterSet(void)
-{
- URLQueryKeyValueAllowedCharacterSet =
- [[OFURLQueryKeyValueAllowedCharacterSet alloc] init];
-}
-
-OF_DIRECT_MEMBERS
-@interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet
-{
- OFCharacterSet *_characterSet;
- bool (*_characterIsMember)(id, SEL, OFUnichar);
-}
-
-- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
-@end
-
-bool
-OFURLIsIPv6Host(OFString *host)
-{
- const char *UTF8String = host.UTF8String;
- bool hasColon = false;
-
- while (*UTF8String != '\0') {
- if (!OFASCIIIsDigit(*UTF8String) && *UTF8String != ':' &&
- (*UTF8String < 'a' || *UTF8String > 'f') &&
- (*UTF8String < 'A' || *UTF8String > 'F'))
- return false;
-
- if (*UTF8String == ':')
- hasColon = true;
-
- UTF8String++;
- }
-
- return hasColon;
-}
-
-@implementation OFURLAllowedCharacterSetBase
-- (instancetype)autorelease
-{
- return self;
-}
-
-- (instancetype)retain
-{
- return self;
-}
-
-- (void)release
-{
-}
-
-- (unsigned int)retainCount
-{
- return OFMaxRetainCount;
-}
-@end
-
-@implementation OFURLAllowedCharacterSet
-- (bool)characterIsMember: (OFUnichar)character
-{
- if (character < CHAR_MAX && OFASCIIIsAlnum(character))
- return true;
-
- switch (character) {
- case '-':
- case '.':
- case '_':
- case '~':
- case '!':
- case '$':
- case '&':
- case '\'':
- case '(':
- case ')':
- case '*':
- case '+':
- case ',':
- case ';':
- case '=':
- return true;
- default:
- return false;
- }
-}
-@end
-
-@implementation OFURLSchemeAllowedCharacterSet
-- (bool)characterIsMember: (OFUnichar)character
-{
- if (character < CHAR_MAX && OFASCIIIsAlnum(character))
- return true;
-
- switch (character) {
- case '+':
- case '-':
- case '.':
- return true;
- default:
- return false;
- }
-}
-@end
-
-@implementation OFURLPathAllowedCharacterSet
-- (bool)characterIsMember: (OFUnichar)character
-{
- if (character < CHAR_MAX && OFASCIIIsAlnum(character))
- return true;
-
- switch (character) {
- case '-':
- case '.':
- case '_':
- case '~':
- case '!':
- case '$':
- case '&':
- case '\'':
- case '(':
- case ')':
- case '*':
- case '+':
- case ',':
- case ';':
- case '=':
- case ':':
- case '@':
- case '/':
- return true;
- default:
- return false;
- }
-}
-@end
-
-@implementation OFURLQueryOrFragmentAllowedCharacterSet
-- (bool)characterIsMember: (OFUnichar)character
-{
- if (character < CHAR_MAX && OFASCIIIsAlnum(character))
- return true;
-
- switch (character) {
- case '-':
- case '.':
- case '_':
- case '~':
- case '!':
- case '$':
- case '&':
- case '\'':
- case '(':
- case ')':
- case '*':
- case '+':
- case ',':
- case ';':
- case '=':
- case ':':
- case '@':
- case '/':
- case '?':
- return true;
- default:
- return false;
- }
-}
-@end
-
-@implementation OFURLQueryKeyValueAllowedCharacterSet
-- (bool)characterIsMember: (OFUnichar)character
-{
- if (character < CHAR_MAX && OFASCIIIsAlnum(character))
- return true;
-
- switch (character) {
- case '-':
- case '.':
- case '_':
- case '~':
- case '!':
- case '$':
- case '\'':
- case '(':
- case ')':
- case '*':
- case '+':
- case ',':
- case ';':
- case ':':
- case '@':
- case '/':
- case '?':
- return true;
- default:
- return false;
- }
-}
-@end
-
-@implementation OFInvertedCharacterSetWithoutPercent
-- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
-{
- self = [super init];
-
- @try {
- _characterSet = [characterSet retain];
- _characterIsMember = (bool (*)(id, SEL, OFUnichar))
- [_characterSet methodForSelector:
- @selector(characterIsMember:)];
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- [_characterSet release];
-
- [super dealloc];
-}
-
-- (bool)characterIsMember: (OFUnichar)character
-{
- return (character != '%' && !_characterIsMember(_characterSet,
- @selector(characterIsMember:), character));
-}
-@end
-
-void
-OFURLVerifyIsEscaped(OFString *string, OFCharacterSet *characterSet)
-{
- void *pool = objc_autoreleasePoolPush();
-
- characterSet = [[[OFInvertedCharacterSetWithoutPercent alloc]
- initWithCharacterSet: characterSet] autorelease];
-
- if ([string indexOfCharacterFromSet: characterSet] != OFNotFound)
- @throw [OFInvalidFormatException exception];
-
- objc_autoreleasePoolPop(pool);
-}
-
-@implementation OFCharacterSet (URLCharacterSets)
-+ (OFCharacterSet *)URLSchemeAllowedCharacterSet
-{
- static OFOnceControl onceControl = OFOnceControlInitValue;
- OFOnce(&onceControl, initURLSchemeAllowedCharacterSet);
-
- return URLSchemeAllowedCharacterSet;
-}
-
-+ (OFCharacterSet *)URLHostAllowedCharacterSet
-{
- OFOnce(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);
-
- return URLAllowedCharacterSet;
-}
-
-+ (OFCharacterSet *)URLUserAllowedCharacterSet
-{
- OFOnce(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);
-
- return URLAllowedCharacterSet;
-}
-
-+ (OFCharacterSet *)URLPasswordAllowedCharacterSet
-{
- OFOnce(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);
-
- return URLAllowedCharacterSet;
-}
-
-+ (OFCharacterSet *)URLPathAllowedCharacterSet
-{
- static OFOnceControl onceControl = OFOnceControlInitValue;
- OFOnce(&onceControl, initURLPathAllowedCharacterSet);
-
- return URLPathAllowedCharacterSet;
-}
-
-+ (OFCharacterSet *)URLQueryAllowedCharacterSet
-{
- OFOnce(&URLQueryOrFragmentAllowedCharacterSetOnce,
- initURLQueryOrFragmentAllowedCharacterSet);
-
- return URLQueryOrFragmentAllowedCharacterSet;
-}
-
-+ (OFCharacterSet *)URLQueryKeyValueAllowedCharacterSet
-{
- static OFOnceControl onceControl = OFOnceControlInitValue;
- OFOnce(&onceControl, initURLQueryKeyValueAllowedCharacterSet);
-
- return URLQueryKeyValueAllowedCharacterSet;
-}
-
-+ (OFCharacterSet *)URLFragmentAllowedCharacterSet
-{
- OFOnce(&URLQueryOrFragmentAllowedCharacterSetOnce,
- initURLQueryOrFragmentAllowedCharacterSet);
-
- return URLQueryOrFragmentAllowedCharacterSet;
-}
-@end
-
-@implementation OFURL
-+ (instancetype)URL
-{
- return [[[self alloc] init] autorelease];
-}
-
-+ (instancetype)URLWithString: (OFString *)string
-{
- return [[[self alloc] initWithString: string] autorelease];
-}
-
-+ (instancetype)URLWithString: (OFString *)string
- relativeToURL: (OFURL *)URL
-{
- return [[[self alloc] initWithString: string
- relativeToURL: URL] autorelease];
-}
-
-#ifdef OF_HAVE_FILES
-+ (instancetype)fileURLWithPath: (OFString *)path
-{
- return [[[self alloc] initFileURLWithPath: path] autorelease];
-}
-
-+ (instancetype)fileURLWithPath: (OFString *)path
- isDirectory: (bool)isDirectory
-{
- return [[[self alloc] initFileURLWithPath: path
- isDirectory: isDirectory] autorelease];
-}
-#endif
-
-- (instancetype)initWithString: (OFString *)string
-{
- char *UTF8String, *UTF8String2 = NULL;
-
- self = [super init];
-
- @try {
- void *pool = objc_autoreleasePoolPush();
- char *tmp, *tmp2;
- bool isIPv6Host = false;
-
- UTF8String = UTF8String2 = OFStrDup(string.UTF8String);
-
- if ((tmp = strchr(UTF8String, ':')) == NULL)
- @throw [OFInvalidFormatException exception];
-
- if (strncmp(tmp, "://", 3) != 0)
- @throw [OFInvalidFormatException exception];
-
- for (tmp2 = UTF8String; tmp2 < tmp; tmp2++)
- *tmp2 = OFASCIIToLower(*tmp2);
-
- _URLEncodedScheme = [[OFString alloc]
- initWithUTF8String: UTF8String
- length: tmp - UTF8String];
-
- OFURLVerifyIsEscaped(_URLEncodedScheme,
- [OFCharacterSet URLSchemeAllowedCharacterSet]);
-
- UTF8String = tmp + 3;
-
- if ((tmp = strchr(UTF8String, '/')) != NULL) {
- *tmp = '\0';
- tmp++;
- }
-
- if ((tmp2 = strchr(UTF8String, '@')) != NULL) {
- char *tmp3;
-
- *tmp2 = '\0';
- tmp2++;
-
- if ((tmp3 = strchr(UTF8String, ':')) != NULL) {
- *tmp3 = '\0';
- tmp3++;
-
- _URLEncodedUser = [[OFString alloc]
- initWithUTF8String: UTF8String];
- _URLEncodedPassword = [[OFString alloc]
- initWithUTF8String: tmp3];
-
- OFURLVerifyIsEscaped(_URLEncodedPassword,
- [OFCharacterSet
- URLPasswordAllowedCharacterSet]);
- } else
- _URLEncodedUser = [[OFString alloc]
- initWithUTF8String: UTF8String];
-
- OFURLVerifyIsEscaped(_URLEncodedUser,
- [OFCharacterSet URLUserAllowedCharacterSet]);
-
- UTF8String = tmp2;
- }
-
- if (UTF8String[0] == '[') {
- tmp2 = UTF8String++;
-
- while (OFASCIIIsDigit(*UTF8String) ||
- *UTF8String == ':' ||
- (*UTF8String >= 'a' && *UTF8String <= 'f') ||
- (*UTF8String >= 'A' && *UTF8String <= 'F'))
- UTF8String++;
-
- if (*UTF8String != ']')
- @throw [OFInvalidFormatException exception];
-
- UTF8String++;
-
- _URLEncodedHost = [[OFString alloc]
- initWithUTF8String: tmp2
- length: UTF8String - tmp2];
-
- if (*UTF8String == ':') {
- OFString *portString;
-
- tmp2 = ++UTF8String;
-
- while (*UTF8String != '\0') {
- if (!OFASCIIIsDigit(*UTF8String))
- @throw [OFInvalidFormatException
- exception];
-
- UTF8String++;
- }
-
- portString = [OFString
- stringWithUTF8String: tmp2
- length: UTF8String - tmp2];
-
- if (portString.length == 0 ||
- portString.unsignedLongLongValue > 65535)
- @throw [OFInvalidFormatException
- exception];
-
- _port = [[OFNumber alloc] initWithUnsignedShort:
- portString.unsignedLongLongValue];
- } else if (*UTF8String != '\0')
- @throw [OFInvalidFormatException exception];
-
- isIPv6Host = true;
- } else if ((tmp2 = strchr(UTF8String, ':')) != NULL) {
- OFString *portString;
-
- *tmp2 = '\0';
- tmp2++;
-
- _URLEncodedHost = [[OFString alloc]
- initWithUTF8String: UTF8String];
-
- portString = [OFString stringWithUTF8String: tmp2];
-
- if (portString.unsignedLongLongValue > 65535)
- @throw [OFInvalidFormatException exception];
-
- _port = [[OFNumber alloc] initWithUnsignedShort:
- portString.unsignedLongLongValue];
- } else {
- _URLEncodedHost = [[OFString alloc]
- initWithUTF8String: UTF8String];
-
- if (_URLEncodedHost.length == 0) {
- [_URLEncodedHost release];
- _URLEncodedHost = nil;
- }
- }
-
- if (_URLEncodedHost != nil && !isIPv6Host)
- OFURLVerifyIsEscaped(_URLEncodedHost,
- [OFCharacterSet URLHostAllowedCharacterSet]);
-
- if ((UTF8String = tmp) != NULL) {
- if ((tmp = strchr(UTF8String, '#')) != NULL) {
- *tmp = '\0';
-
- _URLEncodedFragment = [[OFString alloc]
- initWithUTF8String: tmp + 1];
-
- OFURLVerifyIsEscaped(_URLEncodedFragment,
- [OFCharacterSet
- URLFragmentAllowedCharacterSet]);
- }
-
- if ((tmp = strchr(UTF8String, '?')) != NULL) {
- *tmp = '\0';
-
- _URLEncodedQuery = [[OFString alloc]
- initWithUTF8String: tmp + 1];
-
- OFURLVerifyIsEscaped(_URLEncodedQuery,
- [OFCharacterSet
- URLQueryAllowedCharacterSet]);
- }
-
- /*
- * Some versions of GCC issue a false-positive warning
- * (turned error) about a string overflow. This is a
- * false positive because UTF8String is set to tmp
- * above and tmp is either NULL or points *after* the
- * slash for the path. So all we do here is go back to
- * that slash and restore it.
- */
-#if OF_GCC_VERSION >= 402
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wpragmas"
-# pragma GCC diagnostic ignored "-Wunknown-warning-option"
-# pragma GCC diagnostic ignored "-Wstringop-overflow"
-#endif
- UTF8String--;
- *UTF8String = '/';
-#if OF_GCC_VERSION >= 402
-# pragma GCC diagnostic pop
-#endif
-
- _URLEncodedPath = [[OFString alloc]
- initWithUTF8String: UTF8String];
-
- OFURLVerifyIsEscaped(_URLEncodedPath,
- [OFCharacterSet URLPathAllowedCharacterSet]);
- }
-
- objc_autoreleasePoolPop(pool);
- } @catch (id e) {
- [self release];
- @throw e;
- } @finally {
- OFFreeMemory(UTF8String2);
- }
-
- return self;
-}
-
-- (instancetype)initWithString: (OFString *)string relativeToURL: (OFURL *)URL
-{
- char *UTF8String, *UTF8String2 = NULL;
-
- if ([string containsString: @"://"])
- return [self initWithString: string];
-
- self = [super init];
-
- @try {
- void *pool = objc_autoreleasePoolPush();
- char *tmp;
-
- _URLEncodedScheme = [URL->_URLEncodedScheme copy];
- _URLEncodedHost = [URL->_URLEncodedHost copy];
- _port = [URL->_port copy];
- _URLEncodedUser = [URL->_URLEncodedUser copy];
- _URLEncodedPassword = [URL->_URLEncodedPassword copy];
-
- UTF8String = UTF8String2 = OFStrDup(string.UTF8String);
-
- if ((tmp = strchr(UTF8String, '#')) != NULL) {
- *tmp = '\0';
- _URLEncodedFragment = [[OFString alloc]
- initWithUTF8String: tmp + 1];
-
- OFURLVerifyIsEscaped(_URLEncodedFragment,
- [OFCharacterSet URLFragmentAllowedCharacterSet]);
- }
-
- if ((tmp = strchr(UTF8String, '?')) != NULL) {
- *tmp = '\0';
- _URLEncodedQuery = [[OFString alloc]
- initWithUTF8String: tmp + 1];
-
- OFURLVerifyIsEscaped(_URLEncodedQuery,
- [OFCharacterSet URLQueryAllowedCharacterSet]);
- }
-
- if (*UTF8String == '/')
- _URLEncodedPath = [[OFString alloc]
- initWithUTF8String: UTF8String];
- else {
- OFString *relativePath =
- [OFString stringWithUTF8String: UTF8String];
-
- if ([URL->_URLEncodedPath hasSuffix: @"/"])
- _URLEncodedPath = [[URL->_URLEncodedPath
- stringByAppendingString: relativePath]
- copy];
- else {
- OFMutableString *path = [OFMutableString
- stringWithString:
- (URL->_URLEncodedPath != nil
- ? URL->_URLEncodedPath
- : @"/")];
- OFRange range = [path
- rangeOfString: @"/"
- options: OFStringSearchBackwards];
-
- if (range.location == OFNotFound)
- @throw [OFInvalidFormatException
- exception];
-
- range.location++;
- range.length = path.length - range.location;
-
- [path replaceCharactersInRange: range
- withString: relativePath];
- [path makeImmutable];
-
- _URLEncodedPath = [path copy];
- }
- }
-
- OFURLVerifyIsEscaped(_URLEncodedPath,
- [OFCharacterSet URLPathAllowedCharacterSet]);
-
- objc_autoreleasePoolPop(pool);
- } @catch (id e) {
- [self release];
- @throw e;
- } @finally {
- OFFreeMemory(UTF8String2);
- }
-
- return self;
-}
-
-#ifdef OF_HAVE_FILES
-- (instancetype)initFileURLWithPath: (OFString *)path
-{
- bool isDirectory;
-
- @try {
- void *pool = objc_autoreleasePoolPush();
- isDirectory = [path of_isDirectoryPath];
- objc_autoreleasePoolPop(pool);
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- self = [self initFileURLWithPath: path isDirectory: isDirectory];
-
- return self;
-}
-
-- (instancetype)initFileURLWithPath: (OFString *)path
- isDirectory: (bool)isDirectory
-{
- self = [super init];
-
- @try {
- void *pool = objc_autoreleasePoolPush();
- OFString *URLEncodedHost = nil;
-
- if (!path.absolutePath) {
- OFString *currentDirectoryPath = [OFFileManager
- defaultManager].currentDirectoryPath;
-
- path = [currentDirectoryPath
- stringByAppendingPathComponent: path];
- path = path.stringByStandardizingPath;
- }
-
- path = [path
- of_pathToURLPathWithURLEncodedHost: &URLEncodedHost];
- _URLEncodedHost = [URLEncodedHost copy];
-
- if (isDirectory && ![path hasSuffix: @"/"])
- path = [path stringByAppendingString: @"/"];
-
- _URLEncodedScheme = @"file";
- _URLEncodedPath = [[path
- stringByURLEncodingWithAllowedCharacters:
- [OFCharacterSet URLPathAllowedCharacterSet]] copy];
-
- objc_autoreleasePoolPop(pool);
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-#endif
-
-- (instancetype)initWithSerialization: (OFXMLElement *)element
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *stringValue;
-
- @try {
- if (![element.name isEqual: self.className] ||
- ![element.namespace isEqual: OFSerializationNS])
- @throw [OFInvalidArgumentException exception];
-
- stringValue = element.stringValue;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- self = [self initWithString: stringValue];
-
- objc_autoreleasePoolPop(pool);
-
- return self;
-}
-
-- (void)dealloc
-{
- [_URLEncodedScheme release];
- [_URLEncodedHost release];
- [_port release];
- [_URLEncodedUser release];
- [_URLEncodedPassword release];
- [_URLEncodedPath release];
- [_URLEncodedQuery release];
- [_URLEncodedFragment release];
-
- [super dealloc];
-}
-
-- (bool)isEqual: (id)object
-{
- OFURL *URL;
-
- if (object == self)
- return true;
-
- if (![object isKindOfClass: [OFURL class]])
- return false;
-
- URL = object;
-
- if (URL->_URLEncodedScheme != _URLEncodedScheme &&
- ![URL->_URLEncodedScheme isEqual: _URLEncodedScheme])
- return false;
- if (URL->_URLEncodedHost != _URLEncodedHost &&
- ![URL->_URLEncodedHost isEqual: _URLEncodedHost])
- return false;
- if (URL->_port != _port && ![URL->_port isEqual: _port])
- return false;
- if (URL->_URLEncodedUser != _URLEncodedUser &&
- ![URL->_URLEncodedUser isEqual: _URLEncodedUser])
- return false;
- if (URL->_URLEncodedPassword != _URLEncodedPassword &&
- ![URL->_URLEncodedPassword isEqual: _URLEncodedPassword])
- return false;
- if (URL->_URLEncodedPath != _URLEncodedPath &&
- ![URL->_URLEncodedPath isEqual: _URLEncodedPath])
- return false;
- if (URL->_URLEncodedQuery != _URLEncodedQuery &&
- ![URL->_URLEncodedQuery isEqual: _URLEncodedQuery])
- return false;
- if (URL->_URLEncodedFragment != _URLEncodedFragment &&
- ![URL->_URLEncodedFragment isEqual: _URLEncodedFragment])
- return false;
-
- return true;
-}
-
-- (unsigned long)hash
-{
- unsigned long hash;
-
- OFHashInit(&hash);
-
- OFHashAddHash(&hash, _URLEncodedScheme.hash);
- OFHashAddHash(&hash, _URLEncodedHost.hash);
- OFHashAddHash(&hash, _port.hash);
- OFHashAddHash(&hash, _URLEncodedUser.hash);
- OFHashAddHash(&hash, _URLEncodedPassword.hash);
- OFHashAddHash(&hash, _URLEncodedPath.hash);
- OFHashAddHash(&hash, _URLEncodedQuery.hash);
- OFHashAddHash(&hash, _URLEncodedFragment.hash);
-
- OFHashFinalize(&hash);
-
- return hash;
-}
-
-- (OFString *)scheme
-{
- return _URLEncodedScheme.stringByURLDecoding;
-}
-
-- (OFString *)URLEncodedScheme
-{
- return _URLEncodedScheme;
-}
-
-- (OFString *)host
-{
- if ([_URLEncodedHost hasPrefix: @"["] &&
- [_URLEncodedHost hasSuffix: @"]"]) {
- OFString *host = [_URLEncodedHost substringWithRange:
- OFRangeMake(1, _URLEncodedHost.length - 2)];
-
- if (!OFURLIsIPv6Host(host))
- @throw [OFInvalidArgumentException exception];
-
- return host;
- }
-
- return _URLEncodedHost.stringByURLDecoding;
-}
-
-- (OFString *)URLEncodedHost
-{
- return _URLEncodedHost;
-}
-
-- (OFNumber *)port
-{
- return _port;
-}
-
-- (OFString *)user
-{
- return _URLEncodedUser.stringByURLDecoding;
-}
-
-- (OFString *)URLEncodedUser
-{
- return _URLEncodedUser;
-}
-
-- (OFString *)password
-{
- return _URLEncodedPassword.stringByURLDecoding;
-}
-
-- (OFString *)URLEncodedPassword
-{
- return _URLEncodedPassword;
-}
-
-- (OFString *)path
-{
- return _URLEncodedPath.stringByURLDecoding;
-}
-
-- (OFString *)URLEncodedPath
-{
- return _URLEncodedPath;
-}
-
-- (OFArray *)pathComponents
-{
- void *pool = objc_autoreleasePoolPush();
-#ifdef OF_HAVE_FILES
- bool isFile = [_URLEncodedScheme isEqual: @"file"];
-#endif
- OFMutableArray *ret;
- size_t count;
-
-#ifdef OF_HAVE_FILES
- if (isFile) {
- OFString *path = [_URLEncodedPath
- of_URLPathToPathWithURLEncodedHost: nil];
- ret = [[path.pathComponents mutableCopy] autorelease];
-
- if (![ret.firstObject isEqual: @"/"])
- [ret insertObject: @"/" atIndex: 0];
- } else
-#endif
- ret = [[[_URLEncodedPath componentsSeparatedByString: @"/"]
- mutableCopy] autorelease];
-
- count = ret.count;
-
- if (count > 0 && [ret.firstObject length] == 0)
- [ret replaceObjectAtIndex: 0 withObject: @"/"];
-
- for (size_t i = 0; i < count; i++) {
- OFString *component = [ret objectAtIndex: i];
-
-#ifdef OF_HAVE_FILES
- if (isFile)
- component =
- [component of_pathComponentToURLPathComponent];
-#endif
-
- [ret replaceObjectAtIndex: i
- withObject: component.stringByURLDecoding];
- }
-
- [ret makeImmutable];
- [ret retain];
-
- objc_autoreleasePoolPop(pool);
-
- return [ret autorelease];
-}
-
-- (OFString *)lastPathComponent
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *path = _URLEncodedPath;
- const char *UTF8String, *lastComponent;
- size_t length;
- OFString *ret;
-
- if (path == nil) {
- objc_autoreleasePoolPop(pool);
- return nil;
- }
-
- if ([path isEqual: @"/"]) {
- objc_autoreleasePoolPop(pool);
- return @"/";
- }
-
- if ([path hasSuffix: @"/"])
- path = [path substringToIndex: path.length - 1];
-
- UTF8String = lastComponent = path.UTF8String;
- length = path.UTF8StringLength;
-
- for (size_t i = 1; i <= length; i++) {
- if (UTF8String[length - i] == '/') {
- lastComponent = UTF8String + (length - i) + 1;
- break;
- }
- }
-
- ret = [OFString
- stringWithUTF8String: lastComponent
- length: length - (lastComponent - UTF8String)];
- ret = [ret.stringByURLDecoding retain];
-
- objc_autoreleasePoolPop(pool);
-
- return [ret autorelease];
-}
-
-- (OFString *)query
-{
- return _URLEncodedQuery.stringByURLDecoding;
-}
-
-- (OFString *)URLEncodedQuery
-{
- return _URLEncodedQuery;
-}
-
-- (OFDictionary OF_GENERIC(OFString *, OFString *) *)queryDictionary
-{
- void *pool;
- OFArray OF_GENERIC(OFString *) *pairs;
- OFMutableDictionary OF_GENERIC(OFString *, OFString *) *ret;
-
- if (_URLEncodedQuery == nil)
- return nil;
-
- pool = objc_autoreleasePoolPush();
- pairs = [_URLEncodedQuery componentsSeparatedByString: @"&"];
- ret = [OFMutableDictionary dictionaryWithCapacity: pairs.count];
-
- for (OFString *pair in pairs) {
- OFArray *parts = [pair componentsSeparatedByString: @"="];
-
- if (parts.count != 2)
- @throw [OFInvalidFormatException exception];
-
- [ret setObject: [[parts objectAtIndex: 1] stringByURLDecoding]
- forKey: [[parts objectAtIndex: 0] stringByURLDecoding]];
- }
-
- [ret makeImmutable];
- [ret retain];
-
- objc_autoreleasePoolPop(pool);
-
- return [ret autorelease];
-}
-
-- (OFString *)fragment
-{
- return _URLEncodedFragment.stringByURLDecoding;
-}
-
-- (OFString *)URLEncodedFragment
-{
- return _URLEncodedFragment;
-}
-
-- (id)copy
-{
- return [self retain];
-}
-
-- (id)mutableCopy
-{
- OFURL *copy = [[OFMutableURL alloc] init];
-
- @try {
- copy->_URLEncodedScheme = [_URLEncodedScheme copy];
- copy->_URLEncodedHost = [_URLEncodedHost copy];
- copy->_port = [_port copy];
- copy->_URLEncodedUser = [_URLEncodedUser copy];
- copy->_URLEncodedPassword = [_URLEncodedPassword copy];
- copy->_URLEncodedPath = [_URLEncodedPath copy];
- copy->_URLEncodedQuery = [_URLEncodedQuery copy];
- copy->_URLEncodedFragment = [_URLEncodedFragment copy];
- } @catch (id e) {
- [copy release];
- @throw e;
- }
-
- return copy;
-}
-
-- (OFString *)string
-{
- OFMutableString *ret = [OFMutableString string];
-
- [ret appendFormat: @"%@://", _URLEncodedScheme];
-
- if (_URLEncodedUser != nil && _URLEncodedPassword != nil)
- [ret appendFormat: @"%@:%@@",
- _URLEncodedUser, _URLEncodedPassword];
- else if (_URLEncodedUser != nil)
- [ret appendFormat: @"%@@", _URLEncodedUser];
-
- if (_URLEncodedHost != nil)
- [ret appendString: _URLEncodedHost];
- if (_port != nil)
- [ret appendFormat: @":%@", _port];
-
- if (_URLEncodedPath != nil) {
- if (![_URLEncodedPath hasPrefix: @"/"])
- @throw [OFInvalidFormatException exception];
-
- [ret appendString: _URLEncodedPath];
- }
-
- if (_URLEncodedQuery != nil)
- [ret appendFormat: @"?%@", _URLEncodedQuery];
-
- if (_URLEncodedFragment != nil)
- [ret appendFormat: @"#%@", _URLEncodedFragment];
-
- [ret makeImmutable];
-
- return ret;
-}
-
-#ifdef OF_HAVE_FILES
-- (OFString *)fileSystemRepresentation
-{
- void *pool = objc_autoreleasePoolPush();
- OFString *path;
-
- if (![_URLEncodedScheme isEqual: @"file"])
- @throw [OFInvalidArgumentException exception];
-
- if (![_URLEncodedPath hasPrefix: @"/"])
- @throw [OFInvalidFormatException exception];
-
- path = [self.path of_URLPathToPathWithURLEncodedHost: _URLEncodedHost];
-
- [path retain];
-
- objc_autoreleasePoolPop(pool);
-
- return [path autorelease];
-}
-#endif
-
-- (OFURL *)URLByAppendingPathComponent: (OFString *)component
-{
- OFMutableURL *URL = [[self mutableCopy] autorelease];
- [URL appendPathComponent: component];
- [URL makeImmutable];
- return URL;
-}
-
-- (OFURL *)URLByAppendingPathComponent: (OFString *)component
- isDirectory: (bool)isDirectory
-{
- OFMutableURL *URL = [[self mutableCopy] autorelease];
- [URL appendPathComponent: component isDirectory: isDirectory];
- [URL makeImmutable];
- return URL;
-}
-
-- (OFURL *)URLByStandardizingPath
-{
- OFMutableURL *URL = [[self mutableCopy] autorelease];
- [URL standardizePath];
- [URL makeImmutable];
- return URL;
-}
-
-- (OFString *)description
-{
- return [OFString stringWithFormat: @"<%@: %@>",
- self.class, self.string];
-}
-
-- (OFXMLElement *)XMLElementBySerializing
-{
- void *pool = objc_autoreleasePoolPush();
- OFXMLElement *element;
-
- element = [OFXMLElement elementWithName: self.className
- namespace: OFSerializationNS
- stringValue: self.string];
-
- [element retain];
-
- objc_autoreleasePoolPop(pool);
-
- return [element autorelease];
-}
-@end
DELETED src/OFURLHandler.h
Index: src/OFURLHandler.h
==================================================================
--- src/OFURLHandler.h
+++ src/OFURLHandler.h
@@ -1,226 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFFileManager.h"
-#import "OFObject.h"
-#import "OFString.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-@class OFArray OF_GENERIC(ObjectType);
-@class OFDate;
-@class OFStream;
-@class OFURL;
-
-/**
- * @class OFURLHandler OFURLHandler.h ObjFW/OFURLHandler.h
- *
- * @brief A handler for a URL scheme.
- */
-@interface OFURLHandler: OFObject
-{
- OFString *_scheme;
- OF_RESERVE_IVARS(OFURLHandler, 4)
-}
-
-/**
- * @brief The scheme this OFURLHandler handles.
- */
-@property (readonly, nonatomic) OFString *scheme;
-
-/**
- * @brief Registers the specified class as the handler for the specified scheme.
- *
- * If the same class is specified for two schemes, one instance of it is
- * created per scheme.
- *
- * @param class_ The class to register as the handler for the specified scheme
- * @param scheme The scheme for which to register the handler
- * @return Whether the class was successfully registered. If a handler for the
- * same scheme is already registered, registration fails.
- */
-+ (bool)registerClass: (Class)class_ forScheme: (OFString *)scheme;
-
-/**
- * @brief Returns the handler for the specified URL.
- *
- * @return The handler for the specified URL.
- */
-+ (nullable OF_KINDOF(OFURLHandler *))handlerForURL: (OFURL *)URL;
-
-- (instancetype)init OF_UNAVAILABLE;
-
-/**
- * @brief Initializes the handler for the specified scheme.
- *
- * @param scheme The scheme to initialize for
- * @return An initialized URL handler
- */
-- (instancetype)initWithScheme: (OFString *)scheme OF_DESIGNATED_INITIALIZER;
-
-/**
- * @brief Opens the item at the specified URL.
- *
- * @param URL The URL of the item which should be opened
- * @param mode The mode in which the file should be opened.@n
- * Possible modes are:
- * @n
- * Mode | Description
- * ---------------|-------------------------------------
- * `r` | Read-only
- * `r+` | Read-write
- * `w` | Write-only, create or truncate
- * `wx` | Write-only, create or fail, exclusive
- * `w+` | Read-write, create or truncate
- * `w+x` | Read-write, create or fail, exclusive
- * `a` | Write-only, create or append
- * `a+` | Read-write, create or append
- * @n
- * The handler is allowed to not implement all modes and is also
- * allowed to implement additional, scheme-specific modes.
- */
-- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode;
-
-/**
- * @brief Returns the attributes for the item at the specified URL.
- *
- * @param URL The URL to return the attributes for
- * @return A dictionary of attributes for the specified URL, with the keys of
- * type @ref OFFileAttributeKey
- */
-- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL;
-
-/**
- * @brief Sets the attributes for the item at the specified URL.
- *
- * All attributes not part of the dictionary are left unchanged.
- *
- * @param attributes The attributes to set for the specified URL
- * @param URL The URL of the item to set the attributes for
- */
-- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL;
-
-/**
- * @brief Checks whether a file exists at the specified URL.
- *
- * @param URL The URL to check
- * @return A boolean whether there is a file at the specified URL
- */
-- (bool)fileExistsAtURL: (OFURL *)URL;
-
-/**
- * @brief Checks whether a directory exists at the specified URL.
- *
- * @param URL The URL to check
- * @return A boolean whether there is a directory at the specified URL
- */
-- (bool)directoryExistsAtURL: (OFURL *)URL;
-
-/**
- * @brief Creates a directory at the specified URL.
- *
- * @param URL The URL of the directory to create
- */
-- (void)createDirectoryAtURL: (OFURL *)URL;
-
-/**
- * @brief Returns an array with the URLs of the items in the specified
- * directory.
- *
- * @note `.` and `..` are not part of the returned array.
- *
- * @param URL The URL to the directory whose items should be returned
- * @return An array with the URLs of the items in the specified directory
- */
-- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL;
-
-/**
- * @brief Removes the item at the specified URL.
- *
- * If the item at the specified URL is a directory, it is removed recursively.
- *
- * @param URL The URL to the item which should be removed
- */
-- (void)removeItemAtURL: (OFURL *)URL;
-
-/**
- * @brief Creates a hard link for the specified item.
- *
- * The destination URL must have a full path, which means it must include the
- * name of the item.
- *
- * This method is not available for all URLs.
- *
- * @param source The URL to the item for which a link should be created
- * @param destination The URL to the item which should link to the source
- */
-- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination;
-
-/**
- * @brief Creates a symbolic link for an item.
- *
- * The destination uRL must have a full path, which means it must include the
- * name of the item.
- *
- * This method is not available for all URLs.
- *
- * @note On Windows, this requires at least Windows Vista and administrator
- * privileges!
- *
- * @param URL The URL to the item which should symbolically link to the target
- * @param target The target of the symbolic link
- */
-- (void)createSymbolicLinkAtURL: (OFURL *)URL
- withDestinationPath: (OFString *)target;
-
-/**
- * @brief Tries to efficiently copy an item. If a copy would only be possible
- * by reading the entire item and then writing it, it returns false.
- *
- * The destination URL must have a full path, which means it must include the
- * name of the item.
- *
- * If an item already exists, the copy operation fails. This is also the case
- * if a directory is copied and an item already exists in the destination
- * directory.
- *
- * @param source The file, directory or symbolic link to copy
- * @param destination The destination URL
- * @return True if an efficient copy was performed, false if an efficient copy
- * was not possible. Note that errors while performing a copy are
- * reported via exceptions and not by returning false!
- */
-- (bool)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination;
-
-/**
- * @brief Tries to efficiently move an item. If a move would only be possible
- * by copying the source and deleting it, it returns false.
- *
- * The destination URL must have a full path, which means it must include the
- * name of the item.
- *
- * If the destination is on a different logical device or uses a different
- * scheme, an efficient move is not possible and false is returned.
- *
- * @param source The item to rename
- * @param destination The new name for the item
- * @return True if an efficient move was performed, false if an efficient move
- * was not possible. Note that errors while performing a move are
- * reported via exceptions and not by returning false!
- */
-- (bool)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/OFURLHandler.m
Index: src/OFURLHandler.m
==================================================================
--- src/OFURLHandler.m
+++ src/OFURLHandler.m
@@ -1,201 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFURLHandler.h"
-#import "OFDictionary.h"
-#import "OFNumber.h"
-#import "OFURL.h"
-
-#ifdef OF_HAVE_THREADS
-# import "OFMutex.h"
-#endif
-
-#import "OFEmbeddedFileURLHandler.h"
-#ifdef OF_HAVE_FILES
-# import "OFFileURLHandler.h"
-#endif
-#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
-# import "OFHTTPURLHandler.h"
-#endif
-
-static OFMutableDictionary OF_GENERIC(OFString *, OFURLHandler *) *handlers;
-#ifdef OF_HAVE_THREADS
-static OFMutex *mutex;
-
-static void
-releaseMutex(void)
-{
- [mutex release];
-}
-#endif
-
-@implementation OFURLHandler
-@synthesize scheme = _scheme;
-
-+ (void)initialize
-{
- if (self != [OFURLHandler class])
- return;
-
- handlers = [[OFMutableDictionary alloc] init];
-#ifdef OF_HAVE_THREADS
- mutex = [[OFMutex alloc] init];
- atexit(releaseMutex);
-#endif
-
- [self registerClass: [OFEmbeddedFileURLHandler class]
- forScheme: @"objfw-embedded"];
-#ifdef OF_HAVE_FILES
- [self registerClass: [OFFileURLHandler class] forScheme: @"file"];
-#endif
-#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
- [self registerClass: [OFHTTPURLHandler class] forScheme: @"http"];
- [self registerClass: [OFHTTPURLHandler class] forScheme: @"https"];
-#endif
-}
-
-+ (bool)registerClass: (Class)class forScheme: (OFString *)scheme
-{
-#ifdef OF_HAVE_THREADS
- [mutex lock];
- @try {
-#endif
- OFURLHandler *handler;
-
- if ([handlers objectForKey: scheme] != nil)
- return false;
-
- handler = [[class alloc] initWithScheme: scheme];
- @try {
- [handlers setObject: handler forKey: scheme];
- } @finally {
- [handler release];
- }
-#ifdef OF_HAVE_THREADS
- } @finally {
- [mutex unlock];
- }
-#endif
-
- return true;
-}
-
-+ (OF_KINDOF(OFURLHandler *))handlerForURL: (OFURL *)URL
-{
- OF_KINDOF(OFURLHandler *) handler;
-
-#ifdef OF_HAVE_THREADS
- [mutex lock];
- @try {
-#endif
- handler = [handlers objectForKey: URL.scheme];
-#ifdef OF_HAVE_THREADS
- } @finally {
- [mutex unlock];
- }
-#endif
-
- return handler;
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (instancetype)initWithScheme: (OFString *)scheme
-{
- self = [super init];
-
- @try {
- _scheme = [scheme copy];
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- [_scheme release];
-
- [super dealloc];
-}
-
-- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (bool)fileExistsAtURL: (OFURL *)URL
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (bool)directoryExistsAtURL: (OFURL *)URL
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (void)createDirectoryAtURL: (OFURL *)URL
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (void)removeItemAtURL: (OFURL *)URL
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (void)createSymbolicLinkAtURL: (OFURL *)destination
- withDestinationPath: (OFString *)source
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (bool)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination
-{
- return false;
-}
-
-- (bool)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination
-{
- return false;
-}
-@end
Index: src/OFUTF8String.m
==================================================================
--- src/OFUTF8String.m
+++ src/OFUTF8String.m
@@ -344,11 +344,11 @@
#ifdef HAVE_KOI8_U
CASE(OFStringEncodingKOI8U, OFKOI8UTable)
#endif
#undef CASE
default:
- @throw [OFInvalidEncodingException exception];
+ @throw [OFInvalidArgumentException exception];
}
j = 0;
for (size_t i = 0; i < cStringLength; i++) {
unsigned char character = (unsigned char)cString[i];
@@ -930,13 +930,13 @@
if ((length = OFUTF8StringDecode(_s->cString + i,
_s->cStringLength - i, &c)) <= 0)
@throw [OFInvalidEncodingException exception];
- OFHashAdd(&hash, (c & 0xFF0000) >> 16);
- OFHashAdd(&hash, (c & 0x00FF00) >> 8);
- OFHashAdd(&hash, c & 0x0000FF);
+ OFHashAddByte(&hash, (c & 0xFF0000) >> 16);
+ OFHashAddByte(&hash, (c & 0x00FF00) >> 8);
+ OFHashAddByte(&hash, c & 0x0000FF);
i += length - 1;
}
OFHashFinalize(&hash);
@@ -1004,14 +1004,14 @@
rangeLocation = range.location;
rangeLength = range.length;
}
if (cStringLength == 0)
- return OFRangeMake(0, 0);
+ return OFMakeRange(0, 0);
if (cStringLength > rangeLength)
- return OFRangeMake(OFNotFound, 0);
+ return OFMakeRange(OFNotFound, 0);
if (options & OFStringSearchBackwards) {
for (size_t i = rangeLength - cStringLength;; i--) {
if (memcmp(_s->cString + rangeLocation + i, cString,
cStringLength) == 0) {
@@ -1022,11 +1022,11 @@
return range;
}
/* Did not match and we're at the last char */
if (i == 0)
- return OFRangeMake(OFNotFound, 0);
+ return OFMakeRange(OFNotFound, 0);
}
} else {
for (size_t i = 0; i <= rangeLength - cStringLength; i++) {
if (memcmp(_s->cString + rangeLocation + i, cString,
cStringLength) == 0) {
@@ -1037,11 +1037,11 @@
return range;
}
}
}
- return OFRangeMake(OFNotFound, 0);
+ return OFMakeRange(OFNotFound, 0);
}
- (bool)containsString: (OFString *)string
{
const char *cString = string.UTF8String;
Index: src/OFUUID.h
==================================================================
--- src/OFUUID.h
+++ src/OFUUID.h
@@ -54,10 +54,12 @@
/**
* @brief Creates a new UUID with the specified UUID string.
*
* @param string The UUID string for the UUID
* @return A new, autoreleased OFUUID
+ * @throw OFInvalidFormatException The specified string is not a valid UUID
+ * string
*/
+ (instancetype)UUIDWithUUIDString: (OFString *)string;
/**
* @brief Initializes an already allocated OFUUID as a new random UUID as per
@@ -79,10 +81,12 @@
* @brief Initializes an already allocated OFUUID with the specified UUID
* string.
*
* @param string The UUID string to initialize the OFUUID with
* @return An initialized OFUUID
+ * @throw OFInvalidFormatException The specified string is not a valid UUID
+ * string
*/
- (instancetype)initWithUUIDString: (OFString *)string;
/**
* @brief Compares the UUID to another UUID.
Index: src/OFUUID.m
==================================================================
--- src/OFUUID.m
+++ src/OFUUID.m
@@ -185,11 +185,11 @@
unsigned long hash;
OFHashInit(&hash);
for (size_t i = 0; i < sizeof(_bytes); i++)
- OFHashAdd(&hash, _bytes[i]);
+ OFHashAddByte(&hash, _bytes[i]);
OFHashFinalize(&hash);
return hash;
}
Index: src/OFValue.h
==================================================================
--- src/OFValue.h
+++ src/OFValue.h
@@ -33,46 +33,46 @@
@property (readonly, nonatomic) const char *objCType;
/**
* @brief The value as a pointer to void.
*
- * If the value is not pointer-sized, @ref OFOutOfRangeException is thrown.
+ * @throw OFOutOfRangeException The value is not pointer-sized
*/
@property (readonly, nonatomic) void *pointerValue;
/**
* @brief The value as a non-retained object.
*
- * If the value is not pointer-sized, @ref OFOutOfRangeException is thrown.
+ * @throw OFOutOfRangeException The value is not pointer-sized
*/
@property (readonly, nonatomic) id nonretainedObjectValue;
/**
* @brief The value as an OFRange.
*
- * If the value is not OFRange-sized, @ref OFOutOfRangeException is thrown.
+ * @throw OFOutOfRangeException The value is not OFRange-sized
*/
@property (readonly, nonatomic) OFRange rangeValue;
/**
* @brief The value as an OFPoint.
*
- * If the value is not OFPoint-sized, @ref OFOutOfRangeException is thrown.
+ * @throw OFOutOfRangeException The value is not OFPoint-sized
*/
@property (readonly, nonatomic) OFPoint pointValue;
/**
* @brief The value as an OFSize.
*
- * If the value is not OFSize-sized, @ref OFOutOfRangeException is thrown.
+ * @throw OFOutOfRangeException The value is not OFSize-sized
*/
@property (readonly, nonatomic) OFSize sizeValue;
/**
* @brief The value as a OFRect.
*
- * If the value is not OFRect-sized, @ref OFOutOfRangeException is thrown.
+ * @throw OFOutOfRangeException The value is not OFRect-sized
*/
@property (readonly, nonatomic) OFRect rectValue;
/**
* @brief Creates a new, autorelease OFValue with the specified bytes of the
@@ -152,15 +152,13 @@
objCType: (const char *)objCType;
/**
* @brief Gets the value.
*
- * If the specified size does not match, this raises an
- * @ref OFOutOfRangeException.
- *
* @param value The buffer to copy the value into
* @param size The size of the value
+ * @throw OFOutOfRangeException The specified size does not match the value
*/
- (void)getValue: (void *)value size: (size_t)size;
@end
OF_ASSUME_NONNULL_END
Index: src/OFValue.m
==================================================================
--- src/OFValue.m
+++ src/OFValue.m
@@ -11,10 +11,12 @@
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
+#include "config.h"
+
#import "OFValue.h"
#import "OFBytesValue.h"
#import "OFMethodSignature.h"
#import "OFNonretainedObjectValue.h"
#import "OFPointValue.h"
@@ -130,11 +132,11 @@
[self getValue: value size: size];
OFHashInit(&hash);
for (size_t i = 0; i < size; i++)
- OFHashAdd(&hash, value[i]);
+ OFHashAddByte(&hash, value[i]);
OFHashFinalize(&hash);
} @finally {
OFFreeMemory(value);
}
DELETED src/OFWin32ConsoleStdIOStream.m
Index: src/OFWin32ConsoleStdIOStream.m
==================================================================
--- src/OFWin32ConsoleStdIOStream.m
+++ src/OFWin32ConsoleStdIOStream.m
@@ -1,603 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-/*
- * This file tries to make writing UTF-8 strings to the console "just work" on
- * Windows.
- *
- * While Windows does provide a way to change the codepage of the console to
- * UTF-8, unfortunately, different Windows versions handle that differently.
- * For example, on Windows XP, when using Windows XP's console, changing the
- * codepage to UTF-8 mostly breaks write() and completely breaks read():
- * write() suddenly returns the number of characters - instead of bytes -
- * written and read() just returns 0 as soon as a Unicode character is being
- * read.
- *
- * Therefore, instead of just using the UTF-8 codepage, this captures all reads
- * and writes to OFStd{In,Out,Err} on the low level, interprets the buffer as
- * UTF-8 and converts to / from UTF-16 to use ReadConsoleW() / WriteConsoleW().
- * Doing so is safe, as the console only supports text anyway and thus it does
- * not matter if binary gets garbled by the conversion (e.g. because invalid
- * UTF-8 gets converted to U+FFFD).
- *
- * In order to not do this when redirecting input / output to a file (as the
- * file would then be read / written in the wrong encoding and break reading /
- * writing binary), it checks that the handle is indeed a console.
- */
-
-#include "config.h"
-
-#include
-#include
-#include
-
-#import "OFWin32ConsoleStdIOStream.h"
-#import "OFColor.h"
-#import "OFData.h"
-#import "OFStdIOStream+Private.h"
-#import "OFString.h"
-#import "OFSystemInfo.h"
-
-#import "OFInvalidArgumentException.h"
-#import "OFInvalidEncodingException.h"
-#import "OFOutOfRangeException.h"
-#import "OFReadFailedException.h"
-#import "OFWriteFailedException.h"
-
-#include
-
-static OFStringEncoding
-codepageToEncoding(UINT codepage)
-{
- switch (codepage) {
- case 437:
- return OFStringEncodingCodepage437;
- case 850:
- return OFStringEncodingCodepage850;
- case 858:
- return OFStringEncodingCodepage858;
- case 1251:
- return OFStringEncodingWindows1251;
- case 1252:
- return OFStringEncodingWindows1252;
- default:
- @throw [OFInvalidEncodingException exception];
- }
-}
-
-@implementation OFWin32ConsoleStdIOStream
-+ (void)load
-{
- int fd;
-
- if (self != [OFWin32ConsoleStdIOStream class])
- return;
-
- if ((fd = _fileno(stdin)) >= 0)
- OFStdIn = [[OFWin32ConsoleStdIOStream alloc]
- of_initWithFileDescriptor: fd];
- if ((fd = _fileno(stdout)) >= 0)
- OFStdOut = [[OFWin32ConsoleStdIOStream alloc]
- of_initWithFileDescriptor: fd];
- if ((fd = _fileno(stderr)) >= 0)
- OFStdErr = [[OFWin32ConsoleStdIOStream alloc]
- of_initWithFileDescriptor: fd];
-}
-
-- (instancetype)of_initWithFileDescriptor: (int)fd
-{
- self = [super of_initWithFileDescriptor: fd];
-
- @try {
- DWORD mode;
- CONSOLE_SCREEN_BUFFER_INFO csbi;
-
- _handle = (HANDLE)_get_osfhandle(fd);
- if (_handle == INVALID_HANDLE_VALUE)
- @throw [OFInvalidArgumentException exception];
-
- /* Not a console: Treat it as a regular OFStdIOStream */
- if (!GetConsoleMode(_handle, &mode))
- object_setClass(self, [OFStdIOStream class]);
-
- if (GetConsoleScreenBufferInfo(_handle, &csbi))
- _attributes = csbi.wAttributes;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (size_t)lowlevelReadIntoBuffer: (void *)buffer_ length: (size_t)length
-{
- void *pool = objc_autoreleasePoolPush();
- char *buffer = buffer_;
- OFChar16 *UTF16;
- size_t j = 0;
-
- if (length > UINT32_MAX)
- @throw [OFOutOfRangeException exception];
-
- UTF16 = OFAllocMemory(length, sizeof(OFChar16));
- @try {
- DWORD UTF16Len;
- OFMutableData *rest = nil;
- size_t i = 0;
-
- if ([OFSystemInfo isWindowsNT]) {
- if (!ReadConsoleW(_handle, UTF16, (DWORD)length,
- &UTF16Len, NULL))
- @throw [OFReadFailedException
- exceptionWithObject: self
- requestedLength: length * 2
- errNo: EIO];
- } else {
- OFStringEncoding encoding;
- OFString *string;
- size_t stringLen;
-
- if (!ReadConsoleA(_handle, (char *)UTF16, (DWORD)length,
- &UTF16Len, NULL))
- @throw [OFReadFailedException
- exceptionWithObject: self
- requestedLength: length
- errNo: EIO];
-
- encoding = codepageToEncoding(GetConsoleCP());
- string = [OFString stringWithCString: (char *)UTF16
- encoding: encoding
- length: UTF16Len];
- stringLen = string.UTF16StringLength;
-
- if (stringLen > length)
- @throw [OFOutOfRangeException exception];
-
- UTF16Len = (DWORD)stringLen;
- memcpy(UTF16, string.UTF16String, stringLen);
- }
-
- if (UTF16Len > 0 && _incompleteUTF16Surrogate != 0) {
- OFUnichar c =
- (((_incompleteUTF16Surrogate & 0x3FF) << 10) |
- (UTF16[0] & 0x3FF)) + 0x10000;
- char UTF8[4];
- size_t UTF8Len;
-
- if ((UTF8Len = OFUTF8StringEncode(c, UTF8)) == 0)
- @throw [OFInvalidEncodingException exception];
-
- if (UTF8Len <= length) {
- memcpy(buffer, UTF8, UTF8Len);
- j += UTF8Len;
- } else {
- if (rest == nil)
- rest = [OFMutableData data];
-
- [rest addItems: UTF8 count: UTF8Len];
- }
-
- _incompleteUTF16Surrogate = 0;
- i++;
- }
-
- for (; i < UTF16Len; i++) {
- OFUnichar c = UTF16[i];
- char UTF8[4];
- size_t UTF8Len;
-
- /* Missing high surrogate */
- if ((c & 0xFC00) == 0xDC00)
- @throw [OFInvalidEncodingException exception];
-
- if ((c & 0xFC00) == 0xD800) {
- OFChar16 next;
-
- if (UTF16Len <= i + 1) {
- _incompleteUTF16Surrogate = c;
-
- if (rest != nil) {
- const char *items = rest.items;
- size_t count = rest.count;
-
- [self unreadFromBuffer: items
- length: count];
- }
-
- objc_autoreleasePoolPop(pool);
-
- return j;
- }
-
- next = UTF16[i + 1];
-
- if ((next & 0xFC00) != 0xDC00)
- @throw [OFInvalidEncodingException
- exception];
-
- c = (((c & 0x3FF) << 10) | (next & 0x3FF)) +
- 0x10000;
-
- i++;
- }
-
- if ((UTF8Len = OFUTF8StringEncode(c, UTF8)) == 0)
- @throw [OFInvalidEncodingException exception];
-
- if (j + UTF8Len <= length) {
- memcpy(buffer + j, UTF8, UTF8Len);
- j += UTF8Len;
- } else {
- if (rest == nil)
- rest = [OFMutableData data];
-
- [rest addItems: UTF8 count: UTF8Len];
- }
- }
-
- if (rest != nil)
- [self unreadFromBuffer: rest.items length: rest.count];
- } @finally {
- OFFreeMemory(UTF16);
- }
-
- objc_autoreleasePoolPop(pool);
-
- return j;
-}
-
-- (size_t)lowlevelWriteBuffer: (const void *)buffer_ length: (size_t)length
-{
- const char *buffer = buffer_;
- OFChar16 *tmp;
- size_t i = 0, j = 0;
-
- if (length > SIZE_MAX / 2)
- @throw [OFOutOfRangeException exception];
-
- if (_incompleteUTF8SurrogateLen > 0) {
- OFUnichar c;
- OFChar16 UTF16[2];
- ssize_t UTF8Len;
- size_t toCopy;
- DWORD UTF16Len, bytesWritten;
-
- UTF8Len = -OFUTF8StringDecode(
- _incompleteUTF8Surrogate, _incompleteUTF8SurrogateLen, &c);
-
- OFEnsure(UTF8Len > 0);
-
- toCopy = UTF8Len - _incompleteUTF8SurrogateLen;
- if (toCopy > length)
- toCopy = length;
-
- memcpy(_incompleteUTF8Surrogate + _incompleteUTF8SurrogateLen,
- buffer, toCopy);
- _incompleteUTF8SurrogateLen += toCopy;
-
- if (_incompleteUTF8SurrogateLen < (size_t)UTF8Len)
- return 0;
-
- UTF8Len = OFUTF8StringDecode(
- _incompleteUTF8Surrogate, _incompleteUTF8SurrogateLen, &c);
-
- if (UTF8Len <= 0 || c > 0x10FFFF) {
- assert(UTF8Len == 0 || UTF8Len < -4);
-
- UTF16[0] = 0xFFFD;
- UTF16Len = 1;
- } else {
- if (c > 0xFFFF) {
- c -= 0x10000;
- UTF16[0] = 0xD800 | (c >> 10);
- UTF16[1] = 0xDC00 | (c & 0x3FF);
- UTF16Len = 2;
- } else {
- UTF16[0] = c;
- UTF16Len = 1;
- }
- }
-
- if ([OFSystemInfo isWindowsNT]) {
- if (!WriteConsoleW(_handle, UTF16, UTF16Len,
- &bytesWritten, NULL))
- @throw [OFWriteFailedException
- exceptionWithObject: self
- requestedLength: UTF16Len * 2
- bytesWritten: bytesWritten * 2
- errNo: EIO];
- } else {
- void *pool = objc_autoreleasePoolPush();
- OFString *string = [OFString
- stringWithUTF16String: UTF16
- length: UTF16Len];
- OFStringEncoding encoding =
- codepageToEncoding(GetConsoleOutputCP());
- size_t nativeLen = [string
- cStringLengthWithEncoding: encoding];
-
- if (nativeLen > UINT32_MAX)
- @throw [OFOutOfRangeException exception];
-
- if (!WriteConsoleA(_handle,
- [string cStringWithEncoding: encoding],
- (DWORD)nativeLen, &bytesWritten, NULL))
- @throw [OFWriteFailedException
- exceptionWithObject: self
- requestedLength: nativeLen
- bytesWritten: bytesWritten
- errNo: EIO];
-
- objc_autoreleasePoolPop(pool);
- }
-
- if (bytesWritten != UTF16Len)
- @throw [OFWriteFailedException
- exceptionWithObject: self
- requestedLength: UTF16Len * 2
- bytesWritten: bytesWritten * 2
- errNo: 0];
-
- _incompleteUTF8SurrogateLen = 0;
- i += toCopy;
- }
-
- tmp = OFAllocMemory(length * 2, sizeof(OFChar16));
- @try {
- DWORD bytesWritten;
-
- while (i < length) {
- OFUnichar c;
- ssize_t UTF8Len;
-
- UTF8Len = OFUTF8StringDecode(buffer + i, length - i,
- &c);
-
- if (UTF8Len < 0 && UTF8Len >= -4) {
- OFEnsure(length - i < 4);
-
- memcpy(_incompleteUTF8Surrogate, buffer + i,
- length - i);
- _incompleteUTF8SurrogateLen = length - i;
-
- break;
- }
-
- if (UTF8Len <= 0 || c > 0x10FFFF) {
- tmp[j++] = 0xFFFD;
- i++;
- continue;
- }
-
- if (c > 0xFFFF) {
- c -= 0x10000;
- tmp[j++] = 0xD800 | (c >> 10);
- tmp[j++] = 0xDC00 | (c & 0x3FF);
- } else
- tmp[j++] = c;
-
- i += UTF8Len;
- }
-
- if (j > UINT32_MAX)
- @throw [OFOutOfRangeException exception];
-
- if ([OFSystemInfo isWindowsNT]) {
- if (!WriteConsoleW(_handle, tmp, (DWORD)j,
- &bytesWritten, NULL))
- @throw [OFWriteFailedException
- exceptionWithObject: self
- requestedLength: j * 2
- bytesWritten: bytesWritten * 2
- errNo: EIO];
- } else {
- void *pool = objc_autoreleasePoolPush();
- OFString *string = [OFString stringWithUTF16String: tmp
- length: j];
- OFStringEncoding encoding =
- codepageToEncoding(GetConsoleOutputCP());
- size_t nativeLen = [string
- cStringLengthWithEncoding: encoding];
-
- if (nativeLen > UINT32_MAX)
- @throw [OFOutOfRangeException exception];
-
- if (!WriteConsoleA(_handle,
- [string cStringWithEncoding: encoding],
- (DWORD)nativeLen, &bytesWritten, NULL))
- @throw [OFWriteFailedException
- exceptionWithObject: self
- requestedLength: nativeLen
- bytesWritten: bytesWritten
- errNo: EIO];
-
- objc_autoreleasePoolPop(pool);
- }
-
- if (bytesWritten != j)
- @throw [OFWriteFailedException
- exceptionWithObject: self
- requestedLength: j * 2
- bytesWritten: bytesWritten * 2
- errNo: 0];
- } @finally {
- OFFreeMemory(tmp);
- }
-
- /*
- * We do not count in bytes when writing to the Win32 console. But
- * since any incomplete write is an exception here anyway, we can just
- * return length.
- */
- return length;
-}
-
-- (bool)hasTerminal
-{
- /*
- * We can never get here if there is no terminal, as the initializer
- * changes the class to OFStdIOStream in that case.
- */
- return true;
-}
-
-- (int)columns
-{
- CONSOLE_SCREEN_BUFFER_INFO csbi;
-
- if (!GetConsoleScreenBufferInfo(_handle, &csbi))
- return -1;
-
- return csbi.dwSize.X;
-}
-
-- (int)rows
-{
- /*
- * The buffer size returned is almost always larger than the window
- * size, so this is useless.
- */
- return -1;
-}
-
-- (void)setForegroundColor: (OFColor *)color
-{
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- float red, green, blue;
-
- if (!GetConsoleScreenBufferInfo(_handle, &csbi))
- return;
-
- csbi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN |
- FOREGROUND_BLUE | FOREGROUND_INTENSITY);
-
- [color getRed: &red green: &green blue: &blue alpha: NULL];
-
- if (red >= 0.25)
- csbi.wAttributes |= FOREGROUND_RED;
- if (green >= 0.25)
- csbi.wAttributes |= FOREGROUND_GREEN;
- if (blue >= 0.25)
- csbi.wAttributes |= FOREGROUND_BLUE;
-
- if (red >= 0.75 || green >= 0.75 || blue >= 0.75)
- csbi.wAttributes |= FOREGROUND_INTENSITY;
-
- SetConsoleTextAttribute(_handle, csbi.wAttributes);
-}
-
-- (void)setBackgroundColor: (OFColor *)color
-{
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- float red, green, blue;
-
- if (!GetConsoleScreenBufferInfo(_handle, &csbi))
- return;
-
- csbi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN |
- BACKGROUND_BLUE | BACKGROUND_INTENSITY);
-
- [color getRed: &red green: &green blue: &blue alpha: NULL];
-
- if (red >= 0.25)
- csbi.wAttributes |= BACKGROUND_RED;
- if (green >= 0.25)
- csbi.wAttributes |= BACKGROUND_GREEN;
- if (blue >= 0.25)
- csbi.wAttributes |= BACKGROUND_BLUE;
-
- if (red >= 0.75 || green >= 0.75 || blue >= 0.75)
- csbi.wAttributes |= BACKGROUND_INTENSITY;
-
- SetConsoleTextAttribute(_handle, csbi.wAttributes);
-}
-
-- (void)reset
-{
- SetConsoleTextAttribute(_handle, _attributes);
-}
-
-- (void)clear
-{
- static COORD zero = { 0, 0 };
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- DWORD bytesWritten;
-
- if (!GetConsoleScreenBufferInfo(_handle, &csbi))
- return;
-
- if (!FillConsoleOutputCharacter(_handle, ' ',
- csbi.dwSize.X * csbi.dwSize.Y, zero, &bytesWritten))
- return;
-
- if (!FillConsoleOutputAttribute(_handle, csbi.wAttributes,
- csbi.dwSize.X * csbi.dwSize.Y, zero, &bytesWritten))
- return;
-
- SetConsoleCursorPosition(_handle, zero);
-}
-
-- (void)eraseLine
-{
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- DWORD bytesWritten;
-
- if (!GetConsoleScreenBufferInfo(_handle, &csbi))
- return;
-
- csbi.dwCursorPosition.X = 0;
-
- if (!FillConsoleOutputCharacter(_handle, ' ', csbi.dwSize.X,
- csbi.dwCursorPosition, &bytesWritten))
- return;
-
- FillConsoleOutputAttribute(_handle, csbi.wAttributes, csbi.dwSize.X,
- csbi.dwCursorPosition, &bytesWritten);
-}
-
-- (void)setCursorColumn: (unsigned int)column
-{
- CONSOLE_SCREEN_BUFFER_INFO csbi;
-
- if (!GetConsoleScreenBufferInfo(_handle, &csbi))
- return;
-
- csbi.dwCursorPosition.X = column;
-
- SetConsoleCursorPosition(_handle, csbi.dwCursorPosition);
-}
-
-- (void)setCursorPosition: (OFPoint)position
-{
- if (position.x < 0 || position.y < 0)
- @throw [OFInvalidArgumentException exception];
-
- SetConsoleCursorPosition(_handle, (COORD){ position.x, position.y });
-}
-
-- (void)setRelativeCursorPosition: (OFPoint)position
-{
- CONSOLE_SCREEN_BUFFER_INFO csbi;
-
- if (!GetConsoleScreenBufferInfo(_handle, &csbi))
- return;
-
- csbi.dwCursorPosition.X += position.x;
- csbi.dwCursorPosition.Y += position.y;
-
- SetConsoleCursorPosition(_handle, csbi.dwCursorPosition);
-}
-@end
Index: src/OFWindowsRegistryKey.h
==================================================================
--- src/OFWindowsRegistryKey.h
+++ src/OFWindowsRegistryKey.h
@@ -72,73 +72,53 @@
/**
* @brief Opens the subkey at the specified path.
*
* @param path The path of the subkey to open
- * @param securityAndAccessRights Please refer to the `RegOpenKeyEx()`
- * documentation for `samDesired`
- * @return The subkey with the specified path
- */
-- (OFWindowsRegistryKey *)openSubkeyAtPath: (OFString *)path
- securityAndAccessRights: (REGSAM)securityAndAccessRights;
-
-/**
- * @brief Opens the subkey at the specified path.
- *
- * @param path The path of the subkey to open
- * @param options Please refer to the `RegOpenKeyEx()` documentation for
- * `ulOptions`. Usually 0.
- * @param securityAndAccessRights Please refer to the `RegOpenKeyEx()`
- * documentation for `samDesired`
- * @return The subkey with the specified path
- */
-- (OFWindowsRegistryKey *)openSubkeyAtPath: (OFString *)path
- options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights;
-
-/**
- * @brief Creates a subkey at the specified path or opens it if it already
- * exists.
- *
- * @param path The path of the subkey to create
- * @param securityAndAccessRights Please refer to the `RegCreateKeyEx()`
- * documentation for `samDesired`
- * @return The subkey with the specified path
- */
-- (OFWindowsRegistryKey *)createSubkeyAtPath: (OFString *)path
- securityAndAccessRights: (REGSAM)securityAndAccessRights;
-
-/**
- * @brief Creates a subkey at the specified path or opens it if it already
- * exists.
- *
- * @param path The path of the subkey to create
- * @param options Please refer to the `RegCreateKeyEx()` documentation.
- * Usually 0.
- * @param securityAndAccessRights Please refer to the `RegCreateKeyEx()`
- * documentation for `samDesired`
- * @param securityAttributes Please refer to the `RegCreateKeyEx()`
- * documentation for `lpSecurityAttributes`. Usually
- * NULL.
- * @param disposition Whether the key was created or already existed. Please
- * refer to the `RegCreateKeyEx()` documentation for
- * `lpdwDisposition`.
- * @return The subkey with the specified path
- */
-- (OFWindowsRegistryKey *)
- createSubkeyAtPath: (OFString *)path
- options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights
- securityAttributes: (nullable SECURITY_ATTRIBUTES *)securityAttributes
- disposition: (nullable DWORD *)disposition;
+ * @param accessRights Please refer to the `RegOpenKeyEx()` documentation for
+ * `samDesired`
+ * @param options Please refer to the `RegOpenKeyEx()` documentation for
+ * `ulOptions`. Usually 0.
+ * @return The subkey with the specified path
+ * @throw OFOpenWindowsRegistryKeyFailedException Opening the key failed
+ */
+- (OFWindowsRegistryKey *)openSubkeyAtPath: (OFString *)path
+ accessRights: (REGSAM)accessRights
+ options: (DWORD)options;
+/**
+ * @brief Creates a subkey at the specified path or opens it if it already
+ * exists.
+ *
+ * @param path The path of the subkey to create
+ * @param accessRights Please refer to the `RegCreateKeyEx()` documentation for
+ * `samDesired`
+ * @param securityAttributes Please refer to the `RegCreateKeyEx()`
+ * documentation for `lpSecurityAttributes`. Usually
+ * NULL.
+ * @param options Please refer to the `RegCreateKeyEx()` documentation for
+ * `dwOptions`. Usually 0.
+ * @param disposition A pointer to a variable that will be set to whether the
+ * key was created or already existed, or `NULL`. Please
+ * refer to the `RegCreateKeyEx()` documentation for
+ * `lpdwDisposition`.
+ * @return The subkey with the specified path
+ * @throw OFCreateWindowsRegistryKeyFailedException Creating the key failed
+ */
+- (OFWindowsRegistryKey *)
+ createSubkeyAtPath: (OFString *)path
+ accessRights: (REGSAM)accessRights
+ securityAttributes: (nullable SECURITY_ATTRIBUTES *)securityAttributes
+ options: (DWORD)options
+ disposition: (nullable DWORD *)disposition;
/**
* @brief Returns the data for the specified value at the specified path.
*
* @param name The name of the value to return
* @param type A pointer to store the type of the value, or NULL
* @return The data for the specified value
+ * @throw OFGetWindowsRegistryValueFailedException Getting the value failed
*/
- (nullable OFData *)dataForValueNamed: (nullable OFString *)name
type: (nullable DWORD *)type;
/**
@@ -145,10 +125,11 @@
* @brief Sets the data for the specified value.
*
* @param data The data to set the value to
* @param name The name of the value to set
* @param type The type for the value
+ * @throw OFSetWindowsRegistryValueFailedException Setting the value failed
*/
- (void)setData: (nullable OFData *)data
forValueNamed: (nullable OFString *)name
type: (DWORD)type;
@@ -155,28 +136,33 @@
/**
* @brief Returns the string for the specified value at the specified path.
*
* @param name The name of the value to return
* @return The string for the specified value
+ * @throw OFGetWindowsRegistryValueFailedException Getting the value failed
+ * @throw OFInvalidEncodingException The encoding of the value is invalid
*/
- (nullable OFString *)stringForValueNamed: (nullable OFString *)name;
/**
* @brief Returns the string for the specified value at the specified path.
*
* @param name The name of the value to return
* @param type A pointer to store the type of the value, or NULL
* @return The string for the specified value
+ * @throw OFGetWindowsRegistryValueFailedException Getting the value failed
+ * @throw OFInvalidEncodingException The encoding of the value is invalid
*/
- (nullable OFString *)stringForValueNamed: (nullable OFString *)name
type: (nullable DWORD *)type;
/**
* @brief Sets the string for the specified value.
*
* @param string The string to set the value to
* @param name The name of the value to set
+ * @throw OFSetWindowsRegistryValueFailedException Setting the value failed
*/
- (void)setString: (nullable OFString *)string
forValueNamed: (nullable OFString *)name;
/**
@@ -183,10 +169,11 @@
* @brief Sets the string for the specified value.
*
* @param string The string to set the value to
* @param name The name of the value to set
* @param type The type for the value
+ * @throw OFSetWindowsRegistryValueFailedException Setting the value failed
*/
- (void)setString: (nullable OFString *)string
forValueNamed: (nullable OFString *)name
type: (DWORD)type;
@@ -193,48 +180,56 @@
/**
* @brief Returns the DWORD for the specified value at the specified path.
*
* @param name The name of the value to return
* @return The DWORD for the specified value
+ * @throw OFGetWindowsRegistryValueFailedException Getting the value failed
+ * @throw OFUndefinedKeyException There is no value with the specified key
*/
- (uint32_t)DWORDForValueNamed: (nullable OFString *)name;
/**
* @brief Sets the DWORD for the specified value.
*
* @param dword The DWORD to set the value to
* @param name The name of the value to set
+ * @throw OFSetWindowsRegistryValueFailedException Setting the value failed
*/
- (void)setDWORD: (uint32_t)dword forValueNamed: (nullable OFString *)name;
/**
* @brief Returns the QWORD for the specified value at the specified path.
*
* @param name The name of the value to return
* @return The QWORD for the specified value
+ * @throw OFGetWindowsRegistryValueFailedException Getting the value failed
+ * @throw OFUndefinedKeyException There is no value with the specified key
*/
- (uint64_t)QWORDForValueNamed: (nullable OFString *)name;
/**
* @brief Sets the QWORD for the specified value.
*
* @param qword The QWORD to set the value to
* @param name The name of the value to set
+ * @throw OFSetWindowsRegistryValueFailedException Setting the value failed
*/
- (void)setQWORD: (uint64_t)qword forValueNamed: (nullable OFString *)name;
/**
* @brief Deletes the specified value.
*
* @param name The value to delete
+ * @throw OFDeleteWindowsRegistryValueFailedException Deleting the value failed
*/
- (void)deleteValueNamed: (nullable OFString *)name;
/**
* @brief Deletes the specified subkey.
*
* @param subkeyPath The path of the subkey to delete
+ * @throw OFDeleteWindowsRegistryKeyFailedException Deleting the key failed
*/
- (void)deleteSubkeyAtPath: (OFString *)subkeyPath;
@end
OF_ASSUME_NONNULL_END
Index: src/OFWindowsRegistryKey.m
==================================================================
--- src/OFWindowsRegistryKey.m
+++ src/OFWindowsRegistryKey.m
@@ -91,86 +91,67 @@
[super dealloc];
}
- (OFWindowsRegistryKey *)openSubkeyAtPath: (OFString *)path
- securityAndAccessRights: (REGSAM)securityAndAccessRights
-{
- return [self openSubkeyAtPath: path
- options: 0
- securityAndAccessRights: securityAndAccessRights];
-}
-
-- (OFWindowsRegistryKey *)openSubkeyAtPath: (OFString *)path
+ accessRights: (REGSAM)accessRights
options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights
{
void *pool = objc_autoreleasePoolPush();
LSTATUS status;
HKEY subKey;
if ([OFSystemInfo isWindowsNT])
status = RegOpenKeyExW(_hKey, path.UTF16String, options,
- securityAndAccessRights, &subKey);
+ accessRights, &subKey);
else
status = RegOpenKeyExA(_hKey,
[path cStringWithEncoding: [OFLocale encoding]], options,
- securityAndAccessRights, &subKey);
+ accessRights, &subKey);
if (status != ERROR_SUCCESS)
@throw [OFOpenWindowsRegistryKeyFailedException
exceptionWithRegistryKey: self
path: path
+ accessRights: accessRights
options: options
- securityAndAccessRights: securityAndAccessRights
status: status];
objc_autoreleasePoolPop(pool);
return [[[OFWindowsRegistryKey alloc] of_initWithHKey: subKey
close: true]
autorelease];
}
-- (OFWindowsRegistryKey *)createSubkeyAtPath: (OFString *)path
- securityAndAccessRights: (REGSAM)securityAndAccessRights
-{
- return [self createSubkeyAtPath: path
- options: 0
- securityAndAccessRights: securityAndAccessRights
- securityAttributes: NULL
- disposition: NULL];
-}
-
- (OFWindowsRegistryKey *)
- createSubkeyAtPath: (OFString *)path
- options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights
- securityAttributes: (LPSECURITY_ATTRIBUTES)securityAttributes
- disposition: (DWORD *)disposition
+ createSubkeyAtPath: (OFString *)path
+ accessRights: (REGSAM)accessRights
+ securityAttributes: (LPSECURITY_ATTRIBUTES)securityAttributes
+ options: (DWORD)options
+ disposition: (DWORD *)disposition
{
void *pool = objc_autoreleasePoolPush();
LSTATUS status;
HKEY subKey;
if ([OFSystemInfo isWindowsNT])
status = RegCreateKeyExW(_hKey, path.UTF16String, 0,
- NULL, options, securityAndAccessRights, securityAttributes,
- &subKey, NULL);
+ NULL, options, accessRights, securityAttributes, &subKey,
+ NULL);
else
status = RegCreateKeyExA(_hKey,
[path cStringWithEncoding: [OFLocale encoding]], 0, NULL,
- options, securityAndAccessRights, securityAttributes,
- &subKey, NULL);
+ options, accessRights, securityAttributes, &subKey, NULL);
if (status != ERROR_SUCCESS)
@throw [OFCreateWindowsRegistryKeyFailedException
exceptionWithRegistryKey: self
path: path
- options: options
- securityAndAccessRights: securityAndAccessRights
+ accessRights: accessRights
securityAttributes: securityAttributes
+ options: options
status: status];
objc_autoreleasePoolPop(pool);
return [[[OFWindowsRegistryKey alloc] of_initWithHKey: subKey
Index: src/OFXMLAttribute.h
==================================================================
--- src/OFXMLAttribute.h
+++ src/OFXMLAttribute.h
@@ -22,18 +22,18 @@
/**
* @class OFXMLAttribute OFXMLAttribute.h ObjFW/OFXMLAttribute.h
*
* @brief A representation of an attribute of an XML element as an object.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFXMLAttribute: OFXMLNode
{
#if defined(OF_XML_ELEMENT_M) || defined(OF_XML_PARSER_M)
@public
#endif
OFString *_name, *_Nullable _namespace, *_stringValue;
bool _useDoubleQuotes;
- OF_RESERVE_IVARS(OFXMLAttribute, 4)
}
/**
* @brief The name of the attribute.
*/
@@ -89,11 +89,11 @@
* @param stringValue The string value of the attribute
* @return An initialized OFXMLAttribute with the specified parameters
*/
- (instancetype)initWithName: (OFString *)name
namespace: (nullable OFString *)nameSpace
- stringValue: (OFString *)stringValue;
+ stringValue: (OFString *)stringValue OF_DESIGNATED_INITIALIZER;
- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end
OF_ASSUME_NONNULL_END
Index: src/OFXMLAttribute.m
==================================================================
--- src/OFXMLAttribute.m
+++ src/OFXMLAttribute.m
@@ -68,25 +68,35 @@
return self;
}
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
- self = [super of_init];
+ void *pool;
+ OFString *name, *namespace, *stringValue;
@try {
- void *pool = objc_autoreleasePoolPush();
+ pool = objc_autoreleasePoolPush();
if (![element.name isEqual: self.className] ||
![element.namespace isEqual: OFSerializationNS])
@throw [OFInvalidArgumentException exception];
- _name = [[element attributeForName: @"name"].stringValue copy];
- _namespace = [[element attributeForName: @"namespace"]
- .stringValue copy];
- _stringValue = [[element attributeForName: @"stringValue"]
- .stringValue copy];
+ name = [element attributeForName: @"name"].stringValue;
+ namespace = [element attributeForName: @"namespace"]
+ .stringValue;
+ stringValue = [element attributeForName: @"stringValue"]
+ .stringValue;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ self = [self initWithName: name
+ namespace: namespace
+ stringValue: stringValue];
+ @try {
objc_autoreleasePoolPop(pool);
} @catch (id e) {
[self release];
@throw e;
}
@@ -176,10 +186,11 @@
return [element autorelease];
}
- (OFString *)description
{
- return [OFString stringWithFormat: @"",
- _name, _namespace, _stringValue];
+ return [OFString stringWithFormat: @"<%@: name=%@, namespace=%@, "
+ @"stringValue=%@>",
+ self.class, _name, _namespace,
+ _stringValue];
}
@end
Index: src/OFXMLCDATA.h
==================================================================
--- src/OFXMLCDATA.h
+++ src/OFXMLCDATA.h
@@ -20,14 +20,14 @@
/**
* @class OFXMLCDATA OFXMLCDATA.h ObjFW/OFXMLCDATA.h
*
* @brief A class representing XML CDATA.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFXMLCDATA: OFXMLNode
{
OFString *_CDATA;
- OF_RESERVE_IVARS(OFXMLCDATA, 4)
}
/**
* @brief Creates a new OFXMLCDATA with the specified string.
*
Index: src/OFXMLCDATA.m
==================================================================
--- src/OFXMLCDATA.m
+++ src/OFXMLCDATA.m
@@ -114,21 +114,10 @@
[ret retain];
objc_autoreleasePoolPop(pool);
return [ret autorelease];
}
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
-{
- return self.XMLString;
-}
-
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
- level: (unsigned int)level
-{
- return self.XMLString;
-}
-
- (OFString *)description
{
return self.XMLString;
}
Index: src/OFXMLCharacters.h
==================================================================
--- src/OFXMLCharacters.h
+++ src/OFXMLCharacters.h
@@ -20,14 +20,14 @@
/**
* @class OFXMLCharacters OFXMLCharacters.h ObjFW/OFXMLCharacters.h
*
* @brief A class representing XML characters.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFXMLCharacters: OFXMLNode
{
OFString *_characters;
- OF_RESERVE_IVARS(OFXMLCharacters, 4)
}
/**
* @brief Creates a new OFXMLCharacters with the specified string.
*
Index: src/OFXMLCharacters.m
==================================================================
--- src/OFXMLCharacters.m
+++ src/OFXMLCharacters.m
@@ -106,28 +106,17 @@
- (OFString *)XMLString
{
return _characters.stringByXMLEscaping;
}
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
-{
- return _characters.stringByXMLEscaping;
-}
-
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
- level: (unsigned int)level
-{
- return _characters.stringByXMLEscaping;
-}
-
- (OFString *)description
{
- return _characters.stringByXMLEscaping;
+ return self.XMLString;
}
- (OFXMLElement *)XMLElementBySerializing
{
return [OFXMLElement elementWithName: self.className
namespace: OFSerializationNS
stringValue: _characters];
}
@end
Index: src/OFXMLComment.h
==================================================================
--- src/OFXMLComment.h
+++ src/OFXMLComment.h
@@ -20,14 +20,14 @@
/**
* @class OFXMLComment OFXMLComment.h ObjFW/OFXMLComment.h
*
* @brief A class for representing XML comments.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFXMLComment: OFXMLNode
{
OFString *_text;
- OF_RESERVE_IVARS(OFXMLComment, 4)
}
/**
* @brief The comment text.
*/
Index: src/OFXMLComment.m
==================================================================
--- src/OFXMLComment.m
+++ src/OFXMLComment.m
@@ -103,44 +103,17 @@
- (OFString *)XMLString
{
return [OFString stringWithFormat: @"", _text];
}
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
-{
- return [OFString stringWithFormat: @"", _text];
-}
-
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
- level: (unsigned int)level
-{
- OFString *ret;
-
- if (indentation > 0 && level > 0) {
- char *whitespaces = OFAllocMemory((level * indentation) + 1, 1);
- memset(whitespaces, ' ', level * indentation);
- whitespaces[level * indentation] = 0;
-
- @try {
- ret = [OFString stringWithFormat: @"%s",
- whitespaces, _text];
- } @finally {
- OFFreeMemory(whitespaces);
- }
- } else
- ret = [OFString stringWithFormat: @"", _text];
-
- return ret;
-}
-
- (OFString *)description
{
- return [OFString stringWithFormat: @"", _text];
+ return self.XMLString;
}
- (OFXMLElement *)XMLElementBySerializing
{
return [OFXMLElement elementWithName: self.className
namespace: OFSerializationNS
stringValue: _text];
}
@end
Index: src/OFXMLElement.h
==================================================================
--- src/OFXMLElement.h
+++ src/OFXMLElement.h
@@ -30,11 +30,11 @@
*
* @brief A class which stores an XML element.
*/
@interface OFXMLElement: OFXMLNode
{
- OFString *_name, *_Nullable _namespace, *_Nullable _defaultNamespace;
+ OFString *_name, *_Nullable _namespace;
OFMutableArray OF_GENERIC(OFXMLAttribute *) *_Nullable _attributes;
OFMutableDictionary OF_GENERIC(OFString *, OFString *) *_Nullable
_namespaces;
OFMutableArray OF_GENERIC(OFXMLNode *) *_Nullable _children;
OF_RESERVE_IVARS(OFXMLElement, 4)
@@ -53,16 +53,10 @@
#else
@property OF_NULLABLE_PROPERTY (copy, nonatomic,
getter=namespace, setter=setNamespace:) OFString *nameSpace;
#endif
-/**
- * @brief The default namespace for the element to be used if there is no
- * parent.
- */
-@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *defaultNamespace;
-
/**
* @brief An array with the attributes of the element.
*/
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
OFArray OF_GENERIC(OFXMLAttribute *) *attributes;
@@ -120,33 +114,32 @@
*/
+ (instancetype)elementWithName: (OFString *)name
namespace: (nullable OFString *)nameSpace
stringValue: (nullable OFString *)stringValue;
-/**
- * @brief Creates a new element with the specified element.
- *
- * @param element An OFXMLElement to initialize the OFXMLElement with
- * @return A new autoreleased OFXMLElement with the contents of the specified
- * element
- */
-+ (instancetype)elementWithElement: (OFXMLElement *)element;
-
/**
* @brief Parses the string and returns an OFXMLElement for it.
*
* @param string The string to parse
* @return A new autoreleased OFXMLElement with the contents of the string
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundPrefixException A prefix was used that was not bound to any
+ * namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
*/
+ (instancetype)elementWithXMLString: (OFString *)string;
/**
* @brief Parses the specified stream and returns an OFXMLElement for it.
*
* @param stream The stream to parse
* @return A new autoreleased OFXMLElement with the contents of the specified
* stream
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundPrefixException A prefix was used that was not bound to any
+ * namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
*/
+ (instancetype)elementWithStream: (OFStream *)stream;
- (instancetype)init OF_UNAVAILABLE;
@@ -178,11 +171,12 @@
* @param nameSpace The namespace for the element
* @return An initialized OFXMLElement with the specified element name and
* namespace
*/
- (instancetype)initWithName: (OFString *)name
- namespace: (nullable OFString *)nameSpace;
+ namespace: (nullable OFString *)nameSpace
+ OF_DESIGNATED_INITIALIZER;
/**
* @brief Initializes an already allocated OFXMLElement with the specified name,
* namespace and value.
*
@@ -194,40 +188,36 @@
*/
- (instancetype)initWithName: (OFString *)name
namespace: (nullable OFString *)nameSpace
stringValue: (nullable OFString *)stringValue;
-/**
- * @brief Initializes an already allocated OFXMLElement with the specified
- * element.
- *
- * @param element An OFXMLElement to initialize the OFXMLElement with
- * @return A new autoreleased OFXMLElement with the contents of the specified
- * element
- */
-- (instancetype)initWithElement: (OFXMLElement *)element;
-
/**
* @brief Parses the string and initializes an already allocated OFXMLElement
* with it.
*
* @param string The string to parse
* @return An initialized OFXMLElement with the contents of the string
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundPrefixException A prefix was used that was not bound to any
+ * namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
*/
- (instancetype)initWithXMLString: (OFString *)string;
/**
* @brief Parses the specified stream and initializes an already allocated
* OFXMLElement with it.
*
* @param stream The stream to parse
* @return An initialized OFXMLElement with the contents of the specified stream
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundPrefixException A prefix was used that was not bound to any
+ * namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
*/
- (instancetype)initWithStream: (OFStream *)stream;
-- (instancetype)initWithSerialization: (OFXMLElement *)element;
-
/**
* @brief Sets a prefix for a namespace.
*
* @param prefix The prefix for the namespace
* @param nameSpace The namespace for which the prefix is set
@@ -347,12 +337,12 @@
/**
* @brief Removes the child at the specified index.
*
* @param index The index of the child to remove
*/
-
- (void)removeChildAtIndex: (size_t)index;
+
/**
* @brief Replaces the first child that is equal to the specified OFXMLNode
* with the specified node.
*
* @param child The child to replace
@@ -371,12 +361,12 @@
/**
* @brief Returns all children that have the specified namespace.
*
* @return All children that have the specified namespace
*/
-- (OFArray OF_GENERIC(OFXMLElement *) *)elementsForNamespace:
- (nullable OFString *)elementNS;
+- (OFArray OF_GENERIC(OFXMLElement *) *)
+ elementsForNamespace: (nullable OFString *)elementNS;
/**
* @brief Returns the first child element with the specified name.
*
* @param elementName The name of the element
@@ -388,12 +378,12 @@
* @brief Returns the child elements with the specified name.
*
* @param elementName The name of the elements
* @return The child elements with the specified name
*/
-- (OFArray OF_GENERIC(OFXMLElement *) *)elementsForName:
- (OFString *)elementName;
+- (OFArray OF_GENERIC(OFXMLElement *) *)
+ elementsForName: (OFString *)elementName;
/**
* @brief Returns the first child element with the specified name and namespace.
*
* @param elementName The name of the element
@@ -411,10 +401,38 @@
* @return The child elements with the specified name and namespace
*/
- (OFArray OF_GENERIC(OFXMLElement *) *)
elementsForName: (OFString *)elementName
namespace: (nullable OFString *)elementNS;
+
+/**
+ * @brief Returns an OFString representing the OFXMLElement as an XML string
+ * with the specified indentation per level.
+ *
+ * @param indentation The indentation per level
+ * @return An OFString representing the OFXMLNode as an XML string with
+ * indentation
+ * @throw OFUnboundNamespaceException The node uses a namespace that was not
+ * bound to a prefix in a context where it
+ * needs a prefix
+ */
+- (OFString *)XMLStringWithIndentation: (unsigned int)indentation;
+
+/**
+ * @brief Returns an OFString representing the OFXMLElement as an XML string
+ * with the specified default namespace and indentation per level.
+ *
+ * @param defaultNS The default namespace
+ * @param indentation The indentation per level
+ * @return An OFString representing the OFXMLNode as an XML string with
+ * indentation
+ * @throw OFUnboundNamespaceException The node uses a namespace that was not
+ * bound to a prefix in a context where it
+ * needs a prefix
+ */
+- (OFString *)XMLStringWithDefaultNamespace: (OFString *)defaultNS
+ indentation: (unsigned int)indentation;
@end
OF_ASSUME_NONNULL_END
#import "OFXMLElement+Serialization.h"
Index: src/OFXMLElement.m
==================================================================
--- src/OFXMLElement.m
+++ src/OFXMLElement.m
@@ -71,11 +71,10 @@
}
@end
@implementation OFXMLElement
@synthesize name = _name, namespace = _namespace;
-@synthesize defaultNamespace = _defaultNamespace;
+ (instancetype)elementWithName: (OFString *)name
{
return [[[self alloc] initWithName: name] autorelease];
}
@@ -101,15 +100,10 @@
return [[[self alloc] initWithName: name
namespace: namespace
stringValue: stringValue] autorelease];
}
-+ (instancetype)elementWithElement: (OFXMLElement *)element
-{
- return [[[self alloc] initWithElement: element] autorelease];
-}
-
+ (instancetype)elementWithXMLString: (OFString *)string
{
return [[[self alloc] initWithXMLString: string] autorelease];
}
@@ -137,17 +131,10 @@
}
- (instancetype)initWithName: (OFString *)name
namespace: (OFString *)namespace
{
- return [self initWithName: name namespace: namespace stringValue: nil];
-}
-
-- (instancetype)initWithName: (OFString *)name
- namespace: (OFString *)namespace
- stringValue: (OFString *)stringValue
-{
self = [super of_init];
@try {
if (name == nil)
@throw [OFInvalidArgumentException exception];
@@ -157,36 +144,27 @@
_namespaces = [[OFMutableDictionary alloc]
initWithKeysAndObjects:
@"http://www.w3.org/XML/1998/namespace", @"xml",
@"http://www.w3.org/2000/xmlns/", @"xmlns", nil];
-
- if (stringValue != nil)
- self.stringValue = stringValue;
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
-- (instancetype)initWithElement: (OFXMLElement *)element
+- (instancetype)initWithName: (OFString *)name
+ namespace: (OFString *)namespace
+ stringValue: (OFString *)stringValue
{
- self = [super of_init];
+ self = [self initWithName: name namespace: namespace];
@try {
- if (element == nil ||
- ![element isKindOfClass: [OFXMLElement class]])
- @throw [OFInvalidArgumentException exception];
-
- _name = [element->_name copy];
- _namespace = [element->_namespace copy];
- _defaultNamespace = [element->_defaultNamespace copy];
- _attributes = [element->_attributes mutableCopy];
- _namespaces = [element->_namespaces mutableCopy];
- _children = [element->_children mutableCopy];
+ if (stringValue != nil)
+ self.stringValue = stringValue;
} @catch (id e) {
[self release];
@throw e;
}
@@ -194,110 +172,167 @@
}
- (instancetype)initWithXMLString: (OFString *)string
{
void *pool;
- OFXMLParser *parser;
- OFXMLElementBuilder *builder;
- OFXMLElementElementBuilderDelegate *delegate;
-
- [self release];
-
- if (string == nil)
- @throw [OFInvalidArgumentException exception];
-
- pool = objc_autoreleasePoolPush();
-
- parser = [OFXMLParser parser];
- builder = [OFXMLElementBuilder builder];
- delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
- autorelease];
-
- parser.delegate = builder;
- builder.delegate = delegate;
-
- [parser parseString: string];
-
- if (!parser.hasFinishedParsing)
- @throw [OFMalformedXMLException exceptionWithParser: parser];
-
- self = [delegate->_element retain];
-
- objc_autoreleasePoolPop(pool);
+ OFXMLElement *element;
+
+ @try {
+ OFXMLParser *parser;
+ OFXMLElementBuilder *builder;
+ OFXMLElementElementBuilderDelegate *delegate;
+
+ if (string == nil)
+ @throw [OFInvalidArgumentException exception];
+
+ pool = objc_autoreleasePoolPush();
+
+ parser = [OFXMLParser parser];
+ builder = [OFXMLElementBuilder builder];
+ delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
+ autorelease];
+
+ parser.delegate = builder;
+ builder.delegate = delegate;
+
+ [parser parseString: string];
+
+ if (!parser.hasFinishedParsing)
+ @throw [OFMalformedXMLException
+ exceptionWithParser: parser];
+
+ element = delegate->_element;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ self = [self initWithName: element->_name
+ namespace: element->_namespace];
+
+ @try {
+ [_attributes release];
+ _attributes = [element->_attributes retain];
+ [_namespaces release];
+ _namespaces = [element->_namespaces retain];
+ [_children release];
+ _children = [element->_children retain];
+
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
return self;
}
- (instancetype)initWithStream: (OFStream *)stream
{
void *pool;
- OFXMLParser *parser;
- OFXMLElementBuilder *builder;
- OFXMLElementElementBuilderDelegate *delegate;
-
- [self release];
-
- pool = objc_autoreleasePoolPush();
-
- parser = [OFXMLParser parser];
- builder = [OFXMLElementBuilder builder];
- delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
- autorelease];
-
- parser.delegate = builder;
- builder.delegate = delegate;
-
- [parser parseStream: stream];
-
- if (!parser.hasFinishedParsing)
- @throw [OFMalformedXMLException exceptionWithParser: parser];
-
- self = [delegate->_element retain];
-
- objc_autoreleasePoolPop(pool);
+ OFXMLElement *element;
+
+ @try {
+ OFXMLParser *parser;
+ OFXMLElementBuilder *builder;
+ OFXMLElementElementBuilderDelegate *delegate;
+
+ pool = objc_autoreleasePoolPush();
+
+ parser = [OFXMLParser parser];
+ builder = [OFXMLElementBuilder builder];
+ delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
+ autorelease];
+
+ parser.delegate = builder;
+ builder.delegate = delegate;
+
+ [parser parseStream: stream];
+
+ if (!parser.hasFinishedParsing)
+ @throw [OFMalformedXMLException
+ exceptionWithParser: parser];
+
+ element = delegate->_element;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ self = [self initWithName: element->_name
+ namespace: element->_namespace];
+
+ @try {
+ [_attributes release];
+ _attributes = [element->_attributes retain];
+ [_namespaces release];
+ _namespaces = [element->_namespaces retain];
+ [_children release];
+ _children = [element->_children retain];
+
+ objc_autoreleasePoolPop(pool);
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
return self;
}
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
- self = [super of_init];
+ void *pool;
+ OFString *name, *namespace;
@try {
- void *pool = objc_autoreleasePoolPush();
- OFXMLElement *attributesElement, *namespacesElement;
- OFXMLElement *childrenElement;
- OFEnumerator *keyEnumerator, *objectEnumerator;
- OFString *key, *object;
+ pool = objc_autoreleasePoolPush();
if (![element.name isEqual: self.className] ||
![element.namespace isEqual: OFSerializationNS])
@throw [OFInvalidArgumentException exception];
- _name = [[element attributeForName: @"name"].stringValue copy];
- _namespace = [[element attributeForName: @"namespace"]
- .stringValue copy];
- _defaultNamespace = [[element attributeForName:
- @"defaultNamespace"].stringValue copy];
+ name = [element attributeForName: @"name"].stringValue;
+ namespace =
+ [element attributeForName: @"namespace"].stringValue;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ self = [self initWithName: name namespace: namespace];
+
+ @try {
+ OFXMLElement *attributesElement, *namespacesElement;
+ OFXMLElement *childrenElement;
+ OFEnumerator *keyEnumerator, *objectEnumerator;
+ OFString *key, *object;
attributesElement = [[element
elementForName: @"attributes"
- namespace: OFSerializationNS] elementsForNamespace:
- OFSerializationNS].firstObject;
+ namespace: OFSerializationNS]
+ elementsForNamespace: OFSerializationNS].firstObject;
namespacesElement = [[element
elementForName: @"namespaces"
- namespace: OFSerializationNS] elementsForNamespace:
- OFSerializationNS].firstObject;
+ namespace: OFSerializationNS]
+ elementsForNamespace: OFSerializationNS].firstObject;
childrenElement = [[element
elementForName: @"children"
- namespace: OFSerializationNS] elementsForNamespace:
- OFSerializationNS].firstObject;
+ namespace: OFSerializationNS]
+ elementsForNamespace: OFSerializationNS].firstObject;
+ [_attributes release];
+ _attributes = nil;
_attributes = [attributesElement.objectByDeserializing
mutableCopy];
+
+ [_namespaces release];
+ _namespaces = nil;
_namespaces = [namespacesElement.objectByDeserializing
mutableCopy];
+
+ [_children release];
+ _children = nil;
_children = [childrenElement.objectByDeserializing
mutableCopy];
/* Sanity checks */
if ((_attributes != nil && ![_attributes isKindOfClass:
@@ -330,13 +365,10 @@
setObject: @"xml"
forKey: @"http://www.w3.org/XML/1998/namespace"];
[_namespaces setObject: @"xmlns"
forKey: @"http://www.w3.org/2000/xmlns/"];
- if (_name == nil)
- @throw [OFInvalidArgumentException exception];
-
objc_autoreleasePoolPop(pool);
} @catch (id e) {
[self release];
@throw e;
}
@@ -346,11 +378,10 @@
- (void)dealloc
{
[_name release];
[_namespace release];
- [_defaultNamespace release];
[_attributes release];
[_namespaces release];
[_children release];
[super dealloc];
@@ -403,28 +434,22 @@
[ret makeImmutable];
return ret;
}
-- (OFString *)of_XMLStringWithParent: (OFXMLElement *)parent
- namespaces: (OFDictionary *)allNS
- indentation: (unsigned int)indentation
- level: (unsigned int)level OF_DIRECT
+- (OFString *)of_XMLStringWithDefaultNS: (OFString *)defaultNS
+ namespaces: (OFDictionary *)allNS
+ indentation: (unsigned int)indentation
+ level: (unsigned int)level OF_DIRECT
{
void *pool;
char *cString;
size_t length, i;
- OFString *prefix, *parentPrefix;
- OFString *ret;
- OFString *defaultNS;
+ OFString *prefix, *ret;
pool = objc_autoreleasePoolPush();
- parentPrefix = [allNS objectForKey:
- (parent != nil && parent->_namespace != nil
- ? parent->_namespace : (OFString *)@"")];
-
/* Add the namespaces of the current element */
if (allNS != nil) {
OFEnumerator *keyEnumerator = [_namespaces keyEnumerator];
OFEnumerator *objectEnumerator = [_namespaces objectEnumerator];
OFMutableDictionary *tmp;
@@ -441,17 +466,10 @@
allNS = _namespaces;
prefix = [allNS objectForKey:
(_namespace != nil ? _namespace : (OFString *)@"")];
- if (parent != nil && parent->_namespace != nil && parentPrefix == nil)
- defaultNS = parent->_namespace;
- else if (parent != nil && parent->_defaultNamespace != nil)
- defaultNS = parent->_defaultNamespace;
- else
- defaultNS = _defaultNamespace;
-
i = 0;
length = _name.UTF8StringLength + 3 + (level * indentation);
cString = OFAllocMemory(length, 1);
@try {
@@ -459,11 +477,11 @@
i += level * indentation;
/* Start of tag */
cString[i++] = '<';
- if (prefix != nil && ![_namespace isEqual: defaultNS]) {
+ if (prefix.length > 0) {
length += prefix.UTF8StringLength + 1;
cString = OFResizeMemory(cString, length, 1);
memcpy(cString + i, prefix.UTF8String,
prefix.UTF8StringLength);
@@ -473,22 +491,23 @@
memcpy(cString + i, _name.UTF8String, _name.UTF8StringLength);
i += _name.UTF8StringLength;
/* xmlns if necessary */
- if (prefix == nil && ((_namespace != nil &&
- ![_namespace isEqual: defaultNS]) ||
- (_namespace == nil && defaultNS != nil))) {
+ if (prefix.length == 0 && defaultNS != _namespace &&
+ ![defaultNS isEqual: _namespace]) {
length += _namespace.UTF8StringLength + 9;
cString = OFResizeMemory(cString, length, 1);
memcpy(cString + i, " xmlns='", 8);
i += 8;
memcpy(cString + i, _namespace.UTF8String,
_namespace.UTF8StringLength);
i += _namespace.UTF8StringLength;
cString[i++] = '\'';
+
+ defaultNS = _namespace;
}
/* Attributes */
for (OFXMLAttribute *attribute in _attributes) {
void *pool2 = objc_autoreleasePoolPush();
@@ -501,12 +520,12 @@
attribute.stringValue.stringByXMLEscaping;
char delimiter = (attribute->_useDoubleQuotes
? '"' : '\'');
if (attribute->_namespace != nil &&
- (attributePrefix = [allNS objectForKey:
- attribute->_namespace]) == nil)
+ [(attributePrefix = [allNS objectForKey:
+ attribute->_namespace]) length] == 0)
@throw [OFUnboundNamespaceException
exceptionWithNamespace: attribute.namespace
element: self];
length += attributeNameLength + (attributePrefix != nil
@@ -561,19 +580,21 @@
if (ind)
[tmp addItem: "\n"];
if ([child isKindOfClass: [OFXMLElement class]])
childString = [(OFXMLElement *)child
- of_XMLStringWithParent: self
- namespaces: allNS
- indentation: ind
- level: level + 1];
- else
- childString = [child
- XMLStringWithIndentation: ind
- level: level +
- 1];
+ of_XMLStringWithDefaultNS: defaultNS
+ namespaces: allNS
+ indentation: ind
+ level: level +
+ 1];
+ else {
+ childString = child.XMLString;
+ for (unsigned int j = 0;
+ j < ind * (level + 1); j++)
+ [tmp addItem: " "];
+ }
[tmp addItems: childString.UTF8String
count: childString.UTF8StringLength];
}
@@ -594,11 +615,11 @@
i += level * indentation;
}
cString[i++] = '<';
cString[i++] = '/';
- if (prefix != nil) {
+ if (prefix.length > 0) {
length += prefix.UTF8StringLength + 1;
cString = OFResizeMemory(cString, length, 1);
memcpy(cString + i, prefix.UTF8String,
prefix.UTF8StringLength);
@@ -624,31 +645,31 @@
return ret;
}
- (OFString *)XMLString
{
- return [self of_XMLStringWithParent: nil
- namespaces: nil
- indentation: 0
- level: 0];
+ return [self of_XMLStringWithDefaultNS: nil
+ namespaces: nil
+ indentation: 0
+ level: 0];
}
- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
- return [self of_XMLStringWithParent: nil
- namespaces: nil
- indentation: indentation
- level: 0];
+ return [self of_XMLStringWithDefaultNS: nil
+ namespaces: nil
+ indentation: indentation
+ level: 0];
}
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
- level: (unsigned int)level
+- (OFString *)XMLStringWithDefaultNamespace: (OFString *)defaultNS
+ indentation: (unsigned int)indentation
{
- return [self of_XMLStringWithParent: nil
- namespaces: nil
- indentation: indentation
- level: level];
+ return [self of_XMLStringWithDefaultNS: defaultNS
+ namespaces: nil
+ indentation: indentation
+ level: 0];
}
- (OFXMLElement *)XMLElementBySerializing
{
void *pool = objc_autoreleasePoolPush();
@@ -662,14 +683,10 @@
if (_namespace != nil)
[element addAttributeWithName: @"namespace"
stringValue: _namespace];
- if (_defaultNamespace != nil)
- [element addAttributeWithName: @"defaultNamespace"
- stringValue: _defaultNamespace];
-
if (_attributes != nil) {
OFXMLElement *attributesElement;
attributesElement =
[OFXMLElement elementWithName: @"attributes"
@@ -814,12 +831,10 @@
- (void)setPrefix: (OFString *)prefix forNamespace: (OFString *)namespace
{
if (prefix.length == 0)
@throw [OFInvalidArgumentException exception];
- if (namespace == nil)
- namespace = @"";
[_namespaces setObject: prefix forKey: namespace];
}
- (void)bindPrefix: (OFString *)prefix forNamespace: (OFString *)namespace
@@ -994,13 +1009,10 @@
if (element->_name != _name && ![element->_name isEqual: _name])
return false;
if (element->_namespace != _namespace &&
![element->_namespace isEqual: _namespace])
return false;
- if (element->_defaultNamespace != _defaultNamespace &&
- ![element->_defaultNamespace isEqual: _defaultNamespace])
- return false;
if (element->_attributes != _attributes &&
![element->_attributes isEqual: _attributes])
return false;
if (element->_namespaces != _namespaces &&
![element->_namespaces isEqual: _namespaces])
@@ -1018,11 +1030,10 @@
OFHashInit(&hash);
OFHashAddHash(&hash, _name.hash);
OFHashAddHash(&hash, _namespace.hash);
- OFHashAddHash(&hash, _defaultNamespace.hash);
OFHashAddHash(&hash, _attributes.hash);
OFHashAddHash(&hash, _namespaces.hash);
OFHashAddHash(&hash, _children.hash);
OFHashFinalize(&hash);
@@ -1030,8 +1041,20 @@
return hash;
}
- (id)copy
{
- return [[[self class] alloc] initWithElement: self];
+ OFXMLElement *copy = [[OFXMLElement alloc] of_init];
+ @try {
+ copy->_name = [_name copy];
+ copy->_namespace = [_namespace copy];
+ copy->_attributes = [_attributes mutableCopy];
+ copy->_namespaces = [_namespaces mutableCopy];
+ copy->_children = [_children mutableCopy];
+ } @catch (id e) {
+ [copy release];
+ @throw e;
+ }
+
+ return copy;
}
@end
Index: src/OFXMLElementBuilder.h
==================================================================
--- src/OFXMLElementBuilder.h
+++ src/OFXMLElementBuilder.h
@@ -52,12 +52,12 @@
* root element.
*
* @param builder The builder which built the OFXMLNode without parent
* @param node The OFXMLNode the OFXMLElementBuilder built
*/
-- (void)elementBuilder: (OFXMLElementBuilder *)builder
- didBuildParentlessNode: (OFXMLNode *)node;
+- (void)elementBuilder: (OFXMLElementBuilder *)builder
+ didBuildOrphanNode: (OFXMLNode *)node;
/**
* @brief This callback is called when the OFXMLElementBuilder gets a close tag
* which does not belong there.
*
@@ -100,15 +100,15 @@
*
* It can also be used to build OFXMLElements from parts of the document by
* first parsing stuff using the OFXMLParser with another delegate and then
* setting the OFXMLElementBuilder as delegate for the parser.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFXMLElementBuilder: OFObject
{
OFMutableArray OF_GENERIC(OFXMLElement *) *_stack;
id _Nullable _delegate;
- OF_RESERVE_IVARS(OFXMLElementBuilder, 4)
}
/**
* @brief The delegate for the OFXMLElementBuilder.
*/
Index: src/OFXMLElementBuilder.m
==================================================================
--- src/OFXMLElementBuilder.m
+++ src/OFXMLElementBuilder.m
@@ -56,22 +56,22 @@
[super dealloc];
}
- (void)parser: (OFXMLParser *)parser
foundProcessingInstructionWithTarget: (OFString *)target
- data: (OFString *)data
+ text: (OFString *)text
{
OFXMLProcessingInstruction *node = [OFXMLProcessingInstruction
processingInstructionWithTarget: target
- data: data];
+ text: text];
OFXMLElement *parent = _stack.lastObject;
if (parent != nil)
[parent addChild: node];
else if ([_delegate respondsToSelector:
- @selector(elementBuilder:didBuildParentlessNode:)])
- [_delegate elementBuilder: self didBuildParentlessNode: node];
+ @selector(elementBuilder:didBuildOrphanNode:)])
+ [_delegate elementBuilder: self didBuildOrphanNode: node];
}
- (void)parser: (OFXMLParser *)parser
didStartElement: (OFString *)name
prefix: (OFString *)prefix
@@ -134,12 +134,12 @@
parent = _stack.lastObject;
if (parent != nil)
[parent addChild: node];
else if ([_delegate respondsToSelector:
- @selector(elementBuilder:didBuildParentlessNode:)])
- [_delegate elementBuilder: self didBuildParentlessNode: node];
+ @selector(elementBuilder:didBuildOrphanNode:)])
+ [_delegate elementBuilder: self didBuildOrphanNode: node];
}
- (void)parser: (OFXMLParser *)parser
foundCDATA: (OFString *)CDATA
{
@@ -147,12 +147,12 @@
OFXMLElement *parent = _stack.lastObject;
if (parent != nil)
[parent addChild: node];
else if ([_delegate respondsToSelector:
- @selector(elementBuilder:didBuildParentlessNode:)])
- [_delegate elementBuilder: self didBuildParentlessNode: node];
+ @selector(elementBuilder:didBuildOrphanNode:)])
+ [_delegate elementBuilder: self didBuildOrphanNode: node];
}
- (void)parser: (OFXMLParser *)parser
foundComment: (OFString *)comment
{
@@ -160,12 +160,12 @@
OFXMLElement *parent = _stack.lastObject;
if (parent != nil)
[parent addChild: node];
else if ([_delegate respondsToSelector:
- @selector(elementBuilder:didBuildParentlessNode:)])
- [_delegate elementBuilder: self didBuildParentlessNode: node];
+ @selector(elementBuilder:didBuildOrphanNode:)])
+ [_delegate elementBuilder: self didBuildOrphanNode: node];
}
- (OFString *)parser: (OFXMLParser *)parser
foundUnknownEntityNamed: (OFString *)entity
{
Index: src/OFXMLNode.h
==================================================================
--- src/OFXMLNode.h
+++ src/OFXMLNode.h
@@ -38,58 +38,49 @@
*/
@property (nonatomic, copy) OFString *stringValue;
/**
* @brief The contents of the receiver as a `long long` value.
+ *
+ * @throw OFInvalidFormatException The node cannot be parsed as a `long long`
*/
@property (readonly, nonatomic) long long longLongValue;
/**
* @brief The contents of the receiver as an `unsigned long long` value.
+ *
+ * @throw OFInvalidFormatException The node cannot be parsed as an
+ * `unsigned long long`
*/
@property (readonly, nonatomic) unsigned long long unsignedLongLongValue;
/**
* @brief The contents of the receiver as a float value.
+ *
+ * @throw OFInvalidFormatException The node cannot be parsed as a `float`
*/
@property (readonly, nonatomic) float floatValue;
/**
* @brief The contents of the receiver as a double value.
+ *
+ * @throw OFInvalidFormatException The node cannot be parsed as a `double`
*/
@property (readonly, nonatomic) double doubleValue;
/**
* @brief A string representing the node as an XML string.
+ *
+ * @throw OFUnboundNamespaceException The node uses a namespace that was not
+ * bound to a prefix in a context where it
+ * needs a prefix
*/
@property (readonly, nonatomic) OFString *XMLString;
- (instancetype)init OF_UNAVAILABLE;
- (instancetype)initWithSerialization: (OFXMLElement *)element OF_UNAVAILABLE;
-/**
- * @brief Returns an OFString representing the OFXMLNode as an XML string with
- * indentation.
- *
- * @param indentation The indentation for the XML string
- * @return An OFString representing the OFXMLNode as an XML string with
- * indentation
- */
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation;
-
-/**
- * @brief Returns an OFString representing the OFXMLNode as an XML string with
- * indentation for the specified level.
- *
- * @param indentation The indentation for the XML string
- * @param level The level of indentation
- * @return An OFString representing the OFXMLNode as an XML string with
- * indentation
- */
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
- level: (unsigned int)level;
-
/**
* @brief The contents of the receiver as a `long long` value in the specified
* base.
*
* @param base The base to use. If the base is 0, base 16 is assumed if the
@@ -97,11 +88,11 @@
* string starts with 0, base 8 is assumed. Otherwise, base 10 is
* assumed.
* @return The contents of the receiver as a `long long` value in the specified
* base
*/
-- (long long)longLongValueWithBase: (int)base;
+- (long long)longLongValueWithBase: (unsigned char)base;
/**
* @brief The contents of the receiver as an `unsigned long long` value in the
* specified base.
*
@@ -110,9 +101,9 @@
* string starts with 0, base 8 is assumed. Otherwise, base 10 is
* assumed.
* @return The contents of the receiver as an `unsigned long long` value in the
* specified base
*/
-- (unsigned long long)unsignedLongLongValueWithBase: (int)base;
+- (unsigned long long)unsignedLongLongValueWithBase: (unsigned char)base;
@end
OF_ASSUME_NONNULL_END
Index: src/OFXMLNode.m
==================================================================
--- src/OFXMLNode.m
+++ src/OFXMLNode.m
@@ -47,21 +47,21 @@
- (long long)longLongValue
{
return self.stringValue.longLongValue;
}
-- (long long)longLongValueWithBase: (int)base
+- (long long)longLongValueWithBase: (unsigned char)base
{
return [self.stringValue longLongValueWithBase: base];
}
- (unsigned long long)unsignedLongLongValue
{
return self.stringValue.unsignedLongLongValue;
}
-- (unsigned long long)unsignedLongLongValueWithBase: (int)base
+- (unsigned long long)unsignedLongLongValueWithBase: (unsigned char)base
{
return [self.stringValue unsignedLongLongValueWithBase: base];
}
- (float)floatValue
@@ -74,27 +74,16 @@
return self.stringValue.doubleValue;
}
- (OFString *)XMLString
{
- return [self XMLStringWithIndentation: 0 level: 0];
-}
-
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
-{
- return [self XMLStringWithIndentation: 0 level: 0];
-}
-
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
- level: (unsigned int)level
-{
OF_UNRECOGNIZED_SELECTOR
}
- (OFString *)description
{
- return [self XMLStringWithIndentation: 2 level: 0];
+ return self.XMLString;
}
- (OFXMLElement *)XMLElementBySerializing
{
OF_UNRECOGNIZED_SELECTOR
Index: src/OFXMLParser.h
==================================================================
--- src/OFXMLParser.h
+++ src/OFXMLParser.h
@@ -37,45 +37,45 @@
* @brief This callback is called when the XML parser found a processing
* instruction.
*
* @param parser The parser which found a processing instruction
* @param target The target of the processing instruction
- * @param data The data of the processing instruction
+ * @param text The text of the processing instruction
*/
- (void)parser: (OFXMLParser *)parser
foundProcessingInstructionWithTarget: (OFString *)target
- data: (OFString *)data;
+ text: (OFString *)text;
/**
* @brief This callback is called when the XML parser found the start of a new
* tag.
*
* @param parser The parser which found a new tag
* @param name The name of the tag which just started
* @param prefix The prefix of the tag which just started or `nil`
- * @param ns The namespace of the tag which just started or `nil`
+ * @param nameSpace The namespace of the tag which just started or `nil`
* @param attributes The attributes included in the tag which just started or
* `nil`
*/
- (void)parser: (OFXMLParser *)parser
didStartElement: (OFString *)name
prefix: (nullable OFString *)prefix
- namespace: (nullable OFString *)ns
+ namespace: (nullable OFString *)nameSpace
attributes: (nullable OFArray OF_GENERIC(OFXMLAttribute *) *)attributes;
/**
* @brief This callback is called when the XML parser found the end of a tag.
*
* @param parser The parser which found the end of a tag
* @param name The name of the tag which just ended
* @param prefix The prefix of the tag which just ended or `nil`
- * @param ns The namespace of the tag which just ended or `nil`
+ * @param nameSpace The namespace of the tag which just ended or `nil`
*/
- (void)parser: (OFXMLParser *)parser
didEndElement: (OFString *)name
prefix: (nullable OFString *)prefix
- namespace: (nullable OFString *)ns;
+ namespace: (nullable OFString *)nameSpace;
/**
* @brief This callback is called when the XML parser found characters.
*
* In case there are comments or CDATA, it is possible that this callback is
@@ -185,24 +185,36 @@
/**
* @brief Parses the specified buffer with the specified size.
*
* @param buffer The buffer to parse
* @param length The length of the buffer
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundPrefixException A prefix was used that was not bound to any
+ * namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
*/
- (void)parseBuffer: (const char *)buffer length: (size_t)length;
/**
* @brief Parses the specified string.
*
* @param string The string to parse
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundPrefixException A prefix was used that was not bound to any
+ * namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
*/
- (void)parseString: (OFString *)string;
/**
* @brief Parses the specified stream.
*
* @param stream The stream to parse
+ * @throw OFMalformedXMLException The XML was malformed
+ * @throw OFUnboundPrefixException A prefix was used that was not bound to any
+ * namespace
+ * @throw OFInvalidEncodingException The XML is not in the encoding it specified
*/
- (void)parseStream: (OFStream *)stream;
@end
OF_ASSUME_NONNULL_END
Index: src/OFXMLParser.m
==================================================================
--- src/OFXMLParser.m
+++ src/OFXMLParser.m
@@ -112,11 +112,11 @@
static OF_INLINE void
appendToBuffer(OFMutableData *buffer, const char *string,
OFStringEncoding encoding, size_t length)
{
- if OF_LIKELY(encoding == OFStringEncodingUTF8)
+ if OF_LIKELY (encoding == OFStringEncodingUTF8)
[buffer addItems: string count: length];
else {
void *pool = objc_autoreleasePoolPush();
OFString *tmp = [OFString stringWithCString: string
encoding: encoding
@@ -497,11 +497,11 @@
{
if (self->_data[self->_i] == '?')
self->_level = 1;
else if (self->_level == 1 && self->_data[self->_i] == '>') {
void *pool = objc_autoreleasePoolPush();
- OFString *PI, *target, *data = nil;
+ OFString *PI, *target, *text = nil;
OFCharacterSet *whitespaceCS;
size_t pos;
appendToBuffer(self->_buffer, self->_data + self->_last,
self->_encoding, self->_i - self->_last);
@@ -510,28 +510,28 @@
whitespaceCS = [OFCharacterSet
characterSetWithCharactersInString: @" \r\n\r"];
pos = [PI indexOfCharacterFromSet: whitespaceCS];
if (pos != OFNotFound) {
target = [PI substringToIndex: pos];
- data = [[PI substringFromIndex: pos + 1]
+ text = [[PI substringFromIndex: pos + 1]
stringByDeletingEnclosingWhitespaces];
- if (data.length == 0)
- data = nil;
+ if (text.length == 0)
+ text = nil;
} else
target = PI;
if ([target caseInsensitiveCompare: @"xml"] == OFOrderedSame)
- if (!parseXMLProcessingInstruction(self, data))
+ if (!parseXMLProcessingInstruction(self, text))
@throw [OFMalformedXMLException
exceptionWithParser: self];
if ([self->_delegate respondsToSelector: @selector(
- parser:foundProcessingInstructionWithTarget:data:)])
+ parser:foundProcessingInstructionWithTarget:text:)])
[self->_delegate parser: self
foundProcessingInstructionWithTarget: target
- data: data];
+ text: text];
objc_autoreleasePoolPop(pool);
[self->_buffer removeAllItems];
Index: src/OFXMLProcessingInstruction.h
==================================================================
--- src/OFXMLProcessingInstruction.h
+++ src/OFXMLProcessingInstruction.h
@@ -21,47 +21,47 @@
* @class OFXMLProcessingInstruction \
* OFXMLProcessingInstruction.h ObjFW/OFXMLProcessingInstruction.h
*
* @brief A class for representing an XML processing instruction.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFXMLProcessingInstruction: OFXMLNode
{
- OFString *_target, *_data;
- OF_RESERVE_IVARS(OFXMLProcessingInstruction, 4)
+ OFString *_target, *_Nullable _text;
}
/**
* @brief The target of the processing instruction.
*/
@property (readonly, nonatomic) OFString *target;
/**
- * @brief The data of the processing instruction.
+ * @brief The text of the processing instruction.
*/
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *data;
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *text;
/**
* @brief Creates a new OFXMLProcessingInstruction with the specified target
- * and data.
+ * and text.
*
* @param target The target for the processing instruction
- * @param data The data for the processing instruction
+ * @param text The text for the processing instruction
* @return A new OFXMLProcessingInstruction
*/
+ (instancetype)processingInstructionWithTarget: (OFString *)target
- data: (OFString *)data;
+ text: (OFString *)text;
/**
* @brief Initializes an already allocated OFXMLProcessingInstruction with the
- * specified target and data.
+ * specified target and text.
*
* @param target The target for the processing instruction
- * @param data The data for the processing instruction
+ * @param text The text for the processing instruction
* @return An initialized OFXMLProcessingInstruction
*/
- (instancetype)initWithTarget: (OFString *)target
- data: (OFString *)data OF_DESIGNATED_INITIALIZER;
+ text: (OFString *)text OF_DESIGNATED_INITIALIZER;
- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end
OF_ASSUME_NONNULL_END
Index: src/OFXMLProcessingInstruction.m
==================================================================
--- src/OFXMLProcessingInstruction.m
+++ src/OFXMLProcessingInstruction.m
@@ -24,27 +24,27 @@
#import "OFXMLNode+Private.h"
#import "OFInvalidArgumentException.h"
@implementation OFXMLProcessingInstruction
-@synthesize target = _target, data = _data;
+@synthesize target = _target, text = _text;
+ (instancetype)processingInstructionWithTarget: (OFString *)target
- data: (OFString *)data
+ text: (OFString *)text
{
return [[[self alloc] initWithTarget: target
- data: data] autorelease];
+ text: text] autorelease];
}
- (instancetype)initWithTarget: (OFString *)target
- data: (OFString *)data
+ text: (OFString *)text
{
self = [super of_init];
@try {
_target = [target copy];
- _data = [data copy];
+ _text = [text copy];
} @catch (id e) {
[self release];
@throw e;
}
@@ -65,11 +65,11 @@
namespace: OFSerializationNS];
if (targetAttr.stringValue.length == 0)
@throw [OFInvalidArgumentException exception];
self = [self initWithTarget: targetAttr.stringValue
- data: element.stringValue];
+ text: element.stringValue];
objc_autoreleasePoolPop(pool);
} @catch (id e) {
[self release];
@throw e;
@@ -79,11 +79,11 @@
}
- (void)dealloc
{
[_target release];
- [_data release];
+ [_text release];
[super dealloc];
}
- (bool)isEqual: (id)object
@@ -99,12 +99,12 @@
processingInstruction = object;
if (![processingInstruction->_target isEqual: _target])
return false;
- if (processingInstruction->_data != _data &&
- ![processingInstruction->_data isEqual: _data])
+ if (processingInstruction->_text != _text &&
+ ![processingInstruction->_text isEqual: _text])
return false;
return true;
}
@@ -112,11 +112,11 @@
{
unsigned long hash;
OFHashInit(&hash);
OFHashAddHash(&hash, _target.hash);
- OFHashAddHash(&hash, _data.hash);
+ OFHashAddHash(&hash, _text.hash);
OFHashFinalize(&hash);
return hash;
}
@@ -125,58 +125,27 @@
return @"";
}
- (OFString *)XMLString
{
- if (_data.length > 0)
+ if (_text.length > 0)
return [OFString stringWithFormat: @"%@ %@?>",
- _target, _data];
+ _target, _text];
else
return [OFString stringWithFormat: @"%@?>", _target];
}
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
-{
- return self.XMLString;
-}
-
-- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
- level: (unsigned int)level
-{
- if (indentation > 0 && level > 0) {
- OFString *ret;
- char *whitespaces = OFAllocMemory((level * indentation) + 1, 1);
- memset(whitespaces, ' ', level * indentation);
- whitespaces[level * indentation] = 0;
-
- @try {
- if (_data.length > 0)
- ret = [OFString stringWithFormat:
- @"%s%@ %@?>", whitespaces,
- _target, _data];
- else
- ret = [OFString stringWithFormat:
- @"%s%@?>", whitespaces, _target];
- } @finally {
- OFFreeMemory(whitespaces);
- }
-
- return ret;
- } else
- return self.XMLString;
-}
-
- (OFString *)description
{
return self.XMLString;
}
- (OFXMLElement *)XMLElementBySerializing
{
OFXMLElement *ret = [OFXMLElement elementWithName: self.className
namespace: OFSerializationNS
- stringValue: _data];
+ stringValue: _text];
void *pool = objc_autoreleasePoolPush();
[ret addAttribute: [OFXMLAttribute attributeWithName: @"target"
stringValue: _target]];
Index: src/OFZIPArchive.h
==================================================================
--- src/OFZIPArchive.h
+++ src/OFZIPArchive.h
@@ -31,17 +31,24 @@
*/
OF_SUBCLASSING_RESTRICTED
@interface OFZIPArchive: OFObject
{
OFStream *_stream;
+#ifdef OF_ZIP_ARCHIVE_M
+@public
+#endif
int64_t _offset;
+@protected
uint_least8_t _mode;
uint32_t _diskNumber, _centralDirectoryDisk;
uint64_t _centralDirectoryEntriesInDisk, _centralDirectoryEntries;
uint64_t _centralDirectorySize;
int64_t _centralDirectoryOffset;
OFString *_Nullable _archiveComment;
+#ifdef OF_ZIP_ARCHIVE_M
+@public
+#endif
OFMutableArray OF_GENERIC(OFZIPArchiveEntry *) *_entries;
OFMutableDictionary OF_GENERIC(OFString *, OFZIPArchiveEntry *)
*_pathToEntryMap;
OFStream *_Nullable _lastReturnedStream;
}
@@ -69,25 +76,36 @@
* For read and append mode, this needs to be an OFSeekableStream.
* @param mode The mode for the ZIP file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return A new, autoreleased OFZIPArchive
+ * @throw OFInvalidFormatException The format is not that of a valid ZIP archive
*/
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode;
-#ifdef OF_HAVE_FILES
/**
* @brief Creates a new OFZIPArchive object with the specified file.
*
- * @param path The path to the ZIP file
+ * @param URI The URI to the ZIP file
* @param mode The mode for the ZIP file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return A new, autoreleased OFZIPArchive
+ * @throw OFInvalidFormatException The format is not that of a valid ZIP archive
+ */
++ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode;
+
+/**
+ * @brief Creates a URI for accessing a the specified file within the specified
+ * ZIP archive.
+ *
+ * @param path The path of the file within the archive
+ * @param URI The URI of the archive
+ * @return A URI for accessing the specified file within the specified ZIP
+ * archive
*/
-+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode;
-#endif
++ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI;
- (instancetype)init OF_UNAVAILABLE;
/**
* @brief Initializes an already allocated OFZIPArchive object with the
@@ -97,27 +115,27 @@
* For read and append mode, this needs to be an OFSeekableStream.
* @param mode The mode for the ZIP file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return An initialized OFZIPArchive
+ * @throw OFInvalidFormatException The format is not that of a valid ZIP archive
*/
- (instancetype)initWithStream: (OFStream *)stream
mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;
-#ifdef OF_HAVE_FILES
/**
* @brief Initializes an already allocated OFZIPArchive object with the
* specified file.
*
- * @param path The path to the ZIP file
+ * @param URI The URI to the ZIP file
* @param mode The mode for the ZIP file. Valid modes are "r" for reading,
* "w" for creating a new file and "a" for appending to an existing
* archive.
* @return An initialized OFZIPArchive
+ * @throw OFInvalidFormatException The format is not that of a valid ZIP archive
*/
-- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode;
-#endif
+- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode;
/**
* @brief Returns a stream for reading the specified file from the archive.
*
* @note This method is only available in read mode.
@@ -131,10 +149,18 @@
* invalidated stream will throw an @ref OFReadFailedException or
* @ref OFWriteFailedException!
*
* @param path The path to the file inside the archive
* @return A stream for reading the specified file form the archive
+ * @throw OFNotOpenException The archive is not open
+ * @throw OFInvalidArgumentException The archive is not in read mode
+ * @throw OFOpenItemFailedException Opening the specified file within the
+ * archive failed
+ * @throw OFInvalidFormatException The local header and the header in the
+ * central directory do not match enough
+ * @throw OFUnsupportedVersionException The file uses a version of the ZIP
+ * format that is not supported
*/
- (OFStream *)streamForReadingFile: (OFString *)path;
/**
* @brief Returns a stream for writing the specified entry to the archive.
@@ -157,15 +183,25 @@
* * The compressed size.
* * The uncompressed size.
* * The CRC32.
* * Bit 3 and 11 of the general purpose bit flag.
* @return A stream for writing the specified entry to the archive
+ * @throw OFNotOpenException The archive is not open
+ * @throw OFInvalidArgumentException The archive is not in write mode
+ * @throw OFOpenItemFailedException Opening the specified file within the
+ * archive failed. If @ref errNo is `EEXIST`,
+ * because there is already a file with the
+ * same name in the archive.
+ * @throw OFNotImplementedException The desired compression method is not
+ * implemented
*/
- (OFStream *)streamForWritingEntry: (OFZIPArchiveEntry *)entry;
/**
* @brief Closes the OFZIPArchive.
+ *
+ * @throw OFNotOpenException The archive is not open
*/
- (void)close;
@end
#ifdef __cplusplus
Index: src/OFZIPArchive.m
==================================================================
--- src/OFZIPArchive.m
+++ src/OFZIPArchive.m
@@ -11,28 +11,30 @@
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
+#define OF_ZIP_ARCHIVE_M
+
#include "config.h"
#include
#import "OFZIPArchive.h"
#import "OFZIPArchiveEntry.h"
#import "OFZIPArchiveEntry+Private.h"
+#import "OFArchiveURIHandler.h"
+#import "OFArray.h"
#import "OFCRC32.h"
#import "OFData.h"
-#import "OFArray.h"
#import "OFDictionary.h"
-#import "OFStream.h"
+#import "OFInflate64Stream.h"
+#import "OFInflateStream.h"
#import "OFSeekableStream.h"
-#ifdef OF_HAVE_FILES
-# import "OFFile.h"
-#endif
-#import "OFInflateStream.h"
-#import "OFInflate64Stream.h"
+#import "OFStream.h"
+#import "OFURI.h"
+#import "OFURIHandler.h"
#import "OFChecksumMismatchException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
@@ -58,11 +60,10 @@
OF_DIRECT_MEMBERS
@interface OFZIPArchive ()
- (void)of_readZIPInfo;
- (void)of_readEntries;
-- (void)of_closeLastReturnedStream;
- (void)of_writeCentralDirectory;
@end
OF_DIRECT_MEMBERS
@interface OFZIPArchiveLocalFileHeader: OFObject
@@ -81,33 +82,37 @@
@end
OF_DIRECT_MEMBERS
@interface OFZIPArchiveFileReadStream: OFStream
{
+ OFZIPArchive *_archive;
OFStream *_stream, *_decompressedStream;
OFZIPArchiveEntry *_entry;
- uint64_t _toRead;
+ unsigned long long _toRead;
uint32_t _CRC32;
bool _atEndOfStream;
}
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFZIPArchiveEntry *)entry;
+- (instancetype)of_initWithArchive: (OFZIPArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFZIPArchiveEntry *)entry;
@end
OF_DIRECT_MEMBERS
@interface OFZIPArchiveFileWriteStream: OFStream
{
+ OFZIPArchive *_archive;
OFStream *_stream;
uint32_t _CRC32;
@public
- int64_t _bytesWritten;
+ unsigned long long _bytesWritten;
OFMutableZIPArchiveEntry *_entry;
}
-- (instancetype)initWithStream: (OFStream *)stream
- entry: (OFMutableZIPArchiveEntry *)entry;
+- (instancetype)of_initWithArchive: (OFZIPArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFMutableZIPArchiveEntry *)entry;
@end
uint32_t
OFZIPArchiveReadField32(const uint8_t **data, uint16_t *size)
{
@@ -142,11 +147,11 @@
return field;
}
static void
seekOrThrowInvalidFormat(OFSeekableStream *stream,
- OFFileOffset offset, int whence)
+ OFStreamOffset offset, OFSeekWhence whence)
{
@try {
[stream seekToOffset: offset whence: whence];
} @catch (OFSeekFailedException *e) {
if (e.errNo == EINVAL)
@@ -162,16 +167,19 @@
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode
{
return [[[self alloc] initWithStream: stream mode: mode] autorelease];
}
-#ifdef OF_HAVE_FILES
-+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode
++ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode
{
- return [[[self alloc] initWithPath: path mode: mode] autorelease];
+ return [[[self alloc] initWithURI: URI mode: mode] autorelease];
}
-#endif
+
++ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI
+{
+ return OFArchiveURIHandlerURIForFileInArchive(@"zip", path, URI);
+}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
@@ -203,11 +211,11 @@
}
if (_mode == modeAppend) {
_offset = _centralDirectoryOffset;
seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
- (OFFileOffset)_offset, SEEK_SET);
+ (OFStreamOffset)_offset, OFSeekSet);
}
} @catch (id e) {
/*
* If we are in write or append mode, we do not want -[close]
* to write anything to it on error - after all, it might not
@@ -221,29 +229,31 @@
}
return self;
}
-#ifdef OF_HAVE_FILES
-- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode
-{
- OFFile *file;
-
- if ([mode isEqual: @"a"])
- file = [[OFFile alloc] initWithPath: path mode: @"r+"];
- else
- file = [[OFFile alloc] initWithPath: path mode: mode];
+- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFStream *stream;
@try {
- self = [self initWithStream: file mode: mode];
- } @finally {
- [file release];
+ if ([mode isEqual: @"a"])
+ stream = [OFURIHandler openItemAtURI: URI mode: @"r+"];
+ else
+ stream = [OFURIHandler openItemAtURI: URI mode: mode];
+ } @catch (id e) {
+ [self release];
+ @throw e;
}
+
+ self = [self initWithStream: stream mode: mode];
+
+ objc_autoreleasePoolPop(pool);
return self;
}
-#endif
- (void)dealloc
{
if (_stream != nil)
[self close];
@@ -250,25 +260,24 @@
[_stream release];
[_archiveComment release];
[_entries release];
[_pathToEntryMap release];
- [_lastReturnedStream release];
[super dealloc];
}
- (void)of_readZIPInfo
{
void *pool = objc_autoreleasePoolPush();
uint16_t commentLength;
- OFFileOffset offset = -22;
+ OFStreamOffset offset = -22;
bool valid = false;
do {
seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
- offset, SEEK_END);
+ offset, OFSeekEnd);
if ([_stream readLittleEndianInt32] == 0x06054B50) {
valid = true;
break;
}
@@ -297,11 +306,11 @@
_centralDirectoryOffset == 0xFFFFFFFF) {
int64_t offset64;
uint64_t size;
seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
- offset - 20, SEEK_END);
+ offset - 20, OFSeekEnd);
if ([_stream readLittleEndianInt32] != 0x07064B50) {
objc_autoreleasePoolPop(pool);
return;
}
@@ -311,15 +320,15 @@
* central directory record.
*/
[_stream readLittleEndianInt32];
offset64 = [_stream readLittleEndianInt64];
- if (offset64 < 0 || (OFFileOffset)offset64 != offset64)
+ if (offset64 < 0 || (OFStreamOffset)offset64 != offset64)
@throw [OFOutOfRangeException exception];
seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
- (OFFileOffset)offset64, SEEK_SET);
+ (OFStreamOffset)offset64, OFSeekSet);
if ([_stream readLittleEndianInt32] != 0x06064B50)
@throw [OFInvalidFormatException exception];
size = [_stream readLittleEndianInt64];
@@ -338,11 +347,11 @@
_centralDirectoryEntries = [_stream readLittleEndianInt64];
_centralDirectorySize = [_stream readLittleEndianInt64];
_centralDirectoryOffset = [_stream readLittleEndianInt64];
if (_centralDirectoryOffset < 0 ||
- (OFFileOffset)_centralDirectoryOffset !=
+ (OFStreamOffset)_centralDirectoryOffset !=
_centralDirectoryOffset)
@throw [OFOutOfRangeException exception];
}
objc_autoreleasePoolPop(pool);
@@ -351,15 +360,15 @@
- (void)of_readEntries
{
void *pool = objc_autoreleasePoolPush();
if (_centralDirectoryOffset < 0 ||
- (OFFileOffset)_centralDirectoryOffset != _centralDirectoryOffset)
+ (OFStreamOffset)_centralDirectoryOffset != _centralDirectoryOffset)
@throw [OFOutOfRangeException exception];
seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
- (OFFileOffset)_centralDirectoryOffset, SEEK_SET);
+ (OFStreamOffset)_centralDirectoryOffset, OFSeekSet);
for (size_t i = 0; i < _centralDirectoryEntries; i++) {
OFZIPArchiveEntry *entry = [[[OFZIPArchiveEntry alloc]
of_initWithStream: _stream] autorelease];
@@ -396,40 +405,10 @@
[old release];
objc_autoreleasePoolPop(pool);
}
-- (void)of_closeLastReturnedStream
-{
- @try {
- [_lastReturnedStream close];
- } @catch (OFNotOpenException *e) {
- /* Might have already been closed by the user - that's fine. */
- }
-
- if ((_mode == modeWrite || _mode == modeAppend) &&
- [_lastReturnedStream isKindOfClass:
- [OFZIPArchiveFileWriteStream class]]) {
- OFZIPArchiveFileWriteStream *stream =
- (OFZIPArchiveFileWriteStream *)_lastReturnedStream;
-
- if (INT64_MAX - _offset < stream->_bytesWritten)
- @throw [OFOutOfRangeException exception];
-
- _offset += stream->_bytesWritten;
-
- if (stream->_entry != nil) {
- [_entries addObject: stream->_entry];
- [_pathToEntryMap setObject: stream->_entry
- forKey: [stream->_entry fileName]];
- }
- }
-
- [_lastReturnedStream release];
- _lastReturnedStream = nil;
-}
-
- (OFStream *)streamForReadingFile: (OFString *)path
{
void *pool = objc_autoreleasePoolPush();
OFZIPArchiveEntry *entry;
OFZIPArchiveLocalFileHeader *localFileHeader;
@@ -444,18 +423,23 @@
if ((entry = [_pathToEntryMap objectForKey: path]) == nil)
@throw [OFOpenItemFailedException exceptionWithPath: path
mode: @"r"
errNo: ENOENT];
- [self of_closeLastReturnedStream];
+ @try {
+ [_lastReturnedStream close];
+ } @catch (OFNotOpenException *e) {
+ /* Might have already been closed by the user - that's fine. */
+ }
+ _lastReturnedStream = nil;
offset64 = entry.of_localFileHeaderOffset;
- if (offset64 < 0 || (OFFileOffset)offset64 != offset64)
+ if (offset64 < 0 || (OFStreamOffset)offset64 != offset64)
@throw [OFOutOfRangeException exception];
seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
- (OFFileOffset)offset64, SEEK_SET);
+ (OFStreamOffset)offset64, OFSeekSet);
localFileHeader = [[[OFZIPArchiveLocalFileHeader alloc]
initWithStream: _stream] autorelease];
if (![localFileHeader matchesEntry: entry])
@throw [OFInvalidFormatException exception];
@@ -467,17 +451,18 @@
@throw [OFUnsupportedVersionException
exceptionWithVersion: version];
}
- _lastReturnedStream = [[OFZIPArchiveFileReadStream alloc]
- of_initWithStream: _stream
- entry: entry];
-
objc_autoreleasePoolPop(pool);
- return [[_lastReturnedStream retain] autorelease];
+ _lastReturnedStream = [[[OFZIPArchiveFileReadStream alloc]
+ of_initWithArchive: self
+ stream: _stream
+ entry: entry] autorelease];
+
+ return _lastReturnedStream;
}
- (OFStream *)streamForWritingEntry: (OFZIPArchiveEntry *)entry_
{
/* TODO: Avoid data descriptor when _stream is an OFSeekableStream */
@@ -505,11 +490,16 @@
if (entry.compressionMethod != OFZIPArchiveEntryCompressionMethodNone)
@throw [OFNotImplementedException exceptionWithSelector: _cmd
object: self];
- [self of_closeLastReturnedStream];
+ @try {
+ [_lastReturnedStream close];
+ } @catch (OFNotOpenException *e) {
+ /* Might have already been closed by the user - that's fine. */
+ }
+ _lastReturnedStream = nil;
fileName = entry.fileName;
fileNameLength = fileName.UTF8StringLength;
extraField = entry.extraField;
extraFieldLength = extraField.count;
@@ -558,16 +548,17 @@
@throw [OFOutOfRangeException exception];
_offset += offsetAdd;
_lastReturnedStream = [[OFZIPArchiveFileWriteStream alloc]
- initWithStream: _stream
- entry: entry];
+ of_initWithArchive: self
+ stream: _stream
+ entry: entry];
objc_autoreleasePoolPop(pool);
- return [[_lastReturnedStream retain] autorelease];
+ return [_lastReturnedStream autorelease];
}
- (void)of_writeCentralDirectory
{
void *pool = objc_autoreleasePoolPush();
@@ -620,11 +611,16 @@
- (void)close
{
if (_stream == nil)
@throw [OFNotOpenException exceptionWithObject: self];
- [self of_closeLastReturnedStream];
+ @try {
+ [_lastReturnedStream close];
+ } @catch (OFNotOpenException *e) {
+ /* Might have already been closed by the user - that's fine. */
+ }
+ _lastReturnedStream = nil;
if (_mode == modeWrite || _mode == modeAppend)
[self of_writeCentralDirectory];
[_stream release];
@@ -672,11 +668,11 @@
if (ZIP64Index != OFNotFound) {
const uint8_t *ZIP64 =
[extraField itemAtIndex: ZIP64Index];
OFRange range =
- OFRangeMake(ZIP64Index - 4, ZIP64Size + 4);
+ OFMakeRange(ZIP64Index - 4, ZIP64Size + 4);
if (_uncompressedSize == 0xFFFFFFFF)
_uncompressedSize = OFZIPArchiveReadField64(
&ZIP64, &ZIP64Size);
if (_compressedSize == 0xFFFFFFFF)
@@ -730,16 +726,18 @@
return true;
}
@end
@implementation OFZIPArchiveFileReadStream
-- (instancetype)of_initWithStream: (OFStream *)stream
- entry: (OFZIPArchiveEntry *)entry
+- (instancetype)of_initWithArchive: (OFZIPArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFZIPArchiveEntry *)entry
{
self = [super init];
@try {
+ _archive = [archive retain];
_stream = [stream retain];
switch (entry.compressionMethod) {
case OFZIPArchiveEntryCompressionMethodNone:
_decompressedStream = [stream retain];
@@ -774,10 +772,15 @@
if (_stream != nil || _decompressedStream != nil)
[self close];
[_entry release];
+ if (_archive->_lastReturnedStream == self)
+ _archive->_lastReturnedStream = nil;
+
+ [_archive release];
+
[super dealloc];
}
- (bool)lowlevelIsAtEndOfStream
{
@@ -803,11 +806,11 @@
#if SIZE_MAX >= UINT64_MAX
if (length > UINT64_MAX)
@throw [OFOutOfRangeException exception];
#endif
- if ((uint64_t)length > _toRead)
+ if (length > _toRead)
length = (size_t)_toRead;
ret = [_decompressedStream readIntoBuffer: buffer length: length];
_toRead -= ret;
@@ -857,15 +860,17 @@
[super close];
}
@end
@implementation OFZIPArchiveFileWriteStream
-- (instancetype)initWithStream: (OFStream *)stream
- entry: (OFMutableZIPArchiveEntry *)entry
+- (instancetype)of_initWithArchive: (OFZIPArchive *)archive
+ stream: (OFStream *)stream
+ entry: (OFMutableZIPArchiveEntry *)entry
{
self = [super init];
+ _archive = [archive retain];
_stream = [stream retain];
_entry = [entry retain];
_CRC32 = ~0;
return self;
@@ -876,10 +881,15 @@
if (_stream != nil)
[self close];
[_entry release];
+ if (_archive->_lastReturnedStream == self)
+ _archive->_lastReturnedStream = nil;
+
+ [_archive release];
+
[super dealloc];
}
- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
@@ -886,28 +896,28 @@
#if SIZE_MAX >= INT64_MAX
if (length > INT64_MAX)
@throw [OFOutOfRangeException exception];
#endif
- if (INT64_MAX - _bytesWritten < (int64_t)length)
+ if (ULLONG_MAX - _bytesWritten < length)
@throw [OFOutOfRangeException exception];
@try {
[_stream writeBuffer: buffer length: length];
} @catch (OFWriteFailedException *e) {
OFEnsure(e.bytesWritten <= length);
- _bytesWritten += (int64_t)e.bytesWritten;
+ _bytesWritten += (unsigned long long)e.bytesWritten;
_CRC32 = OFCRC32(_CRC32, buffer, e.bytesWritten);
if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN)
return e.bytesWritten;
@throw e;
}
- _bytesWritten += (int64_t)length;
+ _bytesWritten += (unsigned long long)length;
_CRC32 = OFCRC32(_CRC32, buffer, length);
return length;
}
@@ -914,14 +924,17 @@
- (void)close
{
if (_stream == nil)
@throw [OFNotOpenException exceptionWithObject: self];
+ if (_bytesWritten > UINT64_MAX)
+ @throw [OFOutOfRangeException exception];
+
[_stream writeLittleEndianInt32: 0x08074B50];
[_stream writeLittleEndianInt32: _CRC32];
- [_stream writeLittleEndianInt64: _bytesWritten];
- [_stream writeLittleEndianInt64: _bytesWritten];
+ [_stream writeLittleEndianInt64: (uint64_t)_bytesWritten];
+ [_stream writeLittleEndianInt64: (uint64_t)_bytesWritten];
[_stream release];
_stream = nil;
_entry.CRC32 = ~_CRC32;
@@ -928,9 +941,17 @@
_entry.compressedSize = _bytesWritten;
_entry.uncompressedSize = _bytesWritten;
[_entry makeImmutable];
_bytesWritten += (2 * 4 + 2 * 8);
+
+ [_archive->_entries addObject: _entry];
+ [_archive->_pathToEntryMap setObject: _entry forKey: _entry.fileName];
+
+ if (ULLONG_MAX - _archive->_offset < _bytesWritten)
+ @throw [OFOutOfRangeException exception];
+
+ _archive->_offset += _bytesWritten;
[super close];
}
@end
Index: src/OFZIPArchiveEntry+Private.h
==================================================================
--- src/OFZIPArchiveEntry+Private.h
+++ src/OFZIPArchiveEntry+Private.h
@@ -20,10 +20,11 @@
@interface OFZIPArchiveEntry ()
@property (readonly, nonatomic)
uint16_t of_lastModifiedFileTime, of_lastModifiedFileDate;
@property (readonly, nonatomic) int64_t of_localFileHeaderOffset;
+- (instancetype)of_init OF_METHOD_FAMILY(init);
- (instancetype)of_initWithStream: (OFStream *)stream
OF_METHOD_FAMILY(init) OF_DIRECT;
- (uint64_t)of_writeToStream: (OFStream *)stream OF_DIRECT;
@end
Index: src/OFZIPArchiveEntry.h
==================================================================
--- src/OFZIPArchiveEntry.h
+++ src/OFZIPArchiveEntry.h
@@ -12,10 +12,11 @@
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#import "OFObject.h"
+#import "OFArchiveEntry.h"
OF_ASSUME_NONNULL_BEGIN
/** @file */
@@ -98,19 +99,20 @@
* @class OFZIPArchiveEntry OFZIPArchiveEntry.h ObjFW/OFZIPArchiveEntry.h
*
* @brief A class which represents an entry in the central directory of a ZIP
* archive.
*/
-@interface OFZIPArchiveEntry: OFObject
+@interface OFZIPArchiveEntry: OFObject
{
OFZIPArchiveEntryAttributeCompatibility _versionMadeBy;
OFZIPArchiveEntryAttributeCompatibility _minVersionNeeded;
uint16_t _generalPurposeBitFlag;
OFZIPArchiveEntryCompressionMethod _compressionMethod;
uint16_t _lastModifiedFileTime, _lastModifiedFileDate;
uint32_t _CRC32;
- uint64_t _compressedSize, _uncompressedSize;
+ unsigned long long _compressedSize, _uncompressedSize;
OFString *_fileName;
OFData *_Nullable _extraField;
OFString *_Nullable _fileComment;
uint32_t _startDiskNumber;
uint16_t _internalAttributes;
@@ -117,21 +119,10 @@
uint32_t _versionSpecificAttributes;
int64_t _localFileHeaderOffset;
OF_RESERVE_IVARS(OFZIPArchiveEntry, 4)
}
-/**
- * @brief The file name of the entry.
- */
-@property (readonly, copy, nonatomic) OFString *fileName;
-
-/**
- * @brief The comment of the entry's file.
- */
-@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
- OFString *fileComment;
-
/**
* @brief The extra field of the entry.
*
* The item size *must* be 1!
*/
@@ -155,17 +146,10 @@
* See @ref OFZIPArchiveEntryAttributeCompatibility.
*/
@property (readonly, nonatomic)
OFZIPArchiveEntryAttributeCompatibility minVersionNeeded;
-/**
- * @brief The last modification date of the entry's file.
- *
- * @note Due to limitations of the ZIP format, this has only 2 second precision.
- */
-@property (readonly, retain, nonatomic) OFDate *modificationDate;
-
/**
* @brief The compression method of the entry.
*
* Supported values are:
* Value | Description
@@ -177,20 +161,10 @@
* Other values may be returned, but the file cannot be extracted then.
*/
@property (readonly, nonatomic)
OFZIPArchiveEntryCompressionMethod compressionMethod;
-/**
- * @brief The compressed size of the entry's file.
- */
-@property (readonly, nonatomic) uint64_t compressedSize;
-
-/**
- * @brief The uncompressed size of the entry's file.
- */
-@property (readonly, nonatomic) uint64_t uncompressedSize;
-
/**
* @brief The CRC32 checksum of the entry's file.
*/
@property (readonly, nonatomic) uint32_t CRC32;
@@ -207,28 +181,11 @@
*
* See the ZIP specification for details.
*/
@property (readonly, nonatomic) uint16_t generalPurposeBitFlag;
-/**
- * @brief Creates a new OFZIPArchiveEntry with the specified file name.
- *
- * @param fileName The file name for the OFZIPArchiveEntry
- * @return A new, autoreleased OFZIPArchiveEntry
- */
-+ (instancetype)entryWithFileName: (OFString *)fileName;
-
- (instancetype)init OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated OFZIPArchiveEntry with the specified
- * file name.
- *
- * @param fileName The file name for the OFZIPArchiveEntry
- * @return An initialized OFZIPArchiveEntry
- */
-- (instancetype)initWithFileName: (OFString *)fileName;
@end
#ifdef __cplusplus
extern "C" {
#endif
Index: src/OFZIPArchiveEntry.m
==================================================================
--- src/OFZIPArchiveEntry.m
+++ src/OFZIPArchiveEntry.m
@@ -170,39 +170,25 @@
*size = 0;
return OFNotFound;
}
@implementation OFZIPArchiveEntry
-+ (instancetype)entryWithFileName: (OFString *)fileName
-{
- return [[[self alloc] initWithFileName: fileName] autorelease];
-}
+/*
+ * The following are optional in OFArchiveEntry, but Apple GCC 4.0.1 is buggy
+ * and needs this to stop complaining.
+ */
+@dynamic POSIXPermissions, ownerAccountID, groupOwnerAccountID;
+@dynamic ownerAccountName, groupOwnerAccountName;
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
-- (instancetype)initWithFileName: (OFString *)fileName
-{
- self = [super init];
-
- @try {
- void *pool = objc_autoreleasePoolPush();
-
- if (fileName.UTF8StringLength > UINT16_MAX)
- @throw [OFOutOfRangeException exception];
-
- _fileName = [fileName copy];
-
- objc_autoreleasePoolPop(pool);
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
+- (instancetype)of_init
+{
+ return [super init];
}
- (instancetype)of_initWithStream: (OFStream *)stream
{
self = [super init];
@@ -253,11 +239,11 @@
if (ZIP64Index != OFNotFound) {
const uint8_t *ZIP64 =
[extraField itemAtIndex: ZIP64Index];
OFRange range =
- OFRangeMake(ZIP64Index - 4, ZIP64Size + 4);
+ OFMakeRange(ZIP64Index - 4, ZIP64Size + 4);
if (_uncompressedSize == 0xFFFFFFFF)
_uncompressedSize = OFZIPArchiveReadField64(
&ZIP64, &ZIP64Size);
if (_compressedSize == 0xFFFFFFFF)
@@ -385,16 +371,16 @@
- (OFZIPArchiveEntryCompressionMethod)compressionMethod
{
return _compressionMethod;
}
-- (uint64_t)compressedSize
+- (unsigned long long)compressedSize
{
return _compressedSize;
}
-- (uint64_t)uncompressedSize
+- (unsigned long long)uncompressedSize
{
return _uncompressedSize;
}
- (uint32_t)CRC32
Index: src/ObjFW.h
==================================================================
--- src/ObjFW.h
+++ src/ObjFW.h
@@ -44,12 +44,12 @@
#import "OFInvocation.h"
#import "OFNumber.h"
#import "OFDate.h"
#import "OFUUID.h"
-#import "OFURL.h"
-#import "OFURLHandler.h"
+#import "OFURI.h"
+#import "OFURIHandler.h"
#import "OFColor.h"
#import "OFNotification.h"
#import "OFNotificationCenter.h"
@@ -82,24 +82,23 @@
# import "OFKernelEventObserver.h"
# import "OFDNSQuery.h"
# import "OFDNSResourceRecord.h"
# import "OFDNSResponse.h"
# import "OFDNSResolver.h"
+# ifdef OF_HAVE_UNIX_SOCKETS
+# import "OFUNIXDatagramSocket.h"
+# import "OFUNIXStreamSocket.h"
+# endif
# ifdef OF_HAVE_IPX
# import "OFIPXSocket.h"
# import "OFSPXSocket.h"
# import "OFSPXStreamSocket.h"
# endif
-# ifdef OF_HAVE_UNIX_SOCKETS
-# import "OFUNIXDatagramSocket.h"
-# import "OFUNIXStreamSocket.h"
-# endif
-#endif
-#ifdef OF_HAVE_SOCKETS
-# ifdef OF_HAVE_THREADS
-# import "OFHTTPClient.h"
-# endif
+# ifdef OF_HAVE_APPLETALK
+# import "OFDDPSocket.h"
+# endif
+# import "OFHTTPClient.h"
# import "OFHTTPCookie.h"
# import "OFHTTPCookieManager.h"
# import "OFHTTPRequest.h"
# import "OFHTTPResponse.h"
# import "OFHTTPServer.h"
@@ -155,114 +154,102 @@
#import "OFASN1UTF8String.h"
#import "OFASN1Value.h"
#import "OFAllocFailedException.h"
#import "OFException.h"
-#ifdef OF_HAVE_SOCKETS
-# import "OFAcceptFailedException.h"
-# import "OFAlreadyConnectedException.h"
-# import "OFBindFailedException.h"
-#endif
-#import "OFChangeCurrentDirectoryPathFailedException.h"
+#import "OFChangeCurrentDirectoryFailedException.h"
#import "OFChecksumMismatchException.h"
-#ifdef OF_HAVE_THREADS
-# import "OFConditionBroadcastFailedException.h"
-# import "OFConditionSignalFailedException.h"
-# import "OFConditionStillWaitingException.h"
-# import "OFConditionWaitFailedException.h"
-#endif
-#ifdef OF_HAVE_SOCKETS
-# import "OFConnectionFailedException.h"
-#endif
#import "OFCopyItemFailedException.h"
#import "OFCreateDirectoryFailedException.h"
#import "OFCreateSymbolicLinkFailedException.h"
-#ifdef OF_WINDOWS
-# import "OFCreateWindowsRegistryKeyFailedException.h"
-#endif
-#ifdef OF_HAVE_SOCKETS
-# import "OFDNSQueryFailedException.h"
-#endif
-#ifdef OF_WINDOWS
-# import "OFDeleteWindowsRegistryKeyFailedException.h"
-# import "OFDeleteWindowsRegistryValueFailedException.h"
-#endif
#import "OFEnumerationMutationException.h"
#ifdef OF_HAVE_FILES
-# import "OFGetCurrentDirectoryPathFailedException.h"
+# import "OFGetCurrentDirectoryFailedException.h"
#endif
+#import "OFGetItemAttributesFailedException.h"
#import "OFGetOptionFailedException.h"
-#ifdef OF_WINDOWS
-# import "OFGetWindowsRegistryValueFailedException.h"
-#endif
#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
-#ifdef OF_HAVE_SOCKETS
-# import "OFHTTPRequestFailedException.h"
-#endif
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidJSONException.h"
-#import "OFInvalidServerReplyException.h"
-#import "OFLinkFailedException.h"
-#ifdef OF_HAVE_SOCKETS
-# import "OFListenFailedException.h"
-#endif
+#import "OFInvalidServerResponseException.h"
+#import "OFLinkItemFailedException.h"
#ifdef OF_HAVE_PLUGINS
# import "OFLoadPluginFailedException.h"
#endif
#import "OFLockFailedException.h"
#import "OFMalformedXMLException.h"
#import "OFMoveItemFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
-#ifdef OF_HAVE_SOCKETS
-# import "OFObserveFailedException.h"
-#endif
#import "OFOpenItemFailedException.h"
-#ifdef OF_WINDOWS
-# import "OFOpenWindowsRegistryKeyFailedException.h"
-#endif
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFReadOrWriteFailedException.h"
#import "OFRemoveItemFailedException.h"
-#ifdef OF_HAVE_SOCKETS
-# import "OFResolveHostFailedException.h"
-#endif
-#import "OFRetrieveItemAttributesFailedException.h"
#import "OFSeekFailedException.h"
#import "OFSetItemAttributesFailedException.h"
#import "OFSetOptionFailedException.h"
-#ifdef OF_WINDOWS
-# import "OFSetWindowsRegistryValueFailedException.h"
-#endif
#import "OFStillLockedException.h"
-#ifdef OF_HAVE_THREADS
-# import "OFThreadJoinFailedException.h"
-# import "OFThreadStartFailedException.h"
-# import "OFThreadStillRunningException.h"
-#endif
-#ifdef OF_HAVE_SOCKETS
-# import "OFTLSHandshakeFailedException.h"
-#endif
#import "OFTruncatedDataException.h"
#import "OFUnboundNamespaceException.h"
#import "OFUnboundPrefixException.h"
#import "OFUndefinedKeyException.h"
#import "OFUnknownXMLEntityException.h"
#import "OFUnlockFailedException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"
-
+#ifdef OF_HAVE_SOCKETS
+# import "OFAcceptSocketFailedException.h"
+# import "OFAlreadyConnectedException.h"
+# import "OFBindIPSocketFailedException.h"
+# import "OFBindSocketFailedException.h"
+# import "OFConnectIPSocketFailedException.h"
+# import "OFConnectSocketFailedException.h"
+# import "OFDNSQueryFailedException.h"
+# import "OFHTTPRequestFailedException.h"
+# import "OFListenOnSocketFailedException.h"
+# import "OFObserveKernelEventsFailedException.h"
+# import "OFResolveHostFailedException.h"
+# import "OFTLSHandshakeFailedException.h"
+# ifdef OF_HAVE_UNIX_SOCKETS
+# import "OFBindUNIXSocketFailedException.h"
+# import "OFConnectUNIXSocketFailedException.h"
+# endif
+# ifdef OF_HAVE_IPX
+# import "OFBindIPXSocketFailedException.h"
+# import "OFConnectSPXSocketFailedException.h"
+# endif
+# ifdef OF_HAVE_APPLETALK
+# import "OFBindDDPSocketFailedException.h"
+# endif
+#endif
+#ifdef OF_HAVE_THREADS
+# import "OFBroadcastConditionFailedException.h"
+# import "OFConditionStillWaitingException.h"
+# import "OFJoinThreadFailedException.h"
+# import "OFSignalConditionFailedException.h"
+# import "OFStartThreadFailedException.h"
+# import "OFThreadStillRunningException.h"
+# import "OFWaitForConditionFailedException.h"
+#endif
#ifdef OF_HAVE_PLUGINS
# import "OFPlugin.h"
#endif
+#ifdef OF_WINDOWS
+# import "OFCreateWindowsRegistryKeyFailedException.h"
+# import "OFDeleteWindowsRegistryKeyFailedException.h"
+# import "OFDeleteWindowsRegistryValueFailedException.h"
+# import "OFGetWindowsRegistryValueFailedException.h"
+# import "OFOpenWindowsRegistryKeyFailedException.h"
+# import "OFSetWindowsRegistryValueFailedException.h"
+#endif
#ifdef OF_HAVE_ATOMIC_OPS
# import "OFAtomic.h"
#endif
#import "OFLocking.h"
Index: src/exceptions/Makefile
==================================================================
--- src/exceptions/Makefile
+++ src/exceptions/Makefile
@@ -2,27 +2,27 @@
STATIC_PIC_LIB_NOINST = ${EXCEPTIONS_LIB_A}
STATIC_LIB_NOINST = ${EXCEPTIONS_A}
SRCS = OFAllocFailedException.m \
- OFChangeCurrentDirectoryPathFailedException.m \
OFChecksumMismatchException.m \
OFCopyItemFailedException.m \
OFCreateDirectoryFailedException.m \
OFCreateSymbolicLinkFailedException.m \
OFEnumerationMutationException.m \
OFException.m \
+ OFGetItemAttributesFailedException.m \
OFGetOptionFailedException.m \
OFHashAlreadyCalculatedException.m \
OFHashNotCalculatedException.m \
OFInitializationFailedException.m \
OFInvalidArgumentException.m \
OFInvalidEncodingException.m \
OFInvalidFormatException.m \
OFInvalidJSONException.m \
- OFInvalidServerReplyException.m \
- OFLinkFailedException.m \
+ OFInvalidServerResponseException.m \
+ OFLinkItemFailedException.m \
OFLockFailedException.m \
OFMalformedXMLException.m \
OFMoveItemFailedException.m \
OFNotImplementedException.m \
OFNotOpenException.m \
@@ -30,11 +30,10 @@
OFOutOfMemoryException.m \
OFOutOfRangeException.m \
OFReadFailedException.m \
OFReadOrWriteFailedException.m \
OFRemoveItemFailedException.m \
- OFRetrieveItemAttributesFailedException.m \
OFSeekFailedException.m \
OFSetItemAttributesFailedException.m \
OFSetOptionFailedException.m \
OFStillLockedException.m \
OFTruncatedDataException.m \
@@ -49,38 +48,49 @@
${USE_SRCS_FILES} \
${USE_SRCS_PLUGINS} \
${USE_SRCS_SOCKETS} \
${USE_SRCS_THREADS} \
${USE_SRCS_WINDOWS}
-SRCS_FILES = OFGetCurrentDirectoryPathFailedException.m
+SRCS_FILES = OFChangeCurrentDirectoryFailedException.m \
+ OFGetCurrentDirectoryFailedException.m
SRCS_PLUGINS = OFLoadPluginFailedException.m
-SRCS_SOCKETS = OFAcceptFailedException.m \
+SRCS_SOCKETS = OFAcceptSocketFailedException.m \
OFAlreadyConnectedException.m \
- OFBindFailedException.m \
- OFConnectionFailedException.m \
+ OFBindIPSocketFailedException.m \
+ OFBindSocketFailedException.m \
+ OFConnectIPSocketFailedException.m \
+ OFConnectSocketFailedException.m \
OFDNSQueryFailedException.m \
OFHTTPRequestFailedException.m \
- OFListenFailedException.m \
- OFObserveFailedException.m \
+ OFListenOnSocketFailedException.m \
+ OFObserveKernelEventsFailedException.m \
OFResolveHostFailedException.m \
- OFTLSHandshakeFailedException.m
-SRCS_THREADS = OFConditionBroadcastFailedException.m \
- OFConditionSignalFailedException.m \
+ OFTLSHandshakeFailedException.m \
+ ${USE_SRCS_APPLETALK} \
+ ${USE_SRCS_IPX} \
+ ${USE_SRCS_UNIX_SOCKETS}
+SRCS_APPLETALK = OFBindDDPSocketFailedException.m
+SRCS_IPX = OFBindIPXSocketFailedException.m \
+ OFConnectSPXSocketFailedException.m
+SRCS_UNIX_SOCKETS = OFBindUNIXSocketFailedException.m \
+ OFConnectUNIXSocketFailedException.m
+SRCS_THREADS = OFBroadcastConditionFailedException.m \
OFConditionStillWaitingException.m \
- OFConditionWaitFailedException.m \
- OFThreadJoinFailedException.m \
- OFThreadStartFailedException.m \
- OFThreadStillRunningException.m
+ OFJoinThreadFailedException.m \
+ OFSignalConditionFailedException.m \
+ OFStartThreadFailedException.m \
+ OFThreadStillRunningException.m \
+ OFWaitForConditionFailedException.m
SRCS_WINDOWS = OFCreateWindowsRegistryKeyFailedException.m \
OFDeleteWindowsRegistryKeyFailedException.m \
OFDeleteWindowsRegistryValueFailedException.m \
OFGetWindowsRegistryValueFailedException.m \
OFOpenWindowsRegistryKeyFailedException.m \
OFSetWindowsRegistryValueFailedException.m
INCLUDES := ${SRCS:.m=.h}
-SRCS += OFSandboxActivationFailedException.m
+SRCS += OFActivateSandboxFailedException.m
include ../../buildsys.mk
CPPFLAGS += -I. -I.. -I../.. -I../runtime
DELETED src/exceptions/OFAcceptFailedException.h
Index: src/exceptions/OFAcceptFailedException.h
==================================================================
--- src/exceptions/OFAcceptFailedException.h
+++ src/exceptions/OFAcceptFailedException.h
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-#ifndef OF_HAVE_SOCKETS
-# error No sockets available!
-#endif
-
-OF_ASSUME_NONNULL_BEGIN
-
-/**
- * @class OFAcceptFailedException \
- * OFAcceptFailedException.h ObjFW/OFAcceptFailedException.h
- *
- * @brief An exception indicating that accepting a connection failed.
- */
-@interface OFAcceptFailedException: OFException
-{
- id _socket;
- int _errNo;
-}
-
-/**
- * @brief The socket which could not accept a connection.
- */
-@property (readonly, nonatomic) id socket;
-
-/**
- * @brief The errno from when the exception was created.
- */
-@property (readonly, nonatomic) int errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Creates a new, autoreleased accept failed exception.
- *
- * @param socket The socket which could not accept a connection
- * @param errNo The errno for the error
- * @return A new, autoreleased accept failed exception
- */
-+ (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo;
-
-- (instancetype)init OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated accept failed exception.
- *
- * @param socket The socket which could not accept a connection
- * @param errNo The errno for the error
- * @return An initialized accept failed exception
- */
-- (instancetype)initWithSocket: (id)socket
- errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFAcceptFailedException.m
Index: src/exceptions/OFAcceptFailedException.m
==================================================================
--- src/exceptions/OFAcceptFailedException.m
+++ src/exceptions/OFAcceptFailedException.m
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFAcceptFailedException.h"
-#import "OFString.h"
-
-@implementation OFAcceptFailedException
-@synthesize socket = _socket, errNo = _errNo;
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-+ (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo
-{
- return [[[self alloc] initWithSocket: socket errNo: errNo] autorelease];
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (instancetype)initWithSocket: (id)socket errNo: (int)errNo
-{
- self = [super init];
-
- _socket = [socket retain];
- _errNo = errNo;
-
- return self;
-}
-
-- (void)dealloc
-{
- [_socket release];
-
- [super dealloc];
-}
-
-- (OFString *)description
-{
- return [OFString stringWithFormat:
- @"Failed to accept connection in socket of class %@: %@",
- [_socket class], OFStrError(_errNo)];
-}
-@end
ADDED src/exceptions/OFAcceptSocketFailedException.h
Index: src/exceptions/OFAcceptSocketFailedException.h
==================================================================
--- src/exceptions/OFAcceptSocketFailedException.h
+++ src/exceptions/OFAcceptSocketFailedException.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+#ifndef OF_HAVE_SOCKETS
+# error No sockets available!
+#endif
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFAcceptSocketFailedException \
+ * OFAcceptSocketFailedException.h ObjFW/OFAcceptSocketFailedException.h
+ *
+ * @brief An exception indicating that accepting a connection failed.
+ */
+@interface OFAcceptSocketFailedException: OFException
+{
+ id _socket;
+ int _errNo;
+ OF_RESERVE_IVARS(OFAcceptSocketFailedException, 4)
+}
+
+/**
+ * @brief The socket which could not accept a connection.
+ */
+@property (readonly, nonatomic) id socket;
+
+/**
+ * @brief The errno from when the exception was created.
+ */
+@property (readonly, nonatomic) int errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
+
+/**
+ * @brief Creates a new, autoreleased accept failed exception.
+ *
+ * @param socket The socket which could not accept a connection
+ * @param errNo The errno for the error
+ * @return A new, autoreleased accept failed exception
+ */
++ (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo;
+
+- (instancetype)init OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated accept failed exception.
+ *
+ * @param socket The socket which could not accept a connection
+ * @param errNo The errno for the error
+ * @return An initialized accept failed exception
+ */
+- (instancetype)initWithSocket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFAcceptSocketFailedException.m
Index: src/exceptions/OFAcceptSocketFailedException.m
==================================================================
--- src/exceptions/OFAcceptSocketFailedException.m
+++ src/exceptions/OFAcceptSocketFailedException.m
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFAcceptSocketFailedException.h"
+#import "OFString.h"
+
+@implementation OFAcceptSocketFailedException
+@synthesize socket = _socket, errNo = _errNo;
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ return [[[self alloc] initWithSocket: sock errNo: errNo] autorelease];
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ self = [super init];
+
+ _socket = [sock retain];
+ _errNo = errNo;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_socket release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Failed to accept connection in socket of class %@: %@",
+ [_socket class], OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFActivateSandboxFailedException.h
Index: src/exceptions/OFActivateSandboxFailedException.h
==================================================================
--- src/exceptions/OFActivateSandboxFailedException.h
+++ src/exceptions/OFActivateSandboxFailedException.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFSandbox;
+
+@interface OFActivateSandboxFailedException: OFException
+{
+ OFSandbox *_sandbox;
+ int _errNo;
+}
+
+@property (readonly, nonatomic) OFSandbox *sandbox;
+@property (readonly, nonatomic) int errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
++ (instancetype)exceptionWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo;
+- (instancetype)init OF_UNAVAILABLE;
+- (instancetype)initWithSandbox: (OFSandbox *)sandbox
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFActivateSandboxFailedException.m
Index: src/exceptions/OFActivateSandboxFailedException.m
==================================================================
--- src/exceptions/OFActivateSandboxFailedException.m
+++ src/exceptions/OFActivateSandboxFailedException.m
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFActivateSandboxFailedException.h"
+#import "OFString.h"
+#import "OFSandbox.h"
+
+@implementation OFActivateSandboxFailedException
+@synthesize sandbox = _sandbox, errNo = _errNo;
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo
+{
+ return [[[self alloc] initWithSandbox: sandbox
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo
+{
+ self = [super init];
+
+ _sandbox = [sandbox retain];
+ _errNo = errNo;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_sandbox release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"The sandbox could not be applied: %@", OFStrError(_errNo)];
+}
+@end
Index: src/exceptions/OFAllocFailedException.h
==================================================================
--- src/exceptions/OFAllocFailedException.h
+++ src/exceptions/OFAllocFailedException.h
@@ -32,10 +32,11 @@
* This is the only exception which is not an OFException as it's special. It
* does not know for which class allocation failed and it should not be handled
* like other exceptions, as the exception handling code is not allowed to
* allocate *any* memory.
*/
+OF_SUBCLASSING_RESTRICTED
@interface OFAllocFailedException: OFObject
+ (instancetype)exception OF_UNAVAILABLE;
- (instancetype)init OF_UNAVAILABLE;
/**
Index: src/exceptions/OFAlreadyConnectedException.h
==================================================================
--- src/exceptions/OFAlreadyConnectedException.h
+++ src/exceptions/OFAlreadyConnectedException.h
@@ -29,10 +29,11 @@
* connected or bound socket.
*/
@interface OFAlreadyConnectedException: OFException
{
id _socket;
+ OF_RESERVE_IVARS(OFAlreadyConnectedException, 4)
}
/**
* @brief The socket which is already connected.
*/
Index: src/exceptions/OFAlreadyConnectedException.m
==================================================================
--- src/exceptions/OFAlreadyConnectedException.m
+++ src/exceptions/OFAlreadyConnectedException.m
@@ -19,25 +19,25 @@
#import "OFString.h"
@implementation OFAlreadyConnectedException
@synthesize socket = _socket;
-+ (instancetype)exceptionWithSocket: (id)socket
++ (instancetype)exceptionWithSocket: (id)sock
{
- return [[[self alloc] initWithSocket: socket] autorelease];
+ return [[[self alloc] initWithSocket: sock] autorelease];
}
- (instancetype)init
{
return [self initWithSocket: nil];
}
-- (instancetype)initWithSocket: (id)socket
+- (instancetype)initWithSocket: (id)sock
{
self = [super init];
- _socket = [socket retain];
+ _socket = [sock retain];
return self;
}
- (void)dealloc
ADDED src/exceptions/OFBindDDPSocketFailedException.h
Index: src/exceptions/OFBindDDPSocketFailedException.h
==================================================================
--- src/exceptions/OFBindDDPSocketFailedException.h
+++ src/exceptions/OFBindDDPSocketFailedException.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFBindSocketFailedException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFBindDDPSocketFailedException \
+ * OFBindDDPSocketFailedException.h \
+ * ObjFW/OFBindDDPSocketFailedException.h
+ *
+ * @brief An exception indicating that binding a DDP socket failed.
+ */
+OF_SUBCLASSING_RESTRICTED
+@interface OFBindDDPSocketFailedException: OFBindSocketFailedException
+{
+ uint16_t _network;
+ uint8_t _node, _port, _protocolType;
+}
+
+/**
+ * @brief The DDP network on which binding failed.
+ */
+@property (readonly, nonatomic) uint16_t network;
+
+/**
+ * @brief The DDP node for which binding failed.
+ */
+@property (readonly, nonatomic) uint8_t node;
+
+/**
+ * @brief The DDP port on which binding failed.
+ */
+@property (readonly, nonatomic) uint8_t port;
+
+/**
+ * @brief The DDP protocol type for which binding failed.
+ */
+@property (readonly, nonatomic) uint8_t protocolType;
+
+/**
+ * @brief Creates a new, autoreleased bind DDP socket failed exception.
+ *
+ * @param network The DDP network on which binding failed
+ * @param node The DDP node for which binding failed
+ * @param port The DDP port on which binding failed
+ * @param protocolType The DDP protocol type for which binding failed.
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased bind DDP socket failed exception
+ */
++ (instancetype)exceptionWithNetwork: (uint16_t)network
+ node: (uint8_t)node
+ port: (uint8_t)port
+ protocolType: (uint8_t)protocolType
+ socket: (id)socket
+ errNo: (int)errNo;
+
++ (instancetype)exceptionWithSocket: (id)socket
+ errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated bind DDP socket failed exception.
+ *
+ * @param network The DDP network on which binding failed
+ * @param node The DDP node for which binding failed
+ * @param port The DDP port on which binding failed
+ * @param protocolType The DDP protocol type for which binding failed.
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return An initialized bind DDP socket failed exception
+ */
+- (instancetype)initWithNetwork: (uint16_t)network
+ node: (uint8_t)node
+ port: (uint8_t)port
+ protocolType: (uint8_t)protocolType
+ socket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFBindDDPSocketFailedException.m
Index: src/exceptions/OFBindDDPSocketFailedException.m
==================================================================
--- src/exceptions/OFBindDDPSocketFailedException.m
+++ src/exceptions/OFBindDDPSocketFailedException.m
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFBindDDPSocketFailedException.h"
+#import "OFData.h"
+#import "OFString.h"
+
+@implementation OFBindDDPSocketFailedException
+@synthesize network = _network, node = _node, port = _port;
+@synthesize protocolType = _protocolType;
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithNetwork: (uint16_t)network
+ node: (uint8_t)node
+ port: (uint8_t)port
+ protocolType: (uint8_t)protocolType
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithNetwork: network
+ node: node
+ port: port
+ protocolType: protocolType
+ socket: sock
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithNetwork: (uint16_t)network
+ node: (uint8_t)node
+ port: (uint8_t)port
+ protocolType: (uint8_t)protocolType
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ self = [super initWithSocket: sock errNo: errNo];
+
+ @try {
+ _network = network;
+ _node = node;
+ _port = port;
+ _protocolType = protocolType;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Binding to port %" @PRIx8 @" of node %" @PRIx8 @" on network "
+ @"%" PRIx16 @" with protocol type %" @PRIx8 @" failed in socket of "
+ @"type %@: %@",
+ _port, _node, _network, _protocolType, [_socket class],
+ OFStrError(_errNo)];
+}
+@end
DELETED src/exceptions/OFBindFailedException.h
Index: src/exceptions/OFBindFailedException.h
==================================================================
--- src/exceptions/OFBindFailedException.h
+++ src/exceptions/OFBindFailedException.h
@@ -1,159 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-#ifndef OF_HAVE_SOCKETS
-# error No sockets available!
-#endif
-
-#import "OFSocket.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-/**
- * @class OFBindFailedException \
- * OFBindFailedException.h ObjFW/OFBindFailedException.h
- *
- * @brief An exception indicating that binding a socket failed.
- */
-@interface OFBindFailedException: OFException
-{
- /* IP */
- OFString *_Nullable _host;
- uint16_t _port;
- /* IPX */
- uint8_t _packetType;
- /* UNIX socket */
- OFString *_Nullable _path;
- id _socket;
- int _errNo;
-}
-
-/**
- * @brief The host on which binding failed.
- */
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;
-
-/**
- * @brief The port on which binding failed.
- */
-@property (readonly, nonatomic) uint16_t port;
-
-/**
- * @brief The IPX packet type for which binding failed.
- */
-@property (readonly, nonatomic) uint8_t packetType;
-
-/**
- * @brief The path on which binding failed.
- */
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path;
-
-/**
- * @brief The socket which could not be bound.
- */
-@property (readonly, nonatomic) id socket;
-
-/**
- * @brief The errno of the error that occurred.
- */
-@property (readonly, nonatomic) int errNo;
-
-/**
- * @brief Creates a new, autoreleased bind failed exception.
- *
- * @param host The host on which binding failed
- * @param port The port on which binding failed
- * @param socket The socket which could not be bound
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased bind failed exception
- */
-+ (instancetype)exceptionWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)socket
- errNo: (int)errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Creates a new, autoreleased bind failed exception.
- *
- * @param port The IPX port to which binding failed
- * @param packetType The IPX packet type for which binding failed
- * @param socket The socket which could not be bound
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased bind failed exception
- */
-+ (instancetype)exceptionWithPort: (uint16_t)port
- packetType: (uint8_t)packetType
- socket: (id)socket
- errNo: (int)errNo;
-
-/**
- * @brief Creates a new, autoreleased bind failed exception.
- *
- * @param path The path on which binding failed
- * @param socket The socket which could not be bound
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased bind failed exception
- */
-+ (instancetype)exceptionWithPath: (OFString *)path
- socket: (id)socket
- errNo: (int)errNo;
-
-/**
- * @brief Initializes an already allocated bind failed exception.
- *
- * @param host The host on which binding failed
- * @param port The port on which binding failed
- * @param socket The socket which could not be bound
- * @param errNo The errno of the error that occurred
- * @return An initialized bind failed exception
- */
-- (instancetype)initWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)socket
- errNo: (int)errNo;
-
-/**
- * @brief Initializes an already allocated bind failed exception.
- *
- * @param port The IPX port to which binding failed
- * @param packetType The IPX packet type for which binding failed
- * @param socket The socket which could not be bound
- * @param errNo The errno of the error that occurred
- * @return An initialized bind failed exception
- */
-- (instancetype)initWithPort: (uint16_t)port
- packetType: (uint8_t)packetType
- socket: (id)socket
- errNo: (int)errNo;
-/**
- * @brief Initializes an already allocated bind failed exception.
- *
- * @param path The path on which binding failed
- * @param socket The socket which could not be bound
- * @param errNo The errno of the error that occurred
- * @return An initialized bind failed exception
- */
-- (instancetype)initWithPath: (OFString *)path
- socket: (id)socket
- errNo: (int)errNo;
-
-- (instancetype)init OF_UNAVAILABLE;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFBindFailedException.m
Index: src/exceptions/OFBindFailedException.m
==================================================================
--- src/exceptions/OFBindFailedException.m
+++ src/exceptions/OFBindFailedException.m
@@ -1,150 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFBindFailedException.h"
-#import "OFString.h"
-
-@implementation OFBindFailedException
-@synthesize host = _host, port = _port, packetType = _packetType, path = _path;
-@synthesize socket = _socket, errNo = _errNo;
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-+ (instancetype)exceptionWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)sock
- errNo: (int)errNo
-{
- return [[[self alloc] initWithHost: host
- port: port
- socket: sock
- errNo: errNo] autorelease];
-}
-
-+ (instancetype)exceptionWithPort: (uint16_t)port
- packetType: (uint8_t)packetType
- socket: (id)sock
- errNo: (int)errNo
-{
- return [[[self alloc] initWithPort: port
- packetType: packetType
- socket: sock
- errNo: errNo] autorelease];
-}
-
-+ (instancetype)exceptionWithPath: (OFString *)path
- socket: (id)sock
- errNo: (int)errNo
-{
- return [[[self alloc] initWithPath: path
- socket: sock
- errNo: errNo] autorelease];
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (instancetype)initWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)sock
- errNo: (int)errNo
-{
- self = [super init];
-
- @try {
- _host = [host copy];
- _port = port;
- _socket = [sock retain];
- _errNo = errNo;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (instancetype)initWithPort: (uint16_t)port
- packetType: (uint8_t)packetType
- socket: (id)sock
- errNo: (int)errNo
-{
- self = [super init];
-
- @try {
- _port = port;
- _packetType = packetType;
- _socket = [sock retain];
- _errNo = errNo;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (instancetype)initWithPath: (OFString *)path
- socket: (id)sock
- errNo: (int)errNo
-{
- self = [super init];
-
- @try {
- _path = [path copy];
- _socket = [sock retain];
- _errNo = errNo;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- [_host release];
- [_path release];
- [_socket release];
-
- [super dealloc];
-}
-
-- (OFString *)description
-{
- if (_path != nil)
- return [OFString stringWithFormat:
- @"Binding to path %@ failed in socket of type %@: %@",
- _path, [_socket class], OFStrError(_errNo)];
- else if (_host != nil)
- return [OFString stringWithFormat:
- @"Binding to port %" @PRIu16 @" on host %@ failed in "
- @"socket of type %@: %@",
- _port, _host, [_socket class], OFStrError(_errNo)];
- else
- return [OFString stringWithFormat:
- @"Binding to port %" @PRIx16 @" for packet type %" @PRIx8
- @" failed in socket of type %@: %@",
- _port, _packetType, [_socket class], OFStrError(_errNo)];
-}
-@end
ADDED src/exceptions/OFBindIPSocketFailedException.h
Index: src/exceptions/OFBindIPSocketFailedException.h
==================================================================
--- src/exceptions/OFBindIPSocketFailedException.h
+++ src/exceptions/OFBindIPSocketFailedException.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFBindSocketFailedException.h"
+
+#ifndef OF_HAVE_SOCKETS
+# error No sockets available!
+#endif
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFBindIPSocketFailedException \
+ * OFBindIPSocketFailedException.h ObjFW/OFBindIPSocketFailedException.h
+ *
+ * @brief An exception indicating that binding an IP socket failed.
+ */
+OF_SUBCLASSING_RESTRICTED
+@interface OFBindIPSocketFailedException: OFBindSocketFailedException
+{
+ OFString *_Nullable _host;
+ uint16_t _port;
+}
+
+/**
+ * @brief The host on which binding failed.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;
+
+/**
+ * @brief The port on which binding failed.
+ */
+@property (readonly, nonatomic) uint16_t port;
+
+/**
+ * @brief Creates a new, autoreleased bind IP socket failed exception.
+ *
+ * @param host The host on which binding failed
+ * @param port The port on which binding failed
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased bind IP socket failed exception
+ */
++ (instancetype)exceptionWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)socket
+ errNo: (int)errNo;
+
++ (instancetype)exceptionWithSocket: (id)socket
+ errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated bind IP socket failed exception.
+ *
+ * @param host The host on which binding failed
+ * @param port The port on which binding failed
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return An initialized bind IP socket failed exception
+ */
+- (instancetype)initWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFBindIPSocketFailedException.m
Index: src/exceptions/OFBindIPSocketFailedException.m
==================================================================
--- src/exceptions/OFBindIPSocketFailedException.m
+++ src/exceptions/OFBindIPSocketFailedException.m
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFBindIPSocketFailedException.h"
+#import "OFString.h"
+
+@implementation OFBindIPSocketFailedException
+@synthesize host = _host, port = _port;
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithHost: host
+ port: port
+ socket: sock
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ self = [super initWithSocket: sock errNo: errNo];
+
+ @try {
+ _host = [host copy];
+ _port = port;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_host release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Binding to port %" @PRIu16 @" on host %@ failed in socket of "
+ @"type %@: %@",
+ _port, _host, [_socket class], OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFBindIPXSocketFailedException.h
Index: src/exceptions/OFBindIPXSocketFailedException.h
==================================================================
--- src/exceptions/OFBindIPXSocketFailedException.h
+++ src/exceptions/OFBindIPXSocketFailedException.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFBindSocketFailedException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFBindIPXSocketFailedException \
+ * OFBindIPXSocketFailedException.h \
+ * ObjFW/OFBindIPXSocketFailedException.h
+ *
+ * @brief An exception indicating that binding an IPX socket failed.
+ */
+OF_SUBCLASSING_RESTRICTED
+@interface OFBindIPXSocketFailedException: OFBindSocketFailedException
+{
+ uint32_t _network;
+ unsigned char _node[IPX_NODE_LEN];
+ uint16_t _port;
+ uint8_t _packetType;
+}
+
+/**
+ * @brief The IPX network on which binding failed.
+ */
+@property (readonly, nonatomic) uint32_t network;
+
+/**
+ * @brief The IPX port on which binding failed.
+ */
+@property (readonly, nonatomic) uint16_t port;
+
+/**
+ * @brief The IPX packet type for which binding failed.
+ */
+@property (readonly, nonatomic) uint8_t packetType;
+
+/**
+ * @brief Creates a new, autoreleased bind IPX socket failed exception.
+ *
+ * @param network The IPX network to which binding failed
+ * @param node The IPX node to which binding failed
+ * @param port The IPX port to which binding failed
+ * @param packetType The IPX packet type for which binding failed
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased bind IPX socket failed exception
+ */
++ (instancetype)
+ exceptionWithNetwork: (uint32_t)network
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
+ port: (uint16_t)port
+ packetType: (uint8_t)packetType
+ socket: (id)socket
+ errNo: (int)errNo;
+
++ (instancetype)exceptionWithSocket: (id)socket
+ errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated bind IPX socket failed exception.
+ *
+ * @param network The IPX network to which binding failed
+ * @param node The IPX node to which binding failed
+ * @param port The IPX port to which binding failed
+ * @param packetType The IPX packet type for which binding failed
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return An initialized bind IPX socket failed exception
+ */
+- (instancetype)
+ initWithNetwork: (uint32_t)network
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
+ port: (uint16_t)port
+ packetType: (uint8_t)packetType
+ socket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Get the IPX node for which binding failed.
+ *
+ * @param node A pointer to where to write the node to
+ */
+- (void)getNode: (unsigned char [_Nonnull IPX_NODE_LEN])node;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFBindIPXSocketFailedException.m
Index: src/exceptions/OFBindIPXSocketFailedException.m
==================================================================
--- src/exceptions/OFBindIPXSocketFailedException.m
+++ src/exceptions/OFBindIPXSocketFailedException.m
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+
+#import "OFBindIPXSocketFailedException.h"
+#import "OFData.h"
+#import "OFString.h"
+
+@implementation OFBindIPXSocketFailedException
+@synthesize network = _network, port = _port, packetType = _packetType;
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)
+ exceptionWithNetwork: (uint32_t)network
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
+ port: (uint16_t)port
+ packetType: (uint8_t)packetType
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithNetwork: network
+ node: node
+ port: port
+ packetType: packetType
+ socket: sock
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)
+ initWithNetwork: (uint32_t)network
+ node: (const unsigned char [_Nonnull IPX_NODE_LEN])node
+ port: (uint16_t)port
+ packetType: (uint8_t)packetType
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ self = [super initWithSocket: sock errNo: errNo];
+
+ @try {
+ _network = network;
+ memcpy(_node, node, sizeof(_node));
+ _port = port;
+ _packetType = packetType;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)getNode: (unsigned char [IPX_NODE_LEN])node
+{
+ memcpy(node, _node, sizeof(_node));
+}
+
+- (OFString *)description
+{
+ OFData *node = [OFData dataWithItems: _node count: sizeof(_node)];
+
+ return [OFString stringWithFormat:
+ @"Binding to network %" @PRIx16 " on node %@ with port %" @PRIx16
+ @" failed for packet type %" @PRIx8 " in socket of type %@: %@",
+ _network, node, _port, _packetType, [_socket class],
+ OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFBindSocketFailedException.h
Index: src/exceptions/OFBindSocketFailedException.h
==================================================================
--- src/exceptions/OFBindSocketFailedException.h
+++ src/exceptions/OFBindSocketFailedException.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+#ifndef OF_HAVE_SOCKETS
+# error No sockets available!
+#endif
+
+#import "OFSocket.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFBindSocketFailedException \
+ * OFBindSocketFailedException.h ObjFW/OFBindSocketFailedException.h
+ *
+ * @brief An exception indicating that binding a socket failed.
+ */
+@interface OFBindSocketFailedException: OFException
+{
+ id _socket;
+ int _errNo;
+ OF_RESERVE_IVARS(OFBindSocketFailedException, 4)
+}
+
+/**
+ * @brief The socket which could not be bound.
+ */
+@property (readonly, nonatomic) id socket;
+
+/**
+ * @brief The errno of the error that occurred.
+ */
+@property (readonly, nonatomic) int errNo;
+
+/**
+ * @brief Creates a new, autoreleased bind socket failed exception.
+ *
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased bind socket failed exception
+ */
++ (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated bind socket failed exception.
+ *
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return An initialized bind socket failed exception
+ */
+- (instancetype)initWithSocket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)init OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFBindSocketFailedException.m
Index: src/exceptions/OFBindSocketFailedException.m
==================================================================
--- src/exceptions/OFBindSocketFailedException.m
+++ src/exceptions/OFBindSocketFailedException.m
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFBindSocketFailedException.h"
+#import "OFString.h"
+
+@implementation OFBindSocketFailedException
+@synthesize socket = _socket, errNo = _errNo;
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ return [[[self alloc] initWithSocket: sock errNo: errNo] autorelease];
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ self = [super init];
+
+ @try {
+ _socket = [sock retain];
+ _errNo = errNo;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_socket release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Binding a socket of type %@ failed: %@",
+ [_socket class], OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFBindUNIXSocketFailedException.h
Index: src/exceptions/OFBindUNIXSocketFailedException.h
==================================================================
--- src/exceptions/OFBindUNIXSocketFailedException.h
+++ src/exceptions/OFBindUNIXSocketFailedException.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFBindSocketFailedException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFBindUNIXSocketFailedException \
+ * OFBindUNIXSocketFailedException.h \
+ * ObjFW/OFBindUNIXSocketFailedException.h
+ *
+ * @brief An exception indicating that binding a UNIX socket failed.
+ */
+OF_SUBCLASSING_RESTRICTED
+@interface OFBindUNIXSocketFailedException: OFBindSocketFailedException
+{
+ OFString *_Nullable _path;
+}
+
+/**
+ * @brief The path on which binding failed.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path;
+
+/**
+ * @brief Creates a new, autoreleased bind UNIX socket failed exception.
+ *
+ * @param path The path on which binding failed
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased bind UNIX socket failed exception
+ */
++ (instancetype)exceptionWithPath: (OFString *)path
+ socket: (id)socket
+ errNo: (int)errNo;
+
++ (instancetype)exceptionWithSocket: (id)socket
+ errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated bind UNIX socket failed exception.
+ *
+ * @param path The path on which binding failed
+ * @param socket The socket which could not be bound
+ * @param errNo The errno of the error that occurred
+ * @return An initialized bind UNIX socket failed exception
+ */
+- (instancetype)initWithPath: (OFString *)path
+ socket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFBindUNIXSocketFailedException.m
Index: src/exceptions/OFBindUNIXSocketFailedException.m
==================================================================
--- src/exceptions/OFBindUNIXSocketFailedException.m
+++ src/exceptions/OFBindUNIXSocketFailedException.m
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFBindUNIXSocketFailedException.h"
+#import "OFString.h"
+
+@implementation OFBindUNIXSocketFailedException
+@synthesize path = _path;
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithPath: (OFString *)path
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithPath: path
+ socket: sock
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithPath: (OFString *)path
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ self = [super initWithSocket: sock errNo: errNo];
+
+ @try {
+ _path = [path copy];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_path release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Binding to path %@ failed in socket of type %@: %@",
+ _path, [_socket class], OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFBroadcastConditionFailedException.h
Index: src/exceptions/OFBroadcastConditionFailedException.h
==================================================================
--- src/exceptions/OFBroadcastConditionFailedException.h
+++ src/exceptions/OFBroadcastConditionFailedException.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+#ifndef OF_HAVE_THREADS
+# error No threads available!
+#endif
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFCondition;
+
+/**
+ * @class OFBroadcastConditionFailedException \
+ * OFBroadcastConditionFailedException.h \
+ * ObjFW/OFBroadcastConditionFailedException.h
+ *
+ * @brief An exception indicating broadcasting a condition failed.
+ */
+@interface OFBroadcastConditionFailedException: OFException
+{
+ OFCondition *_condition;
+ int _errNo;
+ OF_RESERVE_IVARS(OFBroadcastConditionFailedException, 4)
+}
+
+/**
+ * @brief The condition which could not be broadcasted.
+ */
+@property (readonly, nonatomic) OFCondition *condition;
+
+/**
+ * @brief The errno of the error that occurred.
+ */
+@property (readonly, nonatomic) int errNo;
+
+/**
+ * @brief Returns a new, autoreleased condition broadcast failed exception.
+ *
+ * @param condition The condition which could not be broadcasted
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased condition broadcast failed exception
+ */
++ (instancetype)exceptionWithCondition: (OFCondition *)condition
+ errNo: (int)errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated condition broadcast failed exception.
+ *
+ * @param condition The condition which could not be broadcasted
+ * @param errNo The errno of the error that occurred
+ * @return An initialized condition broadcast failed exception
+ */
+- (instancetype)initWithCondition: (OFCondition *)condition
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)init OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFBroadcastConditionFailedException.m
Index: src/exceptions/OFBroadcastConditionFailedException.m
==================================================================
--- src/exceptions/OFBroadcastConditionFailedException.m
+++ src/exceptions/OFBroadcastConditionFailedException.m
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+
+#import "OFBroadcastConditionFailedException.h"
+#import "OFString.h"
+#import "OFCondition.h"
+
+@implementation OFBroadcastConditionFailedException
+@synthesize condition = _condition, errNo = _errNo;
+
++ (instancetype)exceptionWithCondition: (OFCondition *)condition
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithCondition: condition
+ errNo: errNo] autorelease];
+}
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (instancetype)initWithCondition: (OFCondition *)condition errNo: (int)errNo
+{
+ self = [super init];
+
+ _condition = [condition retain];
+ _errNo = errNo;
+
+ return self;
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (void)dealloc
+{
+ [_condition release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Broadcasting a condition of type %@ failed: %s",
+ _condition.class, strerror(_errNo)];
+}
+@end
ADDED src/exceptions/OFChangeCurrentDirectoryFailedException.h
Index: src/exceptions/OFChangeCurrentDirectoryFailedException.h
==================================================================
--- src/exceptions/OFChangeCurrentDirectoryFailedException.h
+++ src/exceptions/OFChangeCurrentDirectoryFailedException.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFChangeCurrentDirectoryFailedException \
+ * OFChangeCurrentDirectoryFailedException.h \
+ * ObjFW/OFChangeCurrentDirectoryFailedException.h
+ *
+ * @brief An exception indicating that changing the current directory path
+ * failed.
+ */
+@interface OFChangeCurrentDirectoryFailedException: OFException
+{
+ OFString *_path;
+ int _errNo;
+ OF_RESERVE_IVARS(OFChangeCurrentDirectoryFailedException, 4)
+}
+
+/**
+ * @brief The path of the directory to which the current path could not be
+ * changed.
+ */
+@property (readonly, nonatomic) OFString *path;
+
+/**
+ * @brief The errno of the error that occurred.
+ */
+@property (readonly, nonatomic) int errNo;
+
+/**
+ * @brief Creates a new, autoreleased change current directory path failed
+ * exception.
+ *
+ * @param path The path of the directory to which the current path could not be
+ * changed
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased change current directory path failed exception
+ */
++ (instancetype)exceptionWithPath: (OFString *)path errNo: (int)errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated change directory failed exception.
+ *
+ * @param path The path of the directory to which the current path could not be
+ * changed
+ * @param errNo The errno of the error that occurred
+ * @return An initialized change current directory path failed exception
+ */
+- (instancetype)initWithPath: (OFString *)path
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)init OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFChangeCurrentDirectoryFailedException.m
Index: src/exceptions/OFChangeCurrentDirectoryFailedException.m
==================================================================
--- src/exceptions/OFChangeCurrentDirectoryFailedException.m
+++ src/exceptions/OFChangeCurrentDirectoryFailedException.m
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFChangeCurrentDirectoryFailedException.h"
+#import "OFString.h"
+
+@implementation OFChangeCurrentDirectoryFailedException
+@synthesize path = _path, errNo = _errNo;
+
++ (instancetype)exceptionWithPath: (OFString *)path errNo: (int)errNo
+{
+ return [[[self alloc] initWithPath: path errNo: errNo] autorelease];
+}
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (instancetype)initWithPath: (OFString *)path errNo: (int)errNo
+{
+ self = [super init];
+
+ @try {
+ _path = [path copy];
+ _errNo = errNo;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (void)dealloc
+{
+ [_path release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Failed to change the current directory to %@: %@",
+ _path, OFStrError(_errNo)];
+}
+@end
DELETED src/exceptions/OFChangeCurrentDirectoryPathFailedException.h
Index: src/exceptions/OFChangeCurrentDirectoryPathFailedException.h
==================================================================
--- src/exceptions/OFChangeCurrentDirectoryPathFailedException.h
+++ src/exceptions/OFChangeCurrentDirectoryPathFailedException.h
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-/**
- * @class OFChangeCurrentDirectoryPathFailedException \
- * OFChangeCurrentDirectoryPathFailedException.h \
- * ObjFW/OFChangeCurrentDirectoryPathFailedException.h
- *
- * @brief An exception indicating that changing the current directory path
- * failed.
- */
-@interface OFChangeCurrentDirectoryPathFailedException: OFException
-{
- OFString *_path;
- int _errNo;
-}
-
-/**
- * @brief The path of the directory to which the current path could not be
- * changed.
- */
-@property (readonly, nonatomic) OFString *path;
-
-/**
- * @brief The errno of the error that occurred.
- */
-@property (readonly, nonatomic) int errNo;
-
-/**
- * @brief Creates a new, autoreleased change current directory path failed
- * exception.
- *
- * @param path The path of the directory to which the current path could not be
- * changed
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased change current directory path failed exception
- */
-+ (instancetype)exceptionWithPath: (OFString *)path errNo: (int)errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated change directory failed exception.
- *
- * @param path The path of the directory to which the current path could not be
- * changed
- * @param errNo The errno of the error that occurred
- * @return An initialized change current directory path failed exception
- */
-- (instancetype)initWithPath: (OFString *)path
- errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
-
-- (instancetype)init OF_UNAVAILABLE;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFChangeCurrentDirectoryPathFailedException.m
Index: src/exceptions/OFChangeCurrentDirectoryPathFailedException.m
==================================================================
--- src/exceptions/OFChangeCurrentDirectoryPathFailedException.m
+++ src/exceptions/OFChangeCurrentDirectoryPathFailedException.m
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFChangeCurrentDirectoryPathFailedException.h"
-#import "OFString.h"
-
-@implementation OFChangeCurrentDirectoryPathFailedException
-@synthesize path = _path, errNo = _errNo;
-
-+ (instancetype)exceptionWithPath: (OFString *)path errNo: (int)errNo
-{
- return [[[self alloc] initWithPath: path errNo: errNo] autorelease];
-}
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (instancetype)initWithPath: (OFString *)path errNo: (int)errNo
-{
- self = [super init];
-
- @try {
- _path = [path copy];
- _errNo = errNo;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (void)dealloc
-{
- [_path release];
-
- [super dealloc];
-}
-
-- (OFString *)description
-{
- return [OFString stringWithFormat:
- @"Failed to change the current directory path to %@: %@",
- _path, OFStrError(_errNo)];
-}
-@end
Index: src/exceptions/OFChecksumMismatchException.h
==================================================================
--- src/exceptions/OFChecksumMismatchException.h
+++ src/exceptions/OFChecksumMismatchException.h
@@ -24,10 +24,11 @@
* @brief An exception indicating that a checksum did not match.
*/
@interface OFChecksumMismatchException: OFException
{
OFString *_actualChecksum, *_expectedChecksum;
+ OF_RESERVE_IVARS(OFChecksumMismatchException, 4)
}
/**
* @brief The actual checksum calculated.
*/
DELETED src/exceptions/OFConditionBroadcastFailedException.h
Index: src/exceptions/OFConditionBroadcastFailedException.h
==================================================================
--- src/exceptions/OFConditionBroadcastFailedException.h
+++ src/exceptions/OFConditionBroadcastFailedException.h
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-#ifndef OF_HAVE_THREADS
-# error No threads available!
-#endif
-
-OF_ASSUME_NONNULL_BEGIN
-
-@class OFCondition;
-
-/**
- * @class OFConditionBroadcastFailedException \
- * OFConditionBroadcastFailedException.h \
- * ObjFW/OFConditionBroadcastFailedException.h
- *
- * @brief An exception indicating broadcasting a condition failed.
- */
-@interface OFConditionBroadcastFailedException: OFException
-{
- OFCondition *_condition;
- int _errNo;
-}
-
-/**
- * @brief The condition which could not be broadcasted.
- */
-@property (readonly, nonatomic) OFCondition *condition;
-
-/**
- * @brief The errno of the error that occurred.
- */
-@property (readonly, nonatomic) int errNo;
-
-/**
- * @brief Returns a new, autoreleased condition broadcast failed exception.
- *
- * @param condition The condition which could not be broadcasted
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased condition broadcast failed exception
- */
-+ (instancetype)exceptionWithCondition: (OFCondition *)condition
- errNo: (int)errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated condition broadcast failed exception.
- *
- * @param condition The condition which could not be broadcasted
- * @param errNo The errno of the error that occurred
- * @return An initialized condition broadcast failed exception
- */
-- (instancetype)initWithCondition: (OFCondition *)condition
- errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
-
-- (instancetype)init OF_UNAVAILABLE;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFConditionBroadcastFailedException.m
Index: src/exceptions/OFConditionBroadcastFailedException.m
==================================================================
--- src/exceptions/OFConditionBroadcastFailedException.m
+++ src/exceptions/OFConditionBroadcastFailedException.m
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#include
-
-#import "OFConditionBroadcastFailedException.h"
-#import "OFString.h"
-#import "OFCondition.h"
-
-@implementation OFConditionBroadcastFailedException
-@synthesize condition = _condition, errNo = _errNo;
-
-+ (instancetype)exceptionWithCondition: (OFCondition *)condition
- errNo: (int)errNo
-{
- return [[[self alloc] initWithCondition: condition
- errNo: errNo] autorelease];
-}
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (instancetype)initWithCondition: (OFCondition *)condition errNo: (int)errNo
-{
- self = [super init];
-
- _condition = [condition retain];
- _errNo = errNo;
-
- return self;
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (void)dealloc
-{
- [_condition release];
-
- [super dealloc];
-}
-
-- (OFString *)description
-{
- return [OFString stringWithFormat:
- @"Broadcasting a condition of type %@ failed: %s",
- _condition.class, strerror(_errNo)];
-}
-@end
DELETED src/exceptions/OFConditionSignalFailedException.h
Index: src/exceptions/OFConditionSignalFailedException.h
==================================================================
--- src/exceptions/OFConditionSignalFailedException.h
+++ src/exceptions/OFConditionSignalFailedException.h
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-#ifndef OF_HAVE_THREADS
-# error No threads available!
-#endif
-
-OF_ASSUME_NONNULL_BEGIN
-
-@class OFCondition;
-
-/**
- * @class OFConditionSignalFailedException \
- * OFConditionSignalFailedException.h \
- * ObjFW/OFConditionSignalFailedException.h
- *
- * @brief An exception indicating signaling a condition failed.
- */
-@interface OFConditionSignalFailedException: OFException
-{
- OFCondition *_condition;
- int _errNo;
-}
-
-/**
- * @brief The condition which could not be signaled.
- */
-@property (readonly, nonatomic) OFCondition *condition;
-
-/**
- * @brief The errno of the error that occurred.
- */
-@property (readonly, nonatomic) int errNo;
-
-/**
- * @brief Creates a new, autoreleased condition signal failed exception.
- *
- * @param condition The condition which could not be signaled
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased condition signal failed exception
- */
-+ (instancetype)exceptionWithCondition: (OFCondition *)condition
- errNo: (int)errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated condition signal failed exception.
- *
- * @param condition The condition which could not be signaled
- * @param errNo The errno of the error that occurred
- * @return An initialized condition signal failed exception
- */
-- (instancetype)initWithCondition: (OFCondition *)condition
- errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
-
-- (instancetype)init OF_UNAVAILABLE;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFConditionSignalFailedException.m
Index: src/exceptions/OFConditionSignalFailedException.m
==================================================================
--- src/exceptions/OFConditionSignalFailedException.m
+++ src/exceptions/OFConditionSignalFailedException.m
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#include
-
-#import "OFConditionSignalFailedException.h"
-#import "OFString.h"
-#import "OFCondition.h"
-
-@implementation OFConditionSignalFailedException
-@synthesize condition = _condition, errNo = _errNo;
-
-+ (instancetype)exceptionWithCondition: (OFCondition *)condition
- errNo: (int)errNo
-{
- return [[[self alloc] initWithCondition: condition
- errNo: errNo] autorelease];
-}
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (instancetype)initWithCondition: (OFCondition *)condition errNo: (int)errNo
-{
- self = [super init];
-
- _condition = [condition retain];
- _errNo = errNo;
-
- return self;
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (void)dealloc
-{
- [_condition release];
-
- [super dealloc];
-}
-
-- (OFString *)description
-{
- return [OFString stringWithFormat:
- @"Signaling a condition of type %@ failed: %s",
- _condition.class, strerror(_errNo)];
-}
-@end
Index: src/exceptions/OFConditionStillWaitingException.h
==================================================================
--- src/exceptions/OFConditionStillWaitingException.h
+++ src/exceptions/OFConditionStillWaitingException.h
@@ -32,10 +32,11 @@
* condition.
*/
@interface OFConditionStillWaitingException: OFException
{
OFCondition *_condition;
+ OF_RESERVE_IVARS(OFConditionStillWaitingException, 4)
}
/**
* @brief The condition for which is still being waited.
*/
DELETED src/exceptions/OFConditionWaitFailedException.h
Index: src/exceptions/OFConditionWaitFailedException.h
==================================================================
--- src/exceptions/OFConditionWaitFailedException.h
+++ src/exceptions/OFConditionWaitFailedException.h
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-#ifndef OF_HAVE_THREADS
-# error No threads available!
-#endif
-
-OF_ASSUME_NONNULL_BEGIN
-
-@class OFCondition;
-
-/**
- * @class OFConditionWaitFailedException \
- * OFConditionWaitFailedException.h \
- * ObjFW/OFConditionWaitFailedException.h
- *
- * @brief An exception indicating waiting for a condition failed.
- */
-@interface OFConditionWaitFailedException: OFException
-{
- OFCondition *_condition;
- int _errNo;
-}
-
-/**
- * @brief The condition for which could not be waited.
- */
-@property (readonly, nonatomic) OFCondition *condition;
-
-/**
- * @brief The errno of the error that occurred.
- */
-@property (readonly, nonatomic) int errNo;
-
-/**
- * @brief Creates a new, autoreleased condition wait failed exception.
- *
- * @param condition The condition for which could not be waited
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased condition wait failed exception
- */
-+ (instancetype)exceptionWithCondition: (OFCondition *)condition
- errNo: (int)errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated condition wait failed exception.
- *
- * @param condition The condition for which could not be waited
- * @param errNo The errno of the error that occurred
- * @return An initialized condition wait failed exception
- */
-- (instancetype)initWithCondition: (OFCondition *)condition
- errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
-
-- (instancetype)init OF_UNAVAILABLE;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFConditionWaitFailedException.m
Index: src/exceptions/OFConditionWaitFailedException.m
==================================================================
--- src/exceptions/OFConditionWaitFailedException.m
+++ src/exceptions/OFConditionWaitFailedException.m
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#include
-
-#import "OFConditionWaitFailedException.h"
-#import "OFString.h"
-#import "OFCondition.h"
-
-@implementation OFConditionWaitFailedException
-@synthesize condition = _condition, errNo = _errNo;
-
-+ (instancetype)exceptionWithCondition: (OFCondition *)condition
- errNo: (int)errNo
-{
- return [[[self alloc] initWithCondition: condition
- errNo: errNo] autorelease];
-}
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (instancetype)initWithCondition: (OFCondition *)condition errNo: (int)errNo
-{
- self = [super init];
-
- _condition = [condition retain];
- _errNo = errNo;
-
- return self;
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (void)dealloc
-{
- [_condition release];
-
- [super dealloc];
-}
-
-- (OFString *)description
-{
- return [OFString stringWithFormat:
- @"Waiting for a condition of type %@ failed: %s",
- _condition.class, strerror(_errNo)];
-}
-@end
ADDED src/exceptions/OFConnectIPSocketFailedException.h
Index: src/exceptions/OFConnectIPSocketFailedException.h
==================================================================
--- src/exceptions/OFConnectIPSocketFailedException.h
+++ src/exceptions/OFConnectIPSocketFailedException.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFConnectSocketFailedException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFConnectIPSocketFailedException \
+ * OFConnectIPSocketFailedException.h \
+ * ObjFW/OFConnectIPSocketFailedException.h
+ *
+ * @brief An exception indicating that an IP connection could not be
+ * established.
+ */
+OF_SUBCLASSING_RESTRICTED
+@interface OFConnectIPSocketFailedException: OFConnectSocketFailedException
+{
+ OFString *_Nullable _host;
+ uint16_t _port;
+}
+
+/**
+ * @brief The host to which the connection failed.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;
+
+/**
+ * @brief The port on the host to which the connection failed.
+ */
+@property (readonly, nonatomic) uint16_t port;
+
+/**
+ * @brief Creates a new, autoreleased connect IP socket failed exception.
+ *
+ * @param host The host to which the connection failed
+ * @param port The port on the host to which the connection failed
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased connect IP socket failed exception
+ */
++ (instancetype)exceptionWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)socket
+ errNo: (int)errNo;
+
++ (instancetype)exceptionWithSocket: (id)socket
+ errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated connect IP socket failed exception.
+ *
+ * @param host The host to which the connection failed
+ * @param port The port on the host to which the connection failed
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return An initialized connect IP socket failed exception
+ */
+- (instancetype)initWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFConnectIPSocketFailedException.m
Index: src/exceptions/OFConnectIPSocketFailedException.m
==================================================================
--- src/exceptions/OFConnectIPSocketFailedException.m
+++ src/exceptions/OFConnectIPSocketFailedException.m
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFConnectIPSocketFailedException.h"
+#import "OFString.h"
+
+@implementation OFConnectIPSocketFailedException
+@synthesize host = _host, port = _port;
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithHost: host
+ port: port
+ socket: sock
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithHost: (OFString *)host
+ port: (uint16_t)port
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ self = [super initWithSocket: sock errNo: errNo];
+
+ @try {
+ _host = [host copy];
+ _port = port;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_host release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"A connection to %@ on port %" @PRIu16 @" could not be "
+ @"established in socket of type %@: %@",
+ _host, _port, [_socket class], OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFConnectSPXSocketFailedException.h
Index: src/exceptions/OFConnectSPXSocketFailedException.h
==================================================================
--- src/exceptions/OFConnectSPXSocketFailedException.h
+++ src/exceptions/OFConnectSPXSocketFailedException.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFConnectSocketFailedException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFConnectSPXSocketFailedException \
+ * OFConnectSPXSocketFailedException.h \
+ * ObjFW/OFConnectSocketFailedException.h
+ *
+ * @brief An exception indicating that an SPX connection could not be
+ * established.
+ */
+OF_SUBCLASSING_RESTRICTED
+@interface OFConnectSPXSocketFailedException: OFConnectSocketFailedException
+{
+ uint32_t _network;
+ unsigned char _node[IPX_NODE_LEN];
+ uint16_t _port;
+}
+
+
+/**
+ * @brief The IPX network of the node to which the connection failed.
+ */
+@property (readonly, nonatomic) uint32_t network;
+
+/**
+ * @brief The IPX port on the host to which the connection failed.
+ */
+@property (readonly, nonatomic) uint16_t port;
+
+/**
+ * @brief Creates a new, autoreleased connect SPX socket failed exception.
+ *
+ * @param network The IPX network of the node to which the connection failed
+ * @param node The node to which the connection failed
+ * @param port The port on the node to which the connection failed
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased connect SPX socket failed exception
+ */
++ (instancetype)
+ exceptionWithNetwork: (uint32_t)network
+ node: (const unsigned char [_Nullable IPX_NODE_LEN])node
+ port: (uint16_t)port
+ socket: (id)socket
+ errNo: (int)errNo;
+
++ (instancetype)exceptionWithSocket: (id)socket
+ errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated connect SPX socket failed exception.
+ *
+ * @param network The IPX network of the node to which the connection failed
+ * @param node The node to which the connection failed
+ * @param port The port on the node to which the connection failed
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return An initialized connect SPX socket failed exception
+ */
+- (instancetype)
+ initWithNetwork: (uint32_t)network
+ node: (const unsigned char [_Nullable IPX_NODE_LEN])node
+ port: (uint16_t)port
+ socket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Get the IPX node to which the connection failed.
+ *
+ * @param node A pointer to where to write the node to
+ */
+- (void)getNode: (unsigned char [_Nonnull IPX_NODE_LEN])node;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFConnectSPXSocketFailedException.m
Index: src/exceptions/OFConnectSPXSocketFailedException.m
==================================================================
--- src/exceptions/OFConnectSPXSocketFailedException.m
+++ src/exceptions/OFConnectSPXSocketFailedException.m
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#include
+
+#import "OFConnectSPXSocketFailedException.h"
+#import "OFData.h"
+#import "OFString.h"
+
+@implementation OFConnectSPXSocketFailedException
+@synthesize network = _network, port = _port;
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithNetwork: (uint32_t)network
+ node: (const unsigned char [IPX_NODE_LEN])node
+ port: (uint16_t)port
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithNetwork: network
+ node: node
+ port: port
+ socket: sock
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithNetwork: (uint32_t)network
+ node: (const unsigned char [IPX_NODE_LEN])node
+ port: (uint16_t)port
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ self = [super initWithSocket: sock errNo: errNo];
+
+ @try {
+ _network = network;
+ memcpy(_node, node, IPX_NODE_LEN);
+ _port = port;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)getNode: (unsigned char [IPX_NODE_LEN])node
+{
+ memcpy(node, _node, sizeof(_node));
+}
+
+- (OFString *)description
+{
+ OFData *node = [OFData dataWithItems: _node count: sizeof(_node)];
+
+ return [OFString stringWithFormat:
+ @"A connection to %@ port %" @PRIu16 @" on network %" @PRIX32
+ " could not be established in socket of type %@: %@",
+ node, _port, _network, [_socket class], OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFConnectSocketFailedException.h
Index: src/exceptions/OFConnectSocketFailedException.h
==================================================================
--- src/exceptions/OFConnectSocketFailedException.h
+++ src/exceptions/OFConnectSocketFailedException.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+#ifndef OF_HAVE_SOCKETS
+# error No sockets available!
+#endif
+
+#import "OFSocket.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFConnectSocketFailedException \
+ * OFConnectSocketFailedException.h \
+ * ObjFW/OFConnectSocketFailedException.h
+ *
+ * @brief An exception indicating that a connection could not be established.
+ */
+@interface OFConnectSocketFailedException: OFException
+{
+ id _socket;
+ int _errNo;
+ OF_RESERVE_IVARS(OFConnectSocketFailedException, 4)
+}
+
+/**
+ * @brief The socket which could not connect.
+ */
+@property (readonly, nonatomic) id socket;
+
+/**
+ * @brief The errno of the error that occurred.
+ */
+@property (readonly, nonatomic) int errNo;
+
+/**
+ * @brief Creates a new, autoreleased connect socket failed exception.
+ *
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased connect socket failed exception
+ */
++ (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated connect socket failed exception.
+ *
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return An initialized connect socket failed exception
+ */
+- (instancetype)initWithSocket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)init OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFConnectSocketFailedException.m
Index: src/exceptions/OFConnectSocketFailedException.m
==================================================================
--- src/exceptions/OFConnectSocketFailedException.m
+++ src/exceptions/OFConnectSocketFailedException.m
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFConnectSocketFailedException.h"
+#import "OFString.h"
+
+@implementation OFConnectSocketFailedException
+@synthesize socket = _socket, errNo = _errNo;
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ return [[[self alloc] initWithSocket: sock errNo: errNo] autorelease];
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ self = [super init];
+
+ @try {
+ _socket = [sock retain];
+ _errNo = errNo;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_socket release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"A connection to could not be established in socket of type "
+ @"%@: %@",
+ [_socket class], OFStrError(_errNo)];
+}
+@end
ADDED src/exceptions/OFConnectUNIXSocketFailedException.h
Index: src/exceptions/OFConnectUNIXSocketFailedException.h
==================================================================
--- src/exceptions/OFConnectUNIXSocketFailedException.h
+++ src/exceptions/OFConnectUNIXSocketFailedException.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFConnectSocketFailedException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFConnectUNIXSocketFailedException \
+ * OFConnectUNIXSocketFailedException.h \
+ * ObjFW/OFConnectUNIXSocketFailedException.h
+ *
+ * @brief An exception indicating that a UNIX socket connection could not be
+ * established.
+ */
+OF_SUBCLASSING_RESTRICTED
+@interface OFConnectUNIXSocketFailedException: OFConnectSocketFailedException
+{
+ OFString *_Nullable _path;
+}
+
+/**
+ * @brief The path to which the connection failed.
+ */
+@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path;
+
+/**
+ * @brief Creates a new, autoreleased connect UNIX socket failed exception.
+ *
+ * @param path The path to which the connection failed
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased connect UNIX socket failed exception
+ */
++ (instancetype)exceptionWithPath: (OFString *)path
+ socket: (id)socket
+ errNo: (int)errNo;
+
++ (instancetype)exceptionWithSocket: (id)socket
+ errNo: (int)errNo OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated connect UNIX socket failed exception.
+ *
+ * @param path The path to which the connection failed
+ * @param socket The socket which could not connect
+ * @param errNo The errno of the error that occurred
+ * @return An initialized connect UNIX socket failed exception
+ */
+- (instancetype)initWithPath: (OFString *)path
+ socket: (id)socket
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFConnectUNIXSocketFailedException.m
Index: src/exceptions/OFConnectUNIXSocketFailedException.m
==================================================================
--- src/exceptions/OFConnectUNIXSocketFailedException.m
+++ src/exceptions/OFConnectUNIXSocketFailedException.m
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFConnectUNIXSocketFailedException.h"
+#import "OFString.h"
+
+@implementation OFConnectUNIXSocketFailedException
+@synthesize path = _path;
+
++ (instancetype)exceptionWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithPath: (OFString *)path
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ return [[[self alloc] initWithPath: path
+ socket: sock
+ errNo: errNo] autorelease];
+}
+
+- (instancetype)initWithSocket: (id)sock errNo: (int)errNo
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithPath: (OFString *)path
+ socket: (id)sock
+ errNo: (int)errNo
+{
+ self = [super initWithSocket: sock errNo: errNo];
+
+ @try {
+ _path = [path copy];
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_path release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"A connection to %@ could not be established in socket of type "
+ @"%@: %@",
+ _path, [_socket class], OFStrError(_errNo)];
+}
+@end
DELETED src/exceptions/OFConnectionFailedException.h
Index: src/exceptions/OFConnectionFailedException.h
==================================================================
--- src/exceptions/OFConnectionFailedException.h
+++ src/exceptions/OFConnectionFailedException.h
@@ -1,168 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-#ifndef OF_HAVE_SOCKETS
-# error No sockets available!
-#endif
-
-#import "OFSocket.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-/**
- * @class OFConnectionFailedException \
- * OFConnectionFailedException.h ObjFW/OFConnectionFailedException.h
- *
- * @brief An exception indicating that a connection could not be established.
- */
-@interface OFConnectionFailedException: OFException
-{
- OFString *_Nullable _host;
- uint16_t _port;
- OFString *_Nullable _path;
- uint32_t _network;
- unsigned char _node[IPX_NODE_LEN];
- id _socket;
- int _errNo;
-}
-
-/**
- * @brief The host to which the connection failed.
- */
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;
-
-/**
- * @brief The port on the host to which the connection failed.
- */
-@property (readonly, nonatomic) uint16_t port;
-
-/**
- * @brief The path to which the connection failed.
- */
-@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path;
-
-/**
- * @brief The IPX network of the node to which the connection failed.
- */
-@property (readonly, nonatomic) uint32_t network;
-
-/**
- * @brief The IPX node to which the connection failed.
- */
-@property (readonly, nonatomic) unsigned char *node;
-
-/**
- * @brief The socket which could not connect.
- */
-@property (readonly, nonatomic) id socket;
-
-/**
- * @brief The errno of the error that occurred.
- */
-@property (readonly, nonatomic) int errNo;
-
-/**
- * @brief Creates a new, autoreleased connection failed exception.
- *
- * @param host The host to which the connection failed
- * @param port The port on the host to which the connection failed
- * @param socket The socket which could not connect
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased connection failed exception
- */
-+ (instancetype)exceptionWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)socket
- errNo: (int)errNo;
-
-/**
- * @brief Creates a new, autoreleased connection failed exception.
- *
- * @param path The path to which the connection failed
- * @param socket The socket which could not connect
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased connection failed exception
- */
-+ (instancetype)exceptionWithPath: (OFString *)path
- socket: (id)socket
- errNo: (int)errNo;
-
-/**
- * @brief Creates a new, autoreleased connection failed exception.
- *
- * @param network The IPX network of the node to which the connection failed
- * @param node The node to which the connection failed
- * @param port The port on the node to which the connection failed
- * @param socket The socket which could not connect
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased connection failed exception
- */
-+ (instancetype)
- exceptionWithNetwork: (uint32_t)network
- node: (unsigned char [_Nullable IPX_NODE_LEN])node
- port: (uint16_t)port
- socket: (id)socket
- errNo: (int)errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated connection failed exception.
- *
- * @param host The host to which the connection failed
- * @param port The port on the host to which the connection failed
- * @param socket The socket which could not connect
- * @param errNo The errno of the error that occurred
- * @return An initialized connection failed exception
- */
-- (instancetype)initWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)socket
- errNo: (int)errNo;
-
-/**
- * @brief Initializes an already allocated connection failed exception.
- *
- * @param path The path to which the connection failed
- * @param socket The socket which could not connect
- * @param errNo The errno of the error that occurred
- * @return An initialized connection failed exception
- */
-- (instancetype)initWithPath: (OFString *)path
- socket: (id)socket
- errNo: (int)errNo;
-
-/**
- * @brief Initializes an already allocated connection failed exception.
- *
- * @param network The IPX network of the node to which the connection failed
- * @param node The node to which the connection failed
- * @param port The port on the node to which the connection failed
- * @param socket The socket which could not connect
- * @param errNo The errno of the error that occurred
- * @return An initialized connection failed exception
- */
-- (instancetype)initWithNetwork: (uint32_t)network
- node: (unsigned char [_Nullable IPX_NODE_LEN])node
- port: (uint16_t)port
- socket: (id)socket
- errNo: (int)errNo;
-
-- (instancetype)init OF_UNAVAILABLE;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFConnectionFailedException.m
Index: src/exceptions/OFConnectionFailedException.m
==================================================================
--- src/exceptions/OFConnectionFailedException.m
+++ src/exceptions/OFConnectionFailedException.m
@@ -1,167 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFConnectionFailedException.h"
-#import "OFString.h"
-
-@implementation OFConnectionFailedException
-@synthesize host = _host, port = _port, path = _path, network = _network;
-@synthesize socket = _socket, errNo = _errNo;
-
-+ (instancetype)exceptionWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)sock
- errNo: (int)errNo
-{
- return [[[self alloc] initWithHost: host
- port: port
- socket: sock
- errNo: errNo] autorelease];
-}
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-+ (instancetype)exceptionWithPath: (OFString *)path
- socket: (id)sock
- errNo: (int)errNo
-{
- return [[[self alloc] initWithPath: path
- socket: sock
- errNo: errNo] autorelease];
-}
-
-+ (instancetype)exceptionWithNetwork: (uint32_t)network
- node: (unsigned char [IPX_NODE_LEN])node
- port: (uint16_t)port
- socket: (id)sock
- errNo: (int)errNo
-{
- return [[[self alloc] initWithNetwork: network
- node: node
- port: port
- socket: sock
- errNo: errNo] autorelease];
-}
-
-- (instancetype)initWithHost: (OFString *)host
- port: (uint16_t)port
- socket: (id)sock
- errNo: (int)errNo
-{
- self = [super init];
-
- @try {
- _host = [host copy];
- _port = port;
- _socket = [sock retain];
- _errNo = errNo;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (instancetype)initWithPath: (OFString *)path
- socket: (id)sock
- errNo: (int)errNo
-{
- self = [super init];
-
- @try {
- _path = [path copy];
- _socket = [sock retain];
- _errNo = errNo;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (instancetype)initWithNetwork: (uint32_t)network
- node: (unsigned char [IPX_NODE_LEN])node
- port: (uint16_t)port
- socket: (id)sock
- errNo: (int)errNo
-{
- self = [super init];
-
- @try {
- _network = network;
- memcpy(_node, node, IPX_NODE_LEN);
- _port = port;
- _socket = [sock retain];
- _errNo = errNo;
- } @catch (id e) {
- [self release];
- @throw e;
- }
-
- return self;
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (void)dealloc
-{
- [_host release];
- [_path release];
- [_socket release];
-
- [super dealloc];
-}
-
-- (unsigned char *)node
-{
- return _node;
-}
-
-- (OFString *)description
-{
- if (_path != nil)
- return [OFString stringWithFormat:
- @"A connection to %@ could not be established in socket of "
- @"type %@: %@",
- _path, [_socket class], OFStrError(_errNo)];
- else if (_host != nil)
- return [OFString stringWithFormat:
- @"A connection to %@ on port %" @PRIu16 @" could not be "
- @"established in socket of type %@: %@",
- _host, _port, [_socket class], OFStrError(_errNo)];
- else if (memcmp(_node, "\0\0\0\0\0", IPX_NODE_LEN) == 0)
- return [OFString stringWithFormat:
- @"A connection to %02X%02X%02X%02X%02X%02X port %" @PRIu16
- @" on network %" @PRIX32 " could not be established in "
- @"socket of type %@: %@",
- _node[0], _node[1], _node[2], _node[3], _node[4], _node[5],
- _port, _network, [_socket class], OFStrError(_errNo)];
- else
- return [OFString stringWithFormat:
- @"A connection could not be established in socket of "
- @"type %@: %@",
- [_socket class], OFStrError(_errNo)];
-}
-@end
Index: src/exceptions/OFCopyItemFailedException.h
==================================================================
--- src/exceptions/OFCopyItemFailedException.h
+++ src/exceptions/OFCopyItemFailedException.h
@@ -15,64 +15,65 @@
#import "OFException.h"
OF_ASSUME_NONNULL_BEGIN
-@class OFURL;
+@class OFURI;
/**
* @class OFCopyItemFailedException \
* OFCopyItemFailedException.h ObjFW/OFCopyItemFailedException.h
*
* @brief An exception indicating that copying a item failed.
*/
@interface OFCopyItemFailedException: OFException
{
- OFURL *_sourceURL, *_destinationURL;
+ OFURI *_sourceURI, *_destinationURI;
int _errNo;
+ OF_RESERVE_IVARS(OFCopyItemFailedException, 4)
}
/**
- * @brief The URL of the source item.
+ * @brief The URI of the source item.
*/
-@property (readonly, nonatomic) OFURL *sourceURL;
+@property (readonly, nonatomic) OFURI *sourceURI;
/**
- * @brief The destination URL.
+ * @brief The destination URI.
*/
-@property (readonly, nonatomic) OFURL *destinationURL;
+@property (readonly, nonatomic) OFURI *destinationURI;
/**
* @brief The errno of the error that occurred.
*/
@property (readonly, nonatomic) int errNo;
/**
* @brief Creates a new, autoreleased copy item failed exception.
*
- * @param sourceURL The URL of the source item
- * @param destinationURL The destination URL
+ * @param sourceURI The URI of the source item
+ * @param destinationURI The destination URI
* @param errNo The errno of the error that occurred
* @return A new, autoreleased copy item failed exception
*/
-+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
- destinationURL: (OFURL *)destinationURL
++ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI
+ destinationURI: (OFURI *)destinationURI
errNo: (int)errNo;
+ (instancetype)exception OF_UNAVAILABLE;
/**
* @brief Initializes an already allocated copy item failed exception.
*
- * @param sourceURL The URL of the source item
- * @param destinationURL The destination URL
+ * @param sourceURI The URI of the source item
+ * @param destinationURI The destination URI
* @param errNo The errno of the error that occurred
* @return An initialized copy item failed exception
*/
-- (instancetype)initWithSourceURL: (OFURL *)sourceURL
- destinationURL: (OFURL *)destinationURL
+- (instancetype)initWithSourceURI: (OFURI *)sourceURI
+ destinationURI: (OFURI *)destinationURI
errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
- (instancetype)init OF_UNAVAILABLE;
@end
OF_ASSUME_NONNULL_END
Index: src/exceptions/OFCopyItemFailedException.m
==================================================================
--- src/exceptions/OFCopyItemFailedException.m
+++ src/exceptions/OFCopyItemFailedException.m
@@ -15,44 +15,44 @@
#include "config.h"
#import "OFCopyItemFailedException.h"
#import "OFString.h"
-#import "OFURL.h"
+#import "OFURI.h"
@implementation OFCopyItemFailedException
-@synthesize sourceURL = _sourceURL, destinationURL = _destinationURL;
+@synthesize sourceURI = _sourceURI, destinationURI = _destinationURI;
@synthesize errNo = _errNo;
+ (instancetype)exception
{
OF_UNRECOGNIZED_SELECTOR
}
-+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
- destinationURL: (OFURL *)destinationURL
++ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI
+ destinationURI: (OFURI *)destinationURI
errNo: (int)errNo
{
- return [[[self alloc] initWithSourceURL: sourceURL
- destinationURL: destinationURL
+ return [[[self alloc] initWithSourceURI: sourceURI
+ destinationURI: destinationURI
errNo: errNo] autorelease];
}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
-- (instancetype)initWithSourceURL: (OFURL *)sourceURL
- destinationURL: (OFURL *)destinationURL
+- (instancetype)initWithSourceURI: (OFURI *)sourceURI
+ destinationURI: (OFURI *)destinationURI
errNo: (int)errNo
{
self = [super init];
@try {
- _sourceURL = [sourceURL copy];
- _destinationURL = [destinationURL copy];
+ _sourceURI = [sourceURI copy];
+ _destinationURI = [destinationURI copy];
_errNo = errNo;
} @catch (id e) {
[self release];
@throw e;
}
@@ -60,17 +60,17 @@
return self;
}
- (void)dealloc
{
- [_sourceURL release];
- [_destinationURL release];
+ [_sourceURI release];
+ [_destinationURI release];
[super dealloc];
}
- (OFString *)description
{
return [OFString stringWithFormat: @"Failed to copy item %@ to %@: %@",
- _sourceURL, _destinationURL, OFStrError(_errNo)];
+ _sourceURI, _destinationURI, OFStrError(_errNo)];
}
@end
Index: src/exceptions/OFCreateDirectoryFailedException.h
==================================================================
--- src/exceptions/OFCreateDirectoryFailedException.h
+++ src/exceptions/OFCreateDirectoryFailedException.h
@@ -15,11 +15,11 @@
#import "OFException.h"
OF_ASSUME_NONNULL_BEGIN
-@class OFURL;
+@class OFURI;
/**
* @class OFCreateDirectoryFailedException \
* OFCreateDirectoryFailedException.h \
* ObjFW/OFCreateDirectoryFailedException.h
@@ -26,44 +26,45 @@
*
* @brief An exception indicating a directory couldn't be created.
*/
@interface OFCreateDirectoryFailedException: OFException
{
- OFURL *_URL;
+ OFURI *_URI;
int _errNo;
+ OF_RESERVE_IVARS(OFCreateDirectoryFailedException, 4)
}
/**
- * @brief The URL of the directory which couldn't be created.
+ * @brief The URI of the directory which couldn't be created.
*/
-@property (readonly, nonatomic) OFURL *URL;
+@property (readonly, nonatomic) OFURI *URI;
/**
* @brief The errno of the error that occurred.
*/
@property (readonly, nonatomic) int errNo;
/**
* @brief Creates a new, autoreleased create directory failed exception.
*
- * @param URL The URL of the directory which could not be created
+ * @param URI The URI of the directory which could not be created
* @param errNo The errno of the error that occurred
* @return A new, autoreleased create directory failed exception
*/
-+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo;
++ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo;
+ (instancetype)exception OF_UNAVAILABLE;
/**
* @brief Initializes an already allocated create directory failed exception.
*
- * @param URL The URL of the directory which could not be created
+ * @param URI The URI of the directory which could not be created
* @param errNo The errno of the error that occurred
* @return An initialized create directory failed exception
*/
-- (instancetype)initWithURL: (OFURL *)URL
+- (instancetype)initWithURI: (OFURI *)URI
errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
- (instancetype)init OF_UNAVAILABLE;
@end
OF_ASSUME_NONNULL_END
Index: src/exceptions/OFCreateDirectoryFailedException.m
==================================================================
--- src/exceptions/OFCreateDirectoryFailedException.m
+++ src/exceptions/OFCreateDirectoryFailedException.m
@@ -15,36 +15,36 @@
#include "config.h"
#import "OFCreateDirectoryFailedException.h"
#import "OFString.h"
-#import "OFURL.h"
+#import "OFURI.h"
@implementation OFCreateDirectoryFailedException
-@synthesize URL = _URL, errNo = _errNo;
+@synthesize URI = _URI, errNo = _errNo;
+ (instancetype)exception
{
OF_UNRECOGNIZED_SELECTOR
}
-+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo
++ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo
{
- return [[[self alloc] initWithURL: URL errNo: errNo] autorelease];
+ return [[[self alloc] initWithURI: URI errNo: errNo] autorelease];
}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
-- (instancetype)initWithURL: (OFURL *)URL errNo: (int)errNo
+- (instancetype)initWithURI: (OFURI *)URI errNo: (int)errNo
{
self = [super init];
@try {
- _URL = [URL copy];
+ _URI = [URI copy];
_errNo = errNo;
} @catch (id e) {
[self release];
@throw e;
}
@@ -52,16 +52,16 @@
return self;
}
- (void)dealloc
{
- [_URL release];
+ [_URI release];
[super dealloc];
}
- (OFString *)description
{
return [OFString stringWithFormat:
- @"Failed to create directory %@: %@", _URL, OFStrError(_errNo)];
+ @"Failed to create directory %@: %@", _URI, OFStrError(_errNo)];
}
@end
Index: src/exceptions/OFCreateSymbolicLinkFailedException.h
==================================================================
--- src/exceptions/OFCreateSymbolicLinkFailedException.h
+++ src/exceptions/OFCreateSymbolicLinkFailedException.h
@@ -15,11 +15,11 @@
#import "OFException.h"
OF_ASSUME_NONNULL_BEGIN
-@class OFURL;
+@class OFURI;
/**
* @class OFCreateSymbolicLinkFailedException \
* OFCreateSymbolicLinkFailedException.h \
* ObjFW/OFCreateSymbolicLinkFailedException.h
@@ -26,19 +26,20 @@
*
* @brief An exception indicating that creating a symbolic link failed.
*/
@interface OFCreateSymbolicLinkFailedException: OFException
{
- OFURL *_URL;
+ OFURI *_URI;
OFString *_target;
int _errNo;
+ OF_RESERVE_IVARS(OFCreateSymbolicLinkFailedException, 4)
}
/**
- * @brief The URL at which the symlink should have been created.
+ * @brief The URI at which the symlink should have been created.
*/
-@property (readonly, nonatomic) OFURL *URL;
+@property (readonly, nonatomic) OFURI *URI;
/**
* @brief The target for the symlink.
*/
@property (readonly, nonatomic) OFString *target;
@@ -49,33 +50,33 @@
@property (readonly, nonatomic) int errNo;
/**
* @brief Creates a new, autoreleased create symbolic link failed exception.
*
- * @param URL The URL where the symlink should have been created
+ * @param URI The URI where the symlink should have been created
* @param target The target for the symbolic link
* @param errNo The errno of the error that occurred
* @return A new, autoreleased create symbolic link failed exception
*/
-+ (instancetype)exceptionWithURL: (OFURL *)URL
++ (instancetype)exceptionWithURI: (OFURI *)URI
target: (OFString *)target
errNo: (int)errNo;
+ (instancetype)exception OF_UNAVAILABLE;
/**
* @brief Initializes an already allocated create symbolic link failed
* exception.
*
- * @param URL The URL where the symlink should have been created
+ * @param URI The URI where the symlink should have been created
* @param target The target for the symbolic link
* @param errNo The errno of the error that occurred
* @return An initialized create symbolic link failed exception
*/
-- (instancetype)initWithURL: (OFURL *)URL
+- (instancetype)initWithURI: (OFURI *)URI
target: (OFString *)target
errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
- (instancetype)init OF_UNAVAILABLE;
@end
OF_ASSUME_NONNULL_END
Index: src/exceptions/OFCreateSymbolicLinkFailedException.m
==================================================================
--- src/exceptions/OFCreateSymbolicLinkFailedException.m
+++ src/exceptions/OFCreateSymbolicLinkFailedException.m
@@ -15,42 +15,42 @@
#include "config.h"
#import "OFCreateSymbolicLinkFailedException.h"
#import "OFString.h"
-#import "OFURL.h"
+#import "OFURI.h"
@implementation OFCreateSymbolicLinkFailedException
-@synthesize URL = _URL, target = _target, errNo = _errNo;
+@synthesize URI = _URI, target = _target, errNo = _errNo;
+ (instancetype)exception
{
OF_UNRECOGNIZED_SELECTOR
}
-+ (instancetype)exceptionWithURL: (OFURL *)URL
++ (instancetype)exceptionWithURI: (OFURI *)URI
target: (OFString *)target
errNo: (int)errNo
{
- return [[[self alloc] initWithURL: URL
+ return [[[self alloc] initWithURI: URI
target: target
errNo: errNo] autorelease];
}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
-- (instancetype)initWithURL: (OFURL *)URL
+- (instancetype)initWithURI: (OFURI *)URI
target: (OFString *)target
errNo: (int)errNo
{
self = [super init];
@try {
- _URL = [URL copy];
+ _URI = [URI copy];
_target = [target copy];
_errNo = errNo;
} @catch (id e) {
[self release];
@throw e;
@@ -59,18 +59,18 @@
return self;
}
- (void)dealloc
{
- [_URL release];
+ [_URI release];
[_target release];
[super dealloc];
}
- (OFString *)description
{
return [OFString stringWithFormat:
@"Failed to create symbolic link %@ with target %@: %@",
- _URL, _target, OFStrError(_errNo)];
+ _URI, _target, OFStrError(_errNo)];
}
@end
Index: src/exceptions/OFCreateWindowsRegistryKeyFailedException.h
==================================================================
--- src/exceptions/OFCreateWindowsRegistryKeyFailedException.h
+++ src/exceptions/OFCreateWindowsRegistryKeyFailedException.h
@@ -30,13 +30,14 @@
@interface OFCreateWindowsRegistryKeyFailedException: OFException
{
OFWindowsRegistryKey *_registryKey;
OFString *_path;
DWORD _options;
- REGSAM _securityAndAccessRights;
+ REGSAM _accessRights;
LPSECURITY_ATTRIBUTES _Nullable _securityAttributes;
LSTATUS _status;
+ OF_RESERVE_IVARS(OFCreateWindowsRegistryKeyFailedException, 4)
}
/**
* @brief The registry key on which creating the subkey failed.
*/
@@ -46,26 +47,25 @@
* @brief The path for the subkey that could not be created.
*/
@property (readonly, nonatomic) OFString *path;
/**
- * @brief The options for the subkey that could not be created.
- */
-@property (readonly, nonatomic) DWORD options;
-
-/**
- * @brief The security and access rights for the subkey that could not be
- * created.
- */
-@property (readonly, nonatomic) REGSAM securityAndAccessRights;
+ * @brief The access rights for the subkey that could not be created.
+ */
+@property (readonly, nonatomic) REGSAM accessRights;
/**
* @brief The security options for the subkey that could not be created.
*/
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
LPSECURITY_ATTRIBUTES securityAttributes;
+/**
+ * @brief The options for the subkey that could not be created.
+ */
+@property (readonly, nonatomic) DWORD options;
+
/**
* @brief The status returned by RegCreateKeyEx().
*/
@property (readonly, nonatomic) LSTATUS status;
@@ -73,24 +73,24 @@
* @brief Creates a new, autoreleased create Windows registry key failed
* exception.
*
* @param registryKey The registry key on which creating the subkey failed
* @param path The path for the subkey that could not be created
- * @param options The options for the subkey that could not be created
- * @param securityAndAccessRights The security and access rights for the sub
- * key that could not be created
+ * @param accessRights The access rights for the sub key that could not be
+ * created
* @param securityAttributes The security options for the subkey that could
* not be created
+ * @param options The options for the subkey that could not be created
* @param status The status returned by RegCreateKeyEx()
* @return A new, autoreleased creates Windows registry key failed exception
*/
+ (instancetype)
exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
path: (OFString *)path
- options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights
+ accessRights: (REGSAM)accessRights
securityAttributes: (nullable LPSECURITY_ATTRIBUTES)securityAttributes
+ options: (DWORD)options
status: (LSTATUS)status;
- (instancetype)init OF_UNAVAILABLE;
/**
@@ -97,23 +97,23 @@
* @brief Initializes an already allocated create Windows registry key failed
* exception.
*
* @param registryKey The registry key on which creating the subkey failed
* @param path The path for the subkey that could not be created
- * @param options The options for the subkey that could not be created
- * @param securityAndAccessRights The security and access rights for the sub
- * key that could not be created
+ * @param accessRights The access rights for the sub key that could not be
+ * created
* @param securityAttributes The security options for the subkey that could
* not be created
+ * @param options The options for the subkey that could not be created
* @param status The status returned by RegCreateKeyEx()
* @return An initialized create Windows registry key failed exception
*/
- (instancetype)
initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
path: (OFString *)path
- options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights
+ accessRights: (REGSAM)accessRights
securityAttributes: (nullable LPSECURITY_ATTRIBUTES)securityAttributes
+ options: (DWORD)options
status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;
@end
OF_ASSUME_NONNULL_END
Index: src/exceptions/OFCreateWindowsRegistryKeyFailedException.m
==================================================================
--- src/exceptions/OFCreateWindowsRegistryKeyFailedException.m
+++ src/exceptions/OFCreateWindowsRegistryKeyFailedException.m
@@ -16,51 +16,51 @@
#include "config.h"
#import "OFCreateWindowsRegistryKeyFailedException.h"
@implementation OFCreateWindowsRegistryKeyFailedException
-@synthesize registryKey = _registryKey, path = _path, options = _options;
-@synthesize securityAndAccessRights = _securityAndAccessRights;
-@synthesize securityAttributes = _securityAttributes, status = _status;
+@synthesize registryKey = _registryKey, path = _path;
+@synthesize accessRights = _accessRights;
+@synthesize securityAttributes = _securityAttributes, options = _options;
+@synthesize status = _status;
+ (instancetype)
exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
path: (OFString *)path
- options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights
+ accessRights: (REGSAM)accessRights
securityAttributes: (LPSECURITY_ATTRIBUTES)securityAttributes
+ options: (DWORD)options
status: (LSTATUS)status
{
return [[[self alloc] initWithRegistryKey: registryKey
path: path
- options: options
- securityAndAccessRights: securityAndAccessRights
+ accessRights: accessRights
securityAttributes: securityAttributes
+ options: options
status: status] autorelease];
}
- (instancetype)init
{
OF_INVALID_INIT_METHOD
}
-- (instancetype)
- initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
- path: (OFString *)path
- options: (DWORD)options
- securityAndAccessRights: (REGSAM)securityAndAccessRights
- securityAttributes: (LPSECURITY_ATTRIBUTES)securityAttributes
- status: (LSTATUS)status
+- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
+ path: (OFString *)path
+ accessRights: (REGSAM)accessRights
+ securityAttributes: (LPSECURITY_ATTRIBUTES)securityAttributes
+ options: (DWORD)options
+ status: (LSTATUS)status
{
self = [super init];
@try {
_registryKey = [registryKey retain];
_path = [path copy];
- _options = options;
- _securityAndAccessRights = securityAndAccessRights;
+ _accessRights = accessRights;
_securityAttributes = securityAttributes;
+ _options = options;
_status = status;
} @catch (id e) {
[self release];
@throw e;
}
Index: src/exceptions/OFDNSQueryFailedException.h
==================================================================
--- src/exceptions/OFDNSQueryFailedException.h
+++ src/exceptions/OFDNSQueryFailedException.h
@@ -28,10 +28,11 @@
*/
@interface OFDNSQueryFailedException: OFException
{
OFDNSQuery *_query;
OFDNSResolverErrorCode _errorCode;
+ OF_RESERVE_IVARS(OFDNSQueryFailedException, 4)
}
/**
* @brief The query which could not be performed.
*/
Index: src/exceptions/OFDeleteWindowsRegistryKeyFailedException.h
==================================================================
--- src/exceptions/OFDeleteWindowsRegistryKeyFailedException.h
+++ src/exceptions/OFDeleteWindowsRegistryKeyFailedException.h
@@ -30,10 +30,11 @@
@interface OFDeleteWindowsRegistryKeyFailedException: OFException
{
OFWindowsRegistryKey *_registryKey;
OFString *_subkeyPath;
LSTATUS _status;
+ OF_RESERVE_IVARS(OFDeleteWindowsRegistryKeyFailedException, 4)
}
/**
* @brief The registry key on which deleting the subkey failed.
*/
Index: src/exceptions/OFDeleteWindowsRegistryValueFailedException.h
==================================================================
--- src/exceptions/OFDeleteWindowsRegistryValueFailedException.h
+++ src/exceptions/OFDeleteWindowsRegistryValueFailedException.h
@@ -30,10 +30,11 @@
@interface OFDeleteWindowsRegistryValueFailedException: OFException
{
OFWindowsRegistryKey *_registryKey;
OFString *_Nullable _valueName;
LSTATUS _status;
+ OF_RESERVE_IVARS(OFDeleteWindowsRegistryValueFailedException, 4)
}
/**
* @brief The registry key on which deleting the value failed.
*/
Index: src/exceptions/OFEnumerationMutationException.h
==================================================================
--- src/exceptions/OFEnumerationMutationException.h
+++ src/exceptions/OFEnumerationMutationException.h
@@ -26,10 +26,11 @@
* enumeration.
*/
@interface OFEnumerationMutationException: OFException
{
id _object;
+ OF_RESERVE_IVARS(OFEnumerationMutationException, 4)
}
/**
* @brief The object which was mutated during enumeration.
*/
Index: src/exceptions/OFException.h
==================================================================
--- src/exceptions/OFException.h
+++ src/exceptions/OFException.h
@@ -20,14 +20,14 @@
#endif
OF_ASSUME_NONNULL_BEGIN
@class OFArray OF_GENERIC(ObjectType);
-@class OFMutableArray OF_GENERIC(ObjectType);
@class OFString;
+@class OFValue;
-#define OFBacktraceSize 16
+#define OFStackTraceSize 16
#if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS)
# ifndef EADDRINUSE
# define EADDRINUSE WSAEADDRINUSE
# endif
@@ -147,11 +147,12 @@
* The OFException class is the base class for all exceptions in ObjFW, except
* the OFAllocFailedException.
*/
@interface OFException: OFObject
{
- void *_backtrace[OFBacktraceSize];
+ void *_stackTrace[OFStackTraceSize];
+ OF_RESERVE_IVARS(OFException, 4)
}
/**
* @brief Creates a new, autoreleased exception.
*
@@ -165,16 +166,25 @@
* @return A description of the exception
*/
- (OFString *)description;
/**
- * @brief Returns a backtrace of when the exception was created or nil if no
- * backtrace is available.
+ * @brief Returns a stack trace of when the exception was created or `nil` if
+ * no stack trace is available. The returned array contains OFValues
+ * with @ref OFValue#pointerValue set to the address.
+ *
+ * @return The stack trace as array of addresses
+ */
+- (nullable OFArray OF_GENERIC(OFValue *) *)stackTraceAddresses;
+
+/**
+ * @brief Returns a stack trace of when the exception was created or `nil` if
+ * no stack trace symbols are available.
*
- * @return A backtrace of when the exception was created
+ * @return The stack trace as array of symbols
*/
-- (nullable OFArray OF_GENERIC(OFString *) *)backtrace;
+- (nullable OFArray OF_GENERIC(OFString *) *)stackTraceSymbols;
@end
#ifdef __cplusplus
extern "C" {
#endif
Index: src/exceptions/OFException.m
==================================================================
--- src/exceptions/OFException.m
+++ src/exceptions/OFException.m
@@ -30,10 +30,11 @@
#ifdef OF_HAVE_THREADS
# import "OFPlainMutex.h"
#endif
#import "OFString.h"
#import "OFSystemInfo.h"
+#import "OFValue.h"
#import "OFInitializationFailedException.h"
#import "OFLockFailedException.h"
#import "OFUnlockFailedException.h"
@@ -256,11 +257,11 @@
static _Unwind_Reason_Code
backtraceCallback(struct _Unwind_Context *ctx, void *data)
{
struct BacktraceCtx *bt = data;
- if (bt->i < OFBacktraceSize) {
+ if (bt->i < OFStackTraceSize) {
# ifndef HAVE_ARM_EHABI_EXCEPTIONS
bt->backtrace[bt->i++] = (void *)_Unwind_GetIP(ctx);
# else
uintptr_t ip;
@@ -285,11 +286,11 @@
{
struct BacktraceCtx ctx;
self = [super init];
- ctx.backtrace = _backtrace;
+ ctx.backtrace = _stackTrace;
ctx.i = 0;
_Unwind_Backtrace(backtraceCallback, &ctx);
return self;
}
@@ -299,49 +300,73 @@
{
return [OFString stringWithFormat:
@"An exception of type %@ occurred!", self.class];
}
-- (OFArray OF_GENERIC(OFString *) *)backtrace
+- (OFArray OF_GENERIC(OFValue *) *)stackTraceAddresses
{
#ifdef HAVE__UNWIND_BACKTRACE
- OFMutableArray OF_GENERIC(OFString *) *backtrace =
+ OFMutableArray OF_GENERIC(OFValue *) *stackTrace =
+ [OFMutableArray array];
+ void *pool = objc_autoreleasePoolPush();
+
+ for (uint_fast8_t i = 0; i < OFStackTraceSize &&
+ _stackTrace[i] != NULL; i++)
+ [stackTrace addObject:
+ [OFValue valueWithPointer: _stackTrace[i]]];
+
+ objc_autoreleasePoolPop(pool);
+
+ [stackTrace makeImmutable];
+
+ return stackTrace;
+#else
+ return nil;
+#endif
+}
+
+- (OFArray OF_GENERIC(OFString *) *)stackTraceSymbols
+{
+#if defined(HAVE__UNWIND_BACKTRACE) && defined(HAVE_DLADDR)
+ OFMutableArray OF_GENERIC(OFString *) *stackTrace =
[OFMutableArray array];
void *pool = objc_autoreleasePoolPush();
- for (uint8_t i = 0; i < OFBacktraceSize && _backtrace[i] != NULL; i++) {
-# ifdef HAVE_DLADDR
+ for (uint_fast8_t i = 0; i < OFStackTraceSize &&
+ _stackTrace[i] != NULL; i++) {
Dl_info info;
- if (dladdr(_backtrace[i], &info)) {
+ if (dladdr(_stackTrace[i], &info)) {
+ ptrdiff_t offset = (char *)_stackTrace[i] -
+ (char *)info.dli_saddr;
OFString *frame;
- if (info.dli_sname != NULL) {
- ptrdiff_t offset = (char *)_backtrace[i] -
- (char *)info.dli_saddr;
-
- frame = [OFString stringWithFormat:
- @"%p <%s+%td> at %s",
- _backtrace[i], info.dli_sname, offset,
- info.dli_fname];
- } else
- frame = [OFString stringWithFormat:
- @"%p " @"?> at %s",
- _backtrace[i], info.dli_fname];
-
- [backtrace addObject: frame];
- } else
-# endif
- [backtrace addObject:
- [OFString stringWithFormat: @"%p", _backtrace[i]]];
+ if (info.dli_fname != NULL && info.dli_sname != NULL)
+ frame = [OFString stringWithFormat:
+ @"%s`%s+%td",
+ info.dli_fname, info.dli_sname, offset];
+ else if (info.dli_sname != NULL)
+ frame = [OFString stringWithFormat:
+ @"%s+%td", info.dli_sname, offset];
+ else if (info.dli_fname != NULL)
+ frame = [OFString stringWithFormat:
+ @"%s`%p", info.dli_fname, _stackTrace[i]];
+ else
+ frame = [OFString stringWithFormat:
+ @"%p", _stackTrace[i]];
+
+ [stackTrace addObject: frame];
+ } else
+ [stackTrace addObject:
+ [OFString stringWithFormat: @"%p", _stackTrace[i]]];
}
objc_autoreleasePoolPop(pool);
- [backtrace makeImmutable];
+ [stackTrace makeImmutable];
- return backtrace;
+ return stackTrace;
#else
return nil;
#endif
}
@end
ADDED src/exceptions/OFGetCurrentDirectoryFailedException.h
Index: src/exceptions/OFGetCurrentDirectoryFailedException.h
==================================================================
--- src/exceptions/OFGetCurrentDirectoryFailedException.h
+++ src/exceptions/OFGetCurrentDirectoryFailedException.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+/**
+ * @class OFGetCurrentDirectoryFailedException \
+ * OFGetCurrentDirectoryFailedException.h \
+ * ObjFW/OFGetCurrentDirectoryFailedException.h
+ *
+ * @brief An exception indicating that getting the current directory path
+ * failed.
+ */
+@interface OFGetCurrentDirectoryFailedException: OFException
+{
+ int _errNo;
+ OF_RESERVE_IVARS(OFGetCurrentDirectoryFailedException, 4)
+}
+
+/**
+ * @brief The errno of the error that occurred.
+ */
+@property (readonly, nonatomic) int errNo;
+
+/**
+ * @brief Creates a new, autoreleased get current directory path failed
+ * exception.
+ *
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased get current directory failed exception
+ */
++ (instancetype)exceptionWithErrNo: (int)errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated get current directory path failed
+ * exception.
+ *
+ * @param errNo The errno of the error that occurred
+ * @return An initialized get current directory path failed exception
+ */
+- (instancetype)initWithErrNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)init OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFGetCurrentDirectoryFailedException.m
Index: src/exceptions/OFGetCurrentDirectoryFailedException.m
==================================================================
--- src/exceptions/OFGetCurrentDirectoryFailedException.m
+++ src/exceptions/OFGetCurrentDirectoryFailedException.m
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFGetCurrentDirectoryFailedException.h"
+#import "OFString.h"
+
+@implementation OFGetCurrentDirectoryFailedException
+@synthesize errNo = _errNo;
+
++ (instancetype)exceptionWithErrNo: (int)errNo
+{
+ return [[[self alloc] initWithErrNo: errNo] autorelease];
+}
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
+- (instancetype)initWithErrNo: (int)errNo
+{
+ self = [super init];
+
+ _errNo = errNo;
+
+ return self;
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Getting the current directory path failed: %@",
+ OFStrError(_errNo)];
+}
+@end
DELETED src/exceptions/OFGetCurrentDirectoryPathFailedException.h
Index: src/exceptions/OFGetCurrentDirectoryPathFailedException.h
==================================================================
--- src/exceptions/OFGetCurrentDirectoryPathFailedException.h
+++ src/exceptions/OFGetCurrentDirectoryPathFailedException.h
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-/**
- * @class OFGetCurrentDirectoryPathFailedException \
- * OFGetCurrentDirectoryPathFailedException.h \
- * ObjFW/OFGetCurrentDirectoryPathFailedException.h
- *
- * @brief An exception indicating that getting the current directory path
- * failed.
- */
-@interface OFGetCurrentDirectoryPathFailedException: OFException
-{
- int _errNo;
-}
-
-/**
- * @brief The errno of the error that occurred.
- */
-@property (readonly, nonatomic) int errNo;
-
-/**
- * @brief Creates a new, autoreleased get current directory path failed
- * exception.
- *
- * @param errNo The errno of the error that occurred
- * @return A new, autoreleased get current directory failed exception
- */
-+ (instancetype)exceptionWithErrNo: (int)errNo;
-
-+ (instancetype)exception OF_UNAVAILABLE;
-
-/**
- * @brief Initializes an already allocated get current directory path failed
- * exception.
- *
- * @param errNo The errno of the error that occurred
- * @return An initialized get current directory path failed exception
- */
-- (instancetype)initWithErrNo: (int)errNo OF_DESIGNATED_INITIALIZER;
-
-- (instancetype)init OF_UNAVAILABLE;
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFGetCurrentDirectoryPathFailedException.m
Index: src/exceptions/OFGetCurrentDirectoryPathFailedException.m
==================================================================
--- src/exceptions/OFGetCurrentDirectoryPathFailedException.m
+++ src/exceptions/OFGetCurrentDirectoryPathFailedException.m
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#include "config.h"
-
-#import "OFGetCurrentDirectoryPathFailedException.h"
-#import "OFString.h"
-
-@implementation OFGetCurrentDirectoryPathFailedException
-@synthesize errNo = _errNo;
-
-+ (instancetype)exceptionWithErrNo: (int)errNo
-{
- return [[[self alloc] initWithErrNo: errNo] autorelease];
-}
-
-+ (instancetype)exception
-{
- OF_UNRECOGNIZED_SELECTOR
-}
-
-- (instancetype)initWithErrNo: (int)errNo
-{
- self = [super init];
-
- _errNo = errNo;
-
- return self;
-}
-
-- (instancetype)init
-{
- OF_INVALID_INIT_METHOD
-}
-
-- (OFString *)description
-{
- return [OFString stringWithFormat:
- @"Getting the current directory path failed: %@",
- OFStrError(_errNo)];
-}
-@end
ADDED src/exceptions/OFGetItemAttributesFailedException.h
Index: src/exceptions/OFGetItemAttributesFailedException.h
==================================================================
--- src/exceptions/OFGetItemAttributesFailedException.h
+++ src/exceptions/OFGetItemAttributesFailedException.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#import "OFException.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFURI;
+
+/**
+ * @class OFGetItemAttributesFailedException \
+ * OFGetItemAttributesFailedException.h \
+ * ObjFW/OFGetItemAttributesFailedException.h
+ *
+ * @brief An exception indicating an item's attributes could not be retrieved.
+ */
+@interface OFGetItemAttributesFailedException: OFException
+{
+ OFURI *_URI;
+ int _errNo;
+ OF_RESERVE_IVARS(OFGetItemAttributesFailedException, 4)
+}
+
+/**
+ * @brief The URI of the item whose attributes could not be retrieved.
+ */
+@property (readonly, nonatomic) OFURI *URI;
+
+/**
+ * @brief The errno of the error that occurred.
+ */
+@property (readonly, nonatomic) int errNo;
+
+/**
+ * @brief Creates a new, autoreleased retrieve item attributes failed exception.
+ *
+ * @param URI The URI of the item whose attributes could not be retrieved
+ * @param errNo The errno of the error that occurred
+ * @return A new, autoreleased retrieve item attributes failed exception
+ */
++ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo;
+
++ (instancetype)exception OF_UNAVAILABLE;
+
+/**
+ * @brief Initializes an already allocated retrieve item attributes failed
+ * exception.
+ *
+ * @param URI The URI of the item whose attributes could not be retrieved
+ * @param errNo The errno of the error that occurred
+ * @return An initialized retrieve item attributes failed exception
+ */
+- (instancetype)initWithURI: (OFURI *)URI
+ errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
+
+- (instancetype)init OF_UNAVAILABLE;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/exceptions/OFGetItemAttributesFailedException.m
Index: src/exceptions/OFGetItemAttributesFailedException.m
==================================================================
--- src/exceptions/OFGetItemAttributesFailedException.m
+++ src/exceptions/OFGetItemAttributesFailedException.m
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2008-2022 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This file is part of ObjFW. It may be distributed under the terms of the
+ * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
+ * the packaging of this file.
+ *
+ * Alternatively, it may be distributed under the terms of the GNU General
+ * Public License, either version 2 or 3, which can be found in the file
+ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
+ * file.
+ */
+
+#include "config.h"
+
+#import "OFGetItemAttributesFailedException.h"
+#import "OFString.h"
+#import "OFURI.h"
+
+@implementation OFGetItemAttributesFailedException
+@synthesize URI = _URI, errNo = _errNo;
+
++ (instancetype)exception
+{
+ OF_UNRECOGNIZED_SELECTOR
+}
+
++ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo
+{
+ return [[[self alloc] initWithURI: URI errNo: errNo] autorelease];
+}
+
+- (instancetype)init
+{
+ OF_INVALID_INIT_METHOD
+}
+
+- (instancetype)initWithURI: (OFURI *)URI errNo: (int)errNo
+{
+ self = [super init];
+
+ @try {
+ _URI = [URI copy];
+ _errNo = errNo;
+ } @catch (id e) {
+ [self release];
+ @throw e;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_URI release];
+
+ [super dealloc];
+}
+
+- (OFString *)description
+{
+ return [OFString stringWithFormat:
+ @"Failed to get attributes for item %@: %@",
+ _URI, OFStrError(_errNo)];
+}
+@end
Index: src/exceptions/OFGetOptionFailedException.h
==================================================================
--- src/exceptions/OFGetOptionFailedException.h
+++ src/exceptions/OFGetOptionFailedException.h
@@ -25,10 +25,11 @@
*/
@interface OFGetOptionFailedException: OFException
{
id _object;
int _errNo;
+ OF_RESERVE_IVARS(OFGetOptionFailedException, 4)
}
/**
* @brief The object for which the option could not be retrieved.
*/
Index: src/exceptions/OFGetWindowsRegistryValueFailedException.h
==================================================================
--- src/exceptions/OFGetWindowsRegistryValueFailedException.h
+++ src/exceptions/OFGetWindowsRegistryValueFailedException.h
@@ -29,12 +29,12 @@
*/
@interface OFGetWindowsRegistryValueFailedException: OFException
{
OFWindowsRegistryKey *_registryKey;
OFString *_Nullable _valueName;
- DWORD _flags;
LSTATUS _status;
+ OF_RESERVE_IVARS(OFGetWindowsRegistryValueFailedException, 4)
}
/**
* @brief The registry key on which getting the value at the key path failed.
*/
Index: src/exceptions/OFHTTPRequestFailedException.h
==================================================================
--- src/exceptions/OFHTTPRequestFailedException.h
+++ src/exceptions/OFHTTPRequestFailedException.h
@@ -33,10 +33,11 @@
*/
@interface OFHTTPRequestFailedException: OFException
{
OFHTTPRequest *_request;
OFHTTPResponse *_response;
+ OF_RESERVE_IVARS(OFHTTPRequestFailedException, 4)
}
/**
* @brief The HTTP request which failed.
*/
Index: src/exceptions/OFHTTPRequestFailedException.m
==================================================================
--- src/exceptions/OFHTTPRequestFailedException.m
+++ src/exceptions/OFHTTPRequestFailedException.m
@@ -62,9 +62,9 @@
- (OFString *)description
{
const char *method = OFHTTPRequestMethodName(_request.method);
return [OFString stringWithFormat:
- @"An HTTP %s request with URL %@ failed with code %hd!", method,
- _request.URL, _response.statusCode];
+ @"An HTTP %s request with URI %@ failed with code %hd!", method,
+ _request.URI, _response.statusCode];
}
@end
Index: src/exceptions/OFHashAlreadyCalculatedException.h
==================================================================
--- src/exceptions/OFHashAlreadyCalculatedException.h
+++ src/exceptions/OFHashAlreadyCalculatedException.h
@@ -25,10 +25,11 @@
* @brief An exception indicating that the hash has already been calculated.
*/
@interface OFHashAlreadyCalculatedException: OFException
{
id _object;
+ OF_RESERVE_IVARS(OFHashAlreadyCalculatedException, 4)
}
/**
* @brief The hash which has already been calculated.
*/
Index: src/exceptions/OFHashNotCalculatedException.h
==================================================================
--- src/exceptions/OFHashNotCalculatedException.h
+++ src/exceptions/OFHashNotCalculatedException.h
@@ -24,10 +24,11 @@
* @brief An exception indicating that the hash has not been calculated yet.
*/
@interface OFHashNotCalculatedException: OFException
{
id _object;
+ OF_RESERVE_IVARS(OFHashNotCalculatedException, 4)
}
/**
* @brief The hash which has not been calculated yet.
*/
Index: src/exceptions/OFInitializationFailedException.h
==================================================================
--- src/exceptions/OFInitializationFailedException.h
+++ src/exceptions/OFInitializationFailedException.h
@@ -25,10 +25,11 @@
* @brief An exception indicating that initializing something failed.
*/
@interface OFInitializationFailedException: OFException
{
Class _inClass;
+ OF_RESERVE_IVARS(OFInitializationFailedException, 4)
}
/**
* @brief The class for which initialization failed.
*/
Index: src/exceptions/OFInvalidArgumentException.h
==================================================================
--- src/exceptions/OFInvalidArgumentException.h
+++ src/exceptions/OFInvalidArgumentException.h
@@ -22,8 +22,11 @@
* OFInvalidArgumentException.h ObjFW/OFInvalidArgumentException.h
*
* @brief An exception indicating that the argument is invalid for this method.
*/
@interface OFInvalidArgumentException: OFException
+{
+ OF_RESERVE_IVARS(OFInvalidArgumentException, 4)
+}
@end
OF_ASSUME_NONNULL_END
Index: src/exceptions/OFInvalidEncodingException.h
==================================================================
--- src/exceptions/OFInvalidEncodingException.h
+++ src/exceptions/OFInvalidEncodingException.h
@@ -22,8 +22,11 @@
* OFInvalidEncodingException.h ObjFW/OFInvalidEncodingException.h
*
* @brief An exception indicating that the encoding is invalid for this object.
*/
@interface OFInvalidEncodingException: OFException
+{
+ OF_RESERVE_IVARS(OFInvalidEncodingException, 4)
+}
@end
OF_ASSUME_NONNULL_END
Index: src/exceptions/OFInvalidFormatException.h
==================================================================
--- src/exceptions/OFInvalidFormatException.h
+++ src/exceptions/OFInvalidFormatException.h
@@ -22,8 +22,11 @@
* OFInvalidFormatException.h ObjFW/OFInvalidFormatException.h
*
* @brief An exception indicating that the format is invalid.
*/
@interface OFInvalidFormatException: OFException
+{
+ OF_RESERVE_IVARS(OFInvalidFormatException, 4)
+}
@end
OF_ASSUME_NONNULL_END
Index: src/exceptions/OFInvalidJSONException.h
==================================================================
--- src/exceptions/OFInvalidJSONException.h
+++ src/exceptions/OFInvalidJSONException.h
@@ -25,10 +25,11 @@
*/
@interface OFInvalidJSONException: OFException
{
OFString *_string;
size_t _line;
+ OF_RESERVE_IVARS(OFInvalidJSONException, 4)
}
/**
* @brief The string containing the invalid JSON representation.
*/
DELETED src/exceptions/OFInvalidServerReplyException.h
Index: src/exceptions/OFInvalidServerReplyException.h
==================================================================
--- src/exceptions/OFInvalidServerReplyException.h
+++ src/exceptions/OFInvalidServerReplyException.h
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer
- *
- * All rights reserved.
- *
- * This file is part of ObjFW. It may be distributed under the terms of the
- * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
- * the packaging of this file.
- *
- * Alternatively, it may be distributed under the terms of the GNU General
- * Public License, either version 2 or 3, which can be found in the file
- * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
- * file.
- */
-
-#import "OFException.h"
-
-OF_ASSUME_NONNULL_BEGIN
-
-/**
- * @class OFInvalidServerReplyException \
- * OFInvalidServerReplyException.h ObjFW/OFInvalidServerReplyException.h
- *
- * @brief An exception indicating that the server sent an invalid reply.
- */
-@interface OFInvalidServerReplyException: OFException
-@end
-
-OF_ASSUME_NONNULL_END
DELETED src/exceptions/OFInvalidServerReplyException.m
Index: src/exceptions/OFInvalidServerReplyException.m
==================================================================
--- src/exceptions/OFInvalidServerReplyException.m
+++ src/exceptions/OFInvalidServerReplyException.m
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2008-2022 Jonathan Schleifer