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 - * - * 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 "OFInvalidServerReplyException.h" -#import "OFString.h" - -@implementation OFInvalidServerReplyException -- (OFString *)description -{ - return @"Got an invalid reply from the server!"; -} -@end ADDED src/exceptions/OFInvalidServerResponseException.h Index: src/exceptions/OFInvalidServerResponseException.h ================================================================== --- src/exceptions/OFInvalidServerResponseException.h +++ src/exceptions/OFInvalidServerResponseException.h @@ -0,0 +1,33 @@ +/* + * 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 OFInvalidServerResponseException \ + * OFInvalidServerResponseException.h \ + * ObjFW/OFInvalidServerResponseException.h + * + * @brief An exception indicating that the server sent an invalid response. + */ +@interface OFInvalidServerResponseException: OFException +{ + OF_RESERVE_IVARS(OFInvalidServerResponseException, 4) +} +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/OFInvalidServerResponseException.m Index: src/exceptions/OFInvalidServerResponseException.m ================================================================== --- src/exceptions/OFInvalidServerResponseException.m +++ src/exceptions/OFInvalidServerResponseException.m @@ -0,0 +1,26 @@ +/* + * 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 "OFInvalidServerResponseException.h" +#import "OFString.h" + +@implementation OFInvalidServerResponseException +- (OFString *)description +{ + return @"Got an invalid response from the server!"; +} +@end ADDED src/exceptions/OFJoinThreadFailedException.h Index: src/exceptions/OFJoinThreadFailedException.h ================================================================== --- src/exceptions/OFJoinThreadFailedException.h +++ src/exceptions/OFJoinThreadFailedException.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_THREADS +# error No threads available! +#endif + +OF_ASSUME_NONNULL_BEGIN + +@class OFThread; + +/** + * @class OFJoinThreadFailedException \ + * OFJoinThreadFailedException.h ObjFW/OFJoinThreadFailedException.h + * + * @brief An exception indicating that joining a thread failed. + */ +@interface OFJoinThreadFailedException: OFException +{ + OFThread *_Nullable _thread; + int _errNo; + OF_RESERVE_IVARS(OFJoinThreadFailedException, 4) +} + +/** + * @brief The thread which could not be joined. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread; + +/** + * @brief The errno of the error that occurred. + */ +@property (readonly, nonatomic) int errNo; + +/** + * @brief Creates a new, autoreleased thread join failed exception. + * + * @param thread The thread which could not be joined + * @param errNo The errno of the error that occurred + * @return A new, autoreleased thread join failed exception + */ ++ (instancetype)exceptionWithThread: (nullable OFThread *)thread + errNo: (int)errNo; + ++ (instancetype)exception OF_UNAVAILABLE; + +/** + * @brief Initializes an already allocated thread join failed exception. + * + * @param thread The thread which could not be joined + * @param errNo The errno of the error that occurred + * @return An initialized thread join failed exception + */ +- (instancetype)initWithThread: (nullable OFThread *)thread + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; + +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/OFJoinThreadFailedException.m Index: src/exceptions/OFJoinThreadFailedException.m ================================================================== --- src/exceptions/OFJoinThreadFailedException.m +++ src/exceptions/OFJoinThreadFailedException.m @@ -0,0 +1,65 @@ +/* + * 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 "OFJoinThreadFailedException.h" +#import "OFString.h" +#import "OFThread.h" + +@implementation OFJoinThreadFailedException +@synthesize thread = _thread, errNo = _errNo; + ++ (instancetype)exceptionWithThread: (OFThread *)thread errNo: (int)errNo +{ + return [[[self alloc] initWithThread: thread errNo: errNo] autorelease]; +} + ++ (instancetype)exception +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (instancetype)initWithThread: (OFThread *)thread errNo: (int)errNo +{ + self = [super init]; + + _thread = [thread retain]; + _errNo = errNo; + + return self; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (void)dealloc +{ + [_thread release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"Joining a thread of type %@ failed: %s", + _thread.class, strerror(_errNo)]; +} +@end DELETED src/exceptions/OFLinkFailedException.h Index: src/exceptions/OFLinkFailedException.h ================================================================== --- src/exceptions/OFLinkFailedException.h +++ src/exceptions/OFLinkFailedException.h @@ -1,78 +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 OFURL; - -/** - * @class OFLinkFailedException \ - * OFLinkFailedException.h ObjFW/OFLinkFailedException.h - * - * @brief An exception indicating that creating a link failed. - */ -@interface OFLinkFailedException: OFException -{ - OFURL *_sourceURL, *_destinationURL; - int _errNo; -} - -/** - * @brief A URL with the source for the link. - */ -@property (readonly, nonatomic) OFURL *sourceURL; - -/** - * @brief A URL with the destination for the link. - */ -@property (readonly, nonatomic) OFURL *destinationURL; - -/** - * @brief The errno of the error that occurred. - */ -@property (readonly, nonatomic) int errNo; - -/** - * @brief Creates a new, autoreleased link failed exception. - * - * @param sourceURL The source for the link - * @param destinationURL The destination for the link - * @param errNo The errno of the error that occurred - * @return A new, autoreleased link failed exception - */ -+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL - destinationURL: (OFURL *)destinationURL - errNo: (int)errNo; - -+ (instancetype)exception OF_UNAVAILABLE; - -/** - * @brief Initializes an already allocated link failed exception. - * - * @param sourceURL The source for the link - * @param destinationURL The destination for the link - * @param errNo The errno of the error that occurred - * @return An initialized link failed exception - */ -- (instancetype)initWithSourceURL: (OFURL*)sourceURL - destinationURL: (OFURL *)destinationURL - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - -- (instancetype)init OF_UNAVAILABLE; -@end - -OF_ASSUME_NONNULL_END DELETED src/exceptions/OFLinkFailedException.m Index: src/exceptions/OFLinkFailedException.m ================================================================== --- src/exceptions/OFLinkFailedException.m +++ src/exceptions/OFLinkFailedException.m @@ -1,76 +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 "OFLinkFailedException.h" -#import "OFString.h" -#import "OFURL.h" - -@implementation OFLinkFailedException -@synthesize sourceURL = _sourceURL, destinationURL = _destinationURL; -@synthesize errNo = _errNo; - -+ (instancetype)exception -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL - destinationURL: (OFURL *)destinationURL - errNo: (int)errNo -{ - return [[[self alloc] initWithSourceURL: sourceURL - destinationURL: destinationURL - errNo: errNo] autorelease]; -} - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithSourceURL: (OFURL *)sourceURL - destinationURL: (OFURL *)destinationURL - errNo: (int)errNo -{ - self = [super init]; - - @try { - _sourceURL = [sourceURL copy]; - _destinationURL = [destinationURL copy]; - _errNo = errNo; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_sourceURL release]; - [_destinationURL release]; - - [super dealloc]; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: @"Failed to link file %@ to %@: %@", - _sourceURL, _destinationURL, OFStrError(_errNo)]; -} -@end ADDED src/exceptions/OFLinkItemFailedException.h Index: src/exceptions/OFLinkItemFailedException.h ================================================================== --- src/exceptions/OFLinkItemFailedException.h +++ src/exceptions/OFLinkItemFailedException.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 "OFException.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFURI; + +/** + * @class OFLinkItemFailedException \ + * OFLinkItemFailedException.h ObjFW/OFLinkItemFailedException.h + * + * @brief An exception indicating that creating a link failed. + */ +@interface OFLinkItemFailedException: OFException +{ + OFURI *_sourceURI, *_destinationURI; + int _errNo; + OF_RESERVE_IVARS(OFLinkItemFailedException, 4) +} + +/** + * @brief A URI with the source for the link. + */ +@property (readonly, nonatomic) OFURI *sourceURI; + +/** + * @brief A URI with the destination for the link. + */ +@property (readonly, nonatomic) OFURI *destinationURI; + +/** + * @brief The errno of the error that occurred. + */ +@property (readonly, nonatomic) int errNo; + +/** + * @brief Creates a new, autoreleased link failed exception. + * + * @param sourceURI The source for the link + * @param destinationURI The destination for the link + * @param errNo The errno of the error that occurred + * @return A new, autoreleased link failed exception + */ ++ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI + destinationURI: (OFURI *)destinationURI + errNo: (int)errNo; + ++ (instancetype)exception OF_UNAVAILABLE; + +/** + * @brief Initializes an already allocated link failed exception. + * + * @param sourceURI The source for the link + * @param destinationURI The destination for the link + * @param errNo The errno of the error that occurred + * @return An initialized link failed exception + */ +- (instancetype)initWithSourceURI: (OFURI*)sourceURI + destinationURI: (OFURI *)destinationURI + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; + +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/OFLinkItemFailedException.m Index: src/exceptions/OFLinkItemFailedException.m ================================================================== --- src/exceptions/OFLinkItemFailedException.m +++ src/exceptions/OFLinkItemFailedException.m @@ -0,0 +1,76 @@ +/* + * 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 "OFLinkItemFailedException.h" +#import "OFString.h" +#import "OFURI.h" + +@implementation OFLinkItemFailedException +@synthesize sourceURI = _sourceURI, destinationURI = _destinationURI; +@synthesize errNo = _errNo; + ++ (instancetype)exception +{ + OF_UNRECOGNIZED_SELECTOR +} + ++ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI + destinationURI: (OFURI *)destinationURI + errNo: (int)errNo +{ + return [[[self alloc] initWithSourceURI: sourceURI + destinationURI: destinationURI + errNo: errNo] autorelease]; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithSourceURI: (OFURI *)sourceURI + destinationURI: (OFURI *)destinationURI + errNo: (int)errNo +{ + self = [super init]; + + @try { + _sourceURI = [sourceURI copy]; + _destinationURI = [destinationURI copy]; + _errNo = errNo; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_sourceURI release]; + [_destinationURI release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: @"Failed to link file %@ to %@: %@", + _sourceURI, _destinationURI, OFStrError(_errNo)]; +} +@end DELETED src/exceptions/OFListenFailedException.h Index: src/exceptions/OFListenFailedException.h ================================================================== --- src/exceptions/OFListenFailedException.h +++ src/exceptions/OFListenFailedException.h @@ -1,80 +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 OFListenFailedException \ - * OFListenFailedException.h ObjFW/OFListenFailedException.h - * - * @brief An exception indicating that listening on the socket failed. - */ -@interface OFListenFailedException: OFException -{ - id _socket; - int _backlog, _errNo; -} - -/** - * @brief The socket which failed to listen. - */ -@property (readonly, nonatomic) id socket; - -/** - * @brief The requested back log. - */ -@property (readonly, nonatomic) int backlog; - -/** - * @brief The errno of the error that occurred. - */ -@property (readonly, nonatomic) int errNo; - -/** - * @brief Creates a new, autoreleased listen failed exception. - * - * @param socket The socket which failed to listen - * @param backlog The requested size of the back log - * @param errNo The errno of the error that occurred - * @return A new, autoreleased listen failed exception - */ -+ (instancetype)exceptionWithSocket: (id)socket - backlog: (int)backlog - errNo: (int)errNo; - -+ (instancetype)exception OF_UNAVAILABLE; - -/** - * @brief Initializes an already allocated listen failed exception. - * - * @param socket The socket which failed to listen - * @param backlog The requested size of the back log - * @param errNo The errno of the error that occurred - * @return An initialized listen failed exception - */ -- (instancetype)initWithSocket: (id)socket - backlog: (int)backlog - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - -- (instancetype)init OF_UNAVAILABLE; -@end - -OF_ASSUME_NONNULL_END DELETED src/exceptions/OFListenFailedException.m Index: src/exceptions/OFListenFailedException.m ================================================================== --- src/exceptions/OFListenFailedException.m +++ src/exceptions/OFListenFailedException.m @@ -1,69 +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 "OFListenFailedException.h" -#import "OFString.h" - -@implementation OFListenFailedException -@synthesize socket = _socket, backlog = _backlog, errNo = _errNo; - -+ (instancetype)exception -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)exceptionWithSocket: (id)socket - backlog: (int)backlog - errNo: (int)errNo -{ - return [[[self alloc] initWithSocket: socket - backlog: backlog - errNo: errNo] autorelease]; -} - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithSocket: (id)socket - backlog: (int)backlog - errNo: (int)errNo -{ - self = [super init]; - - _socket = [socket retain]; - _backlog = backlog; - _errNo = errNo; - - return self; -} - -- (void)dealloc -{ - [_socket release]; - - [super dealloc]; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: - @"Failed to listen in socket of type %@ with a back log of %d: %@", - [_socket class], _backlog, OFStrError(_errNo)]; -} -@end ADDED src/exceptions/OFListenOnSocketFailedException.h Index: src/exceptions/OFListenOnSocketFailedException.h ================================================================== --- src/exceptions/OFListenOnSocketFailedException.h +++ src/exceptions/OFListenOnSocketFailedException.h @@ -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. + */ + +#import "OFException.h" + +#ifndef OF_HAVE_SOCKETS +# error No sockets available! +#endif + +OF_ASSUME_NONNULL_BEGIN + +/** + * @class OFListenOnSocketFailedException \ + * OFListenOnSocketFailedException.h \ + * ObjFW/OFListenOnSocketFailedException.h + * + * @brief An exception indicating that listening on the socket failed. + */ +@interface OFListenOnSocketFailedException: OFException +{ + id _socket; + int _backlog, _errNo; + OF_RESERVE_IVARS(OFListenOnSocketFailedException, 4) +} + +/** + * @brief The socket which failed to listen. + */ +@property (readonly, nonatomic) id socket; + +/** + * @brief The requested back log. + */ +@property (readonly, nonatomic) int backlog; + +/** + * @brief The errno of the error that occurred. + */ +@property (readonly, nonatomic) int errNo; + +/** + * @brief Creates a new, autoreleased listen failed exception. + * + * @param socket The socket which failed to listen + * @param backlog The requested size of the back log + * @param errNo The errno of the error that occurred + * @return A new, autoreleased listen failed exception + */ ++ (instancetype)exceptionWithSocket: (id)socket + backlog: (int)backlog + errNo: (int)errNo; + ++ (instancetype)exception OF_UNAVAILABLE; + +/** + * @brief Initializes an already allocated listen failed exception. + * + * @param socket The socket which failed to listen + * @param backlog The requested size of the back log + * @param errNo The errno of the error that occurred + * @return An initialized listen failed exception + */ +- (instancetype)initWithSocket: (id)socket + backlog: (int)backlog + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; + +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/OFListenOnSocketFailedException.m Index: src/exceptions/OFListenOnSocketFailedException.m ================================================================== --- src/exceptions/OFListenOnSocketFailedException.m +++ src/exceptions/OFListenOnSocketFailedException.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 "OFListenOnSocketFailedException.h" +#import "OFString.h" + +@implementation OFListenOnSocketFailedException +@synthesize socket = _socket, backlog = _backlog, errNo = _errNo; + ++ (instancetype)exception +{ + OF_UNRECOGNIZED_SELECTOR +} + ++ (instancetype)exceptionWithSocket: (id)sock + backlog: (int)backlog + errNo: (int)errNo +{ + return [[[self alloc] initWithSocket: sock + backlog: backlog + errNo: errNo] autorelease]; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithSocket: (id)sock backlog: (int)backlog errNo: (int)errNo +{ + self = [super init]; + + _socket = [sock retain]; + _backlog = backlog; + _errNo = errNo; + + return self; +} + +- (void)dealloc +{ + [_socket release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"Failed to listen in socket of type %@ with a back log of %d: %@", + [_socket class], _backlog, OFStrError(_errNo)]; +} +@end Index: src/exceptions/OFLoadPluginFailedException.h ================================================================== --- src/exceptions/OFLoadPluginFailedException.h +++ src/exceptions/OFLoadPluginFailedException.h @@ -24,10 +24,11 @@ * @brief An exception indicating a plugin could not be loaded. */ @interface OFLoadPluginFailedException: OFException { OFString *_path, *_Nullable _error; + OF_RESERVE_IVARS(OFLoadPluginFailedException, 4) } /** * @brief The path of the plugin which could not be loaded */ Index: src/exceptions/OFLockFailedException.h ================================================================== --- src/exceptions/OFLockFailedException.h +++ src/exceptions/OFLockFailedException.h @@ -26,10 +26,11 @@ */ @interface OFLockFailedException: OFException { id _Nullable _lock; int _errNo; + OF_RESERVE_IVARS(OFLockFailedException, 4) } /** * @brief The lock which could not be locked. */ Index: src/exceptions/OFMalformedXMLException.h ================================================================== --- src/exceptions/OFMalformedXMLException.h +++ src/exceptions/OFMalformedXMLException.h @@ -26,10 +26,11 @@ * @brief An exception indicating that a parser encountered malformed XML. */ @interface OFMalformedXMLException: OFException { OFXMLParser *_parser; + OF_RESERVE_IVARS(OFMalformedXMLException, 4) } /** * @brief The parser which encountered malformed XML. */ Index: src/exceptions/OFMoveItemFailedException.h ================================================================== --- src/exceptions/OFMoveItemFailedException.h +++ src/exceptions/OFMoveItemFailedException.h @@ -15,64 +15,65 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURL; +@class OFURI; /** * @class OFMoveItemFailedException \ * OFMoveItemFailedException.h ObjFW/OFMoveItemFailedException.h * * @brief An exception indicating that moving an item failed. */ @interface OFMoveItemFailedException: OFException { - OFURL *_sourceURL, *_destinationURL; + OFURI *_sourceURI, *_destinationURI; int _errNo; + OF_RESERVE_IVARS(OFMoveItemFailedException, 4) } /** - * @brief The original URL. + * @brief The original URI. */ -@property (readonly, nonatomic) OFURL *sourceURL; +@property (readonly, nonatomic) OFURI *sourceURI; /** - * @brief The new URL. + * @brief The new 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 move item failed exception. * - * @param sourceURL The original URL - * @param destinationURL The new URL + * @param sourceURI The original URI + * @param destinationURI The new URI * @param errNo The errno of the error that occurred * @return A new, autoreleased move 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 move item failed exception. * - * @param sourceURL The original URL - * @param destinationURL The new URL + * @param sourceURI The original URI + * @param destinationURI The new URI * @param errNo The errno of the error that occurred * @return An initialized move 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/OFMoveItemFailedException.m ================================================================== --- src/exceptions/OFMoveItemFailedException.m +++ src/exceptions/OFMoveItemFailedException.m @@ -15,44 +15,44 @@ #include "config.h" #import "OFMoveItemFailedException.h" #import "OFString.h" -#import "OFURL.h" +#import "OFURI.h" @implementation OFMoveItemFailedException -@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,18 +60,18 @@ return self; } - (void)dealloc { - [_sourceURL release]; - [_destinationURL release]; + [_sourceURI release]; + [_destinationURI release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to move item at %@ to %@: %@", - _sourceURL, _destinationURL, OFStrError(_errNo)]; + _sourceURI, _destinationURI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFNotImplementedException.h ================================================================== --- src/exceptions/OFNotImplementedException.h +++ src/exceptions/OFNotImplementedException.h @@ -26,10 +26,11 @@ */ @interface OFNotImplementedException: OFException { SEL _selector; id _Nullable _object; + OF_RESERVE_IVARS(OFNotImplementedException, 4) } /** * @brief The selector which is not or not fully implemented. */ Index: src/exceptions/OFNotOpenException.h ================================================================== --- src/exceptions/OFNotOpenException.h +++ src/exceptions/OFNotOpenException.h @@ -23,10 +23,11 @@ * @brief An exception indicating an object is not open, connected or bound. */ @interface OFNotOpenException: OFException { id _object; + OF_RESERVE_IVARS(OFNotOpenException, 4) } /** * @brief The object which is not open, connected or bound. */ DELETED src/exceptions/OFObserveFailedException.h Index: src/exceptions/OFObserveFailedException.h ================================================================== --- src/exceptions/OFObserveFailedException.h +++ src/exceptions/OFObserveFailedException.h @@ -1,69 +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 OFKernelEventObserver; - -/** - * @class OFObserveFailedException \ - * OFObserveFailedException.h ObjFW/OFObserveFailedException.h - * - * @brief An exception indicating that observing failed. - */ -@interface OFObserveFailedException: OFException -{ - OFKernelEventObserver *_observer; - int _errNo; -} - -/** - * @brief The observer which failed to observe. - */ -@property (readonly, nonatomic) OFKernelEventObserver *observer; - -/** - * @brief The errno of the error that occurred. - */ -@property (readonly, nonatomic) int errNo; - -/** - * @brief Creates a new, autoreleased observe failed exception. - * - * @param observer The observer which failed to observe - * @param errNo The errno of the error that occurred - * @return A new, autoreleased observe failed exception - */ -+ (instancetype)exceptionWithObserver: (OFKernelEventObserver *)observer - errNo: (int)errNo; - -+ (instancetype)exception OF_UNAVAILABLE; - -/** - * @brief Initializes an already allocated observe failed exception. - * - * @param observer The observer which failed to observe - * @param errNo The errno of the error that occurred - * @return An initialized observe failed exception - */ -- (instancetype)initWithObserver: (OFKernelEventObserver *)observer - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - -- (instancetype)init OF_UNAVAILABLE; -@end - -OF_ASSUME_NONNULL_END DELETED src/exceptions/OFObserveFailedException.m Index: src/exceptions/OFObserveFailedException.m ================================================================== --- src/exceptions/OFObserveFailedException.m +++ src/exceptions/OFObserveFailedException.m @@ -1,71 +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 "OFObserveFailedException.h" -#import "OFString.h" -#import "OFKernelEventObserver.h" - -@implementation OFObserveFailedException -@synthesize observer = _observer, errNo = _errNo; - -+ (instancetype)exception -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)exceptionWithObserver: (OFKernelEventObserver *)observer - errNo: (int)errNo -{ - return [[[self alloc] initWithObserver: observer - errNo: errNo] autorelease]; -} - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithObserver: (OFKernelEventObserver *)observer - errNo: (int)errNo -{ - self = [super init]; - - @try { - _observer = [observer retain]; - _errNo = errNo; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_observer release]; - - [super dealloc]; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: - @"An observer of class %@ failed to observe: %@", - _observer.class, OFStrError(_errNo)]; -} -@end ADDED src/exceptions/OFObserveKernelEventsFailedException.h Index: src/exceptions/OFObserveKernelEventsFailedException.h ================================================================== --- src/exceptions/OFObserveKernelEventsFailedException.h +++ src/exceptions/OFObserveKernelEventsFailedException.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 OFKernelEventObserver; + +/** + * @class OFObserveKernelEventsFailedException \ + * OFObserveKernelEventsFailedException.h \ + * ObjFW/OFObserveKernelEventsFailedException.h + * + * @brief An exception indicating that observing failed. + */ +@interface OFObserveKernelEventsFailedException: OFException +{ + OFKernelEventObserver *_observer; + int _errNo; + OF_RESERVE_IVARS(OFObserveKernelEventsFailedException, 4) +} + +/** + * @brief The observer which failed to observe. + */ +@property (readonly, nonatomic) OFKernelEventObserver *observer; + +/** + * @brief The errno of the error that occurred. + */ +@property (readonly, nonatomic) int errNo; + +/** + * @brief Creates a new, autoreleased observe failed exception. + * + * @param observer The observer which failed to observe + * @param errNo The errno of the error that occurred + * @return A new, autoreleased observe failed exception + */ ++ (instancetype)exceptionWithObserver: (OFKernelEventObserver *)observer + errNo: (int)errNo; + ++ (instancetype)exception OF_UNAVAILABLE; + +/** + * @brief Initializes an already allocated observe failed exception. + * + * @param observer The observer which failed to observe + * @param errNo The errno of the error that occurred + * @return An initialized observe failed exception + */ +- (instancetype)initWithObserver: (OFKernelEventObserver *)observer + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; + +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/OFObserveKernelEventsFailedException.m Index: src/exceptions/OFObserveKernelEventsFailedException.m ================================================================== --- src/exceptions/OFObserveKernelEventsFailedException.m +++ src/exceptions/OFObserveKernelEventsFailedException.m @@ -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. + */ + +#include "config.h" + +#import "OFObserveKernelEventsFailedException.h" +#import "OFString.h" +#import "OFKernelEventObserver.h" + +@implementation OFObserveKernelEventsFailedException +@synthesize observer = _observer, errNo = _errNo; + ++ (instancetype)exception +{ + OF_UNRECOGNIZED_SELECTOR +} + ++ (instancetype)exceptionWithObserver: (OFKernelEventObserver *)observer + errNo: (int)errNo +{ + return [[[self alloc] initWithObserver: observer + errNo: errNo] autorelease]; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithObserver: (OFKernelEventObserver *)observer + errNo: (int)errNo +{ + self = [super init]; + + @try { + _observer = [observer retain]; + _errNo = errNo; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_observer release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"An observer of class %@ failed to observe: %@", + _observer.class, OFStrError(_errNo)]; +} +@end Index: src/exceptions/OFOpenItemFailedException.h ================================================================== --- src/exceptions/OFOpenItemFailedException.h +++ src/exceptions/OFOpenItemFailedException.h @@ -15,30 +15,31 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURL; +@class OFURI; /** * @class OFOpenItemFailedException \ * OFOpenItemFailedException.h ObjFW/OFOpenItemFailedException.h * * @brief An exception indicating an item could not be opened. */ @interface OFOpenItemFailedException: OFException { - OFURL *_Nullable _URL; + OFURI *_Nullable _URI; OFString *_Nullable _path; OFString *_mode; int _errNo; + OF_RESERVE_IVARS(OFOpenItemFailedException, 4) } /** - * @brief The URL of the item which could not be opened. + * @brief The URI of the item which could not be opened. */ -@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURL *URL; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURI *URI; /** * @brief The path of the item which could not be opened. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path; @@ -54,16 +55,16 @@ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased open item failed exception. * - * @param URL The URL of the item which could not be opened + * @param URI The URI of the item which could not be opened * @param mode A string with the mode in which the item should have been opened * @param errNo The errno of the error that occurred * @return A new, autoreleased open item failed exception */ -+ (instancetype)exceptionWithURL: (OFURL *)URL ++ (instancetype)exceptionWithURI: (OFURI *)URI mode: (nullable OFString *)mode errNo: (int)errNo; /** * @brief Creates a new, autoreleased open item failed exception. @@ -80,16 +81,16 @@ + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated open item failed exception. * - * @param URL The URL of the item which could not be opened + * @param URI The URI of the item which could not be opened * @param mode A string with the mode in which the item should have been opened * @param errNo The errno of the error that occurred * @return An initialized open item failed exception */ -- (instancetype)initWithURL: (OFURL *)URL +- (instancetype)initWithURI: (OFURI *)URI mode: (nullable OFString *)mode errNo: (int)errNo; /** * @brief Initializes an already allocated open item failed exception. Index: src/exceptions/OFOpenItemFailedException.m ================================================================== --- src/exceptions/OFOpenItemFailedException.m +++ src/exceptions/OFOpenItemFailedException.m @@ -15,20 +15,20 @@ #include "config.h" #import "OFOpenItemFailedException.h" #import "OFString.h" -#import "OFURL.h" +#import "OFURI.h" @implementation OFOpenItemFailedException -@synthesize URL = _URL, path = _path, mode = _mode, errNo = _errNo; +@synthesize URI = _URI, path = _path, mode = _mode, errNo = _errNo; -+ (instancetype)exceptionWithURL: (OFURL *)URL ++ (instancetype)exceptionWithURI: (OFURI *)URI mode: (OFString *)mode errNo: (int)errNo { - return [[[self alloc] initWithURL: URL + return [[[self alloc] initWithURI: URI mode: mode errNo: errNo] autorelease]; } + (instancetype)exceptionWithPath: (OFString *)path @@ -43,18 +43,18 @@ + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -- (instancetype)initWithURL: (OFURL *)URL +- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode errNo: (int)errNo { self = [super init]; @try { - _URL = [URL copy]; + _URI = [URI copy]; _mode = [mode copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; @@ -86,11 +86,11 @@ OF_INVALID_INIT_METHOD } - (void)dealloc { - [_URL release]; + [_URI release]; [_path release]; [_mode release]; [super dealloc]; } @@ -97,19 +97,19 @@ - (OFString *)description { id item = nil; - if (_URL != nil) - item = _URL; + if (_URI != nil) + item = _URI; else if (_path != nil) item = _path; if (_mode != nil) return [OFString stringWithFormat: - @"Failed to open item %@ with mode %@: %@", + @"Failed to open file %@ with mode %@: %@", item, _mode, OFStrError(_errNo)]; else return [OFString stringWithFormat: @"Failed to open item %@: %@", item, OFStrError(_errNo)]; } @end Index: src/exceptions/OFOpenWindowsRegistryKeyFailedException.h ================================================================== --- src/exceptions/OFOpenWindowsRegistryKeyFailedException.h +++ src/exceptions/OFOpenWindowsRegistryKeyFailedException.h @@ -29,14 +29,15 @@ */ @interface OFOpenWindowsRegistryKeyFailedException: OFException { OFWindowsRegistryKey *_registryKey; OFString *_path; - DWORD _options; - REGSAM _securityAndAccessRights; + REGSAM _accessRights; LPSECURITY_ATTRIBUTES _Nullable _securityAttributes; + DWORD _options; LSTATUS _status; + OF_RESERVE_IVARS(OFOpenWindowsRegistryKeyFailedException, 4) } /** * @brief The registry key on which opening the subkey failed. */ @@ -45,21 +46,20 @@ /** * @brief The path for the subkey that could not be opened. */ @property (readonly, nonatomic) OFString *path; +/** + * @brief The access rights for the subkey that could not be opened. + */ +@property (readonly, nonatomic) REGSAM accessRights; + /** * @brief The options for the subkey that could not be opened. */ @property (readonly, nonatomic) DWORD options; -/** - * @brief The security and access rights for the subkey that could not be - * opened. - */ -@property (readonly, nonatomic) REGSAM securityAndAccessRights; - /** * @brief The status returned by RegOpenKeyEx(). */ @property (readonly, nonatomic) LSTATUS status; @@ -67,43 +67,41 @@ * @brief Creates a new, autoreleased open Windows registry key failed * exception. * * @param registryKey The registry key on which opening the subkey failed * @param path The path for the subkey that could not be opened + * @param accessRights The access rights for the sub key that could not be + * opened * @param options The options for the subkey that could not be opened - * @param securityAndAccessRights The security and access rights for the sub - * key that could not be opened * @param status The status returned by RegOpenKeyEx() * @return A new, autoreleased open Windows registry key failed exception */ -+ (instancetype) - exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey - path: (OFString *)path - options: (DWORD)options - securityAndAccessRights: (REGSAM)securityAndAccessRights - status: (LSTATUS)status; ++ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey + path: (OFString *)path + accessRights: (REGSAM)accessRights + options: (DWORD)options + status: (LSTATUS)status; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated open Windows registry key failed * exception. * * @param registryKey The registry key on which opening the subkey failed * @param path The path for the subkey that could not be opened + * @param accessRights The access rights for the sub key that could not be + * opened * @param options The options for the subkey that could not be opened - * @param securityAndAccessRights The security and access rights for the sub - * key that could not be opened * @param status The status returned by RegOpenKeyEx() * @return An initialized open Windows registry key failed exception */ -- (instancetype) - initWithRegistryKey: (OFWindowsRegistryKey *)registryKey - path: (OFString *)path - options: (DWORD)options - securityAndAccessRights: (REGSAM)securityAndAccessRights - status: (LSTATUS)status OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey + path: (OFString *)path + accessRights: (REGSAM)accessRights + options: (DWORD)options + status: (LSTATUS)status OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFOpenWindowsRegistryKeyFailedException.m ================================================================== --- src/exceptions/OFOpenWindowsRegistryKeyFailedException.m +++ src/exceptions/OFOpenWindowsRegistryKeyFailedException.m @@ -16,47 +16,44 @@ #include "config.h" #import "OFOpenWindowsRegistryKeyFailedException.h" @implementation OFOpenWindowsRegistryKeyFailedException -@synthesize registryKey = _registryKey, path = _path, options = _options; -@synthesize securityAndAccessRights = _securityAndAccessRights; -@synthesize status = _status; - -+ (instancetype) - exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey - path: (OFString *)path - options: (DWORD)options - securityAndAccessRights: (REGSAM)securityAndAccessRights - status: (LSTATUS)status +@synthesize registryKey = _registryKey, path = _path; +@synthesize accessRights = _accessRights, options = _options, status = _status; + ++ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey + path: (OFString *)path + accessRights: (REGSAM)accessRights + options: (DWORD)options + status: (LSTATUS)status { return [[[self alloc] initWithRegistryKey: registryKey path: path + accessRights: accessRights options: options - securityAndAccessRights: securityAndAccessRights status: status] autorelease]; } + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -- (instancetype) - initWithRegistryKey: (OFWindowsRegistryKey *)registryKey - path: (OFString *)path - options: (DWORD)options - securityAndAccessRights: (REGSAM)securityAndAccessRights - status: (LSTATUS)status +- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey + path: (OFString *)path + accessRights: (REGSAM)accessRights + options: (DWORD)options + status: (LSTATUS)status { self = [super init]; @try { _registryKey = [registryKey retain]; _path = [path copy]; + _accessRights = accessRights; _options = options; - _securityAndAccessRights = securityAndAccessRights; _status = status; } @catch (id e) { [self release]; @throw e; } Index: src/exceptions/OFOutOfMemoryException.h ================================================================== --- src/exceptions/OFOutOfMemoryException.h +++ src/exceptions/OFOutOfMemoryException.h @@ -24,10 +24,11 @@ * @brief An exception indicating there is not enough memory available. */ @interface OFOutOfMemoryException: OFException { size_t _requestedSize; + OF_RESERVE_IVARS(OFOutOfMemoryException, 4) } /** * @brief The size of the memory that could not be allocated. */ Index: src/exceptions/OFOutOfRangeException.h ================================================================== --- src/exceptions/OFOutOfRangeException.h +++ src/exceptions/OFOutOfRangeException.h @@ -22,8 +22,11 @@ * OFOutOfRangeException.h ObjFW/OFOutOfRangeException.h * * @brief An exception indicating the given value is out of range. */ @interface OFOutOfRangeException: OFException +{ + OF_RESERVE_IVARS(OFOutOfRangeException, 4) +} @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFReadFailedException.h ================================================================== --- src/exceptions/OFReadFailedException.h +++ src/exceptions/OFReadFailedException.h @@ -22,8 +22,11 @@ * OFReadFailedException.h ObjFW/OFReadFailedException.h * * @brief An exception indicating that reading from an object failed. */ @interface OFReadFailedException: OFReadOrWriteFailedException +{ + OF_RESERVE_IVARS(OFReadFailedException, 4) +} @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFReadOrWriteFailedException.h ================================================================== --- src/exceptions/OFReadOrWriteFailedException.h +++ src/exceptions/OFReadOrWriteFailedException.h @@ -27,10 +27,11 @@ @interface OFReadOrWriteFailedException: OFException { id _object; size_t _requestedLength; int _errNo; + OF_RESERVE_IVARS(OFReadOrWriteFailedException, 4) } /** * @brief The stream which caused the read or write failed exception. */ Index: src/exceptions/OFRemoveItemFailedException.h ================================================================== --- src/exceptions/OFRemoveItemFailedException.h +++ src/exceptions/OFRemoveItemFailedException.h @@ -15,54 +15,55 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURL; +@class OFURI; /** * @class OFRemoveItemFailedException \ * OFRemoveItemFailedException.h ObjFW/OFRemoveItemFailedException.h * * @brief An exception indicating that removing an item failed. */ @interface OFRemoveItemFailedException: OFException { - OFURL *_URL; + OFURI *_URI; int _errNo; + OF_RESERVE_IVARS(OFRemoveItemFailedException, 4) } /** - * @brief The URL of the item which could not be removed. + * @brief The URI of the item which could not be removed. */ -@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 remove failed exception. * - * @param URL The URL of the item which could not be removed + * @param URI The URI of the item which could not be removed * @param errNo The errno of the error that occurred * @return A new, autoreleased remove item 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 remove failed exception. * - * @param URL The URL of the item which could not be removed + * @param URI The URI of the item which could not be removed * @param errNo The errno of the error that occurred * @return An initialized remove item 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/OFRemoveItemFailedException.m ================================================================== --- src/exceptions/OFRemoveItemFailedException.m +++ src/exceptions/OFRemoveItemFailedException.m @@ -15,36 +15,36 @@ #include "config.h" #import "OFRemoveItemFailedException.h" #import "OFString.h" -#import "OFURL.h" +#import "OFURI.h" @implementation OFRemoveItemFailedException -@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 remove item at URL %@: %@", _URL, OFStrError(_errNo)]; + @"Failed to remove item at URI %@: %@", _URI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFResolveHostFailedException.h ================================================================== --- src/exceptions/OFResolveHostFailedException.h +++ src/exceptions/OFResolveHostFailedException.h @@ -27,10 +27,11 @@ @interface OFResolveHostFailedException: OFException { OFString *_host; OFSocketAddressFamily _addressFamily; OFDNSResolverErrorCode _errorCode; + OF_RESERVE_IVARS(OFResolveHostFailedException, 4) } /** * @brief The host which could not be resolved. */ DELETED src/exceptions/OFRetrieveItemAttributesFailedException.h Index: src/exceptions/OFRetrieveItemAttributesFailedException.h ================================================================== --- src/exceptions/OFRetrieveItemAttributesFailedException.h +++ src/exceptions/OFRetrieveItemAttributesFailedException.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" - -OF_ASSUME_NONNULL_BEGIN - -@class OFURL; - -/** - * @class OFRetrieveItemAttributesFailedException \ - * OFRetrieveItemAttributesFailedException.h \ - * ObjFW/OFRetrieveItemAttributesFailedException.h - * - * @brief An exception indicating an item's attributes could not be retrieved. - */ -@interface OFRetrieveItemAttributesFailedException: OFException -{ - OFURL *_URL; - int _errNo; -} - -/** - * @brief The URL of the item whose attributes could not be retrieved. - */ -@property (readonly, nonatomic) OFURL *URL; - -/** - * @brief The errno of the error that occurred. - */ -@property (readonly, nonatomic) int errNo; - -/** - * @brief Creates a new, autoreleased retrieve item attributes failed exception. - * - * @param URL The URL 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)exceptionWithURL: (OFURL *)URL errNo: (int)errNo; - -+ (instancetype)exception OF_UNAVAILABLE; - -/** - * @brief Initializes an already allocated retrieve item attributes failed - * exception. - * - * @param URL The URL 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)initWithURL: (OFURL *)URL - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - -- (instancetype)init OF_UNAVAILABLE; -@end - -OF_ASSUME_NONNULL_END DELETED src/exceptions/OFRetrieveItemAttributesFailedException.m Index: src/exceptions/OFRetrieveItemAttributesFailedException.m ================================================================== --- src/exceptions/OFRetrieveItemAttributesFailedException.m +++ src/exceptions/OFRetrieveItemAttributesFailedException.m @@ -1,68 +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 "OFRetrieveItemAttributesFailedException.h" -#import "OFString.h" -#import "OFURL.h" - -@implementation OFRetrieveItemAttributesFailedException -@synthesize URL = _URL, errNo = _errNo; - -+ (instancetype)exception -{ - OF_UNRECOGNIZED_SELECTOR -} - -+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo -{ - return [[[self alloc] initWithURL: URL errNo: errNo] autorelease]; -} - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithURL: (OFURL *)URL errNo: (int)errNo -{ - self = [super init]; - - @try { - _URL = [URL copy]; - _errNo = errNo; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_URL release]; - - [super dealloc]; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: - @"Failed to retrieve attributes for item %@: %@", - _URL, OFStrError(_errNo)]; -} -@end DELETED src/exceptions/OFSandboxActivationFailedException.h Index: src/exceptions/OFSandboxActivationFailedException.h ================================================================== --- src/exceptions/OFSandboxActivationFailedException.h +++ src/exceptions/OFSandboxActivationFailedException.h @@ -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. - */ - -#import "OFException.h" - -OF_ASSUME_NONNULL_BEGIN - -@class OFSandbox; - -@interface OFSandboxActivationFailedException: 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 DELETED src/exceptions/OFSandboxActivationFailedException.m Index: src/exceptions/OFSandboxActivationFailedException.m ================================================================== --- src/exceptions/OFSandboxActivationFailedException.m +++ src/exceptions/OFSandboxActivationFailedException.m @@ -1,63 +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 "OFSandboxActivationFailedException.h" -#import "OFString.h" -#import "OFSandbox.h" - -@implementation OFSandboxActivationFailedException -@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/OFSeekFailedException.h ================================================================== --- src/exceptions/OFSeekFailedException.h +++ src/exceptions/OFSeekFailedException.h @@ -25,12 +25,14 @@ * @brief An exception indicating that seeking in a stream failed. */ @interface OFSeekFailedException: OFException { OFSeekableStream *_stream; - OFFileOffset _offset; - int _whence, _errNo; + OFStreamOffset _offset; + OFSeekWhence _whence; + int _errNo; + OF_RESERVE_IVARS(OFSeekFailedException, 4) } /** * @brief The stream for which seeking failed. */ @@ -37,16 +39,16 @@ @property (readonly, nonatomic) OFSeekableStream *stream; /** * @brief The offset to which seeking failed. */ -@property (readonly, nonatomic) OFFileOffset offset; +@property (readonly, nonatomic) OFStreamOffset offset; /** * @brief To what the offset is relative. */ -@property (readonly, nonatomic) int whence; +@property (readonly, nonatomic) OFSeekWhence whence; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -59,12 +61,12 @@ * @param whence To what the offset is relative * @param errNo The errno of the error that occurred * @return A new, autoreleased seek failed exception */ + (instancetype)exceptionWithStream: (OFSeekableStream *)stream - offset: (OFFileOffset)offset - whence: (int)whence + offset: (OFStreamOffset)offset + whence: (OFSeekWhence)whence errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** @@ -75,13 +77,13 @@ * @param whence To what the offset is relative * @param errNo The errno of the error that occurred * @return An initialized seek failed exception */ - (instancetype)initWithStream: (OFSeekableStream *)stream - offset: (OFFileOffset)offset - whence: (int)whence + offset: (OFStreamOffset)offset + whence: (OFSeekWhence)whence errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFSeekFailedException.m ================================================================== --- src/exceptions/OFSeekFailedException.m +++ src/exceptions/OFSeekFailedException.m @@ -27,12 +27,12 @@ { OF_UNRECOGNIZED_SELECTOR } + (instancetype)exceptionWithStream: (OFSeekableStream *)stream - offset: (OFFileOffset)offset - whence: (int)whence + offset: (OFStreamOffset)offset + whence: (OFSeekWhence)whence errNo: (int)errNo { return [[[self alloc] initWithStream: stream offset: offset whence: whence @@ -43,12 +43,12 @@ { OF_INVALID_INIT_METHOD } - (instancetype)initWithStream: (OFSeekableStream *)stream - offset: (OFFileOffset)offset - whence: (int)whence + offset: (OFStreamOffset)offset + whence: (OFSeekWhence)whence errNo: (int)errNo { self = [super init]; _stream = [stream retain]; Index: src/exceptions/OFSetItemAttributesFailedException.h ================================================================== --- src/exceptions/OFSetItemAttributesFailedException.h +++ src/exceptions/OFSetItemAttributesFailedException.h @@ -12,15 +12,15 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFException.h" -#import "OFURLHandler.h" +#import "OFURIHandler.h" OF_ASSUME_NONNULL_BEGIN -@class OFURL; +@class OFURI; /** * @class OFSetItemAttributesFailedException \ * OFSetItemAttributesFailedException.h \ * ObjFW/OFSetItemAttributesFailedException.h @@ -27,20 +27,21 @@ * * @brief An exception indicating an item's attributes could not be set. */ @interface OFSetItemAttributesFailedException: OFException { - OFURL *_URL; + OFURI *_URI; OFFileAttributes _attributes; OFFileAttributeKey _failedAttribute; int _errNo; + OF_RESERVE_IVARS(OFSetItemAttributesFailedException, 4) } /** - * @brief The URL of the item whose attributes could not be set. + * @brief The URI of the item whose attributes could not be set. */ -@property (readonly, nonatomic) OFURL *URL; +@property (readonly, nonatomic) OFURI *URI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -56,38 +57,38 @@ @property (readonly, nonatomic) OFFileAttributeKey failedAttribute; /** * @brief Creates a new, autoreleased set item attributes failed exception. * - * @param URL The URL of the item whose attributes could not be set + * @param URI The URI of the item whose attributes could not be set * @param attributes The attributes that should have been set for the specified * item. * @param failedAttribute The first attribute that could not be set * @param errNo The errno of the error that occurred * @return A new, autoreleased set item attributes failed exception */ -+ (instancetype)exceptionWithURL: (OFURL *)URL ++ (instancetype)exceptionWithURI: (OFURI *)URI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated set item attributes failed exception. * - * @param URL The URL of the item whose attributes could not be set + * @param URI The URI of the item whose attributes could not be set * @param attributes The attributes that should have been set for the specified * item. * @param failedAttribute The first attribute that could not be set * @param errNo The errno of the error that occurred * @return An initialized set item attributes failed exception */ -- (instancetype)initWithURL: (OFURL *)URL +- (instancetype)initWithURI: (OFURI *)URI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFSetItemAttributesFailedException.m ================================================================== --- src/exceptions/OFSetItemAttributesFailedException.m +++ src/exceptions/OFSetItemAttributesFailedException.m @@ -15,27 +15,27 @@ #include "config.h" #import "OFSetItemAttributesFailedException.h" #import "OFString.h" -#import "OFURL.h" +#import "OFURI.h" @implementation OFSetItemAttributesFailedException -@synthesize URL = _URL, attributes = _attributes; +@synthesize URI = _URI, attributes = _attributes; @synthesize failedAttribute = _failedAttribute, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithURL: (OFURL *)URL ++ (instancetype)exceptionWithURI: (OFURI *)URI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo { - return [[[self alloc] initWithURL: URL + return [[[self alloc] initWithURI: URI attributes: attributes failedAttribute: failedAttribute errNo: errNo] autorelease]; } @@ -42,19 +42,19 @@ - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithURL: (OFURL *)URL +- (instancetype)initWithURI: (OFURI *)URI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo { self = [super init]; @try { - _URL = [URL copy]; + _URI = [URI copy]; _attributes = [attributes copy]; _failedAttribute = [failedAttribute copy]; _errNo = errNo; } @catch (id e) { [self release]; @@ -64,11 +64,11 @@ return self; } - (void)dealloc { - [_URL release]; + [_URI release]; [_attributes release]; [_failedAttribute release]; [super dealloc]; } @@ -75,8 +75,8 @@ - (OFString *)description { return [OFString stringWithFormat: @"Failed to set attribute %@ for item %@: %@", - _failedAttribute, _URL, OFStrError(_errNo)]; + _failedAttribute, _URI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFSetOptionFailedException.h ================================================================== --- src/exceptions/OFSetOptionFailedException.h +++ src/exceptions/OFSetOptionFailedException.h @@ -25,10 +25,11 @@ */ @interface OFSetOptionFailedException: OFException { id _object; int _errNo; + OF_RESERVE_IVARS(OFSetOptionFailedException, 4) } /** * @brief The object for which the option could not be set. */ Index: src/exceptions/OFSetWindowsRegistryValueFailedException.h ================================================================== --- src/exceptions/OFSetWindowsRegistryValueFailedException.h +++ src/exceptions/OFSetWindowsRegistryValueFailedException.h @@ -32,10 +32,11 @@ OFWindowsRegistryKey *_registryKey; OFString *_Nullable _valueName; OFData *_Nullable _data; DWORD _type; LSTATUS _status; + OF_RESERVE_IVARS(OFSetWindowsRegistryValueFailedException, 4) } /** * @brief The registry key on which setting the value failed. */ ADDED src/exceptions/OFSignalConditionFailedException.h Index: src/exceptions/OFSignalConditionFailedException.h ================================================================== --- src/exceptions/OFSignalConditionFailedException.h +++ src/exceptions/OFSignalConditionFailedException.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 OFSignalConditionFailedException \ + * OFSignalConditionFailedException.h \ + * ObjFW/OFSignalConditionFailedException.h + * + * @brief An exception indicating signaling a condition failed. + */ +@interface OFSignalConditionFailedException: OFException +{ + OFCondition *_condition; + int _errNo; + OF_RESERVE_IVARS(OFSignalConditionFailedException, 4) +} + +/** + * @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 ADDED src/exceptions/OFSignalConditionFailedException.m Index: src/exceptions/OFSignalConditionFailedException.m ================================================================== --- src/exceptions/OFSignalConditionFailedException.m +++ src/exceptions/OFSignalConditionFailedException.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 "OFSignalConditionFailedException.h" +#import "OFString.h" +#import "OFCondition.h" + +@implementation OFSignalConditionFailedException +@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 ADDED src/exceptions/OFStartThreadFailedException.h Index: src/exceptions/OFStartThreadFailedException.h ================================================================== --- src/exceptions/OFStartThreadFailedException.h +++ src/exceptions/OFStartThreadFailedException.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_THREADS +# error No threads available! +#endif + +OF_ASSUME_NONNULL_BEGIN + +@class OFThread; + +/** + * @class OFStartThreadFailedException \ + * OFStartThreadFailedException.h ObjFW/OFStartThreadFailedException.h + * + * @brief An exception indicating that starting a thread failed. + */ +@interface OFStartThreadFailedException: OFException +{ + OFThread *_Nullable _thread; + int _errNo; + OF_RESERVE_IVARS(OFStartThreadFailedException, 4) +} + +/** + * @brief The thread which could not be started. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread; + +/** + * @brief The errno of the error that occurred. + */ +@property (readonly, nonatomic) int errNo; + +/** + * @brief Creates a new, autoreleased thread start failed exception. + * + * @param thread The thread which could not be started + * @param errNo The errno of the error that occurred + * @return A new, autoreleased thread start failed exception + */ ++ (instancetype)exceptionWithThread: (nullable OFThread *)thread + errNo: (int)errNo; + ++ (instancetype)exception OF_UNAVAILABLE; + +/** + * @brief Initializes an already allocated thread start failed exception. + * + * @param thread The thread which could not be started + * @param errNo The errno of the error that occurred + * @return An initialized thread start failed exception + */ +- (instancetype)initWithThread: (nullable OFThread *)thread + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; + +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/OFStartThreadFailedException.m Index: src/exceptions/OFStartThreadFailedException.m ================================================================== --- src/exceptions/OFStartThreadFailedException.m +++ src/exceptions/OFStartThreadFailedException.m @@ -0,0 +1,65 @@ +/* + * 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 "OFStartThreadFailedException.h" +#import "OFString.h" +#import "OFThread.h" + +@implementation OFStartThreadFailedException +@synthesize thread = _thread, errNo = _errNo; + ++ (instancetype)exceptionWithThread: (OFThread *)thread errNo: (int)errNo +{ + return [[[self alloc] initWithThread: thread errNo: errNo] autorelease]; +} + ++ (instancetype)exception +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (instancetype)initWithThread: (OFThread *)thread errNo: (int)errNo +{ + self = [super init]; + + _thread = [thread retain]; + _errNo = errNo; + + return self; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (void)dealloc +{ + [_thread release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"Starting a thread of type %@ failed: %s", + _thread.class, strerror(_errNo)]; +} +@end Index: src/exceptions/OFStillLockedException.h ================================================================== --- src/exceptions/OFStillLockedException.h +++ src/exceptions/OFStillLockedException.h @@ -25,10 +25,11 @@ * @brief An exception indicating that a lock is still locked. */ @interface OFStillLockedException: OFException { id _Nullable _lock; + OF_RESERVE_IVARS(OFStillLockedException, 4) } /** * @brief The lock which is still locked. */ Index: src/exceptions/OFTLSHandshakeFailedException.h ================================================================== --- src/exceptions/OFTLSHandshakeFailedException.h +++ src/exceptions/OFTLSHandshakeFailedException.h @@ -40,10 +40,11 @@ @interface OFTLSHandshakeFailedException: OFException { OFTLSStream *_stream; OFString *_Nullable _host; OFTLSStreamErrorCode _errorCode; + OF_RESERVE_IVARS(OFTLSHandshakeFailedException, 4) } /** * @brief The TLS stream which failed the handshake. */ DELETED src/exceptions/OFThreadJoinFailedException.h Index: src/exceptions/OFThreadJoinFailedException.h ================================================================== --- src/exceptions/OFThreadJoinFailedException.h +++ src/exceptions/OFThreadJoinFailedException.h @@ -1,73 +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 OFThread; - -/** - * @class OFThreadJoinFailedException \ - * OFThreadJoinFailedException.h ObjFW/OFThreadJoinFailedException.h - * - * @brief An exception indicating that joining a thread failed. - */ -@interface OFThreadJoinFailedException: OFException -{ - OFThread *_Nullable _thread; - int _errNo; -} - -/** - * @brief The thread which could not be joined. - */ -@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread; - -/** - * @brief The errno of the error that occurred. - */ -@property (readonly, nonatomic) int errNo; - -/** - * @brief Creates a new, autoreleased thread join failed exception. - * - * @param thread The thread which could not be joined - * @param errNo The errno of the error that occurred - * @return A new, autoreleased thread join failed exception - */ -+ (instancetype)exceptionWithThread: (nullable OFThread *)thread - errNo: (int)errNo; - -+ (instancetype)exception OF_UNAVAILABLE; - -/** - * @brief Initializes an already allocated thread join failed exception. - * - * @param thread The thread which could not be joined - * @param errNo The errno of the error that occurred - * @return An initialized thread join failed exception - */ -- (instancetype)initWithThread: (nullable OFThread *)thread - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - -- (instancetype)init OF_UNAVAILABLE; -@end - -OF_ASSUME_NONNULL_END DELETED src/exceptions/OFThreadJoinFailedException.m Index: src/exceptions/OFThreadJoinFailedException.m ================================================================== --- src/exceptions/OFThreadJoinFailedException.m +++ src/exceptions/OFThreadJoinFailedException.m @@ -1,65 +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 "OFThreadJoinFailedException.h" -#import "OFString.h" -#import "OFThread.h" - -@implementation OFThreadJoinFailedException -@synthesize thread = _thread, errNo = _errNo; - -+ (instancetype)exceptionWithThread: (OFThread *)thread errNo: (int)errNo -{ - return [[[self alloc] initWithThread: thread errNo: errNo] autorelease]; -} - -+ (instancetype)exception -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (instancetype)initWithThread: (OFThread *)thread errNo: (int)errNo -{ - self = [super init]; - - _thread = [thread retain]; - _errNo = errNo; - - return self; -} - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (void)dealloc -{ - [_thread release]; - - [super dealloc]; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: - @"Joining a thread of type %@ failed: %s", - _thread.class, strerror(_errNo)]; -} -@end DELETED src/exceptions/OFThreadStartFailedException.h Index: src/exceptions/OFThreadStartFailedException.h ================================================================== --- src/exceptions/OFThreadStartFailedException.h +++ src/exceptions/OFThreadStartFailedException.h @@ -1,73 +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 OFThread; - -/** - * @class OFThreadStartFailedException \ - * OFThreadStartFailedException.h ObjFW/OFThreadStartFailedException.h - * - * @brief An exception indicating that starting a thread failed. - */ -@interface OFThreadStartFailedException: OFException -{ - OFThread *_Nullable _thread; - int _errNo; -} - -/** - * @brief The thread which could not be started. - */ -@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread; - -/** - * @brief The errno of the error that occurred. - */ -@property (readonly, nonatomic) int errNo; - -/** - * @brief Creates a new, autoreleased thread start failed exception. - * - * @param thread The thread which could not be started - * @param errNo The errno of the error that occurred - * @return A new, autoreleased thread start failed exception - */ -+ (instancetype)exceptionWithThread: (nullable OFThread *)thread - errNo: (int)errNo; - -+ (instancetype)exception OF_UNAVAILABLE; - -/** - * @brief Initializes an already allocated thread start failed exception. - * - * @param thread The thread which could not be started - * @param errNo The errno of the error that occurred - * @return An initialized thread start failed exception - */ -- (instancetype)initWithThread: (nullable OFThread *)thread - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - -- (instancetype)init OF_UNAVAILABLE; -@end - -OF_ASSUME_NONNULL_END DELETED src/exceptions/OFThreadStartFailedException.m Index: src/exceptions/OFThreadStartFailedException.m ================================================================== --- src/exceptions/OFThreadStartFailedException.m +++ src/exceptions/OFThreadStartFailedException.m @@ -1,65 +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 "OFThreadStartFailedException.h" -#import "OFString.h" -#import "OFThread.h" - -@implementation OFThreadStartFailedException -@synthesize thread = _thread, errNo = _errNo; - -+ (instancetype)exceptionWithThread: (OFThread *)thread errNo: (int)errNo -{ - return [[[self alloc] initWithThread: thread errNo: errNo] autorelease]; -} - -+ (instancetype)exception -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (instancetype)initWithThread: (OFThread *)thread errNo: (int)errNo -{ - self = [super init]; - - _thread = [thread retain]; - _errNo = errNo; - - return self; -} - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (void)dealloc -{ - [_thread release]; - - [super dealloc]; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: - @"Starting a thread of type %@ failed: %s", - _thread.class, strerror(_errNo)]; -} -@end Index: src/exceptions/OFThreadStillRunningException.h ================================================================== --- src/exceptions/OFThreadStillRunningException.h +++ src/exceptions/OFThreadStillRunningException.h @@ -30,10 +30,11 @@ * @brief An exception indicating that a thread is still running. */ @interface OFThreadStillRunningException: OFException { OFThread *_Nullable _thread; + OF_RESERVE_IVARS(OFThreadStillRunningException, 4) } /** * @brief The thread which is still running. */ Index: src/exceptions/OFTruncatedDataException.h ================================================================== --- src/exceptions/OFTruncatedDataException.h +++ src/exceptions/OFTruncatedDataException.h @@ -23,8 +23,11 @@ * * @brief An exception indicating that data was truncated while it should not * have been truncated. */ @interface OFTruncatedDataException: OFException +{ + OF_RESERVE_IVARS(OFTruncatedDataException, 4) +} @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFUnboundNamespaceException.h ================================================================== --- src/exceptions/OFUnboundNamespaceException.h +++ src/exceptions/OFUnboundNamespaceException.h @@ -27,10 +27,11 @@ */ @interface OFUnboundNamespaceException: OFException { OFString *_namespace; OFXMLElement *_element; + OF_RESERVE_IVARS(OFUnboundNamespaceException, 4) } /** * @brief The unbound namespace. */ Index: src/exceptions/OFUnboundPrefixException.h ================================================================== --- src/exceptions/OFUnboundPrefixException.h +++ src/exceptions/OFUnboundPrefixException.h @@ -27,10 +27,11 @@ */ @interface OFUnboundPrefixException: OFException { OFString *_prefix; OFXMLParser *_parser; + OF_RESERVE_IVARS(OFUnboundPrefixException, 4) } /** * @brief The unbound prefix. */ Index: src/exceptions/OFUndefinedKeyException.h ================================================================== --- src/exceptions/OFUndefinedKeyException.h +++ src/exceptions/OFUndefinedKeyException.h @@ -27,10 +27,11 @@ @interface OFUndefinedKeyException: OFException { id _object; OFString *_Nullable _key; id _Nullable _value; + OF_RESERVE_IVARS(OFUndefinedKeyException, 4) } /** * @brief The object on which the key is undefined. */ Index: src/exceptions/OFUnknownXMLEntityException.h ================================================================== --- src/exceptions/OFUnknownXMLEntityException.h +++ src/exceptions/OFUnknownXMLEntityException.h @@ -25,10 +25,11 @@ * entity. */ @interface OFUnknownXMLEntityException: OFException { OFString *_entityName; + OF_RESERVE_IVARS(OFUnknownXMLEntityException, 4) } /** * @brief The name of the unknown XML entity. */ Index: src/exceptions/OFUnlockFailedException.h ================================================================== --- src/exceptions/OFUnlockFailedException.h +++ src/exceptions/OFUnlockFailedException.h @@ -26,10 +26,11 @@ */ @interface OFUnlockFailedException: OFException { id _Nullable _lock; int _errNo; + OF_RESERVE_IVARS(OFUnlockFailedException, 4) } /** * @brief The lock which could not be unlocked. */ Index: src/exceptions/OFUnsupportedProtocolException.h ================================================================== --- src/exceptions/OFUnsupportedProtocolException.h +++ src/exceptions/OFUnsupportedProtocolException.h @@ -15,43 +15,44 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURL; +@class OFURI; /** * @class OFUnsupportedProtocolException \ * OFUnsupportedProtocolException.h \ * ObjFW/OFUnsupportedProtocolException.h * - * @brief An exception indicating that the protocol specified by the URL is not + * @brief An exception indicating that the protocol specified by the URI is not * supported. */ @interface OFUnsupportedProtocolException: OFException { - OFURL *_Nullable _URL; + OFURI *_Nullable _URI; + OF_RESERVE_IVARS(OFUnsupportedProtocolException, 4) } /** - * @brief The URL whose protocol is unsupported. + * @brief The URI whose protocol is unsupported. */ -@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURL *URL; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURI *URI; /** * @brief Creates a new, autoreleased unsupported protocol exception. * - * @param URL The URL whose protocol is unsupported + * @param URI The URI whose protocol is unsupported * @return A new, autoreleased unsupported protocol exception */ -+ (instancetype)exceptionWithURL: (nullable OFURL*)URL; ++ (instancetype)exceptionWithURI: (nullable OFURI*)URI; /** * @brief Initializes an already allocated unsupported protocol exception * - * @param URL The URL whose protocol is unsupported + * @param URI The URI whose protocol is unsupported * @return An initialized unsupported protocol exception */ -- (instancetype)initWithURL: (nullable OFURL*)URL OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithURI: (nullable OFURI*)URI OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFUnsupportedProtocolException.m ================================================================== --- src/exceptions/OFUnsupportedProtocolException.m +++ src/exceptions/OFUnsupportedProtocolException.m @@ -15,40 +15,40 @@ #include "config.h" #import "OFUnsupportedProtocolException.h" #import "OFString.h" -#import "OFURL.h" +#import "OFURI.h" @implementation OFUnsupportedProtocolException -@synthesize URL = _URL; +@synthesize URI = _URI; -+ (instancetype)exceptionWithURL: (OFURL *)URL ++ (instancetype)exceptionWithURI: (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]; - _URL = [URL retain]; + _URI = [URI retain]; return self; } - (void)dealloc { - [_URL release]; + [_URI release]; [super dealloc]; } - (OFString *)description { - if (_URL != nil) + if (_URI != nil) return [OFString stringWithFormat: - @"The protocol of URL %@ is not supported!", _URL]; + @"The protocol of URI %@ is not supported!", _URI]; else return @"The requested protocol is unsupported!"; } @end Index: src/exceptions/OFUnsupportedVersionException.h ================================================================== --- src/exceptions/OFUnsupportedVersionException.h +++ src/exceptions/OFUnsupportedVersionException.h @@ -25,10 +25,11 @@ * protocol is not supported. */ @interface OFUnsupportedVersionException: OFException { OFString *_version; + OF_RESERVE_IVARS(OFUnsupportedVersionException, 4) } /** * @brief The version which is unsupported. */ ADDED src/exceptions/OFWaitForConditionFailedException.h Index: src/exceptions/OFWaitForConditionFailedException.h ================================================================== --- src/exceptions/OFWaitForConditionFailedException.h +++ src/exceptions/OFWaitForConditionFailedException.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 OFWaitForConditionFailedException \ + * OFWaitForConditionFailedException.h \ + * ObjFW/OFWaitForConditionFailedException.h + * + * @brief An exception indicating waiting for a condition failed. + */ +@interface OFWaitForConditionFailedException: OFException +{ + OFCondition *_condition; + int _errNo; + OF_RESERVE_IVARS(OFWaitForConditionFailedException, 4) +} + +/** + * @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 ADDED src/exceptions/OFWaitForConditionFailedException.m Index: src/exceptions/OFWaitForConditionFailedException.m ================================================================== --- src/exceptions/OFWaitForConditionFailedException.m +++ src/exceptions/OFWaitForConditionFailedException.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 "OFWaitForConditionFailedException.h" +#import "OFString.h" +#import "OFCondition.h" + +@implementation OFWaitForConditionFailedException +@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 Index: src/exceptions/OFWriteFailedException.h ================================================================== --- src/exceptions/OFWriteFailedException.h +++ src/exceptions/OFWriteFailedException.h @@ -24,10 +24,11 @@ * @brief An exception indicating that writing to an object failed. */ @interface OFWriteFailedException: OFReadOrWriteFailedException { size_t _bytesWritten; + OF_RESERVE_IVARS(OFWriteFailedException, 4) } /** * @brief The number of bytes already written before the write failed. * Index: src/macros.h ================================================================== --- src/macros.h +++ src/macros.h @@ -383,17 +383,18 @@ } \ \ abort(); #endif #ifdef __clang__ -# define OF_DEALLOC_UNSUPPORTED \ - [self doesNotRecognizeSelector: _cmd]; \ - \ - abort(); \ - \ - _Pragma("clang diagnostic push ignored \"-Wunreachable-code\""); \ - [super dealloc]; /* Get rid of a stupid warning */ \ +# define OF_DEALLOC_UNSUPPORTED \ + [self doesNotRecognizeSelector: _cmd]; \ + \ + abort(); \ + \ + _Pragma("clang diagnostic push"); \ + _Pragma("clang diagnostic ignored \"-Wunreachable-code\""); \ + [super dealloc]; /* Get rid of a stupid warning */ \ _Pragma("clang diagnostic pop"); #else # define OF_DEALLOC_UNSUPPORTED \ [self doesNotRecognizeSelector: _cmd]; \ \ Index: src/objfw-defs.h.in ================================================================== --- src/objfw-defs.h.in +++ src/objfw-defs.h.in @@ -1,9 +1,10 @@ #undef OF_APPLE_RUNTIME #undef OF_BIG_ENDIAN #undef OF_FLOAT_BIG_ENDIAN #undef OF_HAVE_AFUNIX_H +#undef OF_HAVE_APPLETALK #undef OF_HAVE_ATOMIC_BUILTINS #undef OF_HAVE_ATOMIC_OPS #undef OF_HAVE_BUILTIN_BSWAP16 #undef OF_HAVE_BUILTIN_BSWAP32 #undef OF_HAVE_BUILTIN_BSWAP64 @@ -14,25 +15,27 @@ #undef OF_HAVE_IPV6 #undef OF_HAVE_IPX #undef OF_HAVE_LIMITS_H #undef OF_HAVE_LINK #undef OF_HAVE_MAX_ALIGN_T +#undef OF_HAVE_NETATALK_AT_H +#undef OF_HAVE_NETAT_APPLETALK_H #undef OF_HAVE_NETINET_IN_H #undef OF_HAVE_NETINET_TCP_H #undef OF_HAVE_NETIPX_IPX_H #undef OF_HAVE_OSATOMIC #undef OF_HAVE_OSATOMIC_64 #undef OF_HAVE_PIPE #undef OF_HAVE_PLEDGE #undef OF_HAVE_PLUGINS -#undef OF_HAVE_SUBPROCESSES #undef OF_HAVE_PTHREADS #undef OF_HAVE_PTHREAD_SPINLOCKS #undef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES #undef OF_HAVE_SCHED_YIELD #undef OF_HAVE_SOCKETS #undef OF_HAVE_STDNORETURN +#undef OF_HAVE_SUBPROCESSES #undef OF_HAVE_SYMLINK #undef OF_HAVE_SYNC_BUILTINS #undef OF_HAVE_SYS_SOCKET_H #undef OF_HAVE_SYS_TYPES_H #undef OF_HAVE_SYS_UN_H Index: src/platform/AmigaOS/OFString+PathAdditions.m ================================================================== --- src/platform/AmigaOS/OFString+PathAdditions.m +++ src/platform/AmigaOS/OFString+PathAdditions.m @@ -15,11 +15,11 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURLHandler.h" +#import "OFFileURIHandler.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -153,11 +153,11 @@ objc_autoreleasePoolPop(pool); return @""; } components = [components objectsInRange: - OFRangeMake(0, components.count - 1)]; + OFMakeRange(0, components.count - 1)]; ret = [OFString pathWithComponents: components]; [ret retain]; objc_autoreleasePoolPop(pool); return [ret autorelease]; @@ -233,11 +233,11 @@ } if ([component isEqual: @"/"] && parent != nil && ![parent isEqual: @"/"]) { [array removeObjectsInRange: - OFRangeMake(i - 1, 2)]; + OFMakeRange(i - 1, 2)]; done = false; break; } } @@ -291,14 +291,15 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || [self hasSuffix: @":"] || - [OFFileURLHandler of_directoryExistsAtPath: self]); + [OFFileURIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURLPathWithURLEncodedHost: (OFString **)URLEncodedHost +- (OFString *)of_pathToURIPathWithPercentEncodedHost: + (OFString **)percentEncodedHost { OFArray OF_GENERIC(OFString *) *components = self.pathComponents; OFMutableString *ret = [OFMutableString string]; for (OFString *component in components) { @@ -316,11 +317,12 @@ [ret makeImmutable]; return ret; } -- (OFString *)of_URLPathToPathWithURLEncodedHost: (OFString *)URLEncodedHost +- (OFString *)of_URIPathToPathWithPercentEncodedHost: + (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"]) path = [path substringToIndex: path.length - 1]; @@ -349,10 +351,10 @@ } return [OFString pathWithComponents: components]; } -- (OFString *)of_pathComponentToURLPathComponent +- (OFString *)of_pathComponentToURIPathComponent { return self; } @end Index: src/platform/POSIX/OFString+PathAdditions.m ================================================================== --- src/platform/POSIX/OFString+PathAdditions.m +++ src/platform/POSIX/OFString+PathAdditions.m @@ -15,11 +15,11 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURLHandler.h" +#import "OFFileURIHandler.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -275,11 +275,11 @@ } if ([component isEqual: @".."] && parent != nil && ![parent isEqual: @".."]) { [array removeObjectsInRange: - OFRangeMake(i - 1, 2)]; + OFMakeRange(i - 1, 2)]; done = false; break; } } @@ -336,28 +336,30 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || - [OFFileURLHandler of_directoryExistsAtPath: self]); + [OFFileURIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURLPathWithURLEncodedHost: (OFString **)URLEncodedHost +- (OFString *)of_pathToURIPathWithPercentEncodedHost: + (OFString **)percentEncodedHost { return self; } -- (OFString *)of_URLPathToPathWithURLEncodedHost: (OFString *)URLEncodedHost +- (OFString *)of_URIPathToPathWithPercentEncodedHost: + (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"]) path = [path substringToIndex: path.length - 1]; return path; } -- (OFString *)of_pathComponentToURLPathComponent +- (OFString *)of_pathComponentToURIPathComponent { return self; } @end Index: src/platform/POSIX/OFSubprocess.m ================================================================== --- src/platform/POSIX/OFSubprocess.m +++ src/platform/POSIX/OFSubprocess.m @@ -361,12 +361,14 @@ return _writePipe[1]; } - (void)closeForWriting { - if (_writePipe[1] != -1) - close(_writePipe[1]); + if (_readPipe[0] == -1 || _writePipe[1] == -1) + @throw [OFNotOpenException exceptionWithObject: self]; + + close(_writePipe[1]); _writePipe[1] = -1; } - (void)close Index: src/platform/Windows/OFString+PathAdditions.m ================================================================== --- src/platform/Windows/OFString+PathAdditions.m +++ src/platform/Windows/OFString+PathAdditions.m @@ -20,12 +20,12 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURLHandler.h" -#import "OFURL.h" +#import "OFFileURIHandler.h" +#import "OFURI.h" #import "OFInvalidFormatException.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -201,11 +201,11 @@ objc_autoreleasePoolPop(pool); return @"."; } components = [components objectsInRange: - OFRangeMake(0, components.count - 1)]; + OFMakeRange(0, components.count - 1)]; ret = [OFString pathWithComponents: components]; [ret retain]; objc_autoreleasePoolPop(pool); return [ret autorelease]; @@ -286,11 +286,11 @@ ![parent hasSuffix: @":"] && ![parent hasSuffix: @":\\"] && ![parent hasSuffix: @"://"] && (![parent hasPrefix: @"\\"] || i != 1)) { [array removeObjectsInRange: - OFRangeMake(i - 1, 2)]; + OFMakeRange(i - 1, 2)]; done = false; break; } } @@ -341,38 +341,40 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"\\"] || [self hasSuffix: @"/"] || - [OFFileURLHandler of_directoryExistsAtPath: self]); + [OFFileURIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURLPathWithURLEncodedHost: (OFString **)URLEncodedHost +- (OFString *)of_pathToURIPathWithPercentEncodedHost: + (OFString **)percentEncodedHost { OFString *path = self; if ([path hasPrefix: @"\\\\"]) { OFArray *components = path.pathComponents; if (components.count < 2) @throw [OFInvalidFormatException exception]; - *URLEncodedHost = [[components objectAtIndex: 1] - stringByURLEncodingWithAllowedCharacters: - [OFCharacterSet URLHostAllowedCharacterSet]]; + *percentEncodedHost = [[components objectAtIndex: 1] + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet URIHostAllowedCharacterSet]]; path = [OFString pathWithComponents: [components - objectsInRange: OFRangeMake(2, components.count - 2)]]; + objectsInRange: OFMakeRange(2, components.count - 2)]]; } path = [path stringByReplacingOccurrencesOfString: @"\\" withString: @"/"]; - path = [path stringByPrependingString: @"/"]; + path = [@"/" stringByAppendingString: path]; return path; } -- (OFString *)of_URLPathToPathWithURLEncodedHost: (OFString *)URLEncodedHost +- (OFString *)of_URIPathToPathWithPercentEncodedHost: + (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"] && ![path hasSuffix: @":/"]) @@ -380,12 +382,13 @@ path = [path substringFromIndex: 1]; path = [path stringByReplacingOccurrencesOfString: @"/" withString: @"\\"]; - if (URLEncodedHost != nil) { - OFString *host = [URLEncodedHost stringByURLDecoding]; + if (percentEncodedHost != nil) { + OFString *host = [percentEncodedHost + stringByRemovingPercentEncoding]; if (path.length == 0) path = [OFString stringWithFormat: @"\\\\%@", host]; else path = [OFString stringWithFormat: @"\\\\%@\\%@", @@ -393,11 +396,11 @@ } return path; } -- (OFString *)of_pathComponentToURLPathComponent +- (OFString *)of_pathComponentToURIPathComponent { return [self stringByReplacingOccurrencesOfString: @"\\" withString: @"/"]; } @end Index: src/platform/Windows/OFSubprocess.m ================================================================== --- src/platform/Windows/OFSubprocess.m +++ src/platform/Windows/OFSubprocess.m @@ -153,11 +153,11 @@ withString: @"\\\\\""]; [argumentsString replaceOccurrencesOfString: @"\"" withString: @"\\\""]; if ([argumentsString containsString: @" "]) { - [argumentsString prependString: @"\""]; + [argumentsString insertString: @"\"" atIndex: 0]; [argumentsString appendString: @"\""]; } for (OFString *argument in arguments) { OFMutableString *tmp = @@ -368,12 +368,14 @@ return (size_t)bytesWritten; } - (void)closeForWriting { - if (_writePipe[1] != NULL) - CloseHandle(_writePipe[1]); + if (_readPipe[0] == NULL || _writePipe[1] == NULL) + @throw [OFNotOpenException exceptionWithObject: self]; + + CloseHandle(_writePipe[1]); _writePipe[1] = NULL; } - (void)close ADDED src/platform/Windows/OFWin32ConsoleStdIOStream.m Index: src/platform/Windows/OFWin32ConsoleStdIOStream.m ================================================================== --- src/platform/Windows/OFWin32ConsoleStdIOStream.m +++ src/platform/Windows/OFWin32ConsoleStdIOStream.m @@ -0,0 +1,603 @@ +/* + * 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/platform/libfat/OFString+PathAdditions.m ================================================================== --- src/platform/libfat/OFString+PathAdditions.m +++ src/platform/libfat/OFString+PathAdditions.m @@ -15,11 +15,11 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURLHandler.h" +#import "OFFileURIHandler.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -279,11 +279,11 @@ } if ([component isEqual: @".."] && parent != nil && ![parent isEqual: @".."]) { [array removeObjectsInRange: - OFRangeMake(i - 1, 2)]; + OFMakeRange(i - 1, 2)]; done = false; break; } } @@ -337,28 +337,30 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || - [OFFileURLHandler of_directoryExistsAtPath: self]); + [OFFileURIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURLPathWithURLEncodedHost: (OFString **)URLEncodedHost +- (OFString *)of_pathToURIPathWithPercentEncodedHost: + (OFString **)percentEncodedHost { - return [self stringByPrependingString: @"/"]; + return [@"/" stringByAppendingString: self]; } -- (OFString *)of_URLPathToPathWithURLEncodedHost: (OFString *)URLEncodedHost +- (OFString *)of_URIPathToPathWithPercentEncodedHost: + (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"]) path = [path substringToIndex: path.length - 1]; return [path substringFromIndex: 1]; } -- (OFString *)of_pathComponentToURLPathComponent +- (OFString *)of_pathComponentToURIPathComponent { return self; } @end Index: src/runtime/Makefile ================================================================== --- src/runtime/Makefile +++ src/runtime/Makefile @@ -29,15 +29,17 @@ selector.m \ sparsearray.m \ static-instances.m \ synchronized.m \ tagged-pointer.m \ - ${USE_SRCS_THREADS} + ${USE_SRCS_THREADS} \ + ${USE_SRCS_WINDOWS} SRCS_THREADS = OFOnce.m \ OFPlainMutex.m \ OFTLSKey.m \ threading.m +SRCS_WINDOWS = versioninfo.rc INCLUDES = ObjFWRT.h includesubdir = ObjFWRT OBJS_EXTRA = lookup-asm/lookup-asm.a LIB_OBJS_EXTRA = lookup-asm/lookup-asm.lib.a @@ -54,5 +56,10 @@ -DOBJFWRT_LIB_MAJOR=${OBJFWRT_LIB_MAJOR} \ -DOBJFWRT_LIB_MINOR=${OBJFWRT_LIB_MINOR} AMIGA_LIB_CFLAGS += -DOBJC_COMPILING_AMIGA_LIBRARY LD = ${OBJC} FRAMEWORK_LIBS = ${LIBS} +RCFLAGS = --use-temp-file \ + -DOBJFWRT_LIB_MAJOR=${OBJFWRT_LIB_MAJOR} \ + -DOBJFWRT_LIB_MINOR=${OBJFWRT_LIB_MINOR} \ + -DOBJFWRT_LIB_VERSION=\"${OBJFWRT_LIB_MAJOR}.${OBJFWRT_LIB_MINOR}\" \ + -DOBJFWRT_SHARED_LIB=\"${OBJFWRT_SHARED_LIB}\" Index: src/runtime/amiga-end.m ================================================================== --- src/runtime/amiga-end.m +++ src/runtime/amiga-end.m @@ -13,11 +13,11 @@ * file. */ #include "config.h" -#import "platform.h" +#include "platform.h" #ifdef OF_MORPHOS __asm__ ( ".section .eh_frame, \"aw\"\n" " .long 0\n" Index: src/runtime/amiga-library.m ================================================================== --- src/runtime/amiga-library.m +++ src/runtime/amiga-library.m @@ -41,11 +41,11 @@ # define OBJC_M68K_REG(reg) #endif /* This always needs to be the first thing in the file. */ int -_start() +_start(void) { return -1; } #ifdef OF_AMIGAOS_M68K @@ -292,13 +292,13 @@ if (base->parent != NULL) { struct ObjFWRTBase *parent; #ifdef OF_AMIGAOS_M68K if (base->initialized) - for (size_t i = 1; i <= (size_t)_EH_FRAME_BEGINS__; i++) - libc.__deregister_frame_info( - (&_EH_FRAME_BEGINS__)[i]); + for (void *const *frame = _EH_FRAME_BEGINS__; + *frame != NULL;) + libc.__deregister_frame_info(*frame++); #endif parent = base->parent; FreeMem(base->dataSeg - DATA_OFFSET, getDataSize()); @@ -340,16 +340,13 @@ return false; memcpy(&libc, libc_, sizeof(libc)); #ifdef OF_AMIGAOS_M68K - if ((size_t)_EH_FRAME_BEGINS__ != (size_t)_EH_FRAME_OBJECTS__) - return false; - - for (size_t i = 1; i <= (size_t)_EH_FRAME_BEGINS__; i++) - libc.__register_frame_info((&_EH_FRAME_BEGINS__)[i], - (&_EH_FRAME_OBJECTS__)[i]); + for (void *const *frame = _EH_FRAME_OBJECTS__, + **object = _EH_FRAME_OBJECTS__; *frame != NULL;) + libc.__register_frame_info(*frame++, *object++); iter0 = &__CTOR_LIST__[1]; #elif defined(OF_MORPHOS) __asm__ ( "lis %0, __EH_FRAME_BEGIN__@ha\n\t" Index: src/runtime/autorelease.m ================================================================== --- src/runtime/autorelease.m +++ src/runtime/autorelease.m @@ -61,11 +61,11 @@ OBJC_ERROR("Failed to create TLS keys!"); } #endif void * -objc_autoreleasePoolPush() +objc_autoreleasePoolPush(void) { #if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) uintptr_t count = (uintptr_t)OFTLSKeyGet(countKey); #endif return (void *)count; Index: src/runtime/class.m ================================================================== --- src/runtime/class.m +++ src/runtime/class.m @@ -403,11 +403,11 @@ objc_globalMutex_unlock(); } static void -processLoadQueue() +processLoadQueue(void) { for (size_t i = 0; i < loadQueueCount; i++) { setUpClass(loadQueue[i]); if (loadQueue[i]->info & OBJC_CLASS_INFO_SETUP) { @@ -778,10 +778,14 @@ objc_updateDTable(class); } Method +#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 7 +/* Work around an ICE in Clang 3.7.0 on Windows/x86 */ +__attribute__((__optnone__)) +#endif class_getInstanceMethod(Class class, SEL selector) { Method method; Class superclass; Index: src/runtime/private.h ================================================================== --- src/runtime/private.h +++ src/runtime/private.h @@ -14,11 +14,10 @@ */ #include "config.h" #import "macros.h" -#import "platform.h" #if !defined(__has_feature) || !__has_feature(nullability) # ifndef _Nonnull # define _Nonnull # endif Index: src/runtime/static-instances.m ================================================================== --- src/runtime/static-instances.m +++ src/runtime/static-instances.m @@ -93,11 +93,11 @@ } } } void -objc_forgetPendingStaticInstances() +objc_forgetPendingStaticInstances(void) { free(staticInstancesList); staticInstancesList = NULL; staticInstancesCount = 0; } ADDED src/runtime/versioninfo.rc Index: src/runtime/versioninfo.rc ================================================================== --- src/runtime/versioninfo.rc +++ src/runtime/versioninfo.rc @@ -0,0 +1,27 @@ +#include "config.h" +#include "winver.h" + +1 VERSIONINFO + FILEVERSION OBJFWRT_LIB_MAJOR, OBJFWRT_LIB_MINOR, 0, 0 + PRODUCTVERSION OBJFW_VERSION_MAJOR, OBJFW_VERSION_MINOR, 0, 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" { + BLOCK "040904E4" { + VALUE "ProductName", "ObjFW Runtime" + VALUE "ProductVersion", PACKAGE_VERSION + VALUE "FileVersion", OBJFWRT_LIB_VERSION + VALUE "FileDescription", "Objective-C runtime" + VALUE "LegalCopyright", + "(c) 2008-2022 Jonathan Schleifer" + VALUE "InternalName", "ObjFWRT" + VALUE "OriginalFilename", OBJFWRT_SHARED_LIB + } + + } + + BLOCK "VarFileInfo" { + VALUE "Translation", 0x409, 1252 + } +} ADDED src/versioninfo.rc Index: src/versioninfo.rc ================================================================== --- src/versioninfo.rc +++ src/versioninfo.rc @@ -0,0 +1,27 @@ +#include "config.h" +#include "winver.h" + +1 VERSIONINFO + FILEVERSION OBJFW_LIB_MAJOR, OBJFW_LIB_MINOR, 0, 0 + PRODUCTVERSION OBJFW_VERSION_MAJOR, OBJFW_VERSION_MINOR, 0, 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" { + BLOCK "040904E4" { + VALUE "ProductName", "ObjFW" + VALUE "ProductVersion", PACKAGE_VERSION + VALUE "FileVersion", OBJFW_LIB_VERSION + VALUE "FileDescription", "Objective-C framework" + VALUE "LegalCopyright", + "(c) 2008-2022 Jonathan Schleifer" + VALUE "InternalName", "ObjFW" + VALUE "OriginalFilename", OBJFW_SHARED_LIB + } + + } + + BLOCK "VarFileInfo" { + VALUE "Translation", 0x409, 1252 + } +} Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -50,11 +50,11 @@ OFSerializationTests.m \ OFSetTests.m \ OFStreamTests.m \ OFStringTests.m \ OFSystemInfoTests.m \ - OFURLTests.m \ + OFURITests.m \ OFValueTests.m \ OFXMLElementBuilderTests.m \ OFXMLNodeTests.m \ OFXMLParserTests.m \ RuntimeTests.m \ @@ -75,12 +75,14 @@ OFHTTPCookieManagerTests.m \ OFKernelEventObserverTests.m \ OFSocketTests.m \ OFTCPSocketTests.m \ OFUDPSocketTests.m \ + ${USE_SRCS_APPLETALK} \ ${USE_SRCS_IPX} \ ${USE_SRCS_UNIX_SOCKETS} +SRCS_APPLETALK = OFDDPSocketTests.m SRCS_IPX = OFIPXSocketTests.m \ OFSPXSocketTests.m \ OFSPXStreamSocketTests.m SRCS_UNIX_SOCKETS = OFUNIXDatagramSocketTests.m \ OFUNIXStreamSocketTests.m @@ -91,15 +93,15 @@ IOS_TMP ?= /tmp/objfw-test include ../buildsys.mk serialization_xml.m: serialization.xml - ${SHELL} ../utils/objfw-embed serialization.xml /serialization.xml $@ + ${SHELL} ../utils/objfw-embed serialization.xml serialization.xml $@ testfile_bin.m: testfile.bin - ${SHELL} ../utils/objfw-embed testfile.bin /testfile.bin $@ + ${SHELL} ../utils/objfw-embed testfile.bin testfile.bin $@ testfile_ini.m: testfile.ini - ${SHELL} ../utils/objfw-embed testfile.ini /testfile.ini $@ + ${SHELL} ../utils/objfw-embed testfile.ini testfile.ini $@ .PHONY: run run-on-ios run-on-android run: rm -f libobjfw.so.${OBJFW_LIB_MAJOR} rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} Index: tests/OFArrayTests.m ================================================================== --- tests/OFArrayTests.m +++ tests/OFArrayTests.m @@ -184,11 +184,11 @@ TEST(@"-[indexOfObjectIdenticalTo:]", [array2 indexOfObjectIdenticalTo: cArray[1]] == 1) TEST(@"-[objectsInRange:]", - [[array1 objectsInRange: OFRangeMake(1, 2)] isEqual: + [[array1 objectsInRange: OFMakeRange(1, 2)] isEqual: [arrayClass arrayWithObjects: cArray[1], cArray[2], nil]]) TEST(@"-[replaceObject:withObject:]", R([mutableArray1 replaceObject: cArray[1] withObject: cArray[0]]) && [[mutableArray1 objectAtIndex: 0] isEqual: cArray[0]] && @@ -224,11 +224,11 @@ mutableArray2.count == 2 && [[mutableArray2 objectAtIndex: 1] isEqual: cArray[2]]) mutableArray2 = [[array1 mutableCopy] autorelease]; TEST(@"-[removeObjectsInRange:]", - R([mutableArray2 removeObjectsInRange: OFRangeMake(0, 2)]) && + R([mutableArray2 removeObjectsInRange: OFMakeRange(0, 2)]) && mutableArray2.count == 1 && [[mutableArray2 objectAtIndex: 0] isEqual: cArray[2]]) mutableArray2 = [[array1 mutableCopy] autorelease]; [mutableArray2 addObject: @"qux"]; @@ -260,11 +260,11 @@ EXPECT_EXCEPTION(@"Detect out of range in -[objectAtIndex:]", OFOutOfRangeException, [array1 objectAtIndex: array1.count]) EXPECT_EXCEPTION(@"Detect out of range in -[removeObjectsInRange:]", OFOutOfRangeException, [mutableArray1 removeObjectsInRange: - OFRangeMake(0, mutableArray1.count + 1)]) + OFMakeRange(0, mutableArray1.count + 1)]) TEST(@"-[componentsJoinedByString:]", (array2 = [arrayClass arrayWithObjects: @"", @"a", @"b", @"c", nil]) && [[array2 componentsJoinedByString: @" "] isEqual: @" a b c"] && @@ -429,20 +429,20 @@ [OFNumber numberWithInt: 3], [OFNumber numberWithInt: 6], nil]] && [[[arrayClass arrayWithObjects: @"1", @"2", nil] valueForKey: @"@count"] isEqual: [OFNumber numberWithInt: 2]]) mutableArray1 = [mutableArrayClass arrayWithObjects: - [OFMutableURL URLWithString: @"http://foo.bar/"], - [OFMutableURL URLWithString: @"http://bar.qux/"], - [OFMutableURL URLWithString: @"http://qux.quxqux/"], nil]; + [OFMutableURI URIWithString: @"http://foo.bar/"], + [OFMutableURI URIWithString: @"http://bar.qux/"], + [OFMutableURI URIWithString: @"http://qux.quxqux/"], nil]; TEST(@"-[setValue:forKey:]", R([mutableArray1 setValue: [OFNumber numberWithShort: 1234] forKey: @"port"]) && [mutableArray1 isEqual: [arrayClass arrayWithObjects: - [OFURL URLWithString: @"http://foo.bar:1234/"], - [OFURL URLWithString: @"http://bar.qux:1234/"], - [OFURL URLWithString: @"http://qux.quxqux:1234/"], nil]]) + [OFURI URIWithString: @"http://foo.bar:1234/"], + [OFURI URIWithString: @"http://bar.qux:1234/"], + [OFURI URIWithString: @"http://qux.quxqux:1234/"], nil]]) objc_autoreleasePoolPop(pool); } - (void)arrayTests Index: tests/OFCharacterSetTests.m ================================================================== --- tests/OFCharacterSetTests.m +++ tests/OFCharacterSetTests.m @@ -73,11 +73,11 @@ module = @"OFRangeCharacterSet"; TEST(@"+[characterSetWithRange:]", (characterSet = [OFCharacterSet - characterSetWithRange: OFRangeMake('0', 10)]) && + characterSetWithRange: OFMakeRange('0', 10)]) && [characterSet isKindOfClass: [OFRangeCharacterSet class]]) ok = true; for (OFUnichar c = 0; c < 65536; c++) { if (c >= '0' && c <= '9') { ADDED tests/OFDDPSocketTests.m Index: tests/OFDDPSocketTests.m ================================================================== --- tests/OFDDPSocketTests.m +++ tests/OFDDPSocketTests.m @@ -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. + */ + +#include "config.h" + +#include + +#import "TestsAppDelegate.h" + +static OFString *const module = @"OFDDPSocket"; + +@implementation TestsAppDelegate (OFDDPSocketTests) +- (void)DDPSocketTests +{ + void *pool = objc_autoreleasePoolPush(); + OFDDPSocket *sock; + OFSocketAddress address1, address2; + char buffer[5]; + + TEST(@"+[socket]", (sock = [OFDDPSocket socket])) + + @try { + TEST(@"-[bindToNetwork:node:port:]", + R(address1 = [sock bindToNetwork: 0 + node: 0 + port: 0 + protocolType: 11])) + } @catch (OFBindSocketFailedException *e) { + switch (e.errNo) { + case EAFNOSUPPORT: + [OFStdOut setForegroundColor: [OFColor lime]]; + [OFStdOut writeLine: + @"\r[OFDDPSocket] -[bindToNetwork:node:port:" + @"protocolType:] AppleTalk unsupported, skipping " + @"tests"]; + break; + case EADDRNOTAVAIL: + [OFStdOut setForegroundColor: [OFColor lime]]; + [OFStdOut writeLine: + @"\r[OFDDPSocket] -[bindToNetwork:node:port:" + @"protocolType:] AppleTalk not configured, " + @"skipping tests"]; + break; + default: + @throw e; + } + + objc_autoreleasePoolPop(pool); + return; + } + + TEST(@"-[sendBuffer:length:receiver:]", + R([sock sendBuffer: "Hello" length: 5 receiver: &address1])) + + TEST(@"-[receiveIntoBuffer:length:sender:]", + [sock receiveIntoBuffer: buffer length: 5 sender: &address2] == 5 && + memcmp(buffer, "Hello", 5) == 0 && + OFSocketAddressEqual(&address1, &address2) && + OFSocketAddressHash(&address1) == OFSocketAddressHash(&address2)) + + objc_autoreleasePoolPop(pool); +} +@end Index: tests/OFDataTests.m ================================================================== --- tests/OFDataTests.m +++ tests/OFDataTests.m @@ -74,11 +74,11 @@ TEST(@"-[removeLastItem]", R([mutableData removeLastItem]) && mutableData.count == 5 && memcmp(mutableData.items, "abcde", 5) == 0) TEST(@"-[removeItemsInRange:]", - R([mutableData removeItemsInRange: OFRangeMake(1, 2)]) && + R([mutableData removeItemsInRange: OFMakeRange(1, 2)]) && mutableData.count == 3 && memcmp(mutableData.items, "ade", 3) == 0) TEST(@"-[insertItems:atIndex:count:]", R([mutableData insertItems: "bc" atIndex: 1 count: 2]) && mutableData.count == 5 && @@ -88,51 +88,51 @@ range = [data rangeOfData: [OFData dataWithItems: "aa" count: 1 itemSize: 2] options: 0 - range: OFRangeMake(0, 7)]; + range: OFMakeRange(0, 7)]; TEST(@"-[rangeOfData:options:range:] #1", range.location == 0 && range.length == 1) range = [data rangeOfData: [OFData dataWithItems: "aa" count: 1 itemSize: 2] options: OFDataSearchBackwards - range: OFRangeMake(0, 7)]; + range: OFMakeRange(0, 7)]; TEST(@"-[rangeOfData:options:range:] #2", range.location == 5 && range.length == 1) range = [data rangeOfData: [OFData dataWithItems: "ac" count: 1 itemSize: 2] options: 0 - range: OFRangeMake(0, 7)]; + range: OFMakeRange(0, 7)]; TEST(@"-[rangeOfData:options:range:] #3", range.location == 2 && range.length == 1) range = [data rangeOfData: [OFData dataWithItems: "aabb" count: 2 itemSize: 2] options: 0 - range: OFRangeMake(0, 7)]; + range: OFMakeRange(0, 7)]; TEST(@"-[rangeOfData:options:range:] #4", range.location == 5 && range.length == 2) TEST(@"-[rangeOfData:options:range:] #5", R(range = [data rangeOfData: [OFData dataWithItems: "aa" count: 1 itemSize: 2] options: 0 - range: OFRangeMake(1, 6)]) && + range: OFMakeRange(1, 6)]) && range.location == 5 && range.length == 1) range = [data rangeOfData: [OFData dataWithItems: "aa" count: 1 itemSize: 2] options: OFDataSearchBackwards - range: OFRangeMake(0, 5)]; + range: OFMakeRange(0, 5)]; TEST(@"-[rangeOfData:options:range:] #6", range.location == 0 && range.length == 1) EXPECT_EXCEPTION( @"-[rangeOfData:options:range:] failing on different itemSize", @@ -139,32 +139,32 @@ OFInvalidArgumentException, [data rangeOfData: [OFData dataWithItems: "aaa" count: 1 itemSize: 3] options: 0 - range: OFRangeMake(0, 1)]) + range: OFMakeRange(0, 1)]) EXPECT_EXCEPTION( @"-[rangeOfData:options:range:] failing on out of range", OFOutOfRangeException, [data rangeOfData: [OFData dataWithItems: "" count: 0 itemSize: 2] options: 0 - range: OFRangeMake(8, 1)]) + range: OFMakeRange(8, 1)]) TEST(@"-[subdataWithRange:]", - [[data subdataWithRange: OFRangeMake(2, 4)] + [[data subdataWithRange: OFMakeRange(2, 4)] isEqual: [OFData dataWithItems: "accdacaa" count: 4 itemSize: 2]] && - [[mutableData subdataWithRange: OFRangeMake(2, 3)] + [[mutableData subdataWithRange: OFMakeRange(2, 3)] isEqual: [OFData dataWithItems: "cde" count: 3]]) EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #1", OFOutOfRangeException, - [data subdataWithRange: OFRangeMake(7, 1)]) + [data subdataWithRange: OFMakeRange(7, 1)]) EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #2", OFOutOfRangeException, - [mutableData subdataWithRange: OFRangeMake(6, 1)]) + [mutableData subdataWithRange: OFMakeRange(6, 1)]) TEST(@"-[stringByMD5Hashing]", [mutableData.stringByMD5Hashing isEqual: @"ab56b4d92b40713acc5af89985d4b786"]) @@ -216,13 +216,13 @@ OFOutOfRangeException, [mutableData addItems: raw[0] count: SIZE_MAX]) EXPECT_EXCEPTION(@"Detect out of range in -[removeItemsInRange:]", OFOutOfRangeException, - [mutableData removeItemsInRange: OFRangeMake(mutableData.count, 1)]) + [mutableData removeItemsInRange: OFMakeRange(mutableData.count, 1)]) OFFreeMemory(raw[0]); OFFreeMemory(raw[1]); objc_autoreleasePoolPop(pool); } @end Index: tests/OFDictionaryTests.m ================================================================== --- tests/OFDictionaryTests.m +++ tests/OFDictionaryTests.m @@ -249,15 +249,10 @@ TEST(@"Detection of mutation during Fast Enumeration", ok) [mutableDict removeObjectForKey: @""]; - TEST(@"-[stringByURLEncoding]", - [[[OFDictionary dictionaryWithKeysAndObjects: @"foo", @"bar", - @"q&x", @"q=x", nil] - stringByURLEncoding] isEqual: @"q%26x=q%3Dx&foo=bar"]) - #ifdef OF_HAVE_BLOCKS { __block size_t j = 0; __block bool blockOk = true; Index: tests/OFHMACTests.m ================================================================== --- tests/OFHMACTests.m +++ tests/OFHMACTests.m @@ -49,13 +49,12 @@ @implementation TestsAppDelegate (OFHMACTests) - (void)HMACTests { void *pool = objc_autoreleasePoolPush(); - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; OFHMAC *HMACMD5, *HMACSHA1, *HMACRMD160; OFHMAC *HMACSHA256, *HMACSHA384, *HMACSHA512; TEST(@"+[HMACWithHashClass:] with MD5", (HMACMD5 = [OFHMAC HMACWithHashClass: [OFMD5Hash class] Index: tests/OFHTTPClientTests.m ================================================================== --- tests/OFHTTPClientTests.m +++ tests/OFHTTPClientTests.m @@ -99,11 +99,11 @@ - (void)HTTPClientTests { void *pool = objc_autoreleasePoolPush(); HTTPClientTestsServer *server; - OFURL *URL; + OFURI *URI; OFHTTPClient *client; OFHTTPRequest *request; OFData *data; condition = [OFCondition condition]; @@ -114,17 +114,17 @@ [server start]; [condition wait]; [condition unlock]; - URL = [OFURL URLWithString: + URI = [OFURI URIWithString: [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo", server->_port]]; TEST(@"-[asyncPerformRequest:]", (client = [OFHTTPClient client]) && (client.delegate = self) && - (request = [OFHTTPRequest requestWithURL: URL]) && + (request = [OFHTTPRequest requestWithURI: URI]) && (request.headers = [OFDictionary dictionaryWithObject: @"5" forKey: @"Content-Length"]) && R([client asyncPerformRequest: request])) Index: tests/OFHTTPCookieManagerTests.m ================================================================== --- tests/OFHTTPCookieManagerTests.m +++ tests/OFHTTPCookieManagerTests.m @@ -22,75 +22,75 @@ @implementation TestsAppDelegate (OFHTTPCookieManagerTests) - (void)HTTPCookieManagerTests { void *pool = objc_autoreleasePoolPush(); OFHTTPCookieManager *manager = [OFHTTPCookieManager manager]; - OFURL *URL1, *URL2, *URL3, *URL4; + OFURI *URI1, *URI2, *URI3, *URI4; OFHTTPCookie *cookie1, *cookie2, *cookie3, *cookie4, *cookie5; - URL1 = [OFURL URLWithString: @"http://nil.im/foo"]; - URL2 = [OFURL URLWithString: @"https://nil.im/foo/bar"]; - URL3 = [OFURL URLWithString: @"https://test.nil.im/foo/bar"]; - URL4 = [OFURL URLWithString: @"http://webkeks.org/foo/bar"]; + URI1 = [OFURI URIWithString: @"http://nil.im/foo"]; + URI2 = [OFURI URIWithString: @"https://nil.im/foo/bar"]; + URI3 = [OFURI URIWithString: @"https://test.nil.im/foo/bar"]; + URI4 = [OFURI URIWithString: @"http://webkeks.org/foo/bar"]; cookie1 = [OFHTTPCookie cookieWithName: @"test" value: @"1" domain: @"nil.im"]; - TEST(@"-[addCookie:forURL:] #1", - R([manager addCookie: cookie1 forURL: URL1])) + TEST(@"-[addCookie:forURI:] #1", + R([manager addCookie: cookie1 forURI: URI1])) - TEST(@"-[cookiesForURL:] #1", - [[manager cookiesForURL: URL1] isEqual: + TEST(@"-[cookiesForURI:] #1", + [[manager cookiesForURI: URI1] isEqual: [OFArray arrayWithObject: cookie1]]) cookie2 = [OFHTTPCookie cookieWithName: @"test" value: @"2" domain: @"webkeks.org"]; - TEST(@"-[addCookie:forURL:] #2", - R([manager addCookie: cookie2 forURL: URL1])) + TEST(@"-[addCookie:forURI:] #2", + R([manager addCookie: cookie2 forURI: URI1])) - TEST(@"-[cookiesForURL:] #2", - [[manager cookiesForURL: URL1] isEqual: + TEST(@"-[cookiesForURI:] #2", + [[manager cookiesForURI: URI1] isEqual: [OFArray arrayWithObject: cookie1]] && - [[manager cookiesForURL: URL4] isEqual: [OFArray array]]) + [[manager cookiesForURI: URI4] isEqual: [OFArray array]]) cookie3 = [OFHTTPCookie cookieWithName: @"test" value: @"3" domain: @"nil.im"]; cookie3.secure = true; - TEST(@"-[addCookie:forURL:] #3", - R([manager addCookie: cookie3 forURL: URL2])) + TEST(@"-[addCookie:forURI:] #3", + R([manager addCookie: cookie3 forURI: URI2])) - TEST(@"-[cookiesForURL:] #3", - [[manager cookiesForURL: URL2] isEqual: + TEST(@"-[cookiesForURI:] #3", + [[manager cookiesForURI: URI2] isEqual: [OFArray arrayWithObject: cookie3]] && - [[manager cookiesForURL: URL1] isEqual: [OFArray array]]) + [[manager cookiesForURI: URI1] isEqual: [OFArray array]]) cookie3.expires = [OFDate dateWithTimeIntervalSinceNow: -1]; cookie4 = [OFHTTPCookie cookieWithName: @"test" value: @"4" domain: @"nil.im"]; cookie4.domain = @".nil.im"; - TEST(@"-[addCookie:forURL:] #4", - R([manager addCookie: cookie4 forURL: URL2])) + TEST(@"-[addCookie:forURI:] #4", + R([manager addCookie: cookie4 forURI: URI2])) - TEST(@"-[cookiesForURL:] #4", - [[manager cookiesForURL: URL2] isEqual: + TEST(@"-[cookiesForURI:] #4", + [[manager cookiesForURI: URI2] isEqual: [OFArray arrayWithObject: cookie4]] && - [[manager cookiesForURL: URL3] isEqual: + [[manager cookiesForURI: URI3] isEqual: [OFArray arrayWithObject: cookie4]]) cookie5 = [OFHTTPCookie cookieWithName: @"bar" value: @"5" domain: @"test.nil.im"]; - TEST(@"-[addCookie:forURL:] #5", - R([manager addCookie: cookie5 forURL: URL1])) + TEST(@"-[addCookie:forURI:] #5", + R([manager addCookie: cookie5 forURI: URI1])) - TEST(@"-[cookiesForURL:] #5", - [[manager cookiesForURL: URL1] isEqual: + TEST(@"-[cookiesForURI:] #5", + [[manager cookiesForURI: URI1] isEqual: [OFArray arrayWithObject: cookie4]] && - [[manager cookiesForURL: URL3] isEqual: + [[manager cookiesForURI: URI3] isEqual: [OFArray arrayWithObjects: cookie4, cookie5, nil]]) TEST(@"-[purgeExpiredCookies]", [manager.cookies isEqual: [OFArray arrayWithObjects: cookie3, cookie4, cookie5, nil]] && Index: tests/OFHTTPCookieTests.m ================================================================== --- tests/OFHTTPCookieTests.m +++ tests/OFHTTPCookieTests.m @@ -21,30 +21,30 @@ @implementation TestsAppDelegate (OFHTTPCookieTests) - (void)HTTPCookieTests { void *pool = objc_autoreleasePoolPush(); - OFURL *URL = [OFURL URLWithString: @"http://nil.im"]; + OFURI *URI = [OFURI URIWithString: @"http://nil.im"]; OFHTTPCookie *cookie1, *cookie2; OFArray OF_GENERIC(OFHTTPCookie *) *cookies; cookie1 = [OFHTTPCookie cookieWithName: @"foo" value: @"bar" domain: @"nil.im"]; - TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #1", + TEST(@"+[cookiesWithResponseHeaderFields:forURI:] #1", [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary dictionaryWithObject: @"foo=bar" - forKey: @"Set-Cookie"] forURL: URL] + forKey: @"Set-Cookie"] forURI: URI] isEqual: [OFArray arrayWithObject: cookie1]]) cookie2 = [OFHTTPCookie cookieWithName: @"qux" value: @"cookie" domain: @"nil.im"]; - TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #2", + TEST(@"+[cookiesWithResponseHeaderFields:forURI:] #2", [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary dictionaryWithObject: @"foo=bar,qux=cookie" - forKey: @"Set-Cookie"] forURL: URL] + forKey: @"Set-Cookie"] forURI: URI] isEqual: [OFArray arrayWithObjects: cookie1, cookie2, nil]]) cookie1.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890]; cookie2.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890]; cookie1.path = @"/x"; @@ -52,17 +52,17 @@ cookie2.path = @"/objfw"; cookie2.secure = true; cookie2.HTTPOnly = true; [cookie2.extensions addObject: @"foo"]; [cookie2.extensions addObject: @"bar"]; - TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #3", + TEST(@"+[cookiesWithResponseHeaderFields:forURI:] #3", [(cookies = [OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary dictionaryWithObject: @"foo=bar; Expires=Fri, 13 Feb 2009 23:31:30 GMT; Path=/x," @"qux=cookie; Expires=Fri, 13 Feb 2009 23:31:30 GMT; " @"Domain=webkeks.org; Path=/objfw; Secure; HTTPOnly; foo; bar" - forKey: @"Set-Cookie"] forURL: URL]) isEqual: + forKey: @"Set-Cookie"] forURI: URI]) isEqual: [OFArray arrayWithObjects: cookie1, cookie2, nil]]) TEST(@"+[requestHeaderFieldsWithCookies:]", [[OFHTTPCookie requestHeaderFieldsWithCookies: cookies] isEqual: [OFDictionary dictionaryWithObject: @"foo=bar; qux=cookie" Index: tests/OFINIFileTests.m ================================================================== --- tests/OFINIFileTests.m +++ tests/OFINIFileTests.m @@ -41,23 +41,23 @@ @"bool=false\r\n" @"float=0.25\r\n" @"array1=foo\r\n" @"array1=bar\r\n" @"double=0.75\r\n"; - OFURL *URL; + OFURI *URI; OFINIFile *file; OFINICategory *tests, *foobar, *types; OFArray *array; #if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS) - OFURL *writeURL; + OFURI *writeURI; #endif module = @"OFINIFile"; - URL = [OFURL URLWithString: @"objfw-embedded:///testfile.ini"]; - TEST(@"+[fileWithURL:encoding:]", - (file = [OFINIFile fileWithURL: URL + URI = [OFURI URIWithString: @"embedded:testfile.ini"]; + TEST(@"+[fileWithURI:encoding:]", + (file = [OFINIFile fileWithURI: URI encoding: OFStringEncodingCodepage437])) tests = [file categoryForName: @"tests"]; foobar = [file categoryForName: @"foobar"]; types = [file categoryForName: @"types"]; @@ -114,22 +114,22 @@ module = @"OFINIFile"; /* FIXME: Find a way to write files on Nintendo DS */ #if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS) - writeURL = [[OFSystemInfo temporaryDirectoryURL] - URLByAppendingPathComponent: @"objfw-tests.ini" + writeURI = [[OFSystemInfo temporaryDirectoryURI] + URIByAppendingPathComponent: @"objfw-tests.ini" isDirectory: false]; TEST(@"-[writeToFile:encoding:]", - R([file writeToURL: writeURL + R([file writeToURI: writeURI encoding: OFStringEncodingCodepage437]) && - [[OFString stringWithContentsOfURL: writeURL + [[OFString stringWithContentsOfURI: writeURI encoding: OFStringEncodingCodepage437] isEqual: output]) - [[OFFileManager defaultManager] removeItemAtURL: writeURL]; + [[OFFileManager defaultManager] removeItemAtURI: writeURI]; #else (void)output; #endif objc_autoreleasePoolPop(pool); } @end Index: tests/OFIPXSocketTests.m ================================================================== --- tests/OFIPXSocketTests.m +++ tests/OFIPXSocketTests.m @@ -22,33 +22,39 @@ static OFString *const module = @"OFIPXSocket"; @implementation TestsAppDelegate (OFIPXSocketTests) - (void)IPXSocketTests { + const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; void *pool = objc_autoreleasePoolPush(); OFIPXSocket *sock; OFSocketAddress address1, address2; char buffer[5]; + unsigned char node1[IPX_NODE_LEN], node2[IPX_NODE_LEN]; TEST(@"+[socket]", (sock = [OFIPXSocket socket])) @try { - TEST(@"-[bindToPort:packetType:]", - R(address1 = [sock bindToPort: 0 packetType: 0])) - } @catch (OFBindFailedException *e) { + TEST(@"-[bindToNetwork:node:port:packetType:]", + R(address1 = [sock bindToNetwork: 0 + node: zeroNode + port: 0 + packetType: 0])) + } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFIPXSocket] -[bindToPort:packetType:]: " - @"IPX unsupported, skipping tests"]; + @"\r[OFIPXSocket] -[bindToNetwork:node:port:" + @"packetType:]: IPX unsupported, skipping tests"]; break; case EADDRNOTAVAIL: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFIPXSocket] -[bindToPort:packetType:]: " - @"IPX not configured, skipping tests"]; + @"\r[OFIPXSocket] -[bindToNetwork:node:port:" + @"packetType:]: IPX not configured, skipping " + @"tests"]; break; default: @throw e; } @@ -60,11 +66,14 @@ R([sock sendBuffer: "Hello" length: 5 receiver: &address1])) TEST(@"-[receiveIntoBuffer:length:sender:]", [sock receiveIntoBuffer: buffer length: 5 sender: &address2] == 5 && memcmp(buffer, "Hello", 5) == 0 && - OFSocketAddressEqual(&address1, &address2) && - OFSocketAddressHash(&address1) == OFSocketAddressHash(&address2)) + R(OFSocketAddressGetIPXNode(&address1, node1)) && + R(OFSocketAddressGetIPXNode(&address2, node2)) && + memcmp(node1, node2, IPX_NODE_LEN) == 0 && + OFSocketAddressIPXPort(&address1) == + OFSocketAddressIPXPort(&address2)) objc_autoreleasePoolPop(pool); } @end Index: tests/OFLocaleTests.m ================================================================== --- tests/OFLocaleTests.m +++ tests/OFLocaleTests.m @@ -22,20 +22,20 @@ { void *pool = objc_autoreleasePoolPush(); [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeFormat: @"[OFLocale] Language: %@\n", - [OFLocale language]]; + [OFStdOut writeFormat: @"[OFLocale] Language code: %@\n", + [OFLocale languageCode]]; - [OFStdOut writeFormat: @"[OFLocale] Territory: %@\n", - [OFLocale territory]]; + [OFStdOut writeFormat: @"[OFLocale] Country code: %@\n", + [OFLocale countryCode]]; [OFStdOut writeFormat: @"[OFLocale] Encoding: %@\n", OFStringEncodingName([OFLocale encoding])]; - [OFStdOut writeFormat: @"[OFLocale] Decimal point: %@\n", - [OFLocale decimalPoint]]; + [OFStdOut writeFormat: @"[OFLocale] Decimal separator: %@\n", + [OFLocale decimalSeparator]]; objc_autoreleasePoolPop(pool); } @end Index: tests/OFMD5HashTests.m ================================================================== --- tests/OFMD5HashTests.m +++ tests/OFMD5HashTests.m @@ -27,13 +27,12 @@ @implementation TestsAppDelegate (OFMD5HashTests) - (void)MD5HashTests { void *pool = objc_autoreleasePoolPush(); OFMD5Hash *MD5, *MD5Copy; - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (MD5 = [OFMD5Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFMemoryStreamTests.m ================================================================== --- tests/OFMemoryStreamTests.m +++ tests/OFMemoryStreamTests.m @@ -47,17 +47,17 @@ memcmp(buffer, "ijkl", 5) == 0) TEST(@"-[lowlevelIsAtEndOfStream]", [stream lowlevelIsAtEndOfStream]) TEST(@"-[lowlevelSeekToOffset:whence:]", - [stream lowlevelSeekToOffset: 0 whence: SEEK_CUR] == + [stream lowlevelSeekToOffset: 0 whence: OFSeekCurrent] == sizeof(string) && [stream lowlevelIsAtEndOfStream] && - [stream lowlevelSeekToOffset: 4 whence: SEEK_SET] == 4 && + [stream lowlevelSeekToOffset: 4 whence: OFSeekSet] == 4 && ![stream lowlevelIsAtEndOfStream] && [stream lowlevelReadIntoBuffer: buffer length: 10] == 9 && memcmp(buffer, "efghijkl", 9) == 0 && - [stream lowlevelSeekToOffset: -2 whence: SEEK_END] == 11 && + [stream lowlevelSeekToOffset: -2 whence: OFSeekEnd] == 11 && [stream lowlevelReadIntoBuffer: buffer length: 10] == 2 && memcmp(buffer, "l", 2) == 0 && [stream lowlevelReadIntoBuffer: buffer length: 10] == 0) EXPECT_EXCEPTION(@"Writes rejected on read-only stream", @@ -71,11 +71,11 @@ TEST(@"-[lowlevelWriteBuffer:length:]", [stream lowlevelWriteBuffer: "abcde" length: 5] == 5 && [stream lowlevelWriteBuffer: "fgh" length: 3] == 3 && [stream lowlevelWriteBuffer: "ijkl" length: 5] == 5 && memcmp(data.items, string, data.count) == 0 && - [stream lowlevelSeekToOffset: -3 whence: SEEK_END] == 10) + [stream lowlevelSeekToOffset: -3 whence: OFSeekEnd] == 10) EXPECT_EXCEPTION(@"Out of bound writes rejected", OFWriteFailedException, [stream lowlevelWriteBuffer: "xyz" length: 4]) Index: tests/OFRIPEMD160HashTests.m ================================================================== --- tests/OFRIPEMD160HashTests.m +++ tests/OFRIPEMD160HashTests.m @@ -28,13 +28,12 @@ @implementation TestsAppDelegate (OFRIPEMD160HashTests) - (void)RIPEMD160HashTests { void *pool = objc_autoreleasePoolPush(); OFRIPEMD160Hash *RIPEMD160, *RIPEMD160Copy; - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (RIPEMD160 = [OFRIPEMD160Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA1HashTests.m ================================================================== --- tests/OFSHA1HashTests.m +++ tests/OFSHA1HashTests.m @@ -28,13 +28,12 @@ @implementation TestsAppDelegate (SHA1HashTests) - (void)SHA1HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA1Hash *SHA1, *SHA1Copy; - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA1 = [OFSHA1Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA224HashTests.m ================================================================== --- tests/OFSHA224HashTests.m +++ tests/OFSHA224HashTests.m @@ -28,13 +28,12 @@ @implementation TestsAppDelegate (SHA224HashTests) - (void)SHA224HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA224Hash *SHA224, *SHA224Copy; - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA224 = [OFSHA224Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA256HashTests.m ================================================================== --- tests/OFSHA256HashTests.m +++ tests/OFSHA256HashTests.m @@ -28,13 +28,12 @@ @implementation TestsAppDelegate (SHA256HashTests) - (void)SHA256HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA256Hash *SHA256, *SHA256Copy; - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA256 = [OFSHA256Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA384HashTests.m ================================================================== --- tests/OFSHA384HashTests.m +++ tests/OFSHA384HashTests.m @@ -29,13 +29,12 @@ @implementation TestsAppDelegate (SHA384HashTests) - (void)SHA384HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA384Hash *SHA384, *SHA384Copy; - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA384 = [OFSHA384Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA512HashTests.m ================================================================== --- tests/OFSHA512HashTests.m +++ tests/OFSHA512HashTests.m @@ -30,13 +30,12 @@ @implementation TestsAppDelegate (SHA512HashTests) - (void)SHA512HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA512Hash *SHA512, *SHA512Copy; - OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; - OFStream *file = [[OFURLHandler handlerForURL: URL] - openItemAtURL: URL mode: @"r"]; + OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA512 = [OFSHA512Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSPXSocketTests.m ================================================================== --- tests/OFSPXSocketTests.m +++ tests/OFSPXSocketTests.m @@ -50,11 +50,11 @@ return false; } - (void)socket: (OFSPXSocket *)sock didConnectToNetwork: (uint32_t)network - node: (unsigned char [IPX_NODE_LEN])node + node: (const unsigned char [IPX_NODE_LEN])node port: (uint16_t)port exception: (id)exception { OFEnsure(!_connected); @@ -69,10 +69,11 @@ @end @implementation TestsAppDelegate (OFSPXSocketTests) - (void)SPXSocketTests { + const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; void *pool = objc_autoreleasePoolPush(); OFSPXSocket *sockClient, *sockServer = nil, *sockAccepted; OFSocketAddress address1; const OFSocketAddress *address2; uint32_t network; @@ -83,30 +84,32 @@ TEST(@"+[socket]", (sockClient = [OFSPXSocket socket]) && (sockServer = [OFSPXSocket socket])) @try { - TEST(@"-[bindToPort:]", - R(address1 = [sockServer bindToPort: 0])) - } @catch (OFBindFailedException *e) { + TEST(@"-[bindToNetwork:node:port:]", + R(address1 = [sockServer bindToNetwork: 0 + node: zeroNode + port: 0])) + } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFSPXSocket] -[bindToPort:]: " + @"\r[OFSPXSocket] -[bindToNetwork:node:port:]: " @"IPX unsupported, skipping tests"]; break; case ESOCKTNOSUPPORT: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFSPXSocket] -[bindToPort:]: " + @"\r[OFSPXSocket] -[bindToNetwork:node:port:]: " @"SPX unsupported, skipping tests"]; break; case EADDRNOTAVAIL: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFSPXSocket] -[bindToPort:]: " + @"\r[OFSPXSocket] -[bindToNetwork:node:port:]: " @"IPX not configured, skipping tests"]; break; default: @throw e; } @@ -114,12 +117,12 @@ objc_autoreleasePoolPop(pool); return; } network = OFSocketAddressIPXNetwork(&address1); - OFSocketAddressIPXNode(&address1, node); - port = OFSocketAddressPort(&address1); + OFSocketAddressGetIPXNode(&address1, node); + port = OFSocketAddressIPXPort(&address1); TEST(@"-[listen]", R([sockServer listen])) TEST(@"-[connectToNetwork:node:port:]", R([sockClient connectToNetwork: network node: node port: port])) @@ -134,11 +137,11 @@ memcmp(buffer, "Hello", 5) == 0) TEST(@"-[remoteAddress]", (address2 = sockAccepted.remoteAddress) && OFSocketAddressIPXNetwork(address2) == network && - R(OFSocketAddressIPXNode(address2, node2)) && + R(OFSocketAddressGetIPXNode(address2, node2)) && memcmp(node, node2, IPX_NODE_LEN) == 0) delegate = [[[SPXSocketDelegate alloc] init] autorelease]; sockServer = [OFSPXSocket socket]; @@ -147,19 +150,19 @@ sockClient = [OFSPXSocket socket]; delegate->_expectedClientSocket = sockClient; sockClient.delegate = delegate; - address1 = [sockServer bindToPort: 0]; + address1 = [sockServer bindToNetwork: 0 node: zeroNode port: 0]; [sockServer listen]; [sockServer asyncAccept]; delegate->_expectedNetwork = network = OFSocketAddressIPXNetwork(&address1); - OFSocketAddressIPXNode(&address1, node); + OFSocketAddressGetIPXNode(&address1, node); memcpy(delegate->_expectedNode, node, IPX_NODE_LEN); - delegate->_expectedPort = port = OFSocketAddressPort(&address1); + delegate->_expectedPort = port = OFSocketAddressIPXPort(&address1); @try { [sockClient asyncConnectToNetwork: network node: node port: port]; @@ -167,11 +170,11 @@ [[OFRunLoop mainRunLoop] runUntilDate: [OFDate dateWithTimeIntervalSinceNow: 2]]; TEST(@"-[asyncAccept] & -[asyncConnectToNetwork:node:port:]", delegate->_accepted && delegate->_connected) - } @catch (OFObserveFailedException *e) { + } @catch (OFObserveKernelEventsFailedException *e) { switch (e.errNo) { case ENOTSOCK: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: @"\r[OFSPXSocket] -[asyncAccept] & " Index: tests/OFSPXStreamSocketTests.m ================================================================== --- tests/OFSPXStreamSocketTests.m +++ tests/OFSPXStreamSocketTests.m @@ -50,11 +50,11 @@ return false; } - (void)socket: (OFSPXStreamSocket *)sock didConnectToNetwork: (uint32_t)network - node: (unsigned char [IPX_NODE_LEN])node + node: (const unsigned char [IPX_NODE_LEN])node port: (uint16_t)port exception: (id)exception { OFEnsure(!_connected); @@ -69,10 +69,11 @@ @end @implementation TestsAppDelegate (OFSPXStreamSocketTests) - (void)SPXStreamSocketTests { + const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; void *pool = objc_autoreleasePoolPush(); OFSPXStreamSocket *sockClient, *sockServer = nil, *sockAccepted; OFSocketAddress address1; const OFSocketAddress *address2; uint32_t network; @@ -83,31 +84,34 @@ TEST(@"+[socket]", (sockClient = [OFSPXStreamSocket socket]) && (sockServer = [OFSPXStreamSocket socket])) @try { - TEST(@"-[bindToPort:]", - R(address1 = [sockServer bindToPort: 0])) - } @catch (OFBindFailedException *e) { + TEST(@"-[bindToNetwork:node:port:]", + R(address1 = [sockServer bindToNetwork: 0 + node: zeroNode + port: 0])) + } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFSPXStreamSocket] -[bindToPort:]: " - @"IPX unsupported, skipping tests"]; + @"\r[OFSPXStreamSocket] -[bindToNetwork:node:" + @"port:]: IPX unsupported, skipping tests"]; break; case ESOCKTNOSUPPORT: + case EPROTONOSUPPORT: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFSPXStreamSocket] -[bindToPort:]: " - @"SPX unsupported, skipping tests"]; + @"\r[OFSPXStreamSocket] -[bindToNetwork:node:" + @"port:]: SPX unsupported, skipping tests"]; break; case EADDRNOTAVAIL: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: - @"\r[OFSPXStreamSocket] -[bindToPort:]: " - @"IPX not configured, skipping tests"]; + @"\r[OFSPXStreamSocket] -[bindToNetwork:node:" + @"port:]: IPX not configured, skipping tests"]; break; default: @throw e; } @@ -114,12 +118,12 @@ objc_autoreleasePoolPop(pool); return; } network = OFSocketAddressIPXNetwork(&address1); - OFSocketAddressIPXNode(&address1, node); - port = OFSocketAddressPort(&address1); + OFSocketAddressGetIPXNode(&address1, node); + port = OFSocketAddressIPXPort(&address1); TEST(@"-[listen]", R([sockServer listen])) TEST(@"-[connectToNetwork:node:port:]", R([sockClient connectToNetwork: network node: node port: port])) @@ -137,11 +141,11 @@ memcmp(buffer, "llo", 3) == 0) TEST(@"-[remoteAddress]", (address2 = sockAccepted.remoteAddress) && OFSocketAddressIPXNetwork(address2) == network && - R(OFSocketAddressIPXNode(address2, node2)) && + R(OFSocketAddressGetIPXNode(address2, node2)) && memcmp(node, node2, IPX_NODE_LEN) == 0) delegate = [[[SPXStreamSocketDelegate alloc] init] autorelease]; sockServer = [OFSPXStreamSocket socket]; @@ -150,19 +154,19 @@ sockClient = [OFSPXStreamSocket socket]; delegate->_expectedClientSocket = sockClient; sockClient.delegate = delegate; - address1 = [sockServer bindToPort: 0]; + address1 = [sockServer bindToNetwork: 0 node: zeroNode port: 0]; [sockServer listen]; [sockServer asyncAccept]; delegate->_expectedNetwork = network = OFSocketAddressIPXNetwork(&address1); - OFSocketAddressIPXNode(&address1, node); + OFSocketAddressGetIPXNode(&address1, node); memcpy(delegate->_expectedNode, node, IPX_NODE_LEN); - delegate->_expectedPort = port = OFSocketAddressPort(&address1); + delegate->_expectedPort = port = OFSocketAddressIPXPort(&address1); @try { [sockClient asyncConnectToNetwork: network node: node port: port]; @@ -170,11 +174,11 @@ [[OFRunLoop mainRunLoop] runUntilDate: [OFDate dateWithTimeIntervalSinceNow: 2]]; TEST(@"-[asyncAccept] & -[asyncConnectToNetwork:node:port:]", delegate->_accepted && delegate->_connected) - } @catch (OFObserveFailedException *e) { + } @catch (OFObserveKernelEventsFailedException *e) { switch (e.errNo) { case ENOTSOCK: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: @"\r[OFSPXStreamSocket] -[asyncAccept] & " Index: tests/OFSerializationTests.m ================================================================== --- tests/OFSerializationTests.m +++ tests/OFSerializationTests.m @@ -39,11 +39,11 @@ [dict setObject: @"Hello" forKey: array]; [dict setObject: @"B\"la" forKey: @"Blub"]; [list appendObject: @"Hello"]; [list appendObject: @"Wo\rld!\nHow are you?"]; - [list appendObject: [OFURL URLWithString: @"https://objfw.nil.im/"]]; + [list appendObject: [OFURI URIWithString: @"https://objfw.nil.im/"]]; [list appendObject: [OFXMLElement elementWithXMLString: @""]]; [list appendObject: [OFSet setWithObjects: @"foo", @"foo", @"bar", nil]]; [list appendObject: @@ -59,14 +59,14 @@ UUIDWithUUIDString: @"01234567-89AB-CDEF-FEDC-BA9876543210"]; [dict setObject: @"uuid" forKey: UUID]; TEST(@"-[stringBySerializing]", (string = dict.stringBySerializing) && [string isEqual: - [OFString stringWithContentsOfURL: [OFURL URLWithString: - @"objfw-embedded:///serialization.xml"]]]) + [OFString stringWithContentsOfURI: + [OFURI URIWithString: @"embedded:serialization.xml"]]]) TEST(@"-[objectByDeserializing]", [string.objectByDeserializing isEqual: dict]) objc_autoreleasePoolPop(pool); } @end Index: tests/OFSocketTests.m ================================================================== --- tests/OFSocketTests.m +++ tests/OFSocketTests.m @@ -81,11 +81,11 @@ OFSocketAddressParseIP(@"127.0.a.1", 1234)) EXPECT_EXCEPTION(@"Refusing invalid IPv4 #6", OFInvalidFormatException, OFSocketAddressParseIP(@"127.0..1", 1234)) - TEST(@"Port of an IPv4 address", OFSocketAddressPort(&addr) == 1234) + TEST(@"Port of an IPv4 address", OFSocketAddressIPPort(&addr) == 1234) TEST(@"Converting an IPv4 to a string", [OFSocketAddressString(&addr) isEqual: @"127.0.0.1"]) TEST(@"Parsing an IPv6 #1", @@ -143,11 +143,11 @@ OFSocketAddressParseIP(@"1:2:3:4:5:6:7::", 1234)) EXPECT_EXCEPTION(@"Refusing invalid IPv6 #10", OFInvalidFormatException, OFSocketAddressParseIP(@"1:2", 1234)) - TEST(@"Port of an IPv6 address", OFSocketAddressPort(&addr) == 1234) + TEST(@"Port of an IPv6 address", OFSocketAddressIPPort(&addr) == 1234) SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0) TEST(@"Converting an IPv6 to a string #1", [OFSocketAddressString(&addr) isEqual: @"::"]) Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -292,29 +292,26 @@ [mutableString1 characterAtIndex: 5] == 0x1D11E) EXPECT_EXCEPTION(@"Detect out of range in -[characterAtIndex:]", OFOutOfRangeException, [mutableString1 characterAtIndex: 7]) - TEST(@"-[reverse]", - R([mutableString1 reverse]) && [mutableString1 isEqual: @"3𝄞1€sät"]) - mutableString2 = [mutableStringClass stringWithString: @"abc"]; #ifdef OF_HAVE_UNICODE_TABLES TEST(@"-[uppercase]", R([mutableString1 uppercase]) && - [mutableString1 isEqual: @"3𝄞1€SÄT"] && + [mutableString1 isEqual: @"TÄS€1𝄞3"] && R([mutableString2 uppercase]) && [mutableString2 isEqual: @"ABC"]) TEST(@"-[lowercase]", R([mutableString1 lowercase]) && - [mutableString1 isEqual: @"3𝄞1€sät"] && + [mutableString1 isEqual: @"täs€1𝄞3"] && R([mutableString2 lowercase]) && [mutableString2 isEqual: @"abc"]) TEST(@"-[uppercaseString]", - [[mutableString1 uppercaseString] isEqual: @"3𝄞1€SÄT"]) + [[mutableString1 uppercaseString] isEqual: @"TÄS€1𝄞3"]) TEST(@"-[lowercaseString]", R([mutableString1 uppercase]) && - [[mutableString1 lowercaseString] isEqual: @"3𝄞1€sät"]) + [[mutableString1 lowercaseString] isEqual: @"täs€1𝄞3"]) TEST(@"-[capitalizedString]", [C(@"džbla tdžst TDŽST").capitalizedString isEqual: @"Džbla Tdžst Tdžst"]) #else TEST(@"-[uppercase]", R([mutableString1 uppercase]) && @@ -358,12 +355,12 @@ TEST(@"+[stringWithContentsOfFile:encoding]", (string = [stringClass stringWithContentsOfFile: @"testfile.txt" encoding: OFStringEncodingISO8859_1]) && [string isEqual: @"testäöü"]) - TEST(@"+[stringWithContentsOfURL:encoding]", (string = [stringClass - stringWithContentsOfURL: [OFURL fileURLWithPath: @"testfile.txt"] + TEST(@"+[stringWithContentsOfURI:encoding]", (string = [stringClass + stringWithContentsOfURI: [OFURI fileURIWithPath: @"testfile.txt"] encoding: OFStringEncodingISO8859_1]) && [string isEqual: @"testäöü"]) #endif TEST(@"-[appendUTFString:length:]", @@ -507,11 +504,11 @@ options: OFStringSearchBackwards].location == OFNotFound) EXPECT_EXCEPTION( @"Detect out of range in -[rangeOfString:options:range:]", OFOutOfRangeException, - [C(@"𝄞öö") rangeOfString: @"ö" options: 0 range: OFRangeMake(3, 1)]) + [C(@"𝄞öö") rangeOfString: @"ö" options: 0 range: OFMakeRange(3, 1)]) characterSet = [OFCharacterSet characterSetWithCharactersInString: @"cđ"]; TEST(@"-[indexOfCharacterFromSet:]", [C(@"abcđabcđe") indexOfCharacterFromSet: characterSet] == 2 && @@ -518,40 +515,37 @@ [C(@"abcđabcđë") indexOfCharacterFromSet: characterSet options: OFStringSearchBackwards] == 7 && [C(@"abcđabcđë") indexOfCharacterFromSet: characterSet options: 0 - range: OFRangeMake(4, 4)] == 6 && + range: OFMakeRange(4, 4)] == 6 && [C(@"abcđabcđëf") indexOfCharacterFromSet: characterSet options: 0 - range: OFRangeMake(8, 2)] == OFNotFound) + range: OFMakeRange(8, 2)] == OFNotFound) EXPECT_EXCEPTION( @"Detect out of range in -[indexOfCharacterFromSet:options:range:]", OFOutOfRangeException, [C(@"𝄞öö") indexOfCharacterFromSet: characterSet options: 0 - range: OFRangeMake(3, 1)]) + range: OFMakeRange(3, 1)]) TEST(@"-[substringWithRange:]", - [[C(@"𝄞öö") substringWithRange: OFRangeMake(1, 1)] isEqual: @"ö"] && - [[C(@"𝄞öö") substringWithRange: OFRangeMake(3, 0)] isEqual: @""]) + [[C(@"𝄞öö") substringWithRange: OFMakeRange(1, 1)] isEqual: @"ö"] && + [[C(@"𝄞öö") substringWithRange: OFMakeRange(3, 0)] isEqual: @""]) EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #1", OFOutOfRangeException, - [C(@"𝄞öö") substringWithRange: OFRangeMake(2, 2)]) + [C(@"𝄞öö") substringWithRange: OFMakeRange(2, 2)]) EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #2", OFOutOfRangeException, - [C(@"𝄞öö") substringWithRange: OFRangeMake(4, 0)]) + [C(@"𝄞öö") substringWithRange: OFMakeRange(4, 0)]) TEST(@"-[stringByAppendingString:]", [[C(@"foo") stringByAppendingString: @"bar"] isEqual: @"foobar"]) - TEST(@"-[stringByPrependingString:]", - [[C(@"foo") stringByPrependingString: @"bar"] isEqual: @"barfoo"]) - #ifdef OF_HAVE_FILES # if defined(OF_WINDOWS) TEST(@"-[isAbsolutePath]", C(@"C:\\foo").absolutePath && C(@"a:/foo").absolutePath && !C(@"foo").absolutePath && !C(@"b:foo").absolutePath && @@ -602,10 +596,16 @@ isEqual: @"foo.bar"] && [[C(@"c:\\tmp\\foo") stringByAppendingPathExtension: @"bar"] isEqual: @"c:\\tmp\\foo.bar"] && [[C(@"c:\\tmp\\/\\") stringByAppendingPathExtension: @"bar"] isEqual: @"c:\\tmp.bar"]) +# elif defined(OF_AMIGAOS) + TEST(@"-[stringByAppendingPathExtension:]", + [[C(@"foo") stringByAppendingPathExtension: @"bar"] + isEqual: @"foo.bar"] && + [[C(@"foo/bar") stringByAppendingPathExtension: @"baz"] + isEqual: @"foo/bar.baz"]) # else TEST(@"-[stringByAppendingPathExtension:]", [[C(@"foo") stringByAppendingPathExtension: @"bar"] isEqual: @"foo.bar"] && [[C(@"foo/bar") stringByAppendingPathExtension: @"baz"] @@ -1275,30 +1275,33 @@ @"412a9247c3579a329e53a5dc74676b106755e3394f9454a2d4227324" @"2615d32f80437d61"]) characterSet = [OFCharacterSet characterSetWithCharactersInString: @"abfo'_~$🍏"]; - TEST(@"-[stringByURLEncodingWithAllowedCharacters:]", - [[C(@"foo\"ba'_~$]🍏🍌") stringByURLEncodingWithAllowedCharacters: - characterSet] isEqual: @"foo%22ba'_~$%5D🍏%F0%9F%8D%8C"]) + TEST(@"-[stringByAddingPercentEncodingWithAllowedCharacters:]", + [[C(@"foo\"ba'_~$]🍏🍌") + stringByAddingPercentEncodingWithAllowedCharacters: characterSet] + isEqual: @"foo%22ba'_~$%5D🍏%F0%9F%8D%8C"]) - TEST(@"-[stringByURLDecoding]", - [C(@"foo%20bar%22+%24%F0%9F%8D%8C").stringByURLDecoding + TEST(@"-[stringByRemovingPercentEncoding]", + [C(@"foo%20bar%22+%24%F0%9F%8D%8C").stringByRemovingPercentEncoding isEqual: @"foo bar\"+$🍌"]) TEST(@"-[insertString:atIndex:]", (mutableString1 = [mutableStringClass stringWithString: @"𝄞öööbä€"]) && R([mutableString1 insertString: @"äöü" atIndex: 3]) && [mutableString1 isEqual: @"𝄞ööäöüöbä€"]) - EXPECT_EXCEPTION(@"Detect invalid format in -[stringByURLDecoding] " - @"#1", OFInvalidFormatException, - [C(@"foo%xbar") stringByURLDecoding]) - EXPECT_EXCEPTION(@"Detect invalid encoding in -[stringByURLDecoding] " - @"#2", OFInvalidEncodingException, - [C(@"foo%FFbar") stringByURLDecoding]) + EXPECT_EXCEPTION(@"Detect invalid format in " + @"-[stringByRemovingPercentEncoding] #1", + OFInvalidFormatException, + [C(@"foo%xbar") stringByRemovingPercentEncoding]) + EXPECT_EXCEPTION(@"Detect invalid encoding in " + @"-[stringByRemovingPercentEncoding] #2", + OFInvalidEncodingException, + [C(@"foo%FFbar") stringByRemovingPercentEncoding]) TEST(@"-[setCharacter:atIndex:]", (mutableString1 = [mutableStringClass stringWithString: @"abäde"]) && R([mutableString1 setCharacter: 0xF6 atIndex: 2]) && @@ -1311,49 +1314,49 @@ [mutableString1 isEqual: @"axc€e"]) TEST(@"-[deleteCharactersInRange:]", (mutableString1 = [mutableStringClass stringWithString: @"𝄞öööbä€"]) && - R([mutableString1 deleteCharactersInRange: OFRangeMake(1, 3)]) && + R([mutableString1 deleteCharactersInRange: OFMakeRange(1, 3)]) && [mutableString1 isEqual: @"𝄞bä€"] && - R([mutableString1 deleteCharactersInRange: OFRangeMake(0, 4)]) && + R([mutableString1 deleteCharactersInRange: OFMakeRange(0, 4)]) && [mutableString1 isEqual: @""]) TEST(@"-[replaceCharactersInRange:withString:]", (mutableString1 = [mutableStringClass stringWithString: @"𝄞öööbä€"]) && - R([mutableString1 replaceCharactersInRange: OFRangeMake(1, 3) + R([mutableString1 replaceCharactersInRange: OFMakeRange(1, 3) withString: @"äöüß"]) && [mutableString1 isEqual: @"𝄞äöüßbä€"] && - R([mutableString1 replaceCharactersInRange: OFRangeMake(4, 2) + R([mutableString1 replaceCharactersInRange: OFMakeRange(4, 2) withString: @"b"]) && [mutableString1 isEqual: @"𝄞äöübä€"] && - R([mutableString1 replaceCharactersInRange: OFRangeMake(0, 7) + R([mutableString1 replaceCharactersInRange: OFMakeRange(0, 7) withString: @""]) && [mutableString1 isEqual: @""]) EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #1", OFOutOfRangeException, { mutableString1 = [mutableStringClass stringWithString: @"𝄞öö"]; - [mutableString1 deleteCharactersInRange: OFRangeMake(2, 2)]; + [mutableString1 deleteCharactersInRange: OFMakeRange(2, 2)]; }) EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #2", OFOutOfRangeException, - [mutableString1 deleteCharactersInRange: OFRangeMake(4, 0)]) + [mutableString1 deleteCharactersInRange: OFMakeRange(4, 0)]) EXPECT_EXCEPTION(@"Detect OoR in " @"-[replaceCharactersInRange:withString:] #1", OFOutOfRangeException, - [mutableString1 replaceCharactersInRange: OFRangeMake(2, 2) + [mutableString1 replaceCharactersInRange: OFMakeRange(2, 2) withString: @""]) EXPECT_EXCEPTION(@"Detect OoR in " @"-[replaceCharactersInRange:withString:] #2", OFOutOfRangeException, - [mutableString1 replaceCharactersInRange: OFRangeMake(4, 0) + [mutableString1 replaceCharactersInRange: OFMakeRange(4, 0) withString: @""]) TEST(@"-[replaceOccurrencesOfString:withString:]", (mutableString1 = [mutableStringClass stringWithString: @"asd fo asd fofo asd"]) && @@ -1369,11 +1372,11 @@ (mutableString1 = [mutableStringClass stringWithString: @"foofoobarfoobarfoo"]) && R([mutableString1 replaceOccurrencesOfString: @"oo" withString: @"óò" options: 0 - range: OFRangeMake(2, 15)]) && + range: OFMakeRange(2, 15)]) && [mutableString1 isEqual: @"foofóòbarfóòbarfoo"]) TEST(@"-[deleteLeadingWhitespaces]", (mutableString1 = [mutableStringClass stringWithString: whitespace[0]]) && Index: tests/OFSystemInfoTests.m ================================================================== --- tests/OFSystemInfoTests.m +++ tests/OFSystemInfoTests.m @@ -44,18 +44,18 @@ [OFStdOut writeFormat: @"[OFSystemInfo] Operating system version: %@\n", [OFSystemInfo operatingSystemVersion]]; - [OFStdOut writeFormat: @"[OFSystemInfo] User config URL: %@\n", - [OFSystemInfo userConfigURL].string]; - - [OFStdOut writeFormat: @"[OFSystemInfo] User data URL: %@\n", - [OFSystemInfo userDataURL].string]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Temporary directory URL: %@\n", - [OFSystemInfo temporaryDirectoryURL].string]; + [OFStdOut writeFormat: @"[OFSystemInfo] User config URI: %@\n", + [OFSystemInfo userConfigURI].string]; + + [OFStdOut writeFormat: @"[OFSystemInfo] User data URI: %@\n", + [OFSystemInfo userDataURI].string]; + + [OFStdOut writeFormat: @"[OFSystemInfo] Temporary directory URI: %@\n", + [OFSystemInfo temporaryDirectoryURI].string]; [OFStdOut writeFormat: @"[OFSystemInfo] CPU vendor: %@\n", [OFSystemInfo CPUVendor]]; [OFStdOut writeFormat: @"[OFSystemInfo] CPU model: %@\n", Index: tests/OFUDPSocketTests.m ================================================================== --- tests/OFUDPSocketTests.m +++ tests/OFUDPSocketTests.m @@ -42,11 +42,11 @@ TEST(@"-[receiveIntoBuffer:length:sender:]", [sock receiveIntoBuffer: buf length: 6 sender: &addr2] == 6 && !memcmp(buf, "Hello", 6) && [OFSocketAddressString(&addr2) isEqual: @"127.0.0.1"] && - OFSocketAddressPort(&addr2) == port1) + OFSocketAddressIPPort(&addr2) == port1) addr3 = OFSocketAddressParseIP(@"127.0.0.1", port1 + 1); /* * TODO: Move those tests elsewhere as soon as the DNS resolving part Index: tests/OFUNIXDatagramSocketTests.m ================================================================== --- tests/OFUNIXDatagramSocketTests.m +++ tests/OFUNIXDatagramSocketTests.m @@ -29,19 +29,19 @@ OFUNIXDatagramSocket *sock; OFSocketAddress address1, address2; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) - path = [[OFSystemInfo temporaryDirectoryURL] - URLByAppendingPathComponent: [[OFUUID UUID] UUIDString]] + path = [[OFSystemInfo temporaryDirectoryURI] + URIByAppendingPathComponent: [[OFUUID UUID] UUIDString]] .fileSystemRepresentation; #else /* * We can have sockets, including UNIX sockets, while file support is * disabled. * - * We also use this code path for iOS, as the temporaryDirectoryURL is + * We also use this code path for iOS, as the temporaryDirectoryURI is * too long on the iOS simulator. */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif @@ -48,11 +48,11 @@ TEST(@"+[socket]", (sock = [OFUNIXDatagramSocket socket])) @try { TEST(@"-[bindToPath:]", R(address1 = [sock bindToPath: path])) - } @catch (OFBindFailedException *e) { + } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: case EPERM: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: Index: tests/OFUNIXStreamSocketTests.m ================================================================== --- tests/OFUNIXStreamSocketTests.m +++ tests/OFUNIXStreamSocketTests.m @@ -28,19 +28,19 @@ OFString *path; OFUNIXStreamSocket *sockClient, *sockServer, *sockAccepted; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) - path = [[OFSystemInfo temporaryDirectoryURL] - URLByAppendingPathComponent: [[OFUUID UUID] UUIDString]] + path = [[OFSystemInfo temporaryDirectoryURI] + URIByAppendingPathComponent: [[OFUUID UUID] UUIDString]] .fileSystemRepresentation; #else /* * We can have sockets, including UNIX sockets, while file support is * disabled. * - * We also use this code path for iOS, as the temporaryDirectoryURL is + * We also use this code path for iOS, as the temporaryDirectoryURI is * too long on the iOS simulator. */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif @@ -48,11 +48,11 @@ TEST(@"+[socket]", (sockClient = [OFUNIXStreamSocket socket]) && (sockServer = [OFUNIXStreamSocket socket])) @try { TEST(@"-[bindToPath:]", R([sockServer bindToPath: path])) - } @catch (OFBindFailedException *e) { + } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: case EPERM: [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeLine: ADDED tests/OFURITests.m Index: tests/OFURITests.m ================================================================== --- tests/OFURITests.m +++ tests/OFURITests.m @@ -0,0 +1,356 @@ +/* + * 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 "TestsAppDelegate.h" + +static OFString *const module = @"OFURI"; +static OFString *URIString = @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/" + @"pa%3Fth?que%23ry=1&f%26oo=b%3dar#frag%23ment"; + +@implementation TestsAppDelegate (OFURITests) +- (void)URITests +{ + void *pool = objc_autoreleasePoolPush(); + OFURI *URI1, *URI2, *URI3, *URI4, *URI5, *URI6, *URI7, *URI8, *URI9; + OFURI *URI10; + OFMutableURI *mutableURI; + + TEST(@"+[URIWithString:]", + R(URI1 = [OFURI URIWithString: URIString]) && + R(URI2 = [OFURI URIWithString: @"http://foo:80"]) && + R(URI3 = [OFURI URIWithString: @"http://bar/"]) && + R(URI4 = [OFURI URIWithString: @"file:///etc/passwd"]) && + R(URI5 = [OFURI URIWithString: @"http://foo/bar/qux/foo%2fbar"]) && + R(URI6 = [OFURI URIWithString: @"https://[12:34::56:abcd]/"]) && + R(URI7 = [OFURI URIWithString: @"https://[12:34::56:abcd]:234/"]) && + R(URI8 = [OFURI URIWithString: @"urn:qux:foo"]) && + R(URI9 = [OFURI URIWithString: @"file:/foo?query#frag"]) && + R(URI10 = [OFURI URIWithString: @"file:foo@bar/qux?query#frag"])) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #1", + OFInvalidFormatException, + [OFURI URIWithString: @"ht,tp://foo"]) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #2", + OFInvalidFormatException, + [OFURI URIWithString: @"http://f`oo"]) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #3", + OFInvalidFormatException, + [OFURI URIWithString: @"http://foo/`"]) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #4", + OFInvalidFormatException, + [OFURI URIWithString: @"http://foo/foo?`"]) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #5", + OFInvalidFormatException, + [OFURI URIWithString: @"http://foo/foo?foo#`"]) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #6", + OFInvalidFormatException, + [OFURI URIWithString: @"https://[g]/"]) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #7", + OFInvalidFormatException, + [OFURI URIWithString: @"https://[f]:/"]) + + EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #8", + OFInvalidFormatException, + [OFURI URIWithString: @"https://[f]:f/"]) + + TEST(@"+[URIWithString:relativeToURI:]", + [[[OFURI URIWithString: @"/foo" relativeToURI: URI1] string] + isEqual: @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/foo"] && + [[[OFURI URIWithString: @"foo/bar?q" + relativeToURI: [OFURI URIWithString: @"http://h/qux/quux"]] + string] isEqual: @"http://h/qux/foo/bar?q"] && + [[[OFURI URIWithString: @"foo/bar" + relativeToURI: [OFURI URIWithString: @"http://h/qux/?x"]] + string] isEqual: @"http://h/qux/foo/bar"] && + [[[OFURI URIWithString: @"http://foo/?q" + relativeToURI: URI1] string] isEqual: @"http://foo/?q"] && + [[[OFURI URIWithString: @"foo" + relativeToURI: [OFURI URIWithString: @"http://foo/bar"]] + string] isEqual: @"http://foo/foo"] && + [[[OFURI URIWithString: @"foo" + relativeToURI: [OFURI URIWithString: @"http://foo"]] + string] isEqual: @"http://foo/foo"]) + + EXPECT_EXCEPTION( + @"+[URIWithString:relativeToURI:] fails with invalid characters #1", + OFInvalidFormatException, + [OFURI URIWithString: @"`" relativeToURI: URI1]) + + EXPECT_EXCEPTION( + @"+[URIWithString:relativeToURI:] fails with invalid characters #2", + OFInvalidFormatException, + [OFURI URIWithString: @"/`" relativeToURI: URI1]) + + EXPECT_EXCEPTION( + @"+[URIWithString:relativeToURI:] fails with invalid characters #3", + OFInvalidFormatException, + [OFURI URIWithString: @"?`" relativeToURI: URI1]) + + EXPECT_EXCEPTION( + @"+[URIWithString:relativeToURI:] fails with invalid characters #4", + OFInvalidFormatException, + [OFURI URIWithString: @"#`" relativeToURI: URI1]) + +#ifdef OF_HAVE_FILES + TEST(@"+[fileURIWithPath:]", + [[[OFURI fileURIWithPath: @"testfile.txt"] fileSystemRepresentation] + isEqual: [[OFFileManager defaultManager].currentDirectoryPath + stringByAppendingPathComponent: @"testfile.txt"]]) + +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OFURI *tmp; + TEST(@"+[fileURIWithPath:] for c:\\", + (tmp = [OFURI fileURIWithPath: @"c:\\"]) && + [tmp.string isEqual: @"file:/c:/"] && + [tmp.fileSystemRepresentation isEqual: @"c:\\"]) +# endif + +# ifdef OF_WINDOWS + TEST(@"+[fileURIWithPath:] with UNC", + (tmp = [OFURI fileURIWithPath: @"\\\\foo\\bar" + isDirectory: false]) && + [tmp.host isEqual: @"foo"] && [tmp.path isEqual: @"/bar"] && + [tmp.string isEqual: @"file://foo/bar"] && + [tmp.fileSystemRepresentation isEqual: @"\\\\foo\\bar"] && + (tmp = [OFURI fileURIWithPath: @"\\\\test" isDirectory: true]) && + [tmp.host isEqual: @"test"] && [tmp.path isEqual: @"/"] && + [tmp.string isEqual: @"file://test/"] && + [tmp.fileSystemRepresentation isEqual: @"\\\\test"]) +# endif +#endif + + TEST(@"-[string]", + [URI1.string isEqual: URIString] && + [URI2.string isEqual: @"http://foo:80"] && + [URI3.string isEqual: @"http://bar/"] && + [URI4.string isEqual: @"file:///etc/passwd"] && + [URI5.string isEqual: @"http://foo/bar/qux/foo%2fbar"] && + [URI6.string isEqual: @"https://[12:34::56:abcd]/"] && + [URI7.string isEqual: @"https://[12:34::56:abcd]:234/"] && + [URI8.string isEqual: @"urn:qux:foo"] && + [URI9.string isEqual: @"file:/foo?query#frag"] && + [URI10.string isEqual: @"file:foo@bar/qux?query#frag"]) + + TEST(@"-[scheme]", + [URI1.scheme isEqual: @"ht+tp"] && [URI4.scheme isEqual: @"file"] && + [URI9.scheme isEqual: @"file"] && [URI10.scheme isEqual: @"file"]) + + TEST(@"-[user]", [URI1.user isEqual: @"us:er"] && URI4.user == nil && + URI10.user == nil) + TEST(@"-[password]", + [URI1.password isEqual: @"p@w"] && URI4.password == nil && + URI10.password == nil) + TEST(@"-[host]", [URI1.host isEqual: @"ho:st"] && + [URI6.host isEqual: @"12:34::56:abcd"] && + [URI7.host isEqual: @"12:34::56:abcd"] && + URI8.host == nil && URI9.host == nil && URI10.host == nil) + TEST(@"-[port]", URI1.port.unsignedShortValue == 1234 && + [URI4 port] == nil && URI7.port.unsignedShortValue == 234 && + URI8.port == nil && URI9.port == nil && URI10.port == nil) + TEST(@"-[path]", + [URI1.path isEqual: @"/pa?th"] && + [URI4.path isEqual: @"/etc/passwd"] && + [URI8.path isEqual: @"qux:foo"] && + [URI9.path isEqual: @"/foo"] && + [URI10.path isEqual: @"foo@bar/qux"]) + TEST(@"-[pathComponents]", + [URI1.pathComponents isEqual: + [OFArray arrayWithObjects: @"/", @"pa?th", nil]] && + [URI4.pathComponents isEqual: + [OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil]] && + [URI5.pathComponents isEqual: + [OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", nil]]) + TEST(@"-[lastPathComponent]", + [[[OFURI URIWithString: @"http://host/foo//bar/baz"] + lastPathComponent] isEqual: @"baz"] && + [[[OFURI URIWithString: @"http://host/foo//bar/baz/"] + lastPathComponent] isEqual: @"baz"] && + [[[OFURI URIWithString: @"http://host/foo/"] + lastPathComponent] isEqual: @"foo"] && + [[[OFURI URIWithString: @"http://host/"] + lastPathComponent] isEqual: @"/"] && + [URI5.lastPathComponent isEqual: @"foo/bar"]) + TEST(@"-[query]", + [URI1.query isEqual: @"que#ry=1&f&oo=b=ar"] && URI4.query == nil && + [URI9.query isEqual: @"query"] && [URI10.query isEqual: @"query"]) + TEST(@"-[queryItems]", + [URI1.queryItems isEqual: [OFArray arrayWithObjects: + [OFPair pairWithFirstObject: @"que#ry" secondObject: @"1"], + [OFPair pairWithFirstObject: @"f&oo" secondObject: @"b=ar"], nil]]); + TEST(@"-[fragment]", + [URI1.fragment isEqual: @"frag#ment"] && URI4.fragment == nil && + [URI9.fragment isEqual: @"frag"] && + [URI10.fragment isEqual: @"frag"]) + + TEST(@"-[copy]", R(URI4 = [[URI1 copy] autorelease])) + + TEST(@"-[isEqual:]", [URI1 isEqual: URI4] && ![URI2 isEqual: URI3] && + [[OFURI URIWithString: @"HTTP://bar/"] isEqual: URI3]) + + TEST(@"-[hash:]", URI1.hash == URI4.hash && URI2.hash != URI3.hash) + + EXPECT_EXCEPTION(@"Detection of invalid format", + OFInvalidFormatException, [OFURI URIWithString: @"http"]) + + mutableURI = [OFMutableURI URIWithScheme: @"dummy"]; + + EXPECT_EXCEPTION( + @"-[setPercentEncodedScheme:] with invalid characters fails", + OFInvalidFormatException, mutableURI.scheme = @"%20") + + TEST(@"-[setHost:]", + (mutableURI.host = @"ho:st") && + [mutableURI.percentEncodedHost isEqual: @"ho%3Ast"] && + (mutableURI.host = @"12:34:ab") && + [mutableURI.percentEncodedHost isEqual: @"[12:34:ab]"] && + (mutableURI.host = @"12:34:aB") && + [mutableURI.percentEncodedHost isEqual: @"[12:34:aB]"] && + (mutableURI.host = @"12:34:g") && + [mutableURI.percentEncodedHost isEqual: @"12%3A34%3Ag"]) + + TEST(@"-[setPercentEncodedHost:]", + (mutableURI.percentEncodedHost = @"ho%3Ast") && + [mutableURI.host isEqual: @"ho:st"] && + (mutableURI.percentEncodedHost = @"[12:34]") && + [mutableURI.host isEqual: @"12:34"] && + (mutableURI.percentEncodedHost = @"[12::ab]") && + [mutableURI.host isEqual: @"12::ab"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedHost:] with invalid characters fails #1", + OFInvalidFormatException, + mutableURI.percentEncodedHost = @"/") + + EXPECT_EXCEPTION( + @"-[setPercentEncodedHost:] with invalid characters fails #2", + OFInvalidFormatException, + mutableURI.percentEncodedHost = @"[12:34") + + EXPECT_EXCEPTION( + @"-[setPercentEncodedHost:] with invalid characters fails #3", + OFInvalidFormatException, + mutableURI.percentEncodedHost = @"[a::g]") + + TEST(@"-[setUser:]", + (mutableURI.user = @"us:er") && + [mutableURI.percentEncodedUser isEqual: @"us%3Aer"]) + + TEST(@"-[setPercentEncodedUser:]", + (mutableURI.percentEncodedUser = @"us%3Aer") && + [mutableURI.user isEqual: @"us:er"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedUser:] with invalid characters fails", + OFInvalidFormatException, + mutableURI.percentEncodedHost = @"/") + + TEST(@"-[setPassword:]", + (mutableURI.password = @"pass:word") && + [mutableURI.percentEncodedPassword isEqual: @"pass%3Aword"]) + + TEST(@"-[setPercentEncodedPassword:]", + (mutableURI.percentEncodedPassword = @"pass%3Aword") && + [mutableURI.password isEqual: @"pass:word"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedPassword:] with invalid characters fails", + OFInvalidFormatException, + mutableURI.percentEncodedPassword = @"/") + + TEST(@"-[setPath:]", + (mutableURI.path = @"pa/th@?") && + [mutableURI.percentEncodedPath isEqual: @"pa/th@%3F"]) + + TEST(@"-[setPercentEncodedPath:]", + (mutableURI.percentEncodedPath = @"pa/th@%3F") && + [mutableURI.path isEqual: @"pa/th@?"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedPath:] with invalid characters fails", + OFInvalidFormatException, + mutableURI.percentEncodedPath = @"?") + + TEST(@"-[setQuery:]", + (mutableURI.query = @"que/ry?#") && + [mutableURI.percentEncodedQuery isEqual: @"que/ry?%23"]) + + TEST(@"-[setPercentEncodedQuery:]", + (mutableURI.percentEncodedQuery = @"que/ry?%23") && + [mutableURI.query isEqual: @"que/ry?#"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedQuery:] with invalid characters fails", + OFInvalidFormatException, + mutableURI.percentEncodedQuery = @"`") + + TEST(@"-[setQueryItems:]", + (mutableURI.queryItems = [OFArray arrayWithObjects: + [OFPair pairWithFirstObject: @"foo&bar" secondObject: @"baz=qux"], + [OFPair pairWithFirstObject: @"f=oobar" secondObject: @"b&azqux"], + nil]) && [mutableURI.percentEncodedQuery isEqual: + @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"]) + + TEST(@"-[setFragment:]", + (mutableURI.fragment = @"frag/ment?#") && + [mutableURI.percentEncodedFragment isEqual: @"frag/ment?%23"]) + + TEST(@"-[setPercentEncodedFragment:]", + (mutableURI.percentEncodedFragment = @"frag/ment?%23") && + [mutableURI.fragment isEqual: @"frag/ment?#"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedFragment:] with invalid characters fails", + OFInvalidFormatException, + mutableURI.percentEncodedFragment = @"`") + + TEST(@"-[URIByAppendingPathComponent:isDirectory:]", + [[[OFURI URIWithString: @"file:///foo/bar"] + URIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: + [OFURI URIWithString: @"file:///foo/bar/qux"]] && + [[[OFURI URIWithString: @"file:///foo/bar/"] + URIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: + [OFURI URIWithString: @"file:///foo/bar/qux"]] && + [[[OFURI URIWithString: @"file:///foo/bar/"] + URIByAppendingPathComponent: @"qu?x" isDirectory: false] isEqual: + [OFURI URIWithString: @"file:///foo/bar/qu%3Fx"]] && + [[[OFURI URIWithString: @"file:///foo/bar/"] + URIByAppendingPathComponent: @"qu?x" isDirectory: true] isEqual: + [OFURI URIWithString: @"file:///foo/bar/qu%3Fx/"]]) + + TEST(@"-[URIByStandardizingPath]", + [[[OFURI URIWithString: @"http://foo/bar/.."] + URIByStandardizingPath] isEqual: + [OFURI URIWithString: @"http://foo/"]] && + [[[OFURI URIWithString: @"http://foo/bar/%2E%2E/../qux/"] + URIByStandardizingPath] isEqual: + [OFURI URIWithString: @"http://foo/bar/qux/"]] && + [[[OFURI URIWithString: @"http://foo/bar/./././qux/./"] + URIByStandardizingPath] isEqual: + [OFURI URIWithString: @"http://foo/bar/qux/"]] && + [[[OFURI URIWithString: @"http://foo/bar/../../qux"] + URIByStandardizingPath] isEqual: + [OFURI URIWithString: @"http://foo/../qux"]]) + + objc_autoreleasePoolPop(pool); +} +@end DELETED tests/OFURLTests.m Index: tests/OFURLTests.m ================================================================== --- tests/OFURLTests.m +++ tests/OFURLTests.m @@ -1,331 +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 "TestsAppDelegate.h" - -static OFString *const module = @"OFURL"; -static OFString *URLString = @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/" - @"pa%3Fth?que%23ry=1&f%26oo=b%3dar#frag%23ment"; - -@implementation TestsAppDelegate (OFURLTests) -- (void)URLTests -{ - void *pool = objc_autoreleasePoolPush(); - OFURL *URL1, *URL2, *URL3, *URL4, *URL5, *URL6, *URL7; - OFMutableURL *mutableURL; - - TEST(@"+[URLWithString:]", - R(URL1 = [OFURL URLWithString: URLString]) && - R(URL2 = [OFURL URLWithString: @"http://foo:80"]) && - R(URL3 = [OFURL URLWithString: @"http://bar/"]) && - R(URL4 = [OFURL URLWithString: @"file:///etc/passwd"]) && - R(URL5 = [OFURL URLWithString: @"http://foo/bar/qux/foo%2fbar"]) && - R(URL6 = [OFURL URLWithString: @"https://[12:34::56:abcd]/"]) && - R(URL7 = [OFURL URLWithString: @"https://[12:34::56:abcd]:234/"])) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #1", - OFInvalidFormatException, - [OFURL URLWithString: @"ht,tp://foo"]) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #2", - OFInvalidFormatException, - [OFURL URLWithString: @"http://f`oo"]) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #3", - OFInvalidFormatException, - [OFURL URLWithString: @"http://foo/`"]) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #4", - OFInvalidFormatException, - [OFURL URLWithString: @"http://foo/foo?`"]) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #5", - OFInvalidFormatException, - [OFURL URLWithString: @"http://foo/foo?foo#`"]) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #6", - OFInvalidFormatException, - [OFURL URLWithString: @"https://[g]/"]) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #7", - OFInvalidFormatException, - [OFURL URLWithString: @"https://[f]:/"]) - - EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #8", - OFInvalidFormatException, - [OFURL URLWithString: @"https://[f]:f/"]) - - TEST(@"+[URLWithString:relativeToURL:]", - [[[OFURL URLWithString: @"/foo" relativeToURL: URL1] string] - isEqual: @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] && - [[[OFURL URLWithString: @"foo/bar?q" - relativeToURL: [OFURL URLWithString: @"http://h/qux/quux"]] - string] isEqual: @"http://h/qux/foo/bar?q"] && - [[[OFURL URLWithString: @"foo/bar" - relativeToURL: [OFURL URLWithString: @"http://h/qux/?x"]] - string] isEqual: @"http://h/qux/foo/bar"] && - [[[OFURL URLWithString: @"http://foo/?q" - relativeToURL: URL1] string] isEqual: @"http://foo/?q"] && - [[[OFURL URLWithString: @"foo" - relativeToURL: [OFURL URLWithString: @"http://foo/bar"]] - string] isEqual: @"http://foo/foo"] && - [[[OFURL URLWithString: @"foo" - relativeToURL: [OFURL URLWithString: @"http://foo"]] - string] isEqual: @"http://foo/foo"]) - - EXPECT_EXCEPTION( - @"+[URLWithString:relativeToURL:] fails with invalid characters #1", - OFInvalidFormatException, - [OFURL URLWithString: @"`" relativeToURL: URL1]) - - EXPECT_EXCEPTION( - @"+[URLWithString:relativeToURL:] fails with invalid characters #2", - OFInvalidFormatException, - [OFURL URLWithString: @"/`" relativeToURL: URL1]) - - EXPECT_EXCEPTION( - @"+[URLWithString:relativeToURL:] fails with invalid characters #3", - OFInvalidFormatException, - [OFURL URLWithString: @"?`" relativeToURL: URL1]) - - EXPECT_EXCEPTION( - @"+[URLWithString:relativeToURL:] fails with invalid characters #4", - OFInvalidFormatException, - [OFURL URLWithString: @"#`" relativeToURL: URL1]) - -#ifdef OF_HAVE_FILES - TEST(@"+[fileURLWithPath:]", - [[[OFURL fileURLWithPath: @"testfile.txt"] fileSystemRepresentation] - isEqual: [[OFFileManager defaultManager].currentDirectoryPath - stringByAppendingPathComponent: @"testfile.txt"]]) - -# if defined(OF_WINDOWS) || defined(OF_MSDOS) - OFURL *tmp; - TEST(@"+[fileURLWithPath:] for c:\\", - (tmp = [OFURL fileURLWithPath: @"c:\\"]) && - [tmp.string isEqual: @"file:///c:/"] && - [tmp.fileSystemRepresentation isEqual: @"c:\\"]) -# endif - -# ifdef OF_WINDOWS - TEST(@"+[fileURLWithPath:] with UNC", - (tmp = [OFURL fileURLWithPath: @"\\\\foo\\bar"]) && - [tmp.host isEqual: @"foo"] && [tmp.path isEqual: @"/bar"] && - [tmp.string isEqual: @"file://foo/bar"] && - [tmp.fileSystemRepresentation isEqual: @"\\\\foo\\bar"] && - (tmp = [OFURL fileURLWithPath: @"\\\\test"]) && - [tmp.host isEqual: @"test"] && [tmp.path isEqual: @"/"] && - [tmp.string isEqual: @"file://test/"] && - [tmp.fileSystemRepresentation isEqual: @"\\\\test"]) -# endif -#endif - - TEST(@"-[string]", - [URL1.string isEqual: URLString] && - [URL2.string isEqual: @"http://foo:80"] && - [URL3.string isEqual: @"http://bar/"] && - [URL4.string isEqual: @"file:///etc/passwd"]) - - TEST(@"-[scheme]", - [URL1.scheme isEqual: @"ht:tp"] && [URL4.scheme isEqual: @"file"]) - - TEST(@"-[user]", [URL1.user isEqual: @"us:er"] && URL4.user == nil) - TEST(@"-[password]", - [URL1.password isEqual: @"p@w"] && URL4.password == nil) - TEST(@"-[host]", [URL1.host isEqual: @"ho:st"] && - [URL6.host isEqual: @"12:34::56:abcd"] && - [URL7.host isEqual: @"12:34::56:abcd"]) - TEST(@"-[port]", URL1.port.unsignedShortValue == 1234 && - [URL4 port] == nil && URL7.port.unsignedShortValue == 234) - TEST(@"-[path]", - [URL1.path isEqual: @"/pa?th"] && - [URL4.path isEqual: @"/etc/passwd"]) - TEST(@"-[pathComponents]", - [URL1.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"pa?th", nil]] && - [URL4.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil]] && - [URL5.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", nil]]) - TEST(@"-[lastPathComponent]", - [[[OFURL URLWithString: @"http://host/foo//bar/baz"] - lastPathComponent] isEqual: @"baz"] && - [[[OFURL URLWithString: @"http://host/foo//bar/baz/"] - lastPathComponent] isEqual: @"baz"] && - [[[OFURL URLWithString: @"http://host/foo/"] - lastPathComponent] isEqual: @"foo"] && - [[[OFURL URLWithString: @"http://host/"] - lastPathComponent] isEqual: @"/"] && - [URL5.lastPathComponent isEqual: @"foo/bar"]) - TEST(@"-[query]", - [URL1.query isEqual: @"que#ry=1&f&oo=b=ar"] && URL4.query == nil) - TEST(@"-[queryDictionary]", - [URL1.queryDictionary isEqual: - [OFDictionary dictionaryWithKeysAndObjects: - @"que#ry", @"1", @"f&oo", @"b=ar", nil]]); - TEST(@"-[fragment]", - [URL1.fragment isEqual: @"frag#ment"] && URL4.fragment == nil) - - TEST(@"-[copy]", R(URL4 = [[URL1 copy] autorelease])) - - TEST(@"-[isEqual:]", [URL1 isEqual: URL4] && ![URL2 isEqual: URL3] && - [[OFURL URLWithString: @"HTTP://bar/"] isEqual: URL3]) - - TEST(@"-[hash:]", URL1.hash == URL4.hash && URL2.hash != URL3.hash) - - EXPECT_EXCEPTION(@"Detection of invalid format", - OFInvalidFormatException, [OFURL URLWithString: @"http"]) - - mutableURL = [OFMutableURL URL]; - - TEST(@"-[setScheme:]", - (mutableURL.scheme = @"ht:tp") && - [mutableURL.URLEncodedScheme isEqual: @"ht%3Atp"]) - - TEST(@"-[setURLEncodedScheme:]", - (mutableURL.URLEncodedScheme = @"ht%3Atp") && - [mutableURL.scheme isEqual: @"ht:tp"]) - - EXPECT_EXCEPTION( - @"-[setURLEncodedScheme:] with invalid characters fails", - OFInvalidFormatException, mutableURL.URLEncodedScheme = @"~") - - TEST(@"-[setHost:]", - (mutableURL.host = @"ho:st") && - [mutableURL.URLEncodedHost isEqual: @"ho%3Ast"] && - (mutableURL.host = @"12:34:ab") && - [mutableURL.URLEncodedHost isEqual: @"[12:34:ab]"] && - (mutableURL.host = @"12:34:aB") && - [mutableURL.URLEncodedHost isEqual: @"[12:34:aB]"] && - (mutableURL.host = @"12:34:g") && - [mutableURL.URLEncodedHost isEqual: @"12%3A34%3Ag"]) - - TEST(@"-[setURLEncodedHost:]", - (mutableURL.URLEncodedHost = @"ho%3Ast") && - [mutableURL.host isEqual: @"ho:st"] && - (mutableURL.URLEncodedHost = @"[12:34]") && - [mutableURL.host isEqual: @"12:34"] && - (mutableURL.URLEncodedHost = @"[12::ab]") && - [mutableURL.host isEqual: @"12::ab"]) - - EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails" - " #1", OFInvalidFormatException, mutableURL.URLEncodedHost = @"/") - - EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails" - " #2", OFInvalidFormatException, - mutableURL.URLEncodedHost = @"[12:34") - - EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails" - " #3", OFInvalidFormatException, - mutableURL.URLEncodedHost = @"[a::g]") - - TEST(@"-[setUser:]", - (mutableURL.user = @"us:er") && - [mutableURL.URLEncodedUser isEqual: @"us%3Aer"]) - - TEST(@"-[setURLEncodedUser:]", - (mutableURL.URLEncodedUser = @"us%3Aer") && - [mutableURL.user isEqual: @"us:er"]) - - EXPECT_EXCEPTION(@"-[setURLEncodedUser:] with invalid characters fails", - OFInvalidFormatException, mutableURL.URLEncodedHost = @"/") - - TEST(@"-[setPassword:]", - (mutableURL.password = @"pass:word") && - [mutableURL.URLEncodedPassword isEqual: @"pass%3Aword"]) - - TEST(@"-[setURLEncodedPassword:]", - (mutableURL.URLEncodedPassword = @"pass%3Aword") && - [mutableURL.password isEqual: @"pass:word"]) - - EXPECT_EXCEPTION( - @"-[setURLEncodedPassword:] with invalid characters fails", - OFInvalidFormatException, mutableURL.URLEncodedPassword = @"/") - - TEST(@"-[setPath:]", - (mutableURL.path = @"pa/th@?") && - [mutableURL.URLEncodedPath isEqual: @"pa/th@%3F"]) - - TEST(@"-[setURLEncodedPath:]", - (mutableURL.URLEncodedPath = @"pa/th@%3F") && - [mutableURL.path isEqual: @"pa/th@?"]) - - EXPECT_EXCEPTION(@"-[setURLEncodedPath:] with invalid characters fails", - OFInvalidFormatException, mutableURL.URLEncodedPath = @"?") - - TEST(@"-[setQuery:]", - (mutableURL.query = @"que/ry?#") && - [mutableURL.URLEncodedQuery isEqual: @"que/ry?%23"]) - - TEST(@"-[setURLEncodedQuery:]", - (mutableURL.URLEncodedQuery = @"que/ry?%23") && - [mutableURL.query isEqual: @"que/ry?#"]) - - EXPECT_EXCEPTION( - @"-[setURLEncodedQuery:] with invalid characters fails", - OFInvalidFormatException, mutableURL.URLEncodedQuery = @"`") - - TEST(@"-[setQueryDictionary:]", - (mutableURL.queryDictionary = - [OFDictionary dictionaryWithKeysAndObjects: - @"foo&bar", @"baz=qux", @"f=oobar", @"b&azqux", nil]) && - [mutableURL.URLEncodedQuery isEqual: - @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"]) - - TEST(@"-[setFragment:]", - (mutableURL.fragment = @"frag/ment?#") && - [mutableURL.URLEncodedFragment isEqual: @"frag/ment?%23"]) - - TEST(@"-[setURLEncodedFragment:]", - (mutableURL.URLEncodedFragment = @"frag/ment?%23") && - [mutableURL.fragment isEqual: @"frag/ment?#"]) - - EXPECT_EXCEPTION( - @"-[setURLEncodedFragment:] with invalid characters fails", - OFInvalidFormatException, mutableURL.URLEncodedFragment = @"`") - - TEST(@"-[URLByAppendingPathComponent:isDirectory:]", - [[[OFURL URLWithString: @"file:///foo/bar"] - URLByAppendingPathComponent: @"qux" isDirectory: false] isEqual: - [OFURL URLWithString: @"file:///foo/bar/qux"]] && - [[[OFURL URLWithString: @"file:///foo/bar/"] - URLByAppendingPathComponent: @"qux" isDirectory: false] isEqual: - [OFURL URLWithString: @"file:///foo/bar/qux"]] && - [[[OFURL URLWithString: @"file:///foo/bar/"] - URLByAppendingPathComponent: @"qu?x" isDirectory: false] isEqual: - [OFURL URLWithString: @"file:///foo/bar/qu%3Fx"]] && - [[[OFURL URLWithString: @"file:///foo/bar/"] - URLByAppendingPathComponent: @"qu?x" isDirectory: true] isEqual: - [OFURL URLWithString: @"file:///foo/bar/qu%3Fx/"]]) - - TEST(@"-[URLByStandardizingPath]", - [[[OFURL URLWithString: @"http://foo/bar/.."] - URLByStandardizingPath] isEqual: - [OFURL URLWithString: @"http://foo/"]] && - [[[OFURL URLWithString: @"http://foo/bar/%2E%2E/../qux/"] - URLByStandardizingPath] isEqual: - [OFURL URLWithString: @"http://foo/bar/qux/"]] && - [[[OFURL URLWithString: @"http://foo/bar/./././qux/./"] - URLByStandardizingPath] isEqual: - [OFURL URLWithString: @"http://foo/bar/qux/"]] && - [[[OFURL URLWithString: @"http://foo/bar/../../qux"] - URLByStandardizingPath] isEqual: - [OFURL URLWithString: @"http://foo/../qux"]]) - - objc_autoreleasePoolPop(pool); -} -@end Index: tests/OFValueTests.m ================================================================== --- tests/OFValueTests.m +++ tests/OFValueTests.m @@ -23,14 +23,14 @@ @implementation TestsAppDelegate (OFValueTests) - (void)valueTests { void *pool = objc_autoreleasePoolPush(); - OFRange range = OFRangeMake(1, 64), range2; - OFPoint point = OFPointMake(1.5f, 3.0f), point2; - OFSize size = OFSizeMake(4.5f, 5.0f), size2; - OFRect rect = OFRectMake(1.5f, 3.0f, 4.5f, 6.0f), rect2; + OFRange range = OFMakeRange(1, 64), range2; + OFPoint point = OFMakePoint(1.5f, 3.0f), point2; + OFSize size = OFMakeSize(4.5f, 5.0f), size2; + OFRect rect = OFMakeRect(1.5f, 3.0f, 4.5f, 6.0f), rect2; OFValue *value; void *pointer = &value; TEST(@"+[valueWithBytes:objCType:]", (value = [OFValue valueWithBytes: &range @@ -38,11 +38,11 @@ TEST(@"-[objCType]", strcmp(value.objCType, @encode(OFRange)) == 0) TEST(@"-[getValue:size:]", R([value getValue: &range2 size: sizeof(OFRange)]) && - OFRangeEqual(range2, range)) + OFEqualRanges(range2, range)) EXPECT_EXCEPTION(@"-[getValue:size:] with wrong size throws", OFOutOfRangeException, [value getValue: &range size: sizeof(OFRange) - 1]) @@ -74,19 +74,19 @@ TEST(@"+[valueWithRange:]", (value = [OFValue valueWithRange: range])) TEST(@"-[rangeValue]", - OFRangeEqual(value.rangeValue, range) && + OFEqualRanges(value.rangeValue, range) && (value = [OFValue valueWithBytes: &range objCType: @encode(OFRange)]) && - OFRangeEqual(value.rangeValue, range)) + OFEqualRanges(value.rangeValue, range)) TEST(@"-[getValue:size:] for OFRangeValue", (value = [OFValue valueWithRange: range]) && R([value getValue: &range2 size: sizeof(range2)]) && - OFRangeEqual(range2, range)) + OFEqualRanges(range2, range)) EXPECT_EXCEPTION(@"-[rangeValue] with wrong size throws", OFOutOfRangeException, [[OFValue valueWithBytes: "a" objCType: @encode(char)] rangeValue]) @@ -93,19 +93,19 @@ TEST(@"+[valueWithPoint:]", (value = [OFValue valueWithPoint: point])) TEST(@"-[pointValue]", - OFPointEqual(value.pointValue, point) && + OFEqualPoints(value.pointValue, point) && (value = [OFValue valueWithBytes: &point objCType: @encode(OFPoint)]) && - OFPointEqual(value.pointValue, point)) + OFEqualPoints(value.pointValue, point)) TEST(@"-[getValue:size:] for OFPointValue", (value = [OFValue valueWithPoint: point]) && R([value getValue: &point2 size: sizeof(point2)]) && - OFPointEqual(point2, point)) + OFEqualPoints(point2, point)) EXPECT_EXCEPTION(@"-[pointValue] with wrong size throws", OFOutOfRangeException, [[OFValue valueWithBytes: "a" objCType: @encode(char)] pointValue]) @@ -112,19 +112,19 @@ TEST(@"+[valueWithSize:]", (value = [OFValue valueWithSize: size])) TEST(@"-[sizeValue]", - OFSizeEqual(value.sizeValue, size) && + OFEqualSizes(value.sizeValue, size) && (value = [OFValue valueWithBytes: &size objCType: @encode(OFSize)]) && - OFSizeEqual(value.sizeValue, size)) + OFEqualSizes(value.sizeValue, size)) TEST(@"-[getValue:size:] for OFSizeValue", (value = [OFValue valueWithSize: size]) && R([value getValue: &size2 size: sizeof(size2)]) && - OFSizeEqual(size2, size)) + OFEqualSizes(size2, size)) EXPECT_EXCEPTION(@"-[sizeValue] with wrong size throws", OFOutOfRangeException, [[OFValue valueWithBytes: "a" objCType: @encode(char)] sizeValue]) @@ -131,19 +131,19 @@ TEST(@"+[valueWithRect:]", (value = [OFValue valueWithRect: rect])) TEST(@"-[rectValue]", - OFRectEqual(value.rectValue, rect) && + OFEqualRects(value.rectValue, rect) && (value = [OFValue valueWithBytes: &rect objCType: @encode(OFRect)]) && - OFRectEqual(value.rectValue, rect)) + OFEqualRects(value.rectValue, rect)) TEST(@"-[getValue:size:] for OFRectValue", (value = [OFValue valueWithRect: rect]) && R([value getValue: &rect2 size: sizeof(rect2)]) && - OFRectEqual(rect2, rect)) + OFEqualRects(rect2, rect)) EXPECT_EXCEPTION(@"-[rectValue] with wrong size throws", OFOutOfRangeException, [[OFValue valueWithBytes: "a" objCType: @encode(char)] rectValue]) Index: tests/OFWindowsRegistryKeyTests.m ================================================================== --- tests/OFWindowsRegistryKeyTests.m +++ tests/OFWindowsRegistryKeyTests.m @@ -40,24 +40,30 @@ [OFWindowsRegistryKey localMachineKey]) TEST(@"+[OFWindowsRegistryKey usersKey]", [OFWindowsRegistryKey usersKey]) - TEST(@"-[openSubkeyAtPath:securityAndAccessRights:] #1", + TEST(@"-[openSubkeyAtPath:accessRights:options:] #1", (softwareKey = [[OFWindowsRegistryKey currentUserKey] - openSubkeyAtPath: @"Software" - securityAndAccessRights: KEY_ALL_ACCESS])) + openSubkeyAtPath: @"Software" + accessRights: KEY_ALL_ACCESS + options: 0])) - EXPECT_EXCEPTION(@"-[openSubkeyAtPath:securityAndAccessRights:] #2", + EXPECT_EXCEPTION(@"-[openSubkeyAtPath:accessRights:options:] #2", OFOpenWindowsRegistryKeyFailedException, [[OFWindowsRegistryKey currentUserKey] - openSubkeyAtPath: @"nonexistent" - securityAndAccessRights: KEY_ALL_ACCESS]) + openSubkeyAtPath: @"nonexistent" + accessRights: KEY_ALL_ACCESS + options: 0]) - TEST(@"-[createSubkeyAtPath:securityAndAccessRights:]", + TEST(@"-[createSubkeyAtPath:accessRights:securityAttributes:options:" + @"disposition:]", (objFWKey = [softwareKey createSubkeyAtPath: @"ObjFW" - securityAndAccessRights: KEY_ALL_ACCESS])) + accessRights: KEY_ALL_ACCESS + securityAttributes: NULL + options: 0 + disposition: NULL])) TEST(@"-[setData:forValueNamed:type:]", R([objFWKey setData: data forValueNamed: @"data" type: REG_BINARY])) TEST(@"-[dataForValueNamed:subkeyPath:flags:type:]", Index: tests/OFXMLElementBuilderTests.m ================================================================== --- tests/OFXMLElementBuilderTests.m +++ tests/OFXMLElementBuilderTests.m @@ -27,12 +27,12 @@ { OFEnsure(i == 0); nodes[i++] = [element retain]; } -- (void)elementBuilder: (OFXMLElementBuilder *)builder - didBuildParentlessNode: (OFXMLNode *)node +- (void)elementBuilder: (OFXMLElementBuilder *)builder + didBuildOrphanNode: (OFXMLNode *)node { OFEnsure(i == 1); nodes[i++] = [node retain]; } Index: tests/OFXMLParserTests.m ================================================================== --- tests/OFXMLParserTests.m +++ tests/OFXMLParserTests.m @@ -273,19 +273,19 @@ } } - (void)parser: (OFXMLParser *)parser foundProcessingInstructionWithTarget: (OFString *)target - data: (OFString *)data + text: (OFString *)text { [self parser: parser didCreateEvent: eventTypeProcessingInstruction name: target prefix: nil namespace: nil attributes: nil - string: data]; + string: text]; } - (void)parser: (OFXMLParser *)parser didStartElement: (OFString *)name prefix: (OFString *)prefix Index: tests/RuntimeARCTests.m ================================================================== --- tests/RuntimeARCTests.m +++ tests/RuntimeARCTests.m @@ -25,11 +25,11 @@ @implementation RuntimeARCTest - (instancetype)init { self = [super init]; -#ifdef OF_WINDOWS +#if defined(OF_WINDOWS) && defined(OF_X86_64) /* * Clang has a bug on Windows where it creates an invalid call into * objc_retainAutoreleasedReturnValue(). Work around it by not using an * autoreleased exception. */ Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -76,10 +76,14 @@ @end @interface TestsAppDelegate (OFCharacterSetTests) - (void)characterSetTests; @end + +@interface TestsAppDelegate (OFDDPSocketTests) +- (void)DDPSocketTests; +@end @interface TestsAppDelegate (OFDNSResolverTests) - (void)DNSResolverTests; @end @@ -265,12 +269,12 @@ @interface TestsAppDelegate (OFUNIXStreamSocketTests) - (void)UNIXStreamSocketTests; @end -@interface TestsAppDelegate (OFURLTests) -- (void)URLTests; +@interface TestsAppDelegate (OFURITests) +- (void)URITests; @end @interface TestsAppDelegate (OFValueTests) - (void)valueTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -173,17 +173,19 @@ return OFApplicationMain(&argc, &argv, [[TestsAppDelegate alloc] init]); } @catch (id e) { OFString *string = [OFString stringWithFormat: @"\nRuntime error: Unhandled exception:\n%@\n", e]; - OFString *backtrace = [OFString stringWithFormat: - @"\nBacktrace:\n %@\n\n", - [[e backtrace] componentsJoinedByString: @"\n "]]; [OFStdOut setForegroundColor: [OFColor red]]; [OFStdOut writeString: string]; - [OFStdOut writeString: backtrace]; + + if ([e stackTraceAddresses] != nil) + [OFStdOut writeString: @"\nStack trace:\n"]; + + for (OFValue *address in [e stackTraceAddresses]) + [OFStdOut writeFormat: @" %p\n", address.pointerValue]; # if defined(OF_WII) [OFStdOut reset]; [OFStdOut writeString: @"Press home button to exit!"]; @@ -407,25 +409,28 @@ #endif #ifdef OF_HAVE_SOCKETS [self socketTests]; [self TCPSocketTests]; [self UDPSocketTests]; +# ifdef OF_HAVE_UNIX_SOCKETS + [self UNIXDatagramSocketTests]; + [self UNIXStreamSocketTests]; +# endif # ifdef OF_HAVE_IPX [self IPXSocketTests]; [self SPXSocketTests]; [self SPXStreamSocketTests]; # endif -# ifdef OF_HAVE_UNIX_SOCKETS - [self UNIXDatagramSocketTests]; - [self UNIXStreamSocketTests]; +# ifdef OF_HAVE_APPLETALK + [self DDPSocketTests]; # endif [self kernelEventObserverTests]; #endif #ifdef OF_HAVE_THREADS [self threadTests]; #endif - [self URLTests]; + [self URITests]; #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) [self HTTPClientTests]; #endif #ifdef OF_HAVE_SOCKETS [self HTTPCookieTests]; Index: tests/objc_sync/test.m ================================================================== --- tests/objc_sync/test.m +++ tests/objc_sync/test.m @@ -43,25 +43,25 @@ return nil; } @end int -main() +main(void) { MyThread *t1, *t2; lock = [[OFObject alloc] init]; t1 = [MyThread thread]; - [t1 setName: @"A"]; + t1.name = @"A"; t2 = [MyThread thread]; - [t2 setName: @"B"]; + t2.name = @"B"; [t1 start]; [t2 start]; [t1 join]; [t2 join]; return 0; } Index: tests/serialization.xml ================================================================== --- tests/serialization.xml +++ tests/serialization.xml @@ -12,34 +12,15 @@ B"la - MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla - - - data - - - - Qu"xbar -test - 1234 - 40934a456d5cfaad - asd - 40934a456d5cfaad - - - - Hello - - Hello Wo ld! How are you? - https://objfw.nil.im/ + https://objfw.nil.im/ @@ -61,7 +42,26 @@ list + + MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla + + + data + + + + Qu"xbar +test + 1234 + 40934a456d5cfaad + asd + 40934a456d5cfaad + + + + Hello + Index: tests/terminal/TerminalTests.m ================================================================== --- tests/terminal/TerminalTests.m +++ tests/terminal/TerminalTests.m @@ -90,25 +90,25 @@ [OFThread sleepForTimeInterval: 2]; [OFStdOut clear]; [OFThread sleepForTimeInterval: 2]; - [OFStdOut setCursorPosition: OFPointMake(5, 3)]; + [OFStdOut setCursorPosition: OFMakePoint(5, 3)]; [OFStdOut writeString: @"Text at (5, 3)"]; [OFThread sleepForTimeInterval: 2]; - [OFStdOut setRelativeCursorPosition: OFPointMake(-2, 0)]; - [OFThread sleepForTimeInterval: 2]; - [OFStdOut setRelativeCursorPosition: OFPointMake(2, 0)]; - [OFThread sleepForTimeInterval: 2]; - [OFStdOut setRelativeCursorPosition: OFPointMake(0, -2)]; - [OFThread sleepForTimeInterval: 2]; - [OFStdOut setRelativeCursorPosition: OFPointMake(0, 2)]; - [OFThread sleepForTimeInterval: 2]; - [OFStdOut setRelativeCursorPosition: OFPointMake(1, 1)]; - [OFThread sleepForTimeInterval: 2]; - [OFStdOut setRelativeCursorPosition: OFPointMake(-1, -1)]; + [OFStdOut setRelativeCursorPosition: OFMakePoint(-2, 0)]; + [OFThread sleepForTimeInterval: 2]; + [OFStdOut setRelativeCursorPosition: OFMakePoint(2, 0)]; + [OFThread sleepForTimeInterval: 2]; + [OFStdOut setRelativeCursorPosition: OFMakePoint(0, -2)]; + [OFThread sleepForTimeInterval: 2]; + [OFStdOut setRelativeCursorPosition: OFMakePoint(0, 2)]; + [OFThread sleepForTimeInterval: 2]; + [OFStdOut setRelativeCursorPosition: OFMakePoint(1, 1)]; + [OFThread sleepForTimeInterval: 2]; + [OFStdOut setRelativeCursorPosition: OFMakePoint(-1, -1)]; [OFThread sleepForTimeInterval: 2]; [OFStdOut setCursorColumn: 2]; [OFThread sleepForTimeInterval: 2]; Index: utils/Makefile ================================================================== --- utils/Makefile +++ utils/Makefile @@ -1,8 +1,9 @@ include ../extra.mk -SUBDIRS += ${OFARC} \ +SUBDIRS += ${OBJFW_NEW} \ + ${OFARC} \ ${OFDNS} \ ${OFHASH} \ ${OFHTTP} \ completions Index: utils/objfw-compile ================================================================== --- utils/objfw-compile +++ utils/objfw-compile @@ -298,11 +298,11 @@ if test ! -f "$obj" -o "$i" -nt "$obj"; then build="yes" else deps=$($OBJC -E -M $CPPFLAGS $OBJCFLAGS $i | - sed 's/.*: //' | sed 's/\\//g') + sed -e 's/.*: //' -e 's/\\//g') for dep in $deps; do test "$dep" -nt $obj && build="yes" done fi Index: utils/objfw-embed ================================================================== --- utils/objfw-embed +++ utils/objfw-embed @@ -12,15 +12,15 @@ extern void OFRegisterEmbeddedFile(const char *, const uint8_t *, size_t); static const uint8_t bytes[] = { EOF -od -vtx1 $1 | sed '/^[^ ][^ ]*$/d;s/ */ /g;s/ $//g;s/^[^ ][^ ]* //;s/ /, 0x/g;s/^/ 0x/;s/$/,/' +od -vtx1 $1 | sed -e '/^[^ ][^ ]*$/d;s/ */ /g' -e 's/ $//g;s/^[^ ][^ ]* //' -e 's/ /, 0x/g' -e 's/^/ 0x/' -e 's/$/,/' cat < + * + * 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 "OFApplication.h" +#import "OFFile.h" +#import "OFStdIOStream.h" +#import "OFString.h" + +#import "OFOpenItemFailedException.h" + +void +newApp(OFString *name) +{ + OFString *path = [name stringByAppendingPathExtension: @"m"]; + OFFile *file = nil; + @try { + file = [OFFile fileWithPath: path mode: @"wx"]; + } @catch (OFOpenItemFailedException *e) { + if (e.errNo != EEXIST) + @throw e; + + [OFStdErr writeFormat: @"File %@ already exists! Aborting...\n", + e.path]; + [OFApplication terminateWithStatus: 1]; + } + + [file writeFormat: @"#import \n" + @"\n" + @"@interface %@: OFObject \n" + @"@end\n" + @"\n" + @"OF_APPLICATION_DELEGATE(%@)\n" + @"\n" + @"@implementation %@\n" + @"- (void)applicationDidFinishLaunching\n" + @"{\n" + @" [OFApplication terminate];\n" + @"}\n" + @"@end\n", + name, name, name]; + + [file close]; +} ADDED utils/objfw-new/NewClass.m Index: utils/objfw-new/NewClass.m ================================================================== --- utils/objfw-new/NewClass.m +++ utils/objfw-new/NewClass.m @@ -0,0 +1,143 @@ +/* + * 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 "Property.h" + +#import "OFApplication.h" +#import "OFArray.h" +#import "OFFile.h" +#import "OFStdIOStream.h" +#import "OFString.h" + +#import "OFOpenItemFailedException.h" + +void +newClass(OFString *name, OFString *superclass, OFMutableArray *properties) +{ + OFString *headerPath = [name stringByAppendingPathExtension: @"h"]; + OFString *implPath = [name stringByAppendingPathExtension: @"m"]; + OFFile *headerFile = nil, *implFile = nil; + bool needsDealloc = false; + @try { + headerFile = [OFFile fileWithPath: headerPath mode: @"wx"]; + implFile = [OFFile fileWithPath: implPath mode: @"wx"]; + } @catch (OFOpenItemFailedException *e) { + if (e.errNo != EEXIST) + @throw e; + + [OFStdErr writeFormat: @"File %@ already exists! Aborting...\n", + e.path]; + [OFApplication terminateWithStatus: 1]; + } + + if (superclass == nil) + superclass = @"OFObject"; + + for (size_t i = 0; i < properties.count; i++) { + Property *property = [Property propertyWithString: + [properties objectAtIndex: i]]; + [properties replaceObjectAtIndex: i + withObject: property]; + } + + [headerFile writeFormat: @"#import \n" + @"\n" + @"OF_ASSUME_NONNULL_BEGIN\n" + @"\n" + @"@interface %@: %@\n", + name, superclass]; + + if (properties.count > 0) + [headerFile writeString: @"{\n"]; + + for (Property *property in properties) + [headerFile writeFormat: @"\t%@_%@;\n", + property.type, property.name]; + + if (properties.count > 0) + [headerFile writeString: @"}\n\n"]; + + for (Property *property in properties) { + [headerFile writeString: @"@property "]; + + if (property.attributes.count > 0) { + bool first = true; + + if ([property.attributes containsObject: @"nullable"]) + [headerFile writeString: + @"OF_NULLABLE_PROPERTY "]; + + [headerFile writeString: @"("]; + + for (OFString *attribute in property.attributes) { + if ([attribute isEqual: @"nullable"]) + continue; + + if ([attribute isEqual: @"retain"] || + [attribute isEqual: @"copy"]) + needsDealloc = true; + + if (!first) + [headerFile writeString: @", "]; + + [headerFile writeString: attribute]; + first = false; + } + + [headerFile writeString: @") "]; + } + + [headerFile writeFormat: @"%@%@;\n", + property.type, property.name]; + } + + [headerFile writeString: @"@end\n" + @"\n" + @"OF_ASSUME_NONNULL_END\n"]; + + [implFile writeFormat: @"#import \"%@\"\n" + @"\n" + @"@implementation %@\n", + headerPath, name]; + + for (Property *property in properties) + [implFile writeFormat: @"@synthesize %@ = _%@;\n", + property.name, property.name]; + + if (needsDealloc) { + [implFile writeString: @"\n" + @"- (void)dealloc\n" + @"{\n"]; + + for (Property *property in properties) + if ([property.attributes containsObject: @"retain"] || + [property.attributes containsObject: @"copy"]) + [implFile writeFormat: @"\t[_%@ release];\n", + property.name]; + + [implFile writeString: @"\n" + @"\t[super dealloc];\n" + @"}\n"]; + } + + [implFile writeString: @"@end\n"]; + + [headerFile close]; + [implFile close]; +} ADDED utils/objfw-new/ObjFWNew.m Index: utils/objfw-new/ObjFWNew.m ================================================================== --- utils/objfw-new/ObjFWNew.m +++ utils/objfw-new/ObjFWNew.m @@ -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. + */ + +#include "config.h" + +#import "OFApplication.h" +#import "OFArray.h" +#import "OFObject.h" +#import "OFOptionsParser.h" +#import "OFStdIOStream.h" +#import "OFString.h" + +@interface ObjFWNew: OFObject +@end + +extern void newApp(OFString *); +extern void newClass(OFString *, OFString *, OFMutableArray *); + +OF_APPLICATION_DELEGATE(ObjFWNew) + +static void +showUsage(void) +{ + [OFStdErr writeFormat: @"Usage: %@ --app|--class name\n", + [OFApplication programName]]; + + [OFApplication terminateWithStatus: 1]; +} + +@implementation ObjFWNew +- (void)applicationDidFinishLaunching +{ + bool app, class; + OFString *superclass = nil, *name; + OFMutableArray OF_GENERIC(OFString *) *properties = nil; + const OFOptionsParserOption options[] = { + { 'a', @"app", 0, &app, NULL }, + { 'c', @"class", 0, &class, NULL }, + { 's', @"superclass", 1, NULL, &superclass }, + { 'p', @"property", 1, NULL, NULL }, + { '\0', nil, 0, NULL, NULL } + }; + OFOptionsParser *optionsParser; + OFUnichar option; + + optionsParser = [OFOptionsParser parserWithOptions: options]; + while ((option = [optionsParser nextOption]) != '\0') { + switch (option) { + case 'p': + if (properties == nil) + properties = [OFMutableArray array]; + + [properties addObject: optionsParser.argument]; + break; + case '?': + case ':': + case '=': + showUsage(); + break; + } + } + + if ((app ^ class) != 1 || optionsParser.remainingArguments.count != 1) + showUsage(); + + if ((superclass && !class) || (properties != nil && !class)) + showUsage(); + + name = optionsParser.remainingArguments.firstObject; + if ([name rangeOfString: @"."].location != OFNotFound) { + [OFStdErr writeLine: @"Name must not contain dots!"]; + [OFApplication terminate]; + } + + if (app) + newApp(name); + else if (class) + newClass(name, superclass, properties); + else + showUsage(); + + [OFApplication terminate]; +} +@end ADDED utils/objfw-new/Property.h Index: utils/objfw-new/Property.h ================================================================== --- utils/objfw-new/Property.h +++ utils/objfw-new/Property.h @@ -0,0 +1,35 @@ +/* + * 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 + +@interface Property: OFObject +{ + OFString *_name, *_type; + OFArray OF_GENERIC(OFString *) *_attributes; +} + ++ (instancetype)propertyWithString: (OFString *)string; +- (instancetype)initWithString: (OFString *)string; + +@property (readonly, nonatomic) OFString *name; +@property (readonly, nonatomic) OFString *type; +@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *attributes; +@end + +OF_ASSUME_NONNULL_END ADDED utils/objfw-new/Property.m Index: utils/objfw-new/Property.m ================================================================== --- utils/objfw-new/Property.m +++ utils/objfw-new/Property.m @@ -0,0 +1,127 @@ +/* + * 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 "Property.h" + +#import "OFArray.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" +#import "OFOutOfRangeException.h" + +@interface Property () +- (void)parseString: (OFString *)string; +@end + +@implementation Property +@synthesize name = _name, type = _type, attributes = _attributes; + ++ (instancetype)propertyWithString: (OFString *)string +{ + return [[[self alloc] initWithString: string] autorelease]; +} + +- (instancetype)initWithString: (OFString *)string +{ + self = [super init]; + + @try { + [self parseString: string]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)parseString: (OFString *)string +{ + void *pool = objc_autoreleasePoolPush(); + const char *UTF8String = string.UTF8String; + size_t length = string.UTF8StringLength; + ssize_t nameIdx = -1; + OFMutableArray *attributes = nil; + + if (length > SSIZE_MAX) + @throw [OFOutOfRangeException exception]; + + if (UTF8String[0] == '(') { + for (size_t i = 0, level = 0; i < length; i++) { + if (UTF8String[i] == '(') + level++; + else if (UTF8String[i] == ')') { + if (--level == 0) { + OFString *attributesString = [OFString + stringWithUTF8String: UTF8String + 1 + length: i - 1]; + attributes = [[[attributesString + componentsSeparatedByString: @","] + mutableCopy] autorelease]; + + UTF8String += i + 1; + length += i + 1; + + while (*UTF8String == ' ' || + *UTF8String == '\t') { + UTF8String++; + length--; + } + + break; + } + } + } + } + + for (size_t i = 0; i < attributes.count; i++) { + OFString *attribute = [[attributes objectAtIndex: i] + stringByDeletingEnclosingWhitespaces]; + + [attributes replaceObjectAtIndex: i + withObject: attribute]; + } + + [attributes makeImmutable]; + _attributes = [attributes copy]; + + for (ssize_t i = (ssize_t)length - 1; i > 0; i--) { + if (UTF8String[i] == '*' || UTF8String[i] == ' ' || + UTF8String[i] == '\t') { + nameIdx = i + 1; + break; + } + } + + if (nameIdx < 0) + @throw [OFInvalidArgumentException exception]; + + _name = [[OFString alloc] initWithUTF8String: UTF8String + nameIdx]; + _type = [[OFString alloc] initWithUTF8String: UTF8String + length: (size_t)nameIdx]; + + objc_autoreleasePoolPop(pool); +} + +- (void)dealloc +{ + [_name release]; + [_type release]; + + [super dealloc]; +} +@end Index: utils/ofarc/LHAArchive.m ================================================================== --- utils/ofarc/LHAArchive.m +++ utils/ofarc/LHAArchive.m @@ -38,20 +38,20 @@ static void setPermissions(OFString *path, OFLHAArchiveEntry *entry) { #ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS - OFNumber *mode = entry.mode; + OFNumber *POSIXPermissions = entry.POSIXPermissions; - if (mode == nil) + if (POSIXPermissions == nil) return; - mode = [OFNumber numberWithUnsignedShort: - mode.unsignedShortValue & 0777]; + POSIXPermissions = [OFNumber numberWithUnsignedShort: + POSIXPermissions.unsignedShortValue & 0777]; OFFileAttributes attributes = [OFDictionary - dictionaryWithObject: mode + dictionaryWithObject: POSIXPermissions forKey: OFFilePOSIXPermissions]; [[OFFileManager defaultManager] setAttributes: attributes ofItemAtPath: path]; #endif @@ -58,26 +58,12 @@ } static void setModificationDate(OFString *path, OFLHAArchiveEntry *entry) { - OFDate *modificationDate = entry.modificationDate; - OFFileAttributes attributes; - - if (modificationDate == nil) { - /* - * Fall back to the original date if we have no modification - * date, as the modification date is a UNIX extension. - */ - modificationDate = entry.date; - - if (modificationDate == nil) - return; - } - - attributes = [OFDictionary - dictionaryWithObject: modificationDate + OFFileAttributes attributes = [OFDictionary + dictionaryWithObject: entry.modificationDate forKey: OFFileModificationDate]; [[OFFileManager defaultManager] setAttributes: attributes ofItemAtPath: path]; } @@ -132,16 +118,16 @@ void *pool = objc_autoreleasePoolPush(); [OFStdOut writeLine: entry.fileName]; if (app->_outputLevel >= 1) { - OFString *date = [entry.date + OFString *modificationDate = [entry.modificationDate localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"]; OFString *compressedSize = [OFString stringWithFormat: - @"%" PRIu32, entry.compressedSize]; + @"%llu", entry.compressedSize]; OFString *uncompressedSize = [OFString stringWithFormat: - @"%" PRIu32, entry.uncompressedSize]; + @"%llu", entry.uncompressedSize]; OFString *CRC16 = [OFString stringWithFormat: @"%04" PRIX16, entry.CRC16]; [OFStdOut writeString: @"\t"]; [OFStdOut writeLine: OF_LOCALIZED( @@ -173,49 +159,53 @@ [OFStdOut writeString: @"\t"]; [OFStdOut writeLine: OF_LOCALIZED(@"list_crc16", @"CRC16: %[crc16]", @"crc16", CRC16)]; [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED(@"list_date", - @"Date: %[date]", - @"date", date)]; - - if (entry.mode != nil) { - OFString *modeString = [OFString - stringWithFormat: - @"%ho", entry.mode.unsignedShortValue]; - - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED(@"list_mode", - @"Mode: %[mode]", - @"mode", modeString)]; - } - if (entry.UID != nil) { - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED(@"list_uid", - @"UID: %[uid]", - @"uid", entry.UID)]; - } - if (entry.GID != nil) { + [OFStdOut writeLine: OF_LOCALIZED( + @"list_modification_date", + @"Modification date: %[date]", + @"date", modificationDate)]; + + if (entry.POSIXPermissions != nil) { + OFString *permissionsString = [OFString + stringWithFormat: @"%llo", + entry.POSIXPermissions + .unsignedLongLongValue]; + + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_posix_permissions", + @"POSIX permissions: %[perm]", + @"perm", permissionsString)]; + } + if (entry.ownerAccountID != nil) { + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_owner_account_id", + @"Owner account ID: %[id]", + @"id", entry.ownerAccountID)]; + } + if (entry.groupOwnerAccountID != nil) { [OFStdOut writeString: @"\t"]; [OFStdOut writeLine: OF_LOCALIZED(@"list_gid", - @"GID: %[gid]", - @"gid", entry.GID)]; - } - if (entry.owner != nil) { - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED( - @"list_owner", - @"Owner: %[owner]", - @"owner", entry.owner)]; - } - if (entry.group != nil) { - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED( - @"list_group", - @"Group: %[group]", - @"group", entry.group)]; + @"Group owner account ID: %[id]", + @"id", entry.groupOwnerAccountID)]; + } + if (entry.ownerAccountName != nil) { + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_owner_account_name", + @"Owner account name: %[name]", + @"name", entry.ownerAccountName)]; + } + if (entry.groupOwnerAccountName != nil) { + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_group_owner_account_name", + @"Group: %[name]", + @"name", entry.groupOwnerAccountName)]; } if (app->_outputLevel >= 2) { OFString *headerLevel = [OFString stringWithFormat: @"%" PRIu8, @@ -237,21 +227,10 @@ @"list_osid", @"Operating system identifier: " "%[osid]", @"osid", OSID)]; } - - if (entry.modificationDate != nil) { - OFString *modificationDate = - entry.modificationDate.description; - - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED( - @"list_modification_date", - @"Modification date: %[date]", - @"date", modificationDate)]; - } } if (app->_outputLevel >= 3) { OFString *extensions = indent(entry.extensions.description); @@ -280,11 +259,11 @@ void *pool = objc_autoreleasePoolPush(); OFString *fileName = entry.fileName; OFString *outFileName, *directory; OFFile *output; OFStream *stream; - uint64_t written = 0, size = entry.uncompressedSize; + unsigned long long written = 0, size = entry.uncompressedSize; int8_t percent = -1, newPercent; if (!all && ![files containsObject: fileName]) continue; @@ -466,22 +445,24 @@ attributes = [fileManager attributesOfItemAtPath: fileName]; type = attributes.fileType; entry = [OFMutableLHAArchiveEntry entryWithFileName: fileName]; #ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS - entry.mode = [OFNumber numberWithUnsignedLong: - attributes.filePOSIXPermissions]; + entry.POSIXPermissions = + [attributes objectForKey: OFFilePOSIXPermissions]; #endif - entry.date = attributes.fileModificationDate; + entry.modificationDate = attributes.fileModificationDate; #ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - entry.UID = [OFNumber numberWithUnsignedLong: - attributes.fileOwnerAccountID]; - entry.GID = [OFNumber numberWithUnsignedLong: - attributes.fileGroupOwnerAccountID]; - entry.owner = attributes.fileOwnerAccountName; - entry.group = attributes.fileGroupOwnerAccountName; + entry.ownerAccountID = + [attributes objectForKey: OFFileOwnerAccountID]; + entry.groupOwnerAccountID = + [attributes objectForKey: OFFileGroupOwnerAccountID]; + entry.ownerAccountName = + [attributes objectForKey: OFFileOwnerAccountName]; + entry.groupOwnerAccountName = + [attributes objectForKey: OFFileGroupOwnerAccountName]; #endif if ([type isEqual: OFFileTypeDirectory]) { entry.compressionMethod = @"-lhd-"; Index: utils/ofarc/Makefile ================================================================== --- utils/ofarc/Makefile +++ utils/ofarc/Makefile @@ -4,24 +4,24 @@ SRCS = GZIPArchive.m \ LHAArchive.m \ OFArc.m \ TarArchive.m \ ZIPArchive.m -DATA = lang/de.json \ - lang/languages.json +DATA = localization/de.json \ + localization/localizations.json include ../../buildsys.mk PACKAGE_NAME = ofarc ${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2} -CPPFLAGS += -I../../src \ - -I../../src/runtime \ - -I../../src/exceptions \ - -I../.. \ - -DLANGUAGE_DIR=\"${datadir}/ofarc/lang\" +CPPFLAGS += -I../../src \ + -I../../src/runtime \ + -I../../src/exceptions \ + -I../.. \ + -DLOCALIZATION_DIR=\"${datadir}/ofarc/localization\" LIBS := -L../../src -lobjfw \ -L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS} \ ${LIBS} LD = ${OBJC} LDFLAGS += ${LDFLAGS_RPATH} Index: utils/ofarc/OFArc.m ================================================================== --- utils/ofarc/OFArc.m +++ utils/ofarc/OFArc.m @@ -23,11 +23,11 @@ #import "OFFileManager.h" #import "OFLocale.h" #import "OFOptionsParser.h" #import "OFSandbox.h" #import "OFStdIOStream.h" -#import "OFURL.h" +#import "OFURI.h" #import "OFArc.h" #import "GZIPArchive.h" #import "LHAArchive.h" #import "TarArchive.h" @@ -205,13 +205,14 @@ [OFApplication of_activateSandbox: sandbox]; #endif #ifndef OF_AMIGAOS - [OFLocale addLanguageDirectory: @LANGUAGE_DIR]; + [OFLocale addLocalizationDirectory: @LOCALIZATION_DIR]; #else - [OFLocale addLanguageDirectory: @"PROGDIR:/share/ofarc/lang"]; + [OFLocale addLocalizationDirectory: + @"PROGDIR:/share/ofarc/localization"]; #endif optionsParser = [OFOptionsParser parserWithOptions: options]; while ((option = [optionsParser nextOption]) != '\0') { switch (option) { @@ -335,11 +336,11 @@ case 'c': if (remainingArguments.count < 1) help(OFStdErr, false, 1); files = [remainingArguments objectsInRange: - OFRangeMake(1, remainingArguments.count - 1)]; + OFMakeRange(1, remainingArguments.count - 1)]; #ifdef OF_HAVE_SANDBOX if (![remainingArguments.firstObject isEqual: @"-"]) [sandbox unveilPath: remainingArguments.firstObject permissions: (mode == 'a' ? @"rwc" : @"wc")]; @@ -392,11 +393,11 @@ sandbox.allowsUnveil = false; [OFApplication of_activateSandbox: sandbox]; #endif files = [remainingArguments objectsInRange: - OFRangeMake(1, remainingArguments.count - 1)]; + OFMakeRange(1, remainingArguments.count - 1)]; archive = [self openArchiveWithPath: remainingArguments.firstObject type: type mode: mode @@ -407,11 +408,11 @@ case 'x': if (remainingArguments.count < 1) help(OFStdErr, false, 1); files = [remainingArguments objectsInRange: - OFRangeMake(1, remainingArguments.count - 1)]; + OFMakeRange(1, remainingArguments.count - 1)]; #ifdef OF_HAVE_SANDBOX if (![remainingArguments.firstObject isEqual: @"-"]) [sandbox unveilPath: remainingArguments.firstObject permissions: @"r"]; @@ -459,11 +460,11 @@ encoding: [OFLocale encoding]]; [OFStdErr writeString: @"\r"]; [OFStdErr writeLine: OF_LOCALIZED( @"failed_to_create_directory", @"Failed to create directory %[dir]: %[error]", - @"dir", e.URL.fileSystemRepresentation, + @"dir", e.URI.fileSystemRepresentation, @"error", error)]; _exitStatus = 1; } @catch (OFOpenItemFailedException *e) { OFString *error = [OFString stringWithCString: strerror(e.errNo) Index: utils/ofarc/TarArchive.m ================================================================== --- utils/ofarc/TarArchive.m +++ utils/ofarc/TarArchive.m @@ -31,13 +31,14 @@ static void setPermissions(OFString *path, OFTarArchiveEntry *entry) { #ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS - OFNumber *mode = [OFNumber numberWithUnsignedShort: entry.mode & 0777]; + OFNumber *POSIXPermissions = [OFNumber numberWithUnsignedLongLong: + entry.POSIXPermissions.longLongValue & 0777]; OFFileAttributes attributes = [OFDictionary - dictionaryWithObject: mode + dictionaryWithObject: POSIXPermissions forKey: OFFilePOSIXPermissions]; [[OFFileManager defaultManager] setAttributes: attributes ofItemAtPath: path]; #endif @@ -113,17 +114,15 @@ if (app->_outputLevel >= 1) { OFString *date = [entry.modificationDate localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"]; OFString *size = [OFString stringWithFormat: - @"%" PRIu64, entry.size]; - OFString *mode = [OFString stringWithFormat: - @"%06o", entry.mode]; - OFString *UID = [OFString stringWithFormat: - @"%u", entry.UID]; - OFString *GID = [OFString stringWithFormat: - @"%u", entry.GID]; + @"%llu", entry.uncompressedSize]; + OFString *permissionsString = [OFString + stringWithFormat: + @"%llo", entry.POSIXPermissions + .unsignedLongLongValue]; [OFStdOut writeString: @"\t"]; [OFStdOut writeLine: OF_LOCALIZED(@"list_size", @"[" @" 'Size: '," @@ -132,35 +131,38 @@ @" {'': '%[size] bytes'}" @" ]" @"]".objectByParsingJSON, @"size", size)]; [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED(@"list_mode", - @"Mode: %[mode]", - @"mode", mode)]; - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED(@"list_uid", - @"UID: %[uid]", - @"uid", UID)]; - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED(@"list_gid", - @"GID: %[gid]", - @"gid", GID)]; - - if (entry.owner != nil) { - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED( - @"list_owner", - @"Owner: %[owner]", - @"owner", entry.owner)]; - } - if (entry.group != nil) { - [OFStdOut writeString: @"\t"]; - [OFStdOut writeLine: OF_LOCALIZED( - @"list_group", - @"Group: %[group]", - @"group", entry.group)]; + [OFStdOut writeLine: + OF_LOCALIZED(@"list_posix_permissions", + @"POSIX permissions: %[perm]", + @"perm", permissionsString)]; + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_owner_account_id", + @"Owner account ID: %[id]", + @"id", entry.ownerAccountID)]; + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_group_owner_account_id", + @"Group owner account ID: %[id]", + @"id", entry.groupOwnerAccountID)]; + + if (entry.ownerAccountName != nil) { + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_owner_account_name", + @"Owner account name: %[name]", + @"name", entry.ownerAccountName)]; + } + if (entry.groupOwnerAccountName != nil) { + [OFStdOut writeString: @"\t"]; + [OFStdOut writeLine: OF_LOCALIZED( + @"list_group_owner_account_name", + @"Group owner account name: %[name]", + @"name", entry.groupOwnerAccountName)]; } [OFStdOut writeString: @"\t"]; [OFStdOut writeLine: OF_LOCALIZED( @"list_modification_date", @@ -279,11 +281,11 @@ OFString *fileName = entry.fileName; OFTarArchiveEntryType type = entry.type; OFString *outFileName, *directory; OFFile *output; OFStream *stream; - uint64_t written = 0, size = entry.size; + unsigned long long written = 0, size = entry.uncompressedSize; int8_t percent = -1, newPercent; if (!all && ![files containsObject: fileName]) continue; @@ -477,40 +479,45 @@ attributes = [fileManager attributesOfItemAtPath: fileName]; type = attributes.fileType; entry = [OFMutableTarArchiveEntry entryWithFileName: fileName]; #ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS - entry.mode = attributes.filePOSIXPermissions; + entry.POSIXPermissions = + [attributes objectForKey: OFFilePOSIXPermissions]; #endif - entry.size = attributes.fileSize; + entry.uncompressedSize = attributes.fileSize; entry.modificationDate = attributes.fileModificationDate; #ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - entry.UID = attributes.fileOwnerAccountID; - entry.GID = attributes.fileGroupOwnerAccountID; - entry.owner = attributes.fileOwnerAccountName; - entry.group = attributes.fileGroupOwnerAccountName; + entry.ownerAccountID = + [attributes objectForKey: OFFileOwnerAccountID]; + entry.groupOwnerAccountID = + [attributes objectForKey: OFFileGroupOwnerAccountID]; + entry.ownerAccountName = attributes.fileOwnerAccountName; + entry.groupOwnerAccountName = + attributes.fileGroupOwnerAccountName; #endif if ([type isEqual: OFFileTypeRegular]) entry.type = OFTarArchiveEntryTypeFile; else if ([type isEqual: OFFileTypeDirectory]) { entry.type = OFTarArchiveEntryTypeDirectory; - entry.size = 0; + entry.uncompressedSize = 0; } else if ([type isEqual: OFFileTypeSymbolicLink]) { entry.type = OFTarArchiveEntryTypeSymlink; entry.targetFileName = attributes.fileSymbolicLinkDestination; - entry.size = 0; + entry.uncompressedSize = 0; } [entry makeImmutable]; output = [_archive streamForWritingEntry: entry]; if (entry.type == OFTarArchiveEntryTypeFile) { - uint64_t written = 0, size = entry.size; + unsigned long long written = 0; + unsigned long long size = entry.uncompressedSize; int8_t percent = -1, newPercent; OFFile *input = [OFFile fileWithPath: fileName mode: @"r"]; Index: utils/ofarc/ZIPArchive.m ================================================================== --- utils/ofarc/ZIPArchive.m +++ utils/ofarc/ZIPArchive.m @@ -246,11 +246,11 @@ void *pool = objc_autoreleasePoolPush(); OFString *fileName = entry.fileName; OFString *outFileName, *directory; OFStream *stream; OFFile *output; - uint64_t written = 0, size = entry.uncompressedSize; + unsigned long long written = 0, size = entry.uncompressedSize; int8_t percent = -1, newPercent; if (!all && ![files containsObject: fileName]) continue; @@ -437,15 +437,12 @@ @"file", fileName)]; entry = [OFMutableZIPArchiveEntry entryWithFileName: fileName]; size = (isDirectory ? 0 : attributes.fileSize); - if (size > INT64_MAX) - @throw [OFOutOfRangeException exception]; - - entry.compressedSize = (int64_t)size; - entry.uncompressedSize = (int64_t)size; + entry.compressedSize = size; + entry.uncompressedSize = size; entry.compressionMethod = OFZIPArchiveEntryCompressionMethodNone; entry.modificationDate = attributes.fileModificationDate; DELETED utils/ofarc/lang/de.json Index: utils/ofarc/lang/de.json ================================================================== --- utils/ofarc/lang/de.json +++ utils/ofarc/lang/de.json @@ -1,129 +0,0 @@ -{ - "usage": [ - "Benutzung: %[prog] -[acCfhlnpqtvx] archiv.zip [datei1 datei2 ...]" - ], - "full_usage": [ - "Optionen:\n", - " -a --append Zu Archiv hinzufügen\n", - " -c --create Archiv erstellen\n", - " -C --directory In angegebenes Verzeichnis entpacken\n", - " -E --encoding Das Encoding des Archivs (nur tar-Dateien)\n", - " -f --force Existierende Dateien überschreiben\n", - " -h --help Diese Hilfe anzeigen\n", - " -l --list Alle Dateien im Archiv auflisten\n", - " -n --no-clobber Dateien niemals überschreiben\n", - " -p --print Eine oder mehr Dateien aus dem Archiv ausgeben", - "\n", - " -q --quiet Ruhiger Modus (keine Ausgabe außer Fehler)\n", - " -t --type Archiv-Typ (gz, lha, tar, tgz, zip)\n", - " -v --verbose Ausführlicher Modus für Datei-Liste\n", - " -x --extract Dateien entpacken" - ], - "2_options_mutually_exclusive": [ - "Fehler: -%[shortopt1] / --%[longopt1] und ", - "-%[shortopt2] / --%[longopt2] schließen sich gegenseitig aus!" - ], - "5_options_mutually_exclusive": [ - "Fehler: -%[shortopt1] / --%[longopt1], -%[shortopt2] / ", - "--%[longopt2], -%[shortopt3] / --%[longopt3], ", - "-%[shortopt4] / --%[longopt4] und\n", - " -%[shortopt5] / --%[longopt5] schließen sich gegenseitig aus!" - ], - "option_takes_no_argument": "%[prog]: Option --%[opt] nimmt kein Argument", - "long_option_requires_argument": [ - "%[prog]: Option --%[opt] benötigt ein Argument" - ], - "option_requires_argument": "%[prog]: Option -%[opt] benötigt ein Argument", - "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", - "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", - "invalid_encoding": "%[prog]: Invalid encoding: %[encoding]", - "writing_not_supported": [ - "Schreiben von Dateien des Typs %[type] wird (noch) nicht unterstützt!" - ], - "failed_to_create_directory": [ - "Fehler beim Erstellen des Verzeichnis %[dir]: %[error]" - ], - "failed_to_open_file": "Fehler beim Öffnen der Datei %[file]: %[error]", - "unknown_archive_type": "Unbekannter Archivtyp: %[type]", - "failed_to_read_file": "Fehler beim Lesen der Datei %[file]: %[error]", - "failed_to_write_file": "Fehler beim Schreiben der Datei %[file]: %[error]", - "failed_to_seek_in_file": "Fehler beim Suchen in Datei %[file]: %[error]", - "file_is_not_a_valid_archive": "Datei %[file] ist kein gültiges Archiv!", - "file_skipped": "übersprungen", - "ask_overwrite": "%[file] überschreiben? [ynAN?]", - "ask_overwrite_help": [ - " y: Ja\n", - " n: Nein\n", - " A: Immer\n", - " N: Nie" - ], - "skipping_file": "Überspringe %[file]...", - "extracting_file": "Entpacke %[file]...", - "extracting_file_percent": "Entpacke %[file]... %[percent]%", - "extracting_file_done": "Entpacke %[file]... fertig", - "cannot_list_gz": "Kann Dateien eines .gz-Archivs nicht auflisten!", - "cannot_extract_specific_file_from_gz": [ - "Kann keine spezifische Datei aus einem .gz-Archiv entpacken!" - ], - "cannot_print_specific_file_from_gz": [ - "Kann keine spezifische Datei aus einem .gz-Archiv ausgeben!" - ], - "list_size": [ - "Größe: ", - [ - {"size == 1": "1 Byte"}, - {"": "%[size] Bytes"} - ] - ], - "list_mode": "Modus: %[mode]", - "list_owner": "Besitzer: %[owner]", - "list_group": "Gruppe: %[group]", - "list_header_level": "Header-Level: %[level]", - "list_modification_date": "Änderungsdatum: %[date]", - "list_type_normal": "Typ: Normale Datei", - "list_type_hardlink": "Typ: Harter Link", - "list_type_symlink": "Typ: Symbolischer Link", - "list_link_target": "Zieldateiname: %[target]", - "list_type_character_device": "Typ: Zeichenorientiertes Gerät", - "list_type_block_device": "Typ: Blockorientiertes Gerät", - "list_device_major": "Major-Nummer des Geräts: %[major]", - "list_device_minor": "Minor-Nummer des Geräts: %[minor]", - "list_type_directory": "Typ: Verzeichnis", - "list_type_fifo": "Typ: FIFO", - "list_type_contiguous_file": "Typ: Zusammenhängende Datei", - "list_type_unknown": "Typ: Unbekannt", - "list_compressed_size": [ - "Komprimierte Größe: ", - [ - {"size == 1": "1 Byte"}, - {"": "%[size] Bytes"} - ] - ], - "list_uncompressed_size": [ - "Unkomprimierte Größe: ", - [ - {"size == 1": "1 Byte"}, - {"": "%[size] Bytes"} - ] - ], - "list_compression_method": "Kompressionsmethode: %[method]", - "list_date": "Datum: %[date]", - "list_osid": "Betriebssystem-Identifikator: %[osid]", - "list_extensions": "Erweiterungen: %[extensions]", - "list_version_made_by": "Erstellt mit Version: %[version]", - "list_min_version_needed": "Mindestens benötigte Version: %[version]", - "list_general_purpose_bit_flag": "General Purpose Bit Flag: %[gpbf]", - "list_extra_field": "Extra-Feld: %[extra]", - "list_comment": "Kommentar: %[comment]", - "refusing_to_extract_file": "Verweigere Entpacken von %[file]!", - "file_not_in_archive": "Datei %[file] ist nicht im Archiv!", - "print_no_file_specified": [ - "Benötige eine oder mehrere Dateien zum Ausgeben!" - ], - "add_no_file_specified": [ - "Benötige eine oder mehrere Dateien zum Hinzufügen!" - ], - "adding_file": "Füge %[file] hinzu...", - "adding_file_percent": "Füge %[file] hinzu... %[percent]%", - "adding_file_done": "Füge %[file] hinzu... fertig" -} DELETED utils/ofarc/lang/languages.json Index: utils/ofarc/lang/languages.json ================================================================== --- utils/ofarc/lang/languages.json +++ utils/ofarc/lang/languages.json @@ -1,11 +0,0 @@ -{ - "de": { - "": "de" - }, - "deutsch": { - "": "de" - }, - "german": { - "": "de" - } -} ADDED utils/ofarc/localization/de.json Index: utils/ofarc/localization/de.json ================================================================== --- utils/ofarc/localization/de.json +++ utils/ofarc/localization/de.json @@ -0,0 +1,130 @@ +{ + "usage": [ + "Benutzung: %[prog] -[acCfhlnpqtvx] archiv.zip [datei1 datei2 ...]" + ], + "full_usage": [ + "Optionen:\n", + " -a --append Zu Archiv hinzufügen\n", + " -c --create Archiv erstellen\n", + " -C --directory In angegebenes Verzeichnis entpacken\n", + " -E --encoding Das Encoding des Archivs (nur tar-Dateien)\n", + " -f --force Existierende Dateien überschreiben\n", + " -h --help Diese Hilfe anzeigen\n", + " -l --list Alle Dateien im Archiv auflisten\n", + " -n --no-clobber Dateien niemals überschreiben\n", + " -p --print Eine oder mehr Dateien aus dem Archiv ausgeben", + "\n", + " -q --quiet Ruhiger Modus (keine Ausgabe außer Fehler)\n", + " -t --type Archiv-Typ (gz, lha, tar, tgz, zip)\n", + " -v --verbose Ausführlicher Modus für Datei-Liste\n", + " -x --extract Dateien entpacken" + ], + "2_options_mutually_exclusive": [ + "Fehler: -%[shortopt1] / --%[longopt1] und ", + "-%[shortopt2] / --%[longopt2] schließen sich gegenseitig aus!" + ], + "5_options_mutually_exclusive": [ + "Fehler: -%[shortopt1] / --%[longopt1], -%[shortopt2] / ", + "--%[longopt2], -%[shortopt3] / --%[longopt3], ", + "-%[shortopt4] / --%[longopt4] und\n", + " -%[shortopt5] / --%[longopt5] schließen sich gegenseitig aus!" + ], + "option_takes_no_argument": "%[prog]: Option --%[opt] nimmt kein Argument", + "long_option_requires_argument": [ + "%[prog]: Option --%[opt] benötigt ein Argument" + ], + "option_requires_argument": "%[prog]: Option -%[opt] benötigt ein Argument", + "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", + "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", + "invalid_encoding": "%[prog]: Invalid encoding: %[encoding]", + "writing_not_supported": [ + "Schreiben von Dateien des Typs %[type] wird (noch) nicht unterstützt!" + ], + "failed_to_create_directory": [ + "Fehler beim Erstellen des Verzeichnis %[dir]: %[error]" + ], + "failed_to_open_file": "Fehler beim Öffnen der Datei %[file]: %[error]", + "unknown_archive_type": "Unbekannter Archivtyp: %[type]", + "failed_to_read_file": "Fehler beim Lesen der Datei %[file]: %[error]", + "failed_to_write_file": "Fehler beim Schreiben der Datei %[file]: %[error]", + "failed_to_seek_in_file": "Fehler beim Suchen in Datei %[file]: %[error]", + "file_is_not_a_valid_archive": "Datei %[file] ist kein gültiges Archiv!", + "file_skipped": "übersprungen", + "ask_overwrite": "%[file] überschreiben? [ynAN?]", + "ask_overwrite_help": [ + " y: Ja\n", + " n: Nein\n", + " A: Immer\n", + " N: Nie" + ], + "skipping_file": "Überspringe %[file]...", + "extracting_file": "Entpacke %[file]...", + "extracting_file_percent": "Entpacke %[file]... %[percent]%", + "extracting_file_done": "Entpacke %[file]... fertig", + "cannot_list_gz": "Kann Dateien eines .gz-Archivs nicht auflisten!", + "cannot_extract_specific_file_from_gz": [ + "Kann keine spezifische Datei aus einem .gz-Archiv entpacken!" + ], + "cannot_print_specific_file_from_gz": [ + "Kann keine spezifische Datei aus einem .gz-Archiv ausgeben!" + ], + "list_size": [ + "Größe: ", + [ + {"size == 1": "1 Byte"}, + {"": "%[size] Bytes"} + ] + ], + "list_posix_permissions": "POSIX-Berechtigungen: %[perm]", + "list_owner_account_id": "Besitzerkontennummer: %[id]", + "list_group_owner_account_id": "Gruppenbesitzerkontennummer: %[id]", + "list_owner_account_name": "Besitzerkontenname: %[name]", + "list_group_owner_account_name": "Gruppebesitzerkontenname: %[name]", + "list_header_level": "Header-Level: %[level]", + "list_modification_date": "Änderungsdatum: %[date]", + "list_type_normal": "Typ: Normale Datei", + "list_type_hardlink": "Typ: Harter Link", + "list_type_symlink": "Typ: Symbolischer Link", + "list_link_target": "Zieldateiname: %[target]", + "list_type_character_device": "Typ: Zeichenorientiertes Gerät", + "list_type_block_device": "Typ: Blockorientiertes Gerät", + "list_device_major": "Major-Nummer des Geräts: %[major]", + "list_device_minor": "Minor-Nummer des Geräts: %[minor]", + "list_type_directory": "Typ: Verzeichnis", + "list_type_fifo": "Typ: FIFO", + "list_type_contiguous_file": "Typ: Zusammenhängende Datei", + "list_type_unknown": "Typ: Unbekannt", + "list_compressed_size": [ + "Komprimierte Größe: ", + [ + {"size == 1": "1 Byte"}, + {"": "%[size] Bytes"} + ] + ], + "list_uncompressed_size": [ + "Unkomprimierte Größe: ", + [ + {"size == 1": "1 Byte"}, + {"": "%[size] Bytes"} + ] + ], + "list_compression_method": "Kompressionsmethode: %[method]", + "list_osid": "Betriebssystem-Identifikator: %[osid]", + "list_extensions": "Erweiterungen: %[extensions]", + "list_version_made_by": "Erstellt mit Version: %[version]", + "list_min_version_needed": "Mindestens benötigte Version: %[version]", + "list_general_purpose_bit_flag": "General Purpose Bit Flag: %[gpbf]", + "list_extra_field": "Extra-Feld: %[extra]", + "list_comment": "Kommentar: %[comment]", + "refusing_to_extract_file": "Verweigere Entpacken von %[file]!", + "file_not_in_archive": "Datei %[file] ist nicht im Archiv!", + "print_no_file_specified": [ + "Benötige eine oder mehrere Dateien zum Ausgeben!" + ], + "add_no_file_specified": [ + "Benötige eine oder mehrere Dateien zum Hinzufügen!" + ], + "adding_file": "Füge %[file] hinzu...", + "adding_file_percent": "Füge %[file] hinzu... %[percent]%", + "adding_file_done": "Füge %[file] hinzu... fertig" +} ADDED utils/ofarc/localization/localizations.json Index: utils/ofarc/localization/localizations.json ================================================================== --- utils/ofarc/localization/localizations.json +++ utils/ofarc/localization/localizations.json @@ -0,0 +1,11 @@ +{ + "de": { + "": "de" + }, + "deutsch": { + "": "de" + }, + "german": { + "": "de" + } +} Index: utils/ofdns/Makefile ================================================================== --- utils/ofdns/Makefile +++ utils/ofdns/Makefile @@ -1,23 +1,23 @@ include ../../extra.mk PROG = ofdns${PROG_SUFFIX} SRCS = OFDNS.m -DATA = lang/de.json \ - lang/languages.json +DATA = localization/de.json \ + localization/localizations.json include ../../buildsys.mk PACKAGE_NAME = ofdns ${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2} -CPPFLAGS += -I../../src \ - -I../../src/runtime \ - -I../../src/exceptions \ - -I../.. \ - -DLANGUAGE_DIR=\"${datadir}/ofdns/lang\" +CPPFLAGS += -I../../src \ + -I../../src/runtime \ + -I../../src/exceptions \ + -I../.. \ + -DLOCALIZATION_DIR=\"${datadir}/ofdns/localization\" LIBS := -L../../src -lobjfw \ -L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS} \ ${LIBS} LD = ${OBJC} LDFLAGS += ${LDFLAGS_RPATH} Index: utils/ofdns/OFDNS.m ================================================================== --- utils/ofdns/OFDNS.m +++ utils/ofdns/OFDNS.m @@ -97,13 +97,14 @@ OFDNSResolver *resolver; OFDNSClass DNSClass; #ifdef OF_HAVE_FILES # ifndef OF_AMIGAOS - [OFLocale addLanguageDirectory: @LANGUAGE_DIR]; + [OFLocale addLocalizationDirectory: @LOCALIZATION_DIR]; # else - [OFLocale addLanguageDirectory: @"PROGDIR:/share/ofdns/lang"]; + [OFLocale addLocalizationDirectory: + @"PROGDIR:/share/ofdns/localization"]; # endif #endif #ifdef OF_HAVE_SANDBOX OFSandbox *sandbox = [[OFSandbox alloc] init]; DELETED utils/ofdns/lang/de.json Index: utils/ofdns/lang/de.json ================================================================== --- utils/ofdns/lang/de.json +++ utils/ofdns/lang/de.json @@ -1,18 +0,0 @@ -{ - "usage": "Benutzung: %[prog] -[chst] domain1 [domain2 ...]", - "full_usage": [ - "Optionen:\n", - " -c --class Die anzufragende DNS-Klasse (standardmäßig IN)\n", - " -h --help Diese Hilfe anzeigen\n", - " -s --server Der abzufragende Server\n", - " -t --type Der anzufragende Record-Typ (standardmäßig ALL,\n", - " kann wiederholt werden)" - ], - "long_option_requires_argument": [ - "%[prog]: Option --%[opt] benötigt ein Argument" - ], - "option_requires_argument": "%[prog]: Option -%[opt] benötigt ein Argument", - "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", - "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", - "failed_to_resolve": "Auflösen fehlgeschlagen: %[exception]" -} DELETED utils/ofdns/lang/languages.json Index: utils/ofdns/lang/languages.json ================================================================== --- utils/ofdns/lang/languages.json +++ utils/ofdns/lang/languages.json @@ -1,11 +0,0 @@ -{ - "de": { - "": "de" - }, - "deutsch": { - "": "de" - }, - "german": { - "": "de" - } -} ADDED utils/ofdns/localization/de.json Index: utils/ofdns/localization/de.json ================================================================== --- utils/ofdns/localization/de.json +++ utils/ofdns/localization/de.json @@ -0,0 +1,18 @@ +{ + "usage": "Benutzung: %[prog] -[chst] domain1 [domain2 ...]", + "full_usage": [ + "Optionen:\n", + " -c --class Die anzufragende DNS-Klasse (standardmäßig IN)\n", + " -h --help Diese Hilfe anzeigen\n", + " -s --server Der abzufragende Server\n", + " -t --type Der anzufragende Record-Typ (standardmäßig ALL,\n", + " kann wiederholt werden)" + ], + "long_option_requires_argument": [ + "%[prog]: Option --%[opt] benötigt ein Argument" + ], + "option_requires_argument": "%[prog]: Option -%[opt] benötigt ein Argument", + "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", + "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", + "failed_to_resolve": "Auflösen fehlgeschlagen: %[exception]" +} ADDED utils/ofdns/localization/localizations.json Index: utils/ofdns/localization/localizations.json ================================================================== --- utils/ofdns/localization/localizations.json +++ utils/ofdns/localization/localizations.json @@ -0,0 +1,11 @@ +{ + "de": { + "": "de" + }, + "deutsch": { + "": "de" + }, + "german": { + "": "de" + } +} Index: utils/ofhash/Makefile ================================================================== --- utils/ofhash/Makefile +++ utils/ofhash/Makefile @@ -1,23 +1,23 @@ include ../../extra.mk PROG = ofhash${PROG_SUFFIX} SRCS = OFHash.m -DATA = lang/de.json \ - lang/languages.json +DATA = localization/de.json \ + localization/localizations.json include ../../buildsys.mk PACKAGE_NAME = ofhash ${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2} -CPPFLAGS += -I../../src \ - -I../../src/runtime \ - -I../../src/exceptions \ - -I../.. \ - -DLANGUAGE_DIR=\"${datadir}/ofhash/lang\" +CPPFLAGS += -I../../src \ + -I../../src/runtime \ + -I../../src/exceptions \ + -I../.. \ + -DLOCALIZATION_DIR=\"${datadir}/ofhash/localization\" LIBS := -L../../src -lobjfw \ -L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS} \ ${LIBS} LD = ${OBJC} LDFLAGS += ${LDFLAGS_RPATH} Index: utils/ofhash/OFHash.m ================================================================== --- utils/ofhash/OFHash.m +++ utils/ofhash/OFHash.m @@ -93,13 +93,14 @@ OFSHA256Hash *SHA256Hash = nil; OFSHA384Hash *SHA384Hash = nil; OFSHA512Hash *SHA512Hash = nil; #ifndef OF_AMIGAOS - [OFLocale addLanguageDirectory: @LANGUAGE_DIR]; + [OFLocale addLocalizationDirectory: @LOCALIZATION_DIR]; #else - [OFLocale addLanguageDirectory: @"PROGDIR:/share/ofhash/lang"]; + [OFLocale addLocalizationDirectory: + @"PROGDIR:/share/ofhash/localization"]; #endif while ((option = [optionsParser nextOption]) != '\0') { switch (option) { case '?': @@ -132,11 +133,11 @@ sandbox.allowsUserDatabaseReading = true; for (OFString *path in optionsParser.remainingArguments) [sandbox unveilPath: path permissions: @"r"]; - [sandbox unveilPath: @LANGUAGE_DIR permissions: @"r"]; + [sandbox unveilPath: @LOCALIZATION_DIR permissions: @"r"]; [OFApplication of_activateSandbox: sandbox]; } @finally { [sandbox release]; } DELETED utils/ofhash/lang/de.json Index: utils/ofhash/lang/de.json ================================================================== --- utils/ofhash/lang/de.json +++ utils/ofhash/lang/de.json @@ -1,10 +0,0 @@ -{ - "usage": [ - "Benutzung: %[prog] [--md5] [--ripemd160] [--sha1] [--sha224] ", - "[--sha256] [--sha384] [--sha512] datei1 [datei2 ...]" - ], - "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", - "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", - "failed_to_open_file": "Fehler beim Öffnen der Datei %[file]: %[error]", - "failed_to_read_file": "Fehler beim Lesen der Datei %[file]: %[error]" -} DELETED utils/ofhash/lang/languages.json Index: utils/ofhash/lang/languages.json ================================================================== --- utils/ofhash/lang/languages.json +++ utils/ofhash/lang/languages.json @@ -1,11 +0,0 @@ -{ - "de": { - "": "de" - }, - "deutsch": { - "": "de" - }, - "german": { - "": "de" - } -} ADDED utils/ofhash/localization/de.json Index: utils/ofhash/localization/de.json ================================================================== --- utils/ofhash/localization/de.json +++ utils/ofhash/localization/de.json @@ -0,0 +1,10 @@ +{ + "usage": [ + "Benutzung: %[prog] [--md5] [--ripemd160] [--sha1] [--sha224] ", + "[--sha256] [--sha384] [--sha512] datei1 [datei2 ...]" + ], + "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", + "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", + "failed_to_open_file": "Fehler beim Öffnen der Datei %[file]: %[error]", + "failed_to_read_file": "Fehler beim Lesen der Datei %[file]: %[error]" +} ADDED utils/ofhash/localization/localizations.json Index: utils/ofhash/localization/localizations.json ================================================================== --- utils/ofhash/localization/localizations.json +++ utils/ofhash/localization/localizations.json @@ -0,0 +1,11 @@ +{ + "de": { + "": "de" + }, + "deutsch": { + "": "de" + }, + "german": { + "": "de" + } +} Index: utils/ofhttp/Makefile ================================================================== --- utils/ofhttp/Makefile +++ utils/ofhttp/Makefile @@ -1,27 +1,27 @@ include ../../extra.mk PROG = ofhttp${PROG_SUFFIX} SRCS = OFHTTP.m \ ProgressBar.m -DATA = lang/de.json \ - lang/languages.json +DATA = localization/de.json \ + localization/localizations.json include ../../buildsys.mk PACKAGE_NAME = ofhttp ${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2} -CPPFLAGS += -I../../src \ - -I../../src/runtime \ - -I../../src/exceptions \ - -I../../src/tls \ - -I../.. \ - -DLANGUAGE_DIR='"${datadir}/ofhttp/lang"' \ - -DLIB_PREFIX='"${LIB_PREFIX}"' \ +CPPFLAGS += -I../../src \ + -I../../src/runtime \ + -I../../src/exceptions \ + -I../../src/tls \ + -I../.. \ + -DLOCALIZATION_DIR='"${datadir}/ofhttp/localization"' \ + -DLIB_PREFIX='"${LIB_PREFIX}"' \ -DLIB_SUFFIX='"${LIB_SUFFIX}"' LIBS := -L../../src -L../../src/tls ${OFHTTP_LIBS} -lobjfw \ -L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS} \ ${LIBS} LD = ${OBJC} LDFLAGS += ${LDFLAGS_RPATH} Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -32,26 +32,26 @@ #import "OFSandbox.h" #import "OFStdIOStream.h" #import "OFSystemInfo.h" #import "OFTCPSocket.h" #import "OFTLSStream.h" -#import "OFURL.h" +#import "OFURI.h" #ifdef HAVE_TLS_SUPPORT # import "ObjFWTLS.h" #endif -#import "OFConnectionFailedException.h" +#import "OFConnectSocketFailedException.h" +#import "OFGetItemAttributesFailedException.h" #import "OFHTTPRequestFailedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" -#import "OFInvalidServerReplyException.h" +#import "OFInvalidServerResponseException.h" #import "OFOpenItemFailedException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFResolveHostFailedException.h" -#import "OFRetrieveItemAttributesFailedException.h" #import "OFUnsupportedProtocolException.h" #import "OFWriteFailedException.h" #import "ProgressBar.h" @@ -60,12 +60,12 @@ #define KIBIBYTE (1024) @interface OFHTTP: OFObject { - OFArray OF_GENERIC(OFString *) *_URLs; - size_t _URLIndex; + OFArray OF_GENERIC(OFString *) *_URIs; + size_t _URIIndex; int _errorCode; OFString *_outputPath, *_currentFileName; bool _continue, _force, _detectFileName, _detectFileNameRequest; bool _detectedFileName, _quiet, _verbose, _insecure, _ignoreStatus; bool _useUnicode; @@ -77,11 +77,11 @@ OFStream *_output; unsigned long long _received, _length, _resumedFrom; ProgressBar *_progressBar; } -- (void)downloadNextURL; +- (void)downloadNextURI; @end #ifdef HAVE_TLS_SUPPORT void _reference_to_ObjFWTLS(void) @@ -95,11 +95,11 @@ static void help(OFStream *stream, bool full, int status) { [OFStdErr writeLine: OF_LOCALIZED(@"usage", - @"Usage: %[prog] -[cehHmoOPqv] url1 [url2 ...]", + @"Usage: %[prog] -[cehHmoOPqv] uri1 [uri2 ...]", @"prog", [OFApplication programName])]; if (full) { [stream writeString: @"\n"]; [stream writeLine: OF_LOCALIZED(@"full_usage", @@ -353,11 +353,11 @@ contentLength = [OFString stringWithFormat: @"%ju", fileSize]; [_clientHeaders setObject: contentLength forKey: @"Content-Length"]; - } @catch (OFRetrieveItemAttributesFailedException *e) { + } @catch (OFGetItemAttributesFailedException *e) { } } if (contentLength == nil) [_clientHeaders setObject: @"chunked" @@ -449,13 +449,14 @@ [OFApplication of_activateSandbox: sandbox]; #endif #ifndef OF_AMIGAOS - [OFLocale addLanguageDirectory: @LANGUAGE_DIR]; + [OFLocale addLocalizationDirectory: @LOCALIZATION_DIR]; #else - [OFLocale addLanguageDirectory: @"PROGDIR:/share/ofhttp/lang"]; + [OFLocale addLocalizationDirectory: + @"PROGDIR:/share/ofhttp/localization"]; #endif optionsParser = [OFOptionsParser parserWithOptions: options]; while ((option = [optionsParser nextOption]) != '\0') { switch (option) { @@ -543,13 +544,13 @@ sandbox.allowsUnveil = false; [OFApplication of_activateSandbox: sandbox]; #endif _outputPath = [outputPath copy]; - _URLs = [optionsParser.remainingArguments copy]; + _URIs = [optionsParser.remainingArguments copy]; - if (_URLs.count < 1) + if (_URIs.count < 1) help(OFStdErr, false, 1); if (_quiet && _verbose) { [OFStdErr writeLine: OF_LOCALIZED(@"quiet_xor_verbose", @"%[prog]: -q / --quiet and -v / --verbose are mutually " @@ -565,14 +566,14 @@ @"mutually exclusive!", @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } - if (_outputPath != nil && _URLs.count > 1) { + if (_outputPath != nil && _URIs.count > 1) { [OFStdErr writeLine: - OF_LOCALIZED(@"output_only_with_one_url", - @"%[prog]: Cannot use -o / --output when more than one URL " + OF_LOCALIZED(@"output_only_with_one_uri", + @"%[prog]: Cannot use -o / --output when more than one URI " @"has been specified!", @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } @@ -583,11 +584,11 @@ _useUnicode = [OFSystemInfo isWindowsNT]; #else _useUnicode = ([OFLocale encoding] == OFStringEncodingUTF8); #endif - [self performSelector: @selector(downloadNextURL) afterDelay: 0]; + [self performSelector: @selector(downloadNextURI) afterDelay: 0]; } - (void)client: (OFHTTPClient *)client didCreateTLSStream: (OFTLSStream *)stream request: (OFHTTPRequest *)request @@ -606,15 +607,15 @@ size_t length = [_body readIntoBuffer: buffer length: 4096]; [body writeBuffer: buffer length: length]; } } -- (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 { if (_verbose) { void *pool = objc_autoreleasePoolPush(); OFDictionary OF_GENERIC(OFString *, OFString *) *headers = response.headers; @@ -629,13 +630,13 @@ objc_autoreleasePoolPop(pool); } if (!_quiet) { if (_useUnicode) - [OFStdOut writeFormat: @"☇ %@", URL.string]; + [OFStdOut writeFormat: @"☇ %@", URI.string]; else - [OFStdOut writeFormat: @"< %@", URL.string]; + [OFStdOut writeFormat: @"< %@", URI.string]; } _length = 0; return true; @@ -645,11 +646,11 @@ didReadIntoBuffer: (void *)buffer length: (size_t)length exception: (id)exception { if (exception != nil) { - OFString *URL; + OFString *URI; [_progressBar stop]; [_progressBar draw]; [_progressBar release]; _progressBar = nil; @@ -658,21 +659,21 @@ [OFStdOut writeString: @"\n "]; [OFStdOut writeLine: OF_LOCALIZED(@"download_error", @"Error!")]; } - URL = [_URLs objectAtIndex: _URLIndex - 1]; + URI = [_URIs objectAtIndex: _URIIndex - 1]; [OFStdErr writeLine: OF_LOCALIZED( @"download_failed_exception", - @"%[prog]: Failed to download <%[url]>!\n" + @"%[prog]: Failed to download <%[uri]>!\n" @" %[exception]", @"prog", [OFApplication programName], - @"url", URL, + @"uri", URI, @"exception", exception)]; _errorCode = 1; - [self performSelector: @selector(downloadNextURL) + [self performSelector: @selector(downloadNextURI) afterDelay: 0]; return false; } [_output writeBuffer: buffer length: length]; @@ -690,11 +691,11 @@ [OFStdOut writeString: @"\n "]; [OFStdOut writeLine: OF_LOCALIZED(@"download_done", @"Done!")]; } - [self performSelector: @selector(downloadNextURL) + [self performSelector: @selector(downloadNextURI) afterDelay: 0]; return false; } return true; @@ -814,38 +815,38 @@ if (!_quiet) [OFStdOut writeString: @"\n"]; [OFStdErr writeLine: OF_LOCALIZED(@"download_resolve_host_failed", - @"%[prog]: Failed to download <%[url]>!\n" + @"%[prog]: Failed to download <%[uri]>!\n" @" Failed to resolve host: %[exception]", @"prog", [OFApplication programName], - @"url", request.URL.string, + @"uri", request.URI.string, @"exception", exception)]; } else if ([exception isKindOfClass: - [OFConnectionFailedException class]]) { + [OFConnectSocketFailedException class]]) { if (!_quiet) [OFStdOut writeString: @"\n"]; [OFStdErr writeLine: OF_LOCALIZED(@"download_failed_connection_failed", - @"%[prog]: Failed to download <%[url]>!\n" + @"%[prog]: Failed to download <%[uri]>!\n" @" Connection failed: %[exception]", @"prog", [OFApplication programName], - @"url", request.URL.string, + @"uri", request.URI.string, @"exception", exception)]; } else if ([exception isKindOfClass: - [OFInvalidServerReplyException class]]) { + [OFInvalidServerResponseException class]]) { if (!_quiet) [OFStdOut writeString: @"\n"]; [OFStdErr writeLine: OF_LOCALIZED( - @"download_failed_invalid_server_reply", - @"%[prog]: Failed to download <%[url]>!\n" - @" Invalid server reply!", + @"download_failed_invalid_server_response", + @"%[prog]: Failed to download <%[uri]>!\n" + @" Invalid server response!", @"prog", [OFApplication programName], - @"url", request.URL.string)]; + @"uri", request.URI.string)]; } else if ([exception isKindOfClass: [OFUnsupportedProtocolException class]]) { if (!_quiet) [OFStdOut writeString: @"\n"]; @@ -878,14 +879,14 @@ @"write", @"Write failed"); [OFStdErr writeLine: OF_LOCALIZED( @"download_failed_read_or_write_failed", - @"%[prog]: Failed to download <%[url]>!\n" + @"%[prog]: Failed to download <%[uri]>!\n" @" %[error]: %[exception]", @"prog", [OFApplication programName], - @"url", request.URL.string, + @"uri", request.URI.string, @"error", error, @"exception", exception)]; } else if ([exception isKindOfClass: [OFHTTPRequestFailedException class]]) { short statusCode; @@ -898,20 +899,20 @@ statusCode = response.statusCode; codeString = [OFString stringWithFormat: @"%hd %@", statusCode, OFHTTPStatusCodeString(statusCode)]; [OFStdErr writeLine: OF_LOCALIZED(@"download_failed", - @"%[prog]: Failed to download <%[url]>!\n" + @"%[prog]: Failed to download <%[uri]>!\n" @" HTTP status code: %[code]", @"prog", [OFApplication programName], - @"url", request.URL.string, + @"uri", request.URI.string, @"code", codeString)]; } else @throw exception; _errorCode = 1; - [self performSelector: @selector(downloadNextURL) + [self performSelector: @selector(downloadNextURI) afterDelay: 0]; return; } after_exception_handling: @@ -922,14 +923,14 @@ _currentFileName = [fileNameFromContentDisposition( [response.headers objectForKey: @"Content-Disposition"]) copy]; _detectedFileName = true; - /* Handle this URL on the next -[downloadNextURL] call */ - _URLIndex--; + /* Handle this URI on the next -[downloadNextURI] call */ + _URIIndex--; - [self performSelector: @selector(downloadNextURL) + [self performSelector: @selector(downloadNextURI) afterDelay: 0]; return; } if ([_outputPath isEqual: @"-"]) @@ -984,47 +985,47 @@ next: [_currentFileName release]; _currentFileName = nil; - [self performSelector: @selector(downloadNextURL) afterDelay: 0]; + [self performSelector: @selector(downloadNextURI) afterDelay: 0]; } -- (void)downloadNextURL +- (void)downloadNextURI { - OFString *URLString = nil; - OFURL *URL; + OFString *URIString = nil; + OFURI *URI; OFMutableDictionary *clientHeaders; OFHTTPRequest *request; _received = _length = _resumedFrom = 0; if (_output != OFStdOut) [_output release]; _output = nil; - if (_URLIndex >= _URLs.count) + if (_URIIndex >= _URIs.count) [OFApplication terminateWithStatus: _errorCode]; @try { - URLString = [_URLs objectAtIndex: _URLIndex++]; - URL = [OFURL URLWithString: URLString]; + URIString = [_URIs objectAtIndex: _URIIndex++]; + URI = [OFURI URIWithString: URIString]; } @catch (OFInvalidFormatException *e) { - [OFStdErr writeLine: OF_LOCALIZED(@"invalid_url", - @"%[prog]: Invalid URL: <%[url]>!", + [OFStdErr writeLine: OF_LOCALIZED(@"invalid_uri", + @"%[prog]: Invalid URI: <%[uri]>!", @"prog", [OFApplication programName], - @"url", URLString)]; + @"uri", URIString)]; _errorCode = 1; goto next; } - if (![URL.scheme isEqual: @"http"] && ![URL.scheme isEqual: @"https"]) { + if (![URI.scheme isEqual: @"http"] && ![URI.scheme isEqual: @"https"]) { [OFStdErr writeLine: OF_LOCALIZED(@"invalid_scheme", - @"%[prog]: Invalid scheme: <%[url]>!", + @"%[prog]: Invalid scheme: <%[uri]>!", @"prog", [OFApplication programName], - @"url", URLString)]; + @"uri", URIString)]; _errorCode = 1; goto next; } @@ -1031,16 +1032,16 @@ clientHeaders = [[_clientHeaders mutableCopy] autorelease]; if (_detectFileName && !_detectedFileName) { if (!_quiet) { if (_useUnicode) - [OFStdOut writeFormat: @"⠒ %@", URL.string]; + [OFStdOut writeFormat: @"⠒ %@", URI.string]; else - [OFStdOut writeFormat: @"? %@", URL.string]; + [OFStdOut writeFormat: @"? %@", URI.string]; } - request = [OFHTTPRequest requestWithURL: URL]; + request = [OFHTTPRequest requestWithURI: URI]; request.headers = clientHeaders; request.method = OFHTTPRequestMethodHead; _detectFileNameRequest = true; [_HTTPClient asyncPerformRequest: request]; @@ -1055,11 +1056,11 @@ if (_currentFileName == nil) _currentFileName = [_outputPath copy]; if (_currentFileName == nil) - _currentFileName = [URL.path.lastPathComponent copy]; + _currentFileName = [URI.path.lastPathComponent copy]; if ([_currentFileName isEqual: @"/"]) { [_currentFileName release]; _currentFileName = nil; } @@ -1080,28 +1081,28 @@ _resumedFrom = (unsigned long long)size; range = [OFString stringWithFormat: @"bytes=%jd-", _resumedFrom]; [clientHeaders setObject: range forKey: @"Range"]; - } @catch (OFRetrieveItemAttributesFailedException *e) { + } @catch (OFGetItemAttributesFailedException *e) { } } if (!_quiet) { if (_useUnicode) - [OFStdOut writeFormat: @"⇣ %@", URL.string]; + [OFStdOut writeFormat: @"⇣ %@", URI.string]; else - [OFStdOut writeFormat: @"< %@", URL.string]; + [OFStdOut writeFormat: @"< %@", URI.string]; } - request = [OFHTTPRequest requestWithURL: URL]; + request = [OFHTTPRequest requestWithURI: URI]; request.headers = clientHeaders; request.method = _method; _detectFileNameRequest = false; [_HTTPClient asyncPerformRequest: request]; return; next: - [self performSelector: @selector(downloadNextURL) afterDelay: 0]; + [self performSelector: @selector(downloadNextURI) afterDelay: 0]; } @end DELETED utils/ofhttp/lang/de.json Index: utils/ofhttp/lang/de.json ================================================================== --- utils/ofhttp/lang/de.json +++ utils/ofhttp/lang/de.json @@ -1,108 +0,0 @@ -{ - "usage": "Benutzung: %[prog] -[cehHmoOPqv] url1 [url2 ...]", - "full_usage": [ - "Optionen:\n", - " -b --body Angegebene Datei als Body übergeben\n", - " (- für Standard-Eingabe)\n", - " -c --continue Download von existierender Datei ", - "fortsetzen\n", - " -f --force Existierende Datei überschreiben\n", - " -h --help Diese Hilfe anzeigen\n", - " -H --header Einen Header (z.B. X-Foo:Bar) hinzufügen\n", - " -m --method HTTP Request-Methode setzen\n", - " -o --output Ausgabe-Dateiname angeben\n", - " -O --detect-filename Dateiname mittels HEAD-Request ermitteln\n", - " -P --proxy SOCKS5-Proxy angeben\n", - " -q --quiet Ruhiger Modus (keine Ausgabe außer Fehler)", - "\n", - " -v --verbose Ausführlicher Modus (gibt Header aus)\n", - " --insecure TLS-Fehler ignorieren und unsichere\n", - " Weiterleitungen erlauben\n", - " --ignore-status HTTP Status-Code ignorieren" - ], - "invalid_input_header": "%[prog]: Header müssen im Format Name:Wert sein!", - "invalid_input_method": "%[prog]: Ungültige Request-Methode %[method]!", - "invalid_input_proxy": "%[prog]: Proxy muss im Format Host:Port sein!", - "long_argument_missing": "%[prog]: Argument für Option --%[opt] fehlt", - "argument_missing": "%[prog]: Argument für option -%[opt] fehlt", - "option_takes_no_argument": "%[prog]: Option --%[opt] nimmt kein Argument", - "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", - "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", - "quiet_xor_verbose": [ - "%[prog]: -q / --quiet und -v / --verbose schließen sich gegenseitig ", - "aus!" - ], - "output_xor_detect_filename": [ - "%[prog]: -o / --output und -O / --detect-filename schließen sich ", - "gegenseitig aus!" - ], - "output_only_with_one_url": [ - "%[prog]: -o / --output kann nicht mit mehr als einer URL benutzt ", - "werden!" - ], - "download_resolve_host_failed": [ - "%[prog]: Fehler beim Download von <%[url]>!\n", - " Host auflösen fehlgeschlagen: %[exception]" - ], - "download_failed_connection_failed": [ - "%[prog]: Fehler beim Download von <%[url]>!\n", - " Verbindung fehlgeschlagen: %[exception]" - ], - "download_failed_invalid_server_reply": [ - "%[prog]: Fehler beim Download von <%[url]>!\n", - " Ungültige Antwort vom Server!" - ], - "no_tls_support": [ - "%[prog]: Keine TLS-Unterstützung in ObjFW!\n", - " Um via HTTPS runterzuladen, müssen Sie entweder ObjFW mit TLS-", - "Unterstützung\n", - " kompilieren oder eine Bibliothek mittels „preoad” laden, welche ", - "TLS-Support\n", - " zu ObjFW hinzufügt!" - ], - "download_failed_read_or_write_failed_any": "Lesen oder Schreiben", - "download_failed_read_or_write_failed_read": "Lesen", - "download_failed_read_or_write_failed_write": "Schreiben", - "download_failed_read_or_write_failed": [ - "%[prog]: Fehler beim Download von <%[url]>!\n", - " %[error]: %[exception]" - ], - "download_failed": [ - "%[prog]: Fehler beim Download von <%[url]>!\n", - " HTTP Status-Code: %[code]" - ], - "download_error": "Fehler!", - "download_failed_exception": [ - "%[prog]: Fehler beim Download von <%[url]>!\n", - " %[exception]" - ], - "download_done": "Fertig!", - "invalid_url": "%[prog]: Ungültige URL: <%[url]>!", - "invalid_scheme": "%[prog]: Ungültiges Schema: <%[url]>!", - "type_unknown": "unbekannt", - "size_gib": "%[num] GiB", - "size_mib": "%[num] MiB", - "size_kib": "%[num] KiB", - "size_bytes": [ - [ - {"num == 1": "1 Byte"}, - {"": "%[num] Bytes"} - ] - ], - "size_unknown": "unbekannt", - "info_name_unaligned": "Name: %[name]", - "info_name": "Name: %[name]", - "info_type": "Typ: %[type]", - "info_size": "Größe: %[size]", - "output_already_exists": "%[prog]: Datei %[filename] existiert bereits!", - "failed_to_open_output": [ - "%[prog]: Kann Datei %[filename] nicht öffnen: %[exception]" - ], - "eta_days": "%[num] t ", - "progress_bytes": [ - [ - {"num == 1": "1 Byte "}, - {"": "%[num] Bytes"} - ] - ] -} DELETED utils/ofhttp/lang/languages.json Index: utils/ofhttp/lang/languages.json ================================================================== --- utils/ofhttp/lang/languages.json +++ utils/ofhttp/lang/languages.json @@ -1,11 +0,0 @@ -{ - "de": { - "": "de" - }, - "deutsch": { - "": "de" - }, - "german": { - "": "de" - } -} ADDED utils/ofhttp/localization/de.json Index: utils/ofhttp/localization/de.json ================================================================== --- utils/ofhttp/localization/de.json +++ utils/ofhttp/localization/de.json @@ -0,0 +1,108 @@ +{ + "usage": "Benutzung: %[prog] -[cehHmoOPqv] uri1 [uri2 ...]", + "full_usage": [ + "Optionen:\n", + " -b --body Angegebene Datei als Body übergeben\n", + " (- für Standard-Eingabe)\n", + " -c --continue Download von existierender Datei ", + "fortsetzen\n", + " -f --force Existierende Datei überschreiben\n", + " -h --help Diese Hilfe anzeigen\n", + " -H --header Einen Header (z.B. X-Foo:Bar) hinzufügen\n", + " -m --method HTTP Request-Methode setzen\n", + " -o --output Ausgabe-Dateiname angeben\n", + " -O --detect-filename Dateiname mittels HEAD-Request ermitteln\n", + " -P --proxy SOCKS5-Proxy angeben\n", + " -q --quiet Ruhiger Modus (keine Ausgabe außer Fehler)", + "\n", + " -v --verbose Ausführlicher Modus (gibt Header aus)\n", + " --insecure TLS-Fehler ignorieren und unsichere\n", + " Weiterleitungen erlauben\n", + " --ignore-status HTTP Status-Code ignorieren" + ], + "invalid_input_header": "%[prog]: Header müssen im Format Name:Wert sein!", + "invalid_input_method": "%[prog]: Ungültige Request-Methode %[method]!", + "invalid_input_proxy": "%[prog]: Proxy muss im Format Host:Port sein!", + "long_argument_missing": "%[prog]: Argument für Option --%[opt] fehlt", + "argument_missing": "%[prog]: Argument für option -%[opt] fehlt", + "option_takes_no_argument": "%[prog]: Option --%[opt] nimmt kein Argument", + "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]", + "unknown_option": "%[prog]: Unbekannte Option: -%[opt]", + "quiet_xor_verbose": [ + "%[prog]: -q / --quiet und -v / --verbose schließen sich gegenseitig ", + "aus!" + ], + "output_xor_detect_filename": [ + "%[prog]: -o / --output und -O / --detect-filename schließen sich ", + "gegenseitig aus!" + ], + "output_only_with_one_uri": [ + "%[prog]: -o / --output kann nicht mit mehr als einer URI benutzt ", + "werden!" + ], + "download_resolve_host_failed": [ + "%[prog]: Fehler beim Download von <%[uri]>!\n", + " Host auflösen fehlgeschlagen: %[exception]" + ], + "download_failed_connection_failed": [ + "%[prog]: Fehler beim Download von <%[uri]>!\n", + " Verbindung fehlgeschlagen: %[exception]" + ], + "download_failed_invalid_server_response": [ + "%[prog]: Fehler beim Download von <%[uri]>!\n", + " Ungültige Antwort vom Server!" + ], + "no_tls_support": [ + "%[prog]: Keine TLS-Unterstützung in ObjFW!\n", + " Um via HTTPS runterzuladen müssen Sie entweder ObjFW mit TLS-", + "Unterstützung\n", + " kompilieren oder eine Bibliothek mittels „preload” laden, welche ", + "TLS-Support\n", + " zu ObjFW hinzufügt!" + ], + "download_failed_read_or_write_failed_any": "Lesen oder Schreiben", + "download_failed_read_or_write_failed_read": "Lesen", + "download_failed_read_or_write_failed_write": "Schreiben", + "download_failed_read_or_write_failed": [ + "%[prog]: Fehler beim Download von <%[uri]>!\n", + " %[error]: %[exception]" + ], + "download_failed": [ + "%[prog]: Fehler beim Download von <%[uri]>!\n", + " HTTP Status-Code: %[code]" + ], + "download_error": "Fehler!", + "download_failed_exception": [ + "%[prog]: Fehler beim Download von <%[uri]>!\n", + " %[exception]" + ], + "download_done": "Fertig!", + "invalid_uri": "%[prog]: Ungültige URI: <%[uri]>!", + "invalid_scheme": "%[prog]: Ungültiges Schema: <%[uri]>!", + "type_unknown": "unbekannt", + "size_gib": "%[num] GiB", + "size_mib": "%[num] MiB", + "size_kib": "%[num] KiB", + "size_bytes": [ + [ + {"num == 1": "1 Byte"}, + {"": "%[num] Bytes"} + ] + ], + "size_unknown": "unbekannt", + "info_name_unaligned": "Name: %[name]", + "info_name": "Name: %[name]", + "info_type": "Typ: %[type]", + "info_size": "Größe: %[size]", + "output_already_exists": "%[prog]: Datei %[filename] existiert bereits!", + "failed_to_open_output": [ + "%[prog]: Kann Datei %[filename] nicht öffnen: %[exception]" + ], + "eta_days": "%[num] t ", + "progress_bytes": [ + [ + {"num == 1": "1 Byte "}, + {"": "%[num] Bytes"} + ] + ] +} ADDED utils/ofhttp/localization/localizations.json Index: utils/ofhttp/localization/localizations.json ================================================================== --- utils/ofhttp/localization/localizations.json +++ utils/ofhttp/localization/localizations.json @@ -0,0 +1,11 @@ +{ + "de": { + "": "de" + }, + "deutsch": { + "": "de" + }, + "german": { + "": "de" + } +}