ObjFW  Check-in [a43377bd13]

Overview
Comment:Merge trunk into branch "invoke-invocation"
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | invoke-invocation
Files: files | file ages | folders
SHA3-256: a43377bd13b12c0148942e3f80d61f1ca287f759b24936aff95c584d4966be89
User & Date: js on 2022-11-07 00:28:32
Other Links: branch diff | manifest | tags
Context
2022-11-07
00:28
Merge trunk into branch "invoke-invocation" Leaf check-in: a43377bd13 user: js tags: invoke-invocation
00:27
Merge trunk into branch "invoke-invocation" check-in: 96c24a6b6c user: js tags: invoke-invocation
2022-11-06
21:37
Fix OFDDPSocket on NetBSD check-in: 4d97d89c32 user: js tags: trunk
Changes

Deleted .cirrus.yml version [6338675a88].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































Modified .fossil-settings/clean-glob from [c4150a9711] to [06b87ad48d].

46
47
48
49
50
51
52

53
54
55
56
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
tests/tests.nro
tests/tests.rpx
utils/objfw-config

utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp







>




46
47
48
49
50
51
52
53
54
55
56
57
tests/tests
tests/tests.3dsx
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

Modified .fossil-settings/ignore-glob from [ed0ba1aa38] to [23cb69850b].

51
52
53
54
55
56
57

58
59
60
61
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
tests/tests.nro
tests/tests.rpx
utils/objfw-config

utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp







>




51
52
53
54
55
56
57
58
59
60
61
62
tests/tests
tests/tests.3dsx
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

Modified .github/workflows/amiga-gcc.yml from [16471286d2] to [d938c3ac40].

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
name: amiga-gcc
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest

    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 }}'
    - 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)"
    - 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)"





>






<
<




<
<
<
<
<
|

<
<
<
<
<
|

|
<
<
<
<
<
1
2
3
4
5
6
7
8
9
10
11
12


13
14
15
16





17
18





19
20
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:


    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure





      run: ./configure --host=m68k-amigaos ${{ matrix.configure_flags }}
    - name: make





      run: make -j$(nproc)
    - name: make install
      run: make install





Deleted .github/workflows/macos-10.15.yml version [0b4e6d02a3].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 version [a15421158f].































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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

Modified .github/workflows/morphos.yml from [0f27c2ab37] to [c3d30801ba].

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
name: morphos
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest

    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 }}'
    - 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)"
    - 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)"





>






<
<




<
<
<
<
<
|

<
<
<
<
<
|

|
<
<
<
<
<
1
2
3
4
5
6
7
8
9
10
11
12


13
14
15
16





17
18





19
20
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:


    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure





      run: ./configure --host=ppc-morphos ${{ matrix.configure_flags }}
    - name: make





      run: make -j$(nproc)
    - name: make install
      run: make install





Modified .github/workflows/nintendo-3ds.yml from [67061ee611] to [b6f005f187].

28
29
30
31
32
33
34
35
    - name: make install
      run: |
        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)"







|
28
29
30
31
32
33
34
35
    - name: make install
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitARM/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkitarm \
          sh -c "cd /objfw && make install"

Modified .github/workflows/nintendo-ds.yml from [3d41e0f5f7] to [1d6308a4b1].

28
29
30
31
32
33
34
35
    - name: make install
      run: |
        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)"







|
28
29
30
31
32
33
34
35
    - name: make install
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitARM/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkitarm \
          sh -c "cd /objfw && make install"

Modified .github/workflows/nintendo-switch.yml from [2a5a4851c5] to [9d8878050b].

28
29
30
31
32
33
34
35
    - name: make install
      run: |
        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)"







|
28
29
30
31
32
33
34
35
    - name: make install
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitA64/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkita64 \
          sh -c "cd /objfw && make install"

Modified .github/workflows/ubuntu-latest-32bit.yml from [96c9a8ae41] to [3cbf56442c].

1
2
3
4
5
6
7
8
9
10
11
12
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
|



|







1
2
3
4
5
6
7
8
9
10
11
12
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

Modified .github/workflows/ubuntu-latest-gcc-32bit.yml from [0e720701c6] to [b827a9a41f].

1
2
3
4
5
6
7
8
9
10
11
12
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
|



|







1
2
3
4
5
6
7
8
9
10
11
12
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

Modified .github/workflows/ubuntu-latest-gcc.yml from [9e75cc2f04] to [4509107d9f].

1
2
3
4
5
6
7
8
9
10
11
12
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
|



|







1
2
3
4
5
6
7
8
9
10
11
12
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

Modified .github/workflows/ubuntu-latest.yml from [d0186b3566] to [36a38ec742].

1
2
3
4
5
6
7
8
9
10
11
12
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
|



|







1
2
3
4
5
6
7
8
9
10
11
12
name: ubuntu-latest
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        configure_flags:
          -
          - --enable-seluid24
          - --disable-compiler-tls
          - --disable-threads

Modified .github/workflows/wii-u.yml from [b25255900b] to [7d250df7d0].

28
29
30
31
32
33
34
35
    - name: make install
      run: |
        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)"







|
28
29
30
31
32
33
34
35
    - name: make install
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitPPC/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkitppc \
          sh -c "cd /objfw && make install"

Modified .github/workflows/wii.yml from [39c842705b] to [59cebc18ff].

28
29
30
31
32
33
34
35
    - name: make install
      run: |
        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)"







|
28
29
30
31
32
33
34
35
    - name: make install
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitPPC/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkitppc \
          sh -c "cd /objfw && make install"

Modified .gitignore from [42e9f25720] to [726b780402].

51
52
53
54
55
56
57

58
59
60
61
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
tests/tests.nro
tests/tests.rpx
utils/objfw-config

utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp







>




51
52
53
54
55
56
57
58
59
60
61
62
tests/tests
tests/tests.3dsx
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

Modified Doxyfile from [f4ff92fbc3] to [6b8aa42255].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
PROJECT_NAME = "ObjFW"
OUTPUT_DIRECTORY = docs/
INPUT = src src/exceptions src/runtime
FILE_PATTERNS = *.h *.m
HTML_OUTPUT = .
HAVE_DOT = NO
GENERATE_LATEX = NO
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_MEMBERS = YES
TYPEDEF_HIDES_STRUCT = YES
PREDEFINED = __OBJC__				\
	     _Nonnull				\
	     _Nullable				\
	     DOXYGEN				\
	     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_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		\
	     SIGHUP				\
	     SIGUSR1				\
	     SIGUSR2
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
IGNORE_PREFIX = OF of_











|
|

|
|
|







|
|


|
|
|
|
|
|
|
|






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
PROJECT_NAME = "ObjFW"
OUTPUT_DIRECTORY = docs/
INPUT = src src/exceptions src/runtime
FILE_PATTERNS = *.h *.m
HTML_OUTPUT = .
HAVE_DOT = NO
GENERATE_LATEX = NO
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_MEMBERS = YES
TYPEDEF_HIDES_STRUCT = YES
PREDEFINED = __OBJC__				\
	     _Nonnull=				\
	     _Nullable=				\
	     DOXYGEN				\
	     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_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=		\
	     SIGHUP				\
	     SIGUSR1				\
	     SIGUSR2
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
IGNORE_PREFIX = OF of_

Modified Makefile from [63e28abf9d] to [4341fed160].

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
	doxygen >/dev/null

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 -
	cp configure config.h.in objfw-${PACKAGE_VERSION}/
	ofarc -cq objfw-${PACKAGE_VERSION}.tar \
		$$(find objfw-${PACKAGE_VERSION} | sort)
	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..."
	rm -fr docs
	doxygen >/dev/null
	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)
	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







|

|
<












|




24
25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
	doxygen >/dev/null

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 '.fossil*,.git*' | ofarc -ttgz -xq -
	cp configure config.h.in objfw-${PACKAGE_VERSION}/
	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..."
	rm -fr docs
	doxygen >/dev/null
	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 \
		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

Modified README.md from [939967b8be] to [711c05e9ce].

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248












249
250
251
252




253
254
255
256
257
258
259
  The first thing to install is [MSYS2](https://www.msys2.org) to provide a
  basic UNIX-like environment for Windows. Unfortunately, the binaries are not
  signed, so make sure you download it via HTTPS. However, packages you
  download and install via MSYS2 are cryptographically signed.

<h3 id="setting-up-msys2">Setting up MSYS2</h3>

  MSYS2 currently supports 5 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

  For CLANG64, use:

    $ pacman -Syu mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-fossil













  For MINGW32, use:

    $ pacman -Syu mingw-w64-i686-clang mingw-w64-i686-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
  created shortcuts for each of them in your start menu. Just make sure to use
  the correct shortcut for the environment you want to use.







|


|
|
<
|
<
<
<
<
<





>
>
>
>
>
>
>
>
>
>
>
>




>
>
>
>







225
226
227
228
229
230
231
232
233
234
235
236

237





238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  The first thing to install is [MSYS2](https://www.msys2.org) to provide a
  basic UNIX-like environment for Windows. Unfortunately, the binaries are not
  signed, so make sure you download it via HTTPS. However, packages you
  download and install via MSYS2 are cryptographically signed.

<h3 id="setting-up-msys2">Setting up MSYS2</h3>

  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. 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
  created shortcuts for each of them in your start menu. Just make sure to use
  the correct shortcut for the environment you want to use.
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
    $ ./configure --host=m68k-amigaos


<h1 id="first-app">Writing your first application with ObjFW</h1>

  To create your first, empty application, you can use `objfw-new`:

    $ 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":

    [OFStdOut writeLine: @"Hello World!"];







|







317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
    $ ./configure --host=m68k-amigaos


<h1 id="first-app">Writing your first application with ObjFW</h1>

  To create your first, empty application, you can use `objfw-new`:

    $ 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":

    [OFStdOut writeLine: @"Hello World!"];

Modified build-aux/m4/buildsys.m4 from [824bccf73b] to [0e90ccf38e].

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
	*-*-mingw* | *-*-cygwin*)
		AC_MSG_RESULT(MinGW / Cygwin)
		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'
		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'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${bindir}/$$i ${DESTDIR}${libdir}/lib$${i%${LIB_SUFFIX}}.dll.a'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'







|







205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
	*-*-mingw* | *-*-cygwin*)
		AC_MSG_RESULT(MinGW / Cygwin)
		LIB_CFLAGS=''
		LIB_LDFLAGS='-shared -Wl,--export-all-symbols'
		LIB_LDFLAGS_INSTALL_NAME=''
		LIB_PREFIX=''
		LIB_SUFFIX='${LIB_MAJOR}.dll'
		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'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${bindir}/$$i ${DESTDIR}${libdir}/lib$${i%${LIB_SUFFIX}}.dll.a'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'

Modified buildsys.mk.in from [a085f99653] to [2dfee6e012].

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
MKDIR_P = mkdir -p
INSTALL = @INSTALL@
SHELL = @SHELL@
MSGFMT = @MSGFMT@
JAVAC = @JAVAC@
JAVACFLAGS = @JAVACFLAGS@
JAR = @JAR@
WINDRES = @WINDRES@
BUILD_AND_HOST_ARE_DARWIN = @BUILD_AND_HOST_ARE_DARWIN@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
amigalibdir ?= ${prefix}/libs
plugindir ?= ${libdir}/${PACKAGE_NAME}







|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
MKDIR_P = mkdir -p
INSTALL = @INSTALL@
SHELL = @SHELL@
MSGFMT = @MSGFMT@
JAVAC = @JAVAC@
JAVACFLAGS = @JAVACFLAGS@
JAR = @JAR@
RC = @RC@
BUILD_AND_HOST_ARE_DARWIN = @BUILD_AND_HOST_ARE_DARWIN@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
amigalibdir ?= ${prefix}/libs
plugindir ?= ${libdir}/${PACKAGE_NAME}
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
		${COMPILE_FAILED}; \
	fi

.rc.o .rc.lib.o .rc.plugin.o:
	${COMPILE_STATUS}
	in="$<"; \
	out="$@"; \
	if ${WINDRES} ${CPPFLAGS} -J rc -O coff -o $@ $<; then \
		${COMPILE_OK}; \
	else \
		${COMPILE_FAILED}; \
	fi

.S.o .S.amigalib.o:
	${COMPILE_STATUS}







|







575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
		${COMPILE_FAILED}; \
	fi

.rc.o .rc.lib.o .rc.plugin.o:
	${COMPILE_STATUS}
	in="$<"; \
	out="$@"; \
	if ${RC} ${RCFLAGS} ${CPPFLAGS} -J rc -O coff -o $@ $<; then \
		${COMPILE_OK}; \
	else \
		${COMPILE_FAILED}; \
	fi

.S.o .S.amigalib.o:
	${COMPILE_STATUS}
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
		${DIR_ENTER}; \
		${MAKE} -s uninstall || exit $$?; \
		${DIR_LEAVE}; \
	done

	for i in "" ${SHARED_LIB}; do \
		test x"$$i" = x"" && continue; \
		if test -f ${DESTDIR}${libdir}/$$i; then \
			if : @UNINSTALL_LIB@; then \
				${DELETE_OK}; \
			else \
				${DELETE_FAILED}; \
			fi \
		fi; \
	done







|







769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
		${DIR_ENTER}; \
		${MAKE} -s uninstall || exit $$?; \
		${DIR_LEAVE}; \
	done

	for i in "" ${SHARED_LIB}; do \
		test x"$$i" = x"" && continue; \
		if test -f ${DESTDIR}${libdir}/$$i -o -f ${DESTDIR}${bindir}/$$i; then \
			if : @UNINSTALL_LIB@; then \
				${DELETE_OK}; \
			else \
				${DELETE_FAILED}; \
			fi \
		fi; \
	done

Modified configure.ac from [6c9d81b0c1] to [7c5f0e8ed8].

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
	;;
*-msdosdjgpp*)
	enable_shared="no"
	enable_threads="no"
	enable_sockets="no"
	;;
*-*-mingw*)
	LDFLAGS="$LDFLAGS -Wl,--allow-multiple-definition"
	LIBS="$LIBS -lversion"

	AC_SUBST(USE_SRCS_WINDOWS, '${SRCS_WINDOWS}')
	;;
*-psp-*)
	AS_IF([test x"$DEVKITPSP" = x""], [
		AC_MSG_ERROR([DEVKITPSP is not set! Please set DEVKITPSP.])







|







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
	;;
*-msdosdjgpp*)
	enable_shared="no"
	enable_threads="no"
	enable_sockets="no"
	;;
*-*-mingw*)
	LDFLAGS="$LDFLAGS -Wl,--allow-multiple-definition -static-libgcc"
	LIBS="$LIBS -lversion"

	AC_SUBST(USE_SRCS_WINDOWS, '${SRCS_WINDOWS}')
	;;
*-psp-*)
	AS_IF([test x"$DEVKITPSP" = x""], [
		AC_MSG_ERROR([DEVKITPSP is not set! Please set DEVKITPSP.])
397
398
399
400
401
402
403






404
405
406
407
408
409
410
], [
	AC_MSG_RESULT(no)
	AC_MSG_ERROR(Compiler does not support properties!)
])

AC_CHECK_TOOL(AR, ar)
AC_PROG_RANLIB







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
	AC_SUBST(OBJFW_SHARED_LIB, "${LIB_PREFIX}objfw${LIB_SUFFIX}")
	AC_SUBST(EXCEPTIONS_LIB_A, "exceptions.lib.a")







>
>
>
>
>
>







397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
], [
	AC_MSG_RESULT(no)
	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
	AC_SUBST(OBJFW_SHARED_LIB, "${LIB_PREFIX}objfw${LIB_SUFFIX}")
	AC_SUBST(EXCEPTIONS_LIB_A, "exceptions.lib.a")
478
479
480
481
482
483
484

485
486
487
488
489
490
491

	#if defined(__GLIBC__) || defined(__MINGW32__) || \
	    defined(__NEWLIB__) || defined(__MORPHOS__) || defined(__MINT__)
	egrep_cpp_yes
	#endif
], [
	AC_MSG_RESULT(yes)

	CPPFLAGS="-D_GNU_SOURCE $CPPFLAGS"
	gnu_source="yes"
], [
	AC_MSG_RESULT(no)
])

case "$host_os" in







>







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498

	#if defined(__GLIBC__) || defined(__MINGW32__) || \
	    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)
])

case "$host_os" in
1050
1051
1052
1053
1054
1055
1056




1057
1058
1059
1060
1061
1062
1063
		dnl Compiler TLS is broken on AArch64 Android with Clang
		enable_compiler_tls="no"
		;;
	m68k-*-amigaos* | powerpc-*-amigaos*)
		dnl Compiler TLS is broken on AmigaOS
		enable_compiler_tls="no"
		;;




	*-*-morphos*)
		dnl Compiler TLS needs helpers that we don't want in the
		dnl .library
		enable_compiler_tls="no"
		;;
	esac








>
>
>
>







1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
		dnl Compiler TLS is broken on AArch64 Android with Clang
		enable_compiler_tls="no"
		;;
	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"
		;;
	esac

1226
1227
1228
1229
1230
1231
1232

1233
1234
1235
1236
1237
1238
1239
AC_MSG_RESULT($atomic_ops)

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(OFARC, "ofarc")
	AC_SUBST(OFHASH, "ofhash")

	case "$host_os" in
	msdosdjgpp*)
		dnl DJGPP has the type, but it's not really usable.
		;;







>







1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
AC_MSG_RESULT($atomic_ops)

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*)
		dnl DJGPP has the type, but it's not really usable.
		;;
1383
1384
1385
1386
1387
1388
1389

1390
1391
1392
1393
1394
1395
1396
1397
1398

1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
		;;
	esac

	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_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

			#ifdef OF_HAVE_SYS_SOCKET_H







>








|
>



<
<
<
<
<







1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415





1416
1417
1418
1419
1420
1421
1422
		;;
	esac

	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 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_MEMBER([struct sockaddr_in6.sin6_addr], [
		AC_EGREP_CPP(egrep_cpp_yes, [
			#ifdef _WIN32
			typedef int BOOL;
			#endif

			#ifdef OF_HAVE_SYS_SOCKET_H
1486
1487
1488
1489
1490
1491
1492




1493
1494
1495
1496
1497
1498
1499
		#endif

		#ifdef __MINT__
		# error Gives invalid argument at runtime
		#endif
	])





	AC_CHECK_MEMBER(struct sockaddr_ipx.sipx_network, [], [
		AC_CHECK_MEMBER(struct sockaddr_ipx.sa_netnum, [], [], [
			#ifdef _WIN32
			typedef int BOOL;
			#endif

			#ifdef OF_HAVE_NETIPX_IPX_H







>
>
>
>







1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
		#endif

		#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

			#ifdef OF_HAVE_NETIPX_IPX_H
1557
1558
1559
1560
1561
1562
1563



























































































1564
1565
1566
1567
1568
1569
1570
			egrep_cpp_yes
			#endif
		], [
			AC_DEFINE(OF_HAVE_IPX, 1, [Whether we have IPX/SPX])
			AC_SUBST(USE_SRCS_IPX, '${SRCS_IPX}')
		])
	])




























































































	AC_CHECK_FUNCS(paccept accept4, break)

	AC_CHECK_FUNCS(kqueue1 kqueue, [
		AC_DEFINE(HAVE_KQUEUE, 1, [Whether we have kqueue])
		AC_SUBST(OF_KQUEUE_KERNEL_EVENT_OBSERVER_M,
			"OFKqueueKernelEventObserver.m")







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
			egrep_cpp_yes
			#endif
		], [
			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 <sys/types.h>
			#endif
			#if defined(OF_HAVE_NETAT_APPLETALK_H)
			# include <netat/appletalk.h>
			#elif defined(OF_HAVE_NETATALK_AT_H)
			# include <netatalk/at.h>
			#endif

			#ifdef _WIN32
			# ifdef __MINGW32__
			#  include <_mingw.h>
			#  ifdef __MINGW64_VERSION_MAJOR
			#   include <winsock2.h>
			#  endif
			# endif
			# include <windows.h>
			# include <atalkwsh.h>
			#endif
		])
	], [
		#ifdef _WIN32
		typedef int BOOL;
		#endif

		#ifdef OF_HAVE_SYS_TYPES_H
		# include <sys/types.h>
		#endif
		#if defined(OF_HAVE_NETAT_APPLETALK_H)
		# include <netat/appletalk.h>
		#elif defined(OF_HAVE_NETATALK_AT_H)
		# include <netatalk/at.h>
		#endif

		#ifdef _WIN32
		# ifdef __MINGW32__
		#  include <_mingw.h>
		#  ifdef __MINGW64_VERSION_MAJOR
		#   include <winsock2.h>
		#  endif
		# endif
		# include <windows.h>
		# include <atalkwsh.h>
		#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 <sys/socket.h>
			#endif

			#ifdef _WIN32
			# ifdef __MINGW32__
			#  include <_mingw.h>
			#  ifdef __MINGW64_VERSION_MAJOR
			#   include <winsock2.h>
			#  endif
			# endif
			# include <windows.h>
			# include <atalkwsh.h>
			#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])
		AC_SUBST(OF_KQUEUE_KERNEL_EVENT_OBSERVER_M,
			"OFKqueueKernelEventObserver.m")
1633
1634
1635
1636
1637
1638
1639

















1640
1641
1642
1643
1644
1645
1646

				AC_CHECK_FUNCS(SSLCreateContext)
			], [])

			LIBS="$old_LIBS"
		])
	])


















	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*)
			ssl="ssl_shared"
			crypto="crypto_shared"







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767

				AC_CHECK_FUNCS(SSLCreateContext)
			], [])

			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*)
			ssl="ssl_shared"
			crypto="crypto_shared"
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691

				AC_SUBST(OF_OPENSSL_TLS_STREAM_M,
					"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,
			[Whether we have an implementation for TLS])
		AC_CONFIG_FILES(src/tls/Info.plist)







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1782
1783
1784
1785
1786
1787
1788

















1789
1790
1791
1792
1793
1794
1795

				AC_SUBST(OF_OPENSSL_TLS_STREAM_M,
					"OFOpenSSLTLSStream.m")
			])
		], [], [-l$crypto])
	])


















	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,
			[Whether we have an implementation for TLS])
		AC_CONFIG_FILES(src/tls/Info.plist)
1993
1994
1995
1996
1997
1998
1999
2000

2001
2002
2003
2004
2005
2006
2007
			 */
			#ifdef __clang__
			# define OF_DEALLOC_UNSUPPORTED			\
				[self doesNotRecognizeSelector: _cmd];	\
									\
				abort();				\
									\
				_Pragma("clang diagnostic push ignore	\

				    \"-Wunreachable-code\"");		\
				[super dealloc];			\
				_Pragma("clang diagnostic pop");
			#else
			# define OF_DEALLOC_UNSUPPORTED			\
				[self doesNotRecognizeSelector: _cmd];	\
									\







|
>







2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
			 */
			#ifdef __clang__
			# define OF_DEALLOC_UNSUPPORTED			\
				[self doesNotRecognizeSelector: _cmd];	\
									\
				abort();				\
									\
				_Pragma("clang diagnostic push");	\
			       	_Pragma("clang diagnostic ignored	\
				    \"-Wunreachable-code\"");		\
				[super dealloc];			\
				_Pragma("clang diagnostic pop");
			#else
			# define OF_DEALLOC_UNSUPPORTED			\
				[self doesNotRecognizeSelector: _cmd];	\
									\

Modified extra.mk.in from [ab62bc99ee] to [e8bb473c44].

41
42
43
44
45
46
47

48
49
50
51
52
53
54
LIBOBJFW_DEP_LVL2 = @LIBOBJFW_DEP_LVL2@
LINKLIB = @LINKLIB@
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@

OFARC = @OFARC@
OFDNS = @OFDNS@
OFHASH = @OFHASH@
OFHTTP = @OFHTTP@
OFHTTP_LIBS = @OFHTTP_LIBS@
OF_BLOCK_TESTS_M = @OF_BLOCK_TESTS_M@
OF_EPOLL_KERNEL_EVENT_OBSERVER_M = @OF_EPOLL_KERNEL_EVENT_OBSERVER_M@







>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
LIBOBJFW_DEP_LVL2 = @LIBOBJFW_DEP_LVL2@
LINKLIB = @LINKLIB@
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@
OF_BLOCK_TESTS_M = @OF_BLOCK_TESTS_M@
OF_EPOLL_KERNEL_EVENT_OBSERVER_M = @OF_EPOLL_KERNEL_EVENT_OBSERVER_M@
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
TESTS_LIBS = @TESTS_LIBS@
TESTS_STATIC_LIB = @TESTS_STATIC_LIB@
TLS = @TLS@
TLS_CPPFLAGS = @TLS_CPPFLAGS@
TLS_LIBS = @TLS_LIBS@
UNICODE_M = @UNICODE_M@
USE_INCLUDES_ATOMIC = @USE_INCLUDES_ATOMIC@

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@







>








77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
TESTS_LIBS = @TESTS_LIBS@
TESTS_STATIC_LIB = @TESTS_STATIC_LIB@
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@

Modified generators/library/LibraryGenerator.m from [0e687972e3] to [69ede2939c].

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 */

#include "config.h"

#import "OFApplication.h"
#import "OFFile.h"
#import "OFFileManager.h"
#import "OFURL.h"
#import "OFXMLElement.h"

#import "FuncArrayGenerator.h"
#import "GlueGenerator.h"
#import "LinkLibGenerator.h"

@interface LibraryGenerator: OFObject <OFApplicationDelegate>
@end

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"];
	OFXMLElement *runtimeLibrary = [OFXMLElement elementWithStream:
	    [OFFile fileWithPath: runtimeLibraryURL.fileSystemRepresentation
			    mode: @"r"]];
	OFFile *runtimeLinkLib =
	    [OFFile fileWithPath: runtimeLinkLibURL.fileSystemRepresentation
			    mode: @"w"];
	OFFile *runtimeGlueHeader =
	    [OFFile fileWithPath: runtimeGlueHeaderURL.fileSystemRepresentation
			    mode: @"w"];
	OFFile *runtimeGlue =
	    [OFFile fileWithPath: runtimeGlueURL.fileSystemRepresentation
			    mode: @"w"];
	OFFile *runtimeFuncArray =
	    [OFFile fileWithPath: runtimeFuncArrayURL.fileSystemRepresentation
			    mode: @"w"];
	LinkLibGenerator *runtimeLinkLibGenerator = [[[LinkLibGenerator alloc]
	    initWithLibrary: runtimeLibrary
	     implementation: runtimeLinkLib] autorelease];
	GlueGenerator *runtimeGlueGenerator = [[[GlueGenerator alloc]
	    initWithLibrary: runtimeLibrary
		     header: runtimeGlueHeader







|














|
|
|
|
|
|
|
|
|
|
|
|

|


|


|


|


|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 */

#include "config.h"

#import "OFApplication.h"
#import "OFFile.h"
#import "OFFileManager.h"
#import "OFURI.h"
#import "OFXMLElement.h"

#import "FuncArrayGenerator.h"
#import "GlueGenerator.h"
#import "LinkLibGenerator.h"

@interface LibraryGenerator: OFObject <OFApplicationDelegate>
@end

OF_APPLICATION_DELEGATE(LibraryGenerator)

@implementation LibraryGenerator
- (void)applicationDidFinishLaunching
{
	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: runtimeLibraryURI.fileSystemRepresentation
			    mode: @"r"]];
	OFFile *runtimeLinkLib =
	    [OFFile fileWithPath: runtimeLinkLibURI.fileSystemRepresentation
			    mode: @"w"];
	OFFile *runtimeGlueHeader =
	    [OFFile fileWithPath: runtimeGlueHeaderURI.fileSystemRepresentation
			    mode: @"w"];
	OFFile *runtimeGlue =
	    [OFFile fileWithPath: runtimeGlueURI.fileSystemRepresentation
			    mode: @"w"];
	OFFile *runtimeFuncArray =
	    [OFFile fileWithPath: runtimeFuncArrayURI.fileSystemRepresentation
			    mode: @"w"];
	LinkLibGenerator *runtimeLinkLibGenerator = [[[LinkLibGenerator alloc]
	    initWithLibrary: runtimeLibrary
	     implementation: runtimeLinkLib] autorelease];
	GlueGenerator *runtimeGlueGenerator = [[[GlueGenerator alloc]
	    initWithLibrary: runtimeLibrary
		     header: runtimeGlueHeader

Modified generators/unicode/TableGenerator.m from [442fb49647] to [c1835e042f].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "config.h"

#include <string.h>

#import "OFString.h"
#import "OFArray.h"
#import "OFApplication.h"
#import "OFURL.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFHTTPClient.h"
#import "OFFile.h"
#import "OFStdIOStream.h"

#import "OFOutOfRangeException.h"

#import "TableGenerator.h"
#import "copyright.h"

static OFString *const unicodeDataURL =
    @"http://www.unicode.org/Public/UNIDATA/UnicodeData.txt";
static OFString *const caseFoldingURL =
    @"http://www.unicode.org/Public/UNIDATA/CaseFolding.txt";

OF_APPLICATION_DELEGATE(TableGenerator)

@implementation TableGenerator
- (instancetype)init
{







|











|

|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "config.h"

#include <string.h>

#import "OFString.h"
#import "OFArray.h"
#import "OFApplication.h"
#import "OFURI.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFHTTPClient.h"
#import "OFFile.h"
#import "OFStdIOStream.h"

#import "OFOutOfRangeException.h"

#import "TableGenerator.h"
#import "copyright.h"

static OFString *const unicodeDataURI =
    @"http://www.unicode.org/Public/UNIDATA/UnicodeData.txt";
static OFString *const caseFoldingURI =
    @"http://www.unicode.org/Public/UNIDATA/CaseFolding.txt";

OF_APPLICATION_DELEGATE(TableGenerator)

@implementation TableGenerator
- (instancetype)init
{
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

- (void)applicationDidFinishLaunching
{
	OFHTTPRequest *request;

	[OFStdOut writeString: @"Downloading UnicodeData.txt…"];
	_state = stateUnicodeData;
	request = [OFHTTPRequest requestWithURL:
	    [OFURL URLWithString: unicodeDataURL]];
	[_HTTPClient asyncPerformRequest: request];
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response
	  exception: (id)exception







|
|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

- (void)applicationDidFinishLaunching
{
	OFHTTPRequest *request;

	[OFStdOut writeString: @"Downloading UnicodeData.txt…"];
	_state = stateUnicodeData;
	request = [OFHTTPRequest requestWithURI:
	    [OFURI URIWithString: unicodeDataURI]];
	[_HTTPClient asyncPerformRequest: request];
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response
	  exception: (id)exception
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
			OFArray *decomposed = [[components objectAtIndex: 5]
			    componentsSeparatedByString: @" "];
			bool compat = false;
			OFMutableString *string;

			if ([decomposed.firstObject hasPrefix: @"<"]) {
				decomposed = [decomposed objectsInRange:
				    OFRangeMake(1, decomposed.count - 1)];
				compat = true;
			}

			string = [OFMutableString string];

			for (OFString *character in decomposed) {
				OFUnichar unichar = (OFUnichar)[character







|







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
			OFArray *decomposed = [[components objectAtIndex: 5]
			    componentsSeparatedByString: @" "];
			bool compat = false;
			OFMutableString *string;

			if ([decomposed.firstObject hasPrefix: @"<"]) {
				decomposed = [decomposed objectsInRange:
				    OFMakeRange(1, decomposed.count - 1)];
				compat = true;
			}

			string = [OFMutableString string];

			for (OFString *character in decomposed) {
				OFUnichar unichar = (OFUnichar)[character
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
	[self applyDecompositionRecursivelyForTable: _decompositionTable];
	[self applyDecompositionRecursivelyForTable: _decompositionCompatTable];

	[OFStdOut writeLine: @" done"];

	[OFStdOut writeString: @"Downloading CaseFolding.txt…"];
	_state = stateCaseFolding;
	request = [OFHTTPRequest requestWithURL:
	    [OFURL URLWithString: caseFoldingURL]];
	[_HTTPClient asyncPerformRequest: request];
}

- (void)parseCaseFolding: (OFHTTPResponse *)response
{
	OFString *line;








|
|







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
	[self applyDecompositionRecursivelyForTable: _decompositionTable];
	[self applyDecompositionRecursivelyForTable: _decompositionCompatTable];

	[OFStdOut writeLine: @" done"];

	[OFStdOut writeString: @"Downloading CaseFolding.txt…"];
	_state = stateCaseFolding;
	request = [OFHTTPRequest requestWithURI:
	    [OFURI URIWithString: caseFoldingURI]];
	[_HTTPClient asyncPerformRequest: request];
}

- (void)parseCaseFolding: (OFHTTPResponse *)response
{
	OFString *line;

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
			objc_autoreleasePoolPop(pool);
		}
	} while (!done);
}

- (void)writeFiles
{
	OFURL *URL;

	[OFStdOut writeString: @"Writing files…"];

	URL = [OFURL fileURLWithPath: @"../../src/unicode.m"];
	[self writeTablesToFile: URL.fileSystemRepresentation];

	URL = [OFURL fileURLWithPath: @"../../src/unicode.h"];
	[self writeHeaderToFile: URL.fileSystemRepresentation];

	[OFStdOut writeLine: @" done"];

	[OFApplication terminate];
}

- (void)writeTablesToFile: (OFString *)path







|



|
|

|
|







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
			objc_autoreleasePoolPop(pool);
		}
	} while (!done);
}

- (void)writeFiles
{
	OFURI *URI;

	[OFStdOut writeString: @"Writing files…"];

	URI = [OFURI fileURIWithPath: @"../../src/unicode.m"];
	[self writeTablesToFile: URI.fileSystemRepresentation];

	URI = [OFURI fileURIWithPath: @"../../src/unicode.h"];
	[self writeHeaderToFile: URI.fileSystemRepresentation];

	[OFStdOut writeLine: @" done"];

	[OFApplication terminate];
}

- (void)writeTablesToFile: (OFString *)path

Modified objfw.spec from [01416894bd] to [218ec62f80].

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
URL:           https://objfw.nil.im
Source0:       objfw-%{version}.tar.gz

BuildRequires: autoconf
BuildRequires: automake
BuildRequires: clang
BuildRequires: make
BuildRequires: pkgconfig(openssl)
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}
Requires:      ofdns%{_isa} = %{version}-%{release}
Requires:      ofhash%{_isa} = %{version}-%{release}







|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
URL:           https://objfw.nil.im
Source0:       objfw-%{version}.tar.gz

BuildRequires: autoconf
BuildRequires: automake
BuildRequires: clang
BuildRequires: make
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}
Requires:      ofdns%{_isa} = %{version}-%{release}
Requires:      ofhash%{_isa} = %{version}-%{release}
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

%description -n %{libobjfwrt_pkgname}-devel
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

%description -n %{libobjfwtls_pkgname}
The %{libobjfwtls_pkgname} package contains TLS support for ObjFW

%package -n %{libobjfwtls_pkgname}-devel
Summary:       Header files and libraries for %{libobjfwtls_pkgname}
Requires:      %{libobjfwtls_pkgname}%{_isa} = %{version}-%{release}







|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

%description -n %{libobjfwrt_pkgname}-devel
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:      gnutls%{_isa} >= 3.0.5

%description -n %{libobjfwtls_pkgname}
The %{libobjfwtls_pkgname} package contains TLS support for ObjFW

%package -n %{libobjfwtls_pkgname}-devel
Summary:       Header files and libraries for %{libobjfwtls_pkgname}
Requires:      %{libobjfwtls_pkgname}%{_isa} = %{version}-%{release}
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
%package -n ofhttp
Summary:       Command line downloader for HTTP(S)
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.

%prep
%autosetup
./autogen.sh

%build
%configure OBJC=clang --disable-rpath







|
|
|
|







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
%package -n ofhttp
Summary:       Command line downloader for HTTP(S)
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 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

%build
%configure OBJC=clang --disable-rpath

Modified src/Makefile from [d976139930] to [7536b0b5b7].

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
       OFConstantString.m		\
       OFCountedSet.m			\
       OFData.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		\
       OFList.m				\
       OFLocale.m			\







<






<







18
19
20
21
22
23
24

25
26
27
28
29
30

31
32
33
34
35
36
37
       OFConstantString.m		\
       OFCountedSet.m			\
       OFData.m				\
       OFData+CryptographicHashing.m	\
       OFData+MessagePackParsing.m	\
       OFDate.m				\
       OFDictionary.m			\

       OFEnumerator.m			\
       OFFileManager.m			\
       OFGZIPStream.m			\
       OFHMAC.m				\
       OFINICategory.m			\
       OFINIFile.m			\

       OFInflate64Stream.m		\
       OFInflateStream.m		\
       OFInvocation.m			\
       OFLHAArchive.m			\
       OFLHAArchiveEntry.m		\
       OFList.m				\
       OFLocale.m			\
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
       OFMutableDictionary.m		\
       OFMutableLHAArchiveEntry.m	\
       OFMutablePair.m			\
       OFMutableSet.m			\
       OFMutableString.m		\
       OFMutableTarArchiveEntry.m	\
       OFMutableTriple.m		\
       OFMutableURL.m			\
       OFMutableZIPArchiveEntry.m	\
       OFNotification.m			\
       OFNotificationCenter.m		\
       OFNull.m				\
       OFNumber.m			\
       OFObject.m			\
       OFObject+KeyValueCoding.m	\







|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
       OFMutableDictionary.m		\
       OFMutableLHAArchiveEntry.m	\
       OFMutablePair.m			\
       OFMutableSet.m			\
       OFMutableString.m		\
       OFMutableTarArchiveEntry.m	\
       OFMutableTriple.m		\
       OFMutableURI.m			\
       OFMutableZIPArchiveEntry.m	\
       OFNotification.m			\
       OFNotificationCenter.m		\
       OFNull.m				\
       OFNumber.m			\
       OFObject.m			\
       OFObject+KeyValueCoding.m	\
80
81
82
83
84
85
86

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
       OFSet.m				\
       OFSortedList.m			\
       OFStdIOStream.m			\
       OFStream.m			\
       OFString.m			\
       OFString+CryptographicHashing.m	\
       OFString+JSONParsing.m		\

       OFString+PropertyListParsing.m	\
       OFString+Serialization.m		\
       OFString+URLEncoding.m		\
       OFString+XMLEscaping.m		\
       OFString+XMLUnescaping.m		\
       ${OF_SUBPROCESS_M}		\
       OFSettings.m			\
       OFSystemInfo.m			\
       OFTarArchive.m			\
       OFTarArchiveEntry.m		\
       OFThread.m			\
       OFTimer.m			\
       OFTriple.m			\
       OFURL.m				\
       OFURLHandler.m			\
       OFUUID.m				\
       OFValue.m			\
       OFXMLAttribute.m			\
       OFXMLCDATA.m			\
       OFXMLCharacters.m		\
       OFXMLComment.m			\
       OFXMLElement.m			\







>


<










|
|







78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
       OFSet.m				\
       OFSortedList.m			\
       OFStdIOStream.m			\
       OFStream.m			\
       OFString.m			\
       OFString+CryptographicHashing.m	\
       OFString+JSONParsing.m		\
       OFString+PercentEncoding.m	\
       OFString+PropertyListParsing.m	\
       OFString+Serialization.m		\

       OFString+XMLEscaping.m		\
       OFString+XMLUnescaping.m		\
       ${OF_SUBPROCESS_M}		\
       OFSettings.m			\
       OFSystemInfo.m			\
       OFTarArchive.m			\
       OFTarArchiveEntry.m		\
       OFThread.m			\
       OFTimer.m			\
       OFTriple.m			\
       OFURI.m				\
       OFURIHandler.m			\
       OFUUID.m				\
       OFValue.m			\
       OFXMLAttribute.m			\
       OFXMLCDATA.m			\
       OFXMLCharacters.m		\
       OFXMLComment.m			\
       OFXMLElement.m			\
134
135
136
137
138
139
140

141
142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166
167
168
169
170
171

172
173
174
175
176
177
178
179
180

181
182
183
184
185
186

187

188
189
190
191
192
193
194
	       OFHTTPServer.m			\
	       OFSequencedPacketSocket.m	\
	       OFSocket.m			\
	       OFStreamSocket.m			\
	       OFTCPSocket.m			\
	       OFTLSStream.m			\
	       OFUDPSocket.m			\

	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_UNIX_SOCKETS}

SRCS_IPX = OFIPXSocket.m	\
	   OFSPXSocket.m	\
	   OFSPXStreamSocket.m
SRCS_UNIX_SOCKETS = OFUNIXDatagramSocket.m	\
		    OFUNIXStreamSocket.m
SRCS_THREADS = OFCondition.m		\
	       OFMutex.m		\
	       OFPlainCondition.m	\
	       OFPlainMutex.m		\
	       OFPlainThread.m		\
	       OFRecursiveMutex.m	\
	       OFTLSKey.m
SRCS_WINDOWS = OFWin32ConsoleStdIOStream.m	\
	       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}			\

	    OFCollection.h			\
	    OFCryptographicHash.h		\
	    OFJSONRepresentation.h		\
	    OFKernelEventObserver.h		\
	    OFKeyValueCoding.h			\
	    OFLocking.h				\
	    OFMessagePackRepresentation.h	\

	    ObjFW.h				\
	    macros.h				\
	    objfw-defs.h			\
	    platform.h				\
	    ${USE_INCLUDES_ATOMIC}

SRCS += OFASPrintF.m			\
	OFAdjacentArray.m		\
	OFAdjacentSubarray.m		\

	OFBase64.m			\
	OFBitSetCharacterSet.m		\
	OFBytesValue.m			\
	OFCRC16.m			\
	OFCRC32.m			\
	OFCountedMapTableSet.m		\

	OFHuffmanTree.m			\

	OFInvertedCharacterSet.m	\
	OFLHADecompressingStream.m	\
	OFMapTableDictionary.m		\
	OFMapTableSet.m			\
	OFMutableAdjacentArray.m	\
	OFMutableMapTableDictionary.m	\
	OFMutableMapTableSet.m		\







>


>












<
|








>







>









>






>

>







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
	       OFHTTPServer.m			\
	       OFSequencedPacketSocket.m	\
	       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
SRCS_THREADS = OFCondition.m		\
	       OFMutex.m		\
	       OFPlainCondition.m	\
	       OFPlainMutex.m		\
	       OFPlainThread.m		\
	       OFRecursiveMutex.m	\
	       OFTLSKey.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}			\
	    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	\
	OFMutableMapTableDictionary.m	\
	OFMutableMapTableSet.m		\
203
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
220
221


222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240





	OFSizeValue.m			\
	OFStrPTime.m			\
	OFSubarray.m			\
	OFUTF8String.m			\
	${LIBBASES_M}			\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}		\
       ${UNICODE_M}
SRCS_FILES += OFFileURLHandler.m

SRCS_SOCKETS += OFDNSResolverSettings.m			\
		${OF_EPOLL_KERNEL_EVENT_OBSERVER_M}	\
		OFHTTPURLHandler.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



OBJS_EXTRA = exceptions/exceptions.a	\
	     encodings/encodings.a	\
	     forwarding/forwarding.a	\
	     invocation/invocation.a
LIB_OBJS_EXTRA = exceptions/exceptions.lib.a	\
		 encodings/encodings.lib.a	\
		 forwarding/forwarding.lib.a	\
		 invocation/invocation.lib.a

include ../buildsys.mk

CPPFLAGS += -I. -I.. -Iexceptions -Iruntime
LD = ${OBJC}
FRAMEWORK_LIBS := -Fruntime				\
		  ${RUNTIME_FRAMEWORK_LIBS}		\
		  ${REEXPORT_RUNTIME_FRAMEWORK}		\
		  ${LIBS}
LIBS := -Lruntime ${RUNTIME_LIBS} ${REEXPORT_RUNTIME} ${LIBS}












|
|
>
|

|

<





>
>



















>
>
>
>
>
207
208
209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
	OFSizeValue.m			\
	OFStrPTime.m			\
	OFSubarray.m			\
	OFUTF8String.m			\
	${LIBBASES_M}			\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}		\
	${UNICODE_M}
SRCS_FILES += OFFileURIHandler.m
SRCS_SOCKETS += OFAsyncIPSocketConnector.m		\
		OFDNSResolverSettings.m			\
		${OF_EPOLL_KERNEL_EVENT_OBSERVER_M}	\
		OFHTTPURIHandler.m			\
		OFHostAddressResolver.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	\
	     invocation/invocation.a
LIB_OBJS_EXTRA = exceptions/exceptions.lib.a	\
		 encodings/encodings.lib.a	\
		 forwarding/forwarding.lib.a	\
		 invocation/invocation.lib.a

include ../buildsys.mk

CPPFLAGS += -I. -I.. -Iexceptions -Iruntime
LD = ${OBJC}
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}\"

Modified src/OFASPrintF.m from [31044637f2] to [882db55cea].

578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
			return false;

		/*
		 * 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];

		if (!ctx->useLocale && point != nil && ![point isEqual: @"."]) {
			void *pool = objc_autoreleasePoolPush();
			char *tmp2;

			@try {
				OFMutableString *tmpStr = [OFMutableString







|







578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
			return false;

		/*
		 * 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 decimalSeparator];

		if (!ctx->useLocale && point != nil && ![point isEqual: @"."]) {
			void *pool = objc_autoreleasePoolPush();
			char *tmp2;

			@try {
				OFMutableString *tmpStr = [OFMutableString

Modified src/OFApplication.m from [bde88848cd] to [1a4d3713d8].

38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
#import "OFRunLoop.h"
#import "OFSandbox.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFThread+Private.h"
#import "OFThread.h"


#import "OFInvalidArgumentException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFSandboxActivationFailedException.h"

#if defined(OF_MACOS)
# include <crt_externs.h>
#elif defined(OF_WINDOWS)
# include <windows.h>
extern int _CRT_glob;
extern void __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, int *);







>



<







38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
#import "OFRunLoop.h"
#import "OFSandbox.h"
#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"


#if defined(OF_MACOS)
# include <crt_externs.h>
#elif defined(OF_WINDOWS)
# include <windows.h>
extern int _CRT_glob;
extern void __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, int *);
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
	}

	sandbox->_unveiledPathsIndex = unveiledPathsCount;

	promises = [sandbox.pledgeString cStringWithEncoding: encoding];

	if (pledge(promises, NULL) != 0)
		@throw [OFSandboxActivationFailedException
		    exceptionWithSandbox: sandbox
				   errNo: errno];

	objc_autoreleasePoolPop(pool);

	if (_activeSandbox == nil)
		_activeSandbox = [sandbox retain];







|







650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
	}

	sandbox->_unveiledPathsIndex = unveiledPathsCount;

	promises = [sandbox.pledgeString cStringWithEncoding: encoding];

	if (pledge(promises, NULL) != 0)
		@throw [OFActivateSandboxFailedException
		    exceptionWithSandbox: sandbox
				   errNo: errno];

	objc_autoreleasePoolPop(pool);

	if (_activeSandbox == nil)
		_activeSandbox = [sandbox retain];
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
	if (sandbox.unveiledPaths.count != 0)
		@throw [OFInvalidArgumentException exception];

	promises = [sandbox.pledgeString
	    cStringWithEncoding: [OFLocale encoding]];

	if (pledge(NULL, promises) != 0)
		@throw [OFSandboxActivationFailedException
		    exceptionWithSandbox: sandbox
				   errNo: errno];

	objc_autoreleasePoolPop(pool);

	if (_activeSandboxForChildProcesses == nil)
		_activeSandboxForChildProcesses = [sandbox retain];
# endif
}
#endif
@end







|











678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
	if (sandbox.unveiledPaths.count != 0)
		@throw [OFInvalidArgumentException exception];

	promises = [sandbox.pledgeString
	    cStringWithEncoding: [OFLocale encoding]];

	if (pledge(NULL, promises) != 0)
		@throw [OFActivateSandboxFailedException
		    exceptionWithSandbox: sandbox
				   errNo: errno];

	objc_autoreleasePoolPop(pool);

	if (_activeSandboxForChildProcesses == nil)
		_activeSandboxForChildProcesses = [sandbox retain];
# endif
}
#endif
@end

Added src/OFArchiveEntry.h version [70aea150d9].























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * 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 <OFObject>

/**
 * @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 version [7c81cf8db2].

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [010e8050b6].

















































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * 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 <errno.h>

#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;
}

Modified src/OFArray.m from [7359b37923] to [c89de989b4].

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
- (id const *)objects
{
	size_t count = self.count;
	id *buffer = OFAllocMemory(count, sizeof(id));
	id const *ret;

	@try {
		[self getObjects: buffer inRange: OFRangeMake(0, count)];

		ret = [[OFData dataWithItemsNoCopy: buffer
					     count: count
					  itemSize: sizeof(id)
				      freeWhenDone: true] items];
	} @catch (id e) {
		OFFreeMemory(buffer);







|







238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
- (id const *)objects
{
	size_t count = self.count;
	id *buffer = OFAllocMemory(count, sizeof(id));
	id const *ret;

	@try {
		[self getObjects: buffer inRange: OFMakeRange(0, count)];

		ret = [[OFData dataWithItemsNoCopy: buffer
					     count: count
					  itemSize: sizeof(id)
				      freeWhenDone: true] items];
	} @catch (id e) {
		OFFreeMemory(buffer);
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
	if (self.count == 0)
		return @"()";

	pool = objc_autoreleasePoolPush();
	ret = [[self componentsJoinedByString: @",\n"] mutableCopy];

	@try {
		[ret prependString: @"(\n"];
		[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];
		[ret appendString: @"\n)"];
	} @catch (id e) {
		[ret release];
		@throw e;
	}








|







531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
	if (self.count == 0)
		return @"()";

	pool = objc_autoreleasePoolPush();
	ret = [[self componentsJoinedByString: @",\n"] mutableCopy];

	@try {
		[ret insertString: @"(\n" atIndex: 0];
		[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];
		[ret appendString: @"\n)"];
	} @catch (id e) {
		[ret release];
		@throw e;
	}

747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
	return new;
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	OFRange range = OFRangeMake(state->state, count);

	if (range.length > SIZE_MAX - range.location)
		@throw [OFOutOfRangeException exception];

	if (range.location + range.length > self.count)
		range.length = self.count - range.location;








|







747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
	return new;
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	OFRange range = OFMakeRange(state->state, count);

	if (range.length > SIZE_MAX - range.location)
		@throw [OFOutOfRangeException exception];

	if (range.location + range.length > self.count)
		range.length = self.count - range.location;

Modified src/OFAsyncIPSocketConnector.h from [248ffd2ce4] to [00aff2415e].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

#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 <OFRunLoopConnectDelegate,
    OFDNSResolverHostDelegate>
{
	id _socket;
	OFString *_host;
	uint16_t _port;
	id _Nullable _delegate;
	id _Nullable _block;







|







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

#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 <OFRunLoopConnectDelegate,
    OFDNSResolverHostDelegate>
{
	id _socket;
	OFString *_host;
	uint16_t _port;
	id _Nullable _delegate;
	id _Nullable _block;

Modified src/OFAsyncIPSocketConnector.m from [e235fd33e4] to [b9084cfb8c].

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 * file.
 */

#include "config.h"

#include <errno.h>

#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];







|





|


|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 * file.
 */

#include "config.h"

#include <errno.h>

#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];
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
	}

	[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;
		}







|
|
|
|








|



|







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
	}

	[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;
		}
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
						      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;
			}







|







183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
						      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;
			}

Modified src/OFColor.m from [5191910ddc] to [ff82ab6aae].

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
	unsigned long hash;
	float tmp;

	OFHashInit(&hash);

	tmp = OFToLittleEndianFloat(_red);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAdd(&hash, ((char *)&tmp)[i]);

	tmp = OFToLittleEndianFloat(_green);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAdd(&hash, ((char *)&tmp)[i]);

	tmp = OFToLittleEndianFloat(_blue);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAdd(&hash, ((char *)&tmp)[i]);

	tmp = OFToLittleEndianFloat(_alpha);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAdd(&hash, ((char *)&tmp)[i]);

	OFHashFinalize(&hash);

	return hash;
}

- (void)getRed: (float *)red







|



|



|



|







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
	unsigned long hash;
	float tmp;

	OFHashInit(&hash);

	tmp = OFToLittleEndianFloat(_red);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAddByte(&hash, ((char *)&tmp)[i]);

	tmp = OFToLittleEndianFloat(_green);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAddByte(&hash, ((char *)&tmp)[i]);

	tmp = OFToLittleEndianFloat(_blue);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAddByte(&hash, ((char *)&tmp)[i]);

	tmp = OFToLittleEndianFloat(_alpha);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OFHashAddByte(&hash, ((char *)&tmp)[i]);

	OFHashFinalize(&hash);

	return hash;
}

- (void)getRed: (float *)red

Modified src/OFCondition.h from [b8eb2412a1] to [2de2d56fa5].

41
42
43
44
45
46
47


48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86

87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120


121
122
123
124
125


126
127
128
129
130

/**
 * @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!


 */
- (void)wait;

#ifdef OF_AMIGAOS
/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast or an Exec Signal is received.
 *
 * @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.

 */
- (void)waitForConditionOrExecSignal: (ULONG *)signalMask;
#endif

/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast or the timeout is reached.
 *
 * @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

 */
- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval;

#ifdef OF_AMIGAOS
/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast, the timeout is reached or an Exec Signal is received.
 *
 * @note This is only available on AmigaOS!
 *
 * @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

 */
- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval
	       orExecSignal: (ULONG *)signalMask;
#endif

/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast or the timeout is reached.
 *
 * @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

 */
- (bool)waitUntilDate: (OFDate *)date;

#ifdef OF_AMIGAOS
/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast, the timeout is reached or an Exec Signal is received.
 *
 * @note This is only available on AmigaOS!
 *
 * @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

 */
- (bool)waitUntilDate: (OFDate *)date orExecSignal: (ULONG *)signalMask;
#endif

/**
 * @brief Signals the next waiting thread to continue.


 */
- (void)signal;

/**
 * @brief Signals all threads to continue.


 */
- (void)broadcast;
@end

OF_ASSUME_NONNULL_END







>
>












>













>














>














>














>






>
>





>
>





41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

/**
 * @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
/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast or an Exec Signal is received.
 *
 * @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

/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast or the timeout is reached.
 *
 * @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
/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast, the timeout is reached or an Exec Signal is received.
 *
 * @note This is only available on AmigaOS!
 *
 * @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

/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast or the timeout is reached.
 *
 * @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
/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast, the timeout is reached or an Exec Signal is received.
 *
 * @note This is only available on AmigaOS!
 *
 * @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

Modified src/OFCondition.m from [8de6591c81] to [6291bb097b].

16
17
18
19
20
21
22
23
24
25
26
27


28
29
30
31
32
33
34
#include "config.h"

#include <errno.h>

#import "OFCondition.h"
#import "OFDate.h"

#import "OFConditionBroadcastFailedException.h"
#import "OFConditionSignalFailedException.h"
#import "OFConditionStillWaitingException.h"
#import "OFConditionWaitFailedException.h"
#import "OFInitializationFailedException.h"



@implementation OFCondition
+ (instancetype)condition
{
	return [[[self alloc] init] autorelease];
}








<
|

<

>
>







16
17
18
19
20
21
22

23
24

25
26
27
28
29
30
31
32
33
34
#include "config.h"

#include <errno.h>

#import "OFCondition.h"
#import "OFDate.h"


#import "OFBroadcastConditionFailedException.h"
#import "OFConditionStillWaitingException.h"

#import "OFInitializationFailedException.h"
#import "OFSignalConditionFailedException.h"
#import "OFWaitForConditionFailedException.h"

@implementation OFCondition
+ (instancetype)condition
{
	return [[[self alloc] init] autorelease];
}

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
}

- (void)wait
{
	int error = OFPlainConditionWait(&_condition, &_mutex);

	if (error != 0)
		@throw [OFConditionWaitFailedException
		    exceptionWithCondition: self
				     errNo: error];
}

#ifdef OF_AMIGAOS
- (void)waitForConditionOrExecSignal: (ULONG *)signalMask
{
	int error = OFPlainConditionWaitOrExecSignal(&_condition, &_mutex,
	    signalMask);

	if (error != 0)
		@throw [OFConditionWaitFailedException
		    exceptionWithCondition: self
				     errNo: error];
}
#endif

- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval
{
	int error = OFPlainConditionTimedWait(&_condition, &_mutex,
	    timeInterval);

	if (error == ETIMEDOUT)
		return false;

	if (error != 0)
		@throw [OFConditionWaitFailedException
		    exceptionWithCondition: self
				     errNo: error];

	return true;
}

#ifdef OF_AMIGAOS
- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval
	       orExecSignal: (ULONG *)signalMask
{
	int error = OFPlainConditionTimedWaitOrExecSignal(&_condition, &_mutex,
	    timeInterval, signalMask);

	if (error == ETIMEDOUT)
		return false;

	if (error != 0)
		@throw [OFConditionWaitFailedException
		    exceptionWithCondition: self
				     errNo: error];

	return true;
}
#endif








|











|














|

















|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
}

- (void)wait
{
	int error = OFPlainConditionWait(&_condition, &_mutex);

	if (error != 0)
		@throw [OFWaitForConditionFailedException
		    exceptionWithCondition: self
				     errNo: error];
}

#ifdef OF_AMIGAOS
- (void)waitForConditionOrExecSignal: (ULONG *)signalMask
{
	int error = OFPlainConditionWaitOrExecSignal(&_condition, &_mutex,
	    signalMask);

	if (error != 0)
		@throw [OFWaitForConditionFailedException
		    exceptionWithCondition: self
				     errNo: error];
}
#endif

- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval
{
	int error = OFPlainConditionTimedWait(&_condition, &_mutex,
	    timeInterval);

	if (error == ETIMEDOUT)
		return false;

	if (error != 0)
		@throw [OFWaitForConditionFailedException
		    exceptionWithCondition: self
				     errNo: error];

	return true;
}

#ifdef OF_AMIGAOS
- (bool)waitForTimeInterval: (OFTimeInterval)timeInterval
	       orExecSignal: (ULONG *)signalMask
{
	int error = OFPlainConditionTimedWaitOrExecSignal(&_condition, &_mutex,
	    timeInterval, signalMask);

	if (error == ETIMEDOUT)
		return false;

	if (error != 0)
		@throw [OFWaitForConditionFailedException
		    exceptionWithCondition: self
				     errNo: error];

	return true;
}
#endif

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#endif

- (void)signal
{
	int error = OFPlainConditionSignal(&_condition);

	if (error != 0)
		@throw [OFConditionSignalFailedException
		    exceptionWithCondition: self
				     errNo: error];
}

- (void)broadcast
{
	int error = OFPlainConditionBroadcast(&_condition);

	if (error != 0)
		@throw [OFConditionBroadcastFailedException
		    exceptionWithCondition: self
				     errNo: error];
}
@end







|









|




135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#endif

- (void)signal
{
	int error = OFPlainConditionSignal(&_condition);

	if (error != 0)
		@throw [OFSignalConditionFailedException
		    exceptionWithCondition: self
				     errNo: error];
}

- (void)broadcast
{
	int error = OFPlainConditionBroadcast(&_condition);

	if (error != 0)
		@throw [OFBroadcastConditionFailedException
		    exceptionWithCondition: self
				     errNo: error];
}
@end

Modified src/OFConstantString.m from [d768085319] to [3adc07df05].

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371

- (OFString *)stringByAppendingPathExtension: (OFString *)extension
{
	[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
					       withString: replacement];
}







<
<
<
<
<
<







352
353
354
355
356
357
358






359
360
361
362
363
364
365

- (OFString *)stringByAppendingPathExtension: (OFString *)extension
{
	[self finishInitialization];
	return [self stringByAppendingPathExtension: extension];
}







- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
{
	[self finishInitialization];
	return [self stringByReplacingOccurrencesOfString: string
					       withString: replacement];
}
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505

- (long long)longLongValue
{
	[self finishInitialization];
	return self.longLongValue;
}

- (long long)longLongValueWithBase: (int)base
{
	[self finishInitialization];
	return [self longLongValueWithBase: base];
}

- (unsigned long long)unsignedLongLongValue
{
	[self finishInitialization];
	return self.unsignedLongLongValue;
}

- (unsigned long long)unsignedLongLongValueWithBase: (int)base
{
	[self finishInitialization];
	return [self unsignedLongLongValueWithBase: base];
}

- (float)floatValue
{







|











|







473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499

- (long long)longLongValue
{
	[self finishInitialization];
	return self.longLongValue;
}

- (long long)longLongValueWithBase: (unsigned char)base
{
	[self finishInitialization];
	return [self longLongValueWithBase: base];
}

- (unsigned long long)unsignedLongLongValue
{
	[self finishInitialization];
	return self.unsignedLongLongValue;
}

- (unsigned long long)unsignedLongLongValueWithBase: (unsigned char)base
{
	[self finishInitialization];
	return [self unsignedLongLongValueWithBase: base];
}

- (float)floatValue
{
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding
{
	[self finishInitialization];
	[self writeToFile: path encoding: encoding];
}
#endif

- (void)writeToURL: (OFURL *)URL
{
	[self finishInitialization];
	[self writeToURL: URL];
}

- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
{
	[self finishInitialization];
	[self writeToURL: URL encoding: encoding];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block
{
	[self finishInitialization];
	[self enumerateLinesUsingBlock: block];
}
#endif
@end







|


|


|


|










581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding
{
	[self finishInitialization];
	[self writeToFile: path encoding: encoding];
}
#endif

- (void)writeToURI: (OFURI *)URI
{
	[self finishInitialization];
	[self writeToURI: URI];
}

- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
	[self finishInitialization];
	[self writeToURI: URI encoding: encoding];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block
{
	[self finishInitialization];
	[self enumerateLinesUsingBlock: block];
}
#endif
@end

Modified src/OFCryptographicHash.h from [21f208b184] to [635d4f982a].

54
55
56
57
58
59
60


61
62
63
64
65
66
67
@property (readonly, nonatomic, getter=isCalculated) bool calculated;

/**
 * @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.


 */
@property (readonly, nonatomic) const unsigned char *digest
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Creates a new cryptographic hash.
 *







>
>







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@property (readonly, nonatomic, getter=isCalculated) bool calculated;

/**
 * @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;

/**
 * @brief Creates a new cryptographic hash.
 *
93
94
95
96
97
98
99

100
101
102
103
104


105
106
107
108
109
110
111
- (instancetype)init OF_UNAVAILABLE;

/**
 * @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

 */
- (void)updateWithBuffer: (const void *)buffer length: (size_t)length;

/**
 * @brief Performs the final calculation of the cryptographic hash.


 */
- (void)calculate;

/**
 * @brief Resets all state so that a new hash can be calculated.
 *
 * @warning This invalidates any pointer previously returned by @ref digest. If







>





>
>







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
- (instancetype)init OF_UNAVAILABLE;

/**
 * @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.
 *
 * @warning This invalidates any pointer previously returned by @ref digest. If

Added src/OFDDPSocket.h version [86e7a28476].

























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * 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 <OFDatagramSocketDelegate>
@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 <OFDDPSocketDelegate> 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 version [d532fdd70f].























































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * 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 <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#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 <netat/ddp.h>
# include <sys/ioctl.h>

/* 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

Modified src/OFDNSQuery.h from [9c2a0f4bc6] to [b49f52a5a4].

21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
@class OFString;

/**
 * @class OFDNSQuery OFDNSQuery.h ObjFW/OFDNSQuery.h
 *
 * @brief A class representing a DNS query.
 */

@interface OFDNSQuery: OFObject <OFCopying>
{
	OFString *_domainName;
	OFDNSClass _DNSClass;
	OFDNSRecordType _recordType;
	OF_RESERVE_IVARS(OFDNSQuery, 4)
}

/**
 * @brief The domain name of the query.
 */
@property (readonly, nonatomic) OFString *domainName;








>





<







21
22
23
24
25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
@class OFString;

/**
 * @class OFDNSQuery OFDNSQuery.h ObjFW/OFDNSQuery.h
 *
 * @brief A class representing a DNS query.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFDNSQuery: OFObject <OFCopying>
{
	OFString *_domainName;
	OFDNSClass _DNSClass;
	OFDNSRecordType _recordType;

}

/**
 * @brief The domain name of the query.
 */
@property (readonly, nonatomic) OFString *domainName;

Modified src/OFDNSQuery.m from [03ea4234b3] to [eb5858939c].

93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);
	OFHashAddHash(&hash, _domainName.hash);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType);
	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{







|
|







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);
	OFHashAddHash(&hash, _domainName.hash);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType);
	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{

Modified src/OFDNSResolver.h from [0eee98275d] to [a91104c174].

256
257
258
259
260
261
262


263
264
265
266
267
268
269
270
271
272
273

/**
 * @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


 */
- (OFData *)resolveAddressesForHost: (OFString *)host
		      addressFamily: (OFSocketAddressFamily)addressFamily;

/**
 * @brief Closes all sockets and cancels all ongoing queries.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END







>
>











256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

/**
 * @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;

/**
 * @brief Closes all sockets and cancels all ongoing queries.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Modified src/OFDNSResolver.m from [6722a88381] to [9048cbfc52].

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#import "OFUDPSocket.h"
#import "OFUDPSocket+Private.h"

#import "OFDNSQueryFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"

#ifndef SOCK_DNS
# define SOCK_DNS 0
#endif







|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#import "OFUDPSocket.h"
#import "OFUDPSocket+Private.h"

#import "OFDNSQueryFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerResponseException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"

#ifndef SOCK_DNS
# define SOCK_DNS 0
#endif
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
		componentLength = buffer[(*i)++];

		if (componentLength & 0xC0) {
			size_t j;
			OFString *suffix;

			if (pointerLevel == 0)
				@throw [OFInvalidServerReplyException
				    exception];

			if (*i >= length)
				@throw [OFTruncatedDataException exception];

			j = ((componentLength & 0x3F) << 8) | buffer[(*i)++];

			if (j == *i - 2)
				/* Pointing to itself?! */
				@throw [OFInvalidServerReplyException
				    exception];

			suffix = parseName(buffer, length, &j,
			    pointerLevel - 1);

			if (components.count == 0)
				return suffix;







|









|







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
		componentLength = buffer[(*i)++];

		if (componentLength & 0xC0) {
			size_t j;
			OFString *suffix;

			if (pointerLevel == 0)
				@throw [OFInvalidServerResponseException
				    exception];

			if (*i >= length)
				@throw [OFTruncatedDataException exception];

			j = ((componentLength & 0x3F) << 8) | buffer[(*i)++];

			if (j == *i - 2)
				/* Pointing to itself?! */
				@throw [OFInvalidServerResponseException
				    exception];

			suffix = parseName(buffer, length, &j,
			    pointerLevel - 1);

			if (components.count == 0)
				return suffix;
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
    OFDNSRecordType recordType, uint32_t TTL, const unsigned char *buffer,
    size_t length, size_t i, uint16_t dataLength)
{
	if (recordType == OFDNSRecordTypeA && DNSClass == OFDNSClassIN) {
		OFSocketAddress address;

		if (dataLength != 4)
			@throw [OFInvalidServerReplyException exception];

		memset(&address, 0, sizeof(address));
		address.family = OFSocketAddressFamilyIPv4;
		address.length = sizeof(address.sockaddr.in);

		address.sockaddr.in.sin_family = AF_INET;
		memcpy(&address.sockaddr.in.sin_addr.s_addr, buffer + i, 4);

		return [[[OFADNSResourceRecord alloc]
		    initWithName: name
			 address: &address
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeNS) {
		size_t j = i;
		OFString *authoritativeHost = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		return [[[OFNSDNSResourceRecord alloc]
			 initWithName: name
			     DNSClass: DNSClass
		    authoritativeHost: authoritativeHost
				  TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeCNAME) {
		size_t j = i;
		OFString *alias = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		return [[[OFCNAMEDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
			   alias: alias
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeSOA) {
		size_t j = i;
		OFString *primaryNameServer = parseName(buffer, length, &j,
		    maxAllowedPointers);
		OFString *responsiblePerson;
		uint32_t serialNumber, refreshInterval, retryInterval;
		uint32_t expirationInterval, minTTL;

		if (j > i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		responsiblePerson = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (dataLength - (j - i) != 20)
			@throw [OFInvalidServerReplyException 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) |
		    buffer[j + 7];
		retryInterval = (buffer[j + 8] << 24) | (buffer[j + 9] << 16) |







|


















|












|















|





|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
    OFDNSRecordType recordType, uint32_t TTL, const unsigned char *buffer,
    size_t length, size_t i, uint16_t dataLength)
{
	if (recordType == OFDNSRecordTypeA && DNSClass == OFDNSClassIN) {
		OFSocketAddress address;

		if (dataLength != 4)
			@throw [OFInvalidServerResponseException exception];

		memset(&address, 0, sizeof(address));
		address.family = OFSocketAddressFamilyIPv4;
		address.length = sizeof(address.sockaddr.in);

		address.sockaddr.in.sin_family = AF_INET;
		memcpy(&address.sockaddr.in.sin_addr.s_addr, buffer + i, 4);

		return [[[OFADNSResourceRecord alloc]
		    initWithName: name
			 address: &address
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeNS) {
		size_t j = i;
		OFString *authoritativeHost = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		return [[[OFNSDNSResourceRecord alloc]
			 initWithName: name
			     DNSClass: DNSClass
		    authoritativeHost: authoritativeHost
				  TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeCNAME) {
		size_t j = i;
		OFString *alias = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		return [[[OFCNAMEDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
			   alias: alias
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeSOA) {
		size_t j = i;
		OFString *primaryNameServer = parseName(buffer, length, &j,
		    maxAllowedPointers);
		OFString *responsiblePerson;
		uint32_t serialNumber, refreshInterval, retryInterval;
		uint32_t expirationInterval, minTTL;

		if (j > i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		responsiblePerson = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (dataLength - (j - i) != 20)
			@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) |
		    buffer[j + 7];
		retryInterval = (buffer[j + 8] << 24) | (buffer[j + 9] << 16) |
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
				   TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypePTR) {
		size_t j = i;
		OFString *domainName = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		return [[[OFPTRDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
		      domainName: domainName
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeHINFO) {
		size_t j = i;
		OFString *CPU = parseString(buffer, length, &j);
		OFString *OS;

		if (j > i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		OS = parseString(buffer, length, &j);

		if (j != i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		return [[[OFHINFODNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
			     CPU: CPU
			      OS: OS
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeMX) {
		uint16_t preference;
		size_t j;
		OFString *mailExchange;

		if (dataLength < 2)
			@throw [OFInvalidServerReplyException exception];

		preference = (buffer[i] << 8) | buffer[i + 1];

		j = i + 2;
		mailExchange = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		return [[[OFMXDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
		      preference: preference
		    mailExchange: mailExchange
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeTXT) {
		OFMutableArray *textStrings = [OFMutableArray array];

		while (dataLength > 0) {
			uint_fast8_t stringLength = buffer[i++];
			dataLength--;

			if (stringLength > dataLength)
				@throw [OFInvalidServerReplyException
				    exception];

			[textStrings addObject:
			    [OFData dataWithItems: buffer + i
					    count: stringLength]];

			i += stringLength;







|












|




|













|








|















|







257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
				   TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypePTR) {
		size_t j = i;
		OFString *domainName = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		return [[[OFPTRDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
		      domainName: domainName
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeHINFO) {
		size_t j = i;
		OFString *CPU = parseString(buffer, length, &j);
		OFString *OS;

		if (j > i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		OS = parseString(buffer, length, &j);

		if (j != i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		return [[[OFHINFODNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
			     CPU: CPU
			      OS: OS
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeMX) {
		uint16_t preference;
		size_t j;
		OFString *mailExchange;

		if (dataLength < 2)
			@throw [OFInvalidServerResponseException exception];

		preference = (buffer[i] << 8) | buffer[i + 1];

		j = i + 2;
		mailExchange = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		return [[[OFMXDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
		      preference: preference
		    mailExchange: mailExchange
			     TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeTXT) {
		OFMutableArray *textStrings = [OFMutableArray array];

		while (dataLength > 0) {
			uint_fast8_t stringLength = buffer[i++];
			dataLength--;

			if (stringLength > dataLength)
				@throw [OFInvalidServerResponseException
				    exception];

			[textStrings addObject:
			    [OFData dataWithItems: buffer + i
					    count: stringLength]];

			i += stringLength;
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
	} else if (recordType == OFDNSRecordTypeRP) {
		size_t j = i;
		OFString *mailbox = parseName(buffer, length, &j,
		    maxAllowedPointers);
		OFString *TXTDomainName;

		if (j > i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		TXTDomainName = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerReplyException exception];

		return [[[OFRPDNSResourceRecord alloc]
		     initWithName: name
			 DNSClass: DNSClass
			  mailbox: mailbox
		    TXTDomainName: TXTDomainName
			      TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeAAAA &&
	    DNSClass == OFDNSClassIN) {
		OFSocketAddress address;

		if (dataLength != 16)
			@throw [OFInvalidServerReplyException exception];

		memset(&address, 0, sizeof(address));
		address.family = OFSocketAddressFamilyIPv6;
		address.length = sizeof(address.sockaddr.in6);

#ifdef AF_INET6
		address.sockaddr.in6.sin6_family = AF_INET6;







|





|












|







339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
	} else if (recordType == OFDNSRecordTypeRP) {
		size_t j = i;
		OFString *mailbox = parseName(buffer, length, &j,
		    maxAllowedPointers);
		OFString *TXTDomainName;

		if (j > i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		TXTDomainName = parseName(buffer, length, &j,
		    maxAllowedPointers);

		if (j != i + dataLength)
			@throw [OFInvalidServerResponseException exception];

		return [[[OFRPDNSResourceRecord alloc]
		     initWithName: name
			 DNSClass: DNSClass
			  mailbox: mailbox
		    TXTDomainName: TXTDomainName
			      TTL: TTL] autorelease];
	} else if (recordType == OFDNSRecordTypeAAAA &&
	    DNSClass == OFDNSClassIN) {
		OFSocketAddress address;

		if (dataLength != 16)
			@throw [OFInvalidServerResponseException exception];

		memset(&address, 0, sizeof(address));
		address.family = OFSocketAddressFamilyIPv6;
		address.length = sizeof(address.sockaddr.in6);

#ifdef AF_INET6
		address.sockaddr.in6.sin6_family = AF_INET6;
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
	} else if (recordType == OFDNSRecordTypeSRV &&
	    DNSClass == OFDNSClassIN) {
		uint16_t priority, weight, port;
		size_t j;
		OFString *target;

		if (dataLength < 6)
			@throw [OFInvalidServerReplyException 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];

		return [[[OFSRVDNSResourceRecord alloc]
		    initWithName: name
			priority: priority
			  weight: weight
			  target: target
			    port: port







|









|







382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
	} else if (recordType == OFDNSRecordTypeSRV &&
	    DNSClass == OFDNSClassIN) {
		uint16_t priority, weight, port;
		size_t j;
		OFString *target;

		if (dataLength < 6)
			@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 [OFInvalidServerResponseException exception];

		return [[[OFSRVDNSResourceRecord alloc]
		    initWithName: name
			priority: priority
			  weight: weight
			  target: target
			    port: port
906
907
908
909
910
911
912

913
914
915
916
917
918
919
920

	if (context == nil)
		return true;

	if (context->_TCPSocket != nil) {
		if ([_TCPQueries objectForKey: context->_TCPSocket] != context)
			return true;

	} else if (!OFSocketAddressEqual(sender, &context->_usedNameServer))
		return true;

	[context->_cancelTimer invalidate];
	[context->_cancelTimer release];
	context->_cancelTimer = nil;
	[_queries removeObjectForKey: ID];








>
|







906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921

	if (context == nil)
		return true;

	if (context->_TCPSocket != nil) {
		if ([_TCPQueries objectForKey: context->_TCPSocket] != context)
			return true;
	} else if (sender == NULL ||
	    !OFSocketAddressEqual(sender, &context->_usedNameServer))
		return true;

	[context->_cancelTimer invalidate];
	[context->_cancelTimer release];
	context->_cancelTimer = nil;
	[_queries removeObjectForKey: ID];

933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
		    context->_queryData.count < 12)
			@throw [OFInvalidArgumentException exception];

		queryDataBuffer = context->_queryData.items;

		/* QR */
		if ((buffer[2] & 0x80) == 0)
			@throw [OFInvalidServerReplyException exception];

		/* Opcode */
		if ((buffer[2] & 0x78) != (queryDataBuffer[2] & 0x78))
			@throw [OFInvalidServerReplyException exception];

		/* TC */
		if (buffer[2] & 0x02) {
			OFRunLoopMode runLoopMode;

			if (context->_settings->_usesTCP)
				@throw [OFTruncatedDataException exception];







|



|







934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
		    context->_queryData.count < 12)
			@throw [OFInvalidArgumentException exception];

		queryDataBuffer = context->_queryData.items;

		/* QR */
		if ((buffer[2] & 0x80) == 0)
			@throw [OFInvalidServerResponseException exception];

		/* Opcode */
		if ((buffer[2] & 0x78) != (queryDataBuffer[2] & 0x78))
			@throw [OFInvalidServerResponseException exception];

		/* TC */
		if (buffer[2] & 0x02) {
			OFRunLoopMode runLoopMode;

			if (context->_settings->_usesTCP)
				@throw [OFTruncatedDataException exception];

Modified src/OFDNSResolverSettings.m from [a43cd003ad] to [1a84838633].

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

static OFArray OF_GENERIC(OFString *) *
parseNetStackArray(OFString *string)
{
	if (![string hasPrefix: @"["] || ![string hasSuffix: @"]"])
		return nil;

	string = [string substringWithRange: OFRangeMake(1, string.length - 2)];

	return [string componentsSeparatedByString: @"|"];
}
#endif

@implementation OFDNSResolverSettings
- (void)dealloc







|







178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

static OFArray OF_GENERIC(OFString *) *
parseNetStackArray(OFString *string)
{
	if (![string hasPrefix: @"["] || ![string hasSuffix: @"]"])
		return nil;

	string = [string substringWithRange: OFMakeRange(1, string.length - 2)];

	return [string componentsSeparatedByString: @"|"];
}
#endif

@implementation OFDNSResolverSettings
- (void)dealloc
264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
	} @catch (OFOpenItemFailedException *e) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	staticHosts = [OFMutableDictionary dictionary];

	while ((line = [file readLine]) != nil) {

		OFArray *components, *hosts;
		size_t pos;
		OFString *address;

		pos = [line rangeOfString: @"#"].location;
		if (pos != OFNotFound)
			line = [line substringToIndex: pos];

		components = [line
		    componentsSeparatedByCharactersInSet: whitespaceCharacterSet
		    options: OFStringSkipEmptyComponents];

		if (components.count < 2)
			continue;

		address = components.firstObject;
		hosts = [components objectsInRange:
		    OFRangeMake(1, components.count - 1)];

		for (OFString *host in hosts) {
			OFMutableArray *addresses =
			    [staticHosts objectForKey: host];

			if (addresses == nil) {
				addresses = [OFMutableArray array];







|
>

















|







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
	} @catch (OFOpenItemFailedException *e) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	staticHosts = [OFMutableDictionary dictionary];

	while ((line =
	    [file readLineWithEncoding: [OFLocale encoding]]) != nil) {
		OFArray *components, *hosts;
		size_t pos;
		OFString *address;

		pos = [line rangeOfString: @"#"].location;
		if (pos != OFNotFound)
			line = [line substringToIndex: pos];

		components = [line
		    componentsSeparatedByCharactersInSet: whitespaceCharacterSet
		    options: OFStringSkipEmptyComponents];

		if (components.count < 2)
			continue;

		address = components.firstObject;
		hosts = [components objectsInRange:
		    OFMakeRange(1, components.count - 1)];

		for (OFString *host in hosts) {
			OFMutableArray *addresses =
			    [staticHosts objectForKey: host];

			if (addresses == nil) {
				addresses = [OFMutableArray array];
364
365
366
367
368
369
370
371

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
		objc_autoreleasePoolPop(pool);
		return;
	}

	if (nameServers == nil)
		nameServers = [OFMutableArray array];

	while ((line = [file readLine]) != nil) {

		void *pool2 = objc_autoreleasePoolPush();
		size_t pos;
		OFArray *components, *arguments;
		OFString *option;

		pos = [line indexOfCharacterFromSet: commentCharacters];
		if (pos != OFNotFound)
			line = [line substringToIndex: pos];

		components = [line
		    componentsSeparatedByCharactersInSet: whitespaceCharacterSet
		    options: OFStringSkipEmptyComponents];

		if (components.count < 2) {
			objc_autoreleasePoolPop(pool2);
			continue;
		}

		option = components.firstObject;
		arguments = [components objectsInRange:
		    OFRangeMake(1, components.count - 1)];

		if ([option isEqual: @"nameserver"]) {
			if (arguments.count != 1) {
				objc_autoreleasePoolPop(pool2);
				continue;
			}








|
>




















|







365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
		objc_autoreleasePoolPop(pool);
		return;
	}

	if (nameServers == nil)
		nameServers = [OFMutableArray array];

	while ((line =
	    [file readLineWithEncoding: [OFLocale encoding]]) != nil) {
		void *pool2 = objc_autoreleasePoolPush();
		size_t pos;
		OFArray *components, *arguments;
		OFString *option;

		pos = [line indexOfCharacterFromSet: commentCharacters];
		if (pos != OFNotFound)
			line = [line substringToIndex: pos];

		components = [line
		    componentsSeparatedByCharactersInSet: whitespaceCharacterSet
		    options: OFStringSkipEmptyComponents];

		if (components.count < 2) {
			objc_autoreleasePoolPop(pool2);
			continue;
		}

		option = components.firstObject;
		arguments = [components objectsInRange:
		    OFMakeRange(1, components.count - 1)];

		if ([option isEqual: @"nameserver"]) {
			if (arguments.count != 1) {
				objc_autoreleasePoolPop(pool2);
				continue;
			}

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
		OFArray *hosts;

		if (components.count < 2)
			continue;

		address = components.firstObject;
		hosts = [components objectsInRange:
		    OFRangeMake(1, components.count - 1)];

		for (OFString *host in hosts) {
			OFMutableArray *addresses =
			    [staticHosts objectForKey: host];

			if (addresses == nil) {
				addresses = [OFMutableArray array];







|







486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
		OFArray *hosts;

		if (components.count < 2)
			continue;

		address = components.firstObject;
		hosts = [components objectsInRange:
		    OFMakeRange(1, components.count - 1)];

		for (OFString *host in hosts) {
			OFMutableArray *addresses =
			    [staticHosts objectForKey: host];

			if (addresses == nil) {
				addresses = [OFMutableArray array];
619
620
621
622
623
624
625
626

627
628
629
630
631
632
633
	[self setDefaults];

#if defined(OF_WINDOWS)
# ifdef OF_HAVE_FILES
	OFWindowsRegistryKey *key = [[OFWindowsRegistryKey localMachineKey]
		   openSubkeyAtPath: @"SYSTEM\\CurrentControlSet\\Services\\"
				     @"Tcpip\\Parameters"
	    securityAndAccessRights: KEY_QUERY_VALUE];

	path = [[[key stringForValueNamed: @"DataBasePath"]
	    stringByAppendingPathComponent: @"hosts"]
	    stringByExpandingWindowsEnvironmentStrings];

	if (path != nil)
		[self parseHosts: path];
# endif







|
>







621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
	[self setDefaults];

#if defined(OF_WINDOWS)
# ifdef OF_HAVE_FILES
	OFWindowsRegistryKey *key = [[OFWindowsRegistryKey localMachineKey]
		   openSubkeyAtPath: @"SYSTEM\\CurrentControlSet\\Services\\"
				     @"Tcpip\\Parameters"
		       accessRights: KEY_QUERY_VALUE
			    options: 0];
	path = [[[key stringForValueNamed: @"DataBasePath"]
	    stringByAppendingPathComponent: @"hosts"]
	    stringByExpandingWindowsEnvironmentStrings];

	if (path != nil)
		[self parseHosts: path];
# endif

Modified src/OFDNSResourceRecord.h from [1c8f2631a7] to [30273f1f1b].

637
638
639
640
641
642
643

644
645
646
647
648
649
650
651

652
653
654
655
656
657
658
extern OFString *_Nonnull OFDNSRecordTypeName(OFDNSRecordType recordType);

/**
 * @brief Parses the specified string as an @ref OFDNSClass.
 *
 * @param string The string to parse as an @ref OFDNSClass
 * @return The parsed OFDNSClass

 */
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

 */
extern OFDNSRecordType OFDNSRecordTypeParseName(OFString *_Nonnull string);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







>








>







637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
extern OFString *_Nonnull OFDNSRecordTypeName(OFDNSRecordType recordType);

/**
 * @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

Modified src/OFDNSResourceRecord.m from [5aceee5a6c] to [b7ec251a31].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include "config.h"

#import "OFDNSResourceRecord.h"
#import "OFArray.h"
#import "OFData.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"

OFString *
OFDNSClassName(OFDNSClass DNSClass)
{
	switch (DNSClass) {
	case OFDNSClassIN:







<







15
16
17
18
19
20
21

22
23
24
25
26
27
28

#include "config.h"

#import "OFDNSResourceRecord.h"
#import "OFArray.h"
#import "OFData.h"


#import "OFInvalidFormatException.h"

OFString *
OFDNSClassName(OFDNSClass DNSClass)
{
	switch (DNSClass) {
	case OFDNSClassIN:
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
	OFDNSClass DNSClass;

	string = string.uppercaseString;

	if ([string isEqual: @"IN"])
		DNSClass = OFDNSClassIN;
	else {
		@try {
			DNSClass = (OFDNSClass)
			    [string unsignedLongLongValueWithBase: 0];
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidArgumentException exception];
		}
	}

	objc_autoreleasePoolPop(pool);

	return DNSClass;
}








<
|
|
<
<
<







74
75
76
77
78
79
80

81
82



83
84
85
86
87
88
89
	OFDNSClass DNSClass;

	string = string.uppercaseString;

	if ([string isEqual: @"IN"])
		DNSClass = OFDNSClassIN;
	else {

		DNSClass =
		    (OFDNSClass)[string unsignedLongLongValueWithBase: 0];



	}

	objc_autoreleasePoolPop(pool);

	return DNSClass;
}

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
	else if ([string isEqual: @"AAAA"])
		recordType = OFDNSRecordTypeAAAA;
	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];
		}
	}

	objc_autoreleasePoolPop(pool);

	return recordType;
}








<
|
|
<
<
<







116
117
118
119
120
121
122

123
124



125
126
127
128
129
130
131
	else if ([string isEqual: @"AAAA"])
		recordType = OFDNSRecordTypeAAAA;
	else if ([string isEqual: @"SRV"])
		recordType = OFDNSRecordTypeSRV;
	else if ([string isEqual: @"ALL"])
		recordType = OFDNSRecordTypeAll;
	else {

		recordType =
		    (OFDNSRecordType)[string unsignedLongLongValueWithBase: 0];



	}

	objc_autoreleasePoolPop(pool);

	return recordType;
}

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, OFSocketAddressHash(&_address));

	OFHashFinalize(&hash);

	return hash;
}








|
|
|
|







237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, OFSocketAddressHash(&_address));

	OFHashFinalize(&hash);

	return hash;
}

331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, OFSocketAddressHash(&_address));

	OFHashFinalize(&hash);

	return hash;
}








|
|
|
|







322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, OFSocketAddressHash(&_address));

	OFHashFinalize(&hash);

	return hash;
}

426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, _alias.hash);

	OFHashFinalize(&hash);

	return hash;
}








|
|
|
|







417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, _alias.hash);

	OFHashFinalize(&hash);

	return hash;
}

528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, _CPU.hash);
	OFHashAddHash(&hash, _OS.hash);

	OFHashFinalize(&hash);

	return hash;
}







|
|
|
|







519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, _CPU.hash);
	OFHashAddHash(&hash, _OS.hash);

	OFHashFinalize(&hash);

	return hash;
}
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
- (unsigned long)hash
{
	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);
	OFHashAddHash(&hash, _mailExchange.hash);

	OFHashFinalize(&hash);

	return hash;
}








|
|
|
|
|
|







623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	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;
}

733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, _authoritativeHost.hash);

	OFHashFinalize(&hash);

	return hash;
}








|
|
|
|







724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, _authoritativeHost.hash);

	OFHashFinalize(&hash);

	return hash;
}

831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, _domainName.hash);

	OFHashFinalize(&hash);

	return hash;
}








|
|
|
|







822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, _domainName.hash);

	OFHashFinalize(&hash);

	return hash;
}

936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, _mailbox.hash);
	OFHashAddHash(&hash, _TXTDomainName.hash);

	OFHashFinalize(&hash);

	return hash;
}







|
|
|
|







927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, _mailbox.hash);
	OFHashAddHash(&hash, _TXTDomainName.hash);

	OFHashFinalize(&hash);

	return hash;
}
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&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);

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description







|
|
|
|


|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, _primaryNameServer.hash);
	OFHashAddHash(&hash, _responsiblePerson.hash);
	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;
}

- (OFString *)description
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
- (unsigned long)hash
{
	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);
	OFHashAddHash(&hash, _target.hash);
	OFHashAdd(&hash, _port >> 8);
	OFHashAdd(&hash, _port);

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description







|
|
|
|
|
|
|
|

|
|







1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	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);
	OFHashAddByte(&hash, _port >> 8);
	OFHashAddByte(&hash, _port);

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAdd(&hash, _DNSClass >> 8);
	OFHashAdd(&hash, _DNSClass);
	OFHashAdd(&hash, _recordType >> 8);
	OFHashAdd(&hash, _recordType);
	OFHashAddHash(&hash, _textStrings.hash);

	OFHashFinalize(&hash);

	return hash;
}








|
|
|
|







1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddByte(&hash, _DNSClass >> 8);
	OFHashAddByte(&hash, _DNSClass);
	OFHashAddByte(&hash, _recordType >> 8);
	OFHashAddByte(&hash, _recordType);
	OFHashAddHash(&hash, _textStrings.hash);

	OFHashFinalize(&hash);

	return hash;
}

Modified src/OFDNSResponse.h from [12d5300b31] to [229f42ebfc].

25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
    OF_KINDOF(OFDNSResourceRecord *)) *) *OFDNSResponseRecords;

/**
 * @class OFDNSResponse OFDNSResponse.h ObjFW/OFDNSResponse.h
 *
 * @brief A class storing a response from @ref OFDNSResolver.
 */

@interface OFDNSResponse: OFObject
{
	OFString *_domainName;
	OFDNSResponseRecords _answerRecords, _authorityRecords;
	OFDNSResponseRecords _additionalRecords;
	OF_RESERVE_IVARS(OFDNSResponse, 4)
}

/**
 * @brief The domain name of the response.
 */
@property (readonly, nonatomic) OFString *domainName;








>





<







25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
    OF_KINDOF(OFDNSResourceRecord *)) *) *OFDNSResponseRecords;

/**
 * @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;

}

/**
 * @brief The domain name of the response.
 */
@property (readonly, nonatomic) OFString *domainName;

Modified src/OFData+MessagePackParsing.h from [38e581e80e] to [1a7c410992].

25
26
27
28
29
30
31






32
33
34
35
36
37
38
39
40





41
42
43
44
45
}
#endif

@interface OFData (MessagePackParsing)
/**
 * @brief The data interpreted as MessagePack representation and parsed as an
 *	  object.






 */
@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





 */
- (id)objectByParsingMessagePackWithDepthLimit: (size_t)depthLimit;
@end

OF_ASSUME_NONNULL_END







>
>
>
>
>
>









>
>
>
>
>





25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
}
#endif

@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

Modified src/OFData.h from [858a27b08c] to [750f7eff94].

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#import "OFMessagePackRepresentation.h"

/*! @file */

OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFURL;

/**
 * @brief Options for searching in data.
 *
 * This is a bit mask.
 */
typedef enum {







|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#import "OFMessagePackRepresentation.h"

/*! @file */

OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFURI;

/**
 * @brief Options for searching in data.
 *
 * This is a bit mask.
 */
typedef enum {
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182


183
184
185
186
187
188
189
190
191


192
193
194
195
196
197
198
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithContentsOfFile: (OFString *)path;
#endif

/**
 * @brief Creates a new OFData with an item size of 1, containing the data of
 *	  the specified URL.
 *
 * @param URL The URL to the contents for the OFData
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithContentsOfURL: (OFURL *)URL;

/**
 * @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


 */
+ (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


 */
+ (instancetype)dataWithBase64EncodedString: (OFString *)string;

/**
 * @brief Initializes an already allocated OFData with the specified `count`
 *	  items of size 1.
 *







|

|


|







>
>









>
>







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithContentsOfFile: (OFString *)path;
#endif

/**
 * @brief Creates a new OFData with an item size of 1, containing the data of
 *	  the specified URI.
 *
 * @param URI The URI to the contents for the OFData
 * @return A new autoreleased OFData
 */
+ (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`
 *	  items of size 1.
 *
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281


282
283
284
285
286
287
288
289
290


291
292
293
294
295
296
297
 * @return An initialized OFData
 */
- (instancetype)initWithContentsOfFile: (OFString *)path;
#endif

/**
 * @brief Initializes an already allocated OFData with an item size of 1,
 *	  containing the data of the specified URL.
 *
 * @param URL The URL to the contents for the OFData
 * @return A new autoreleased OFData
 */
- (instancetype)initWithContentsOfURL: (OFURL *)URL;

/**
 * @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


 */
- (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


 */
- (instancetype)initWithBase64EncodedString: (OFString *)string;

/**
 * @brief Compares the data to other data.
 *
 * @param data Data to compare the data to







|

|


|







>
>









>
>







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
 * @return An initialized OFData
 */
- (instancetype)initWithContentsOfFile: (OFString *)path;
#endif

/**
 * @brief Initializes an already allocated OFData with an item size of 1,
 *	  containing the data of the specified URI.
 *
 * @param URI The URI to the contents for the OFData
 * @return A new autoreleased OFData
 */
- (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.
 *
 * @param data Data to compare the data to
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
 *
 * @param path The path of the file to write to
 */
- (void)writeToFile: (OFString *)path;
#endif

/**
 * @brief Writes the OFData to the specified URL.
 *
 * @param URL The URL to write to
 */
- (void)writeToURL: (OFURL *)URL;
@end

OF_ASSUME_NONNULL_END

#import "OFMutableData.h"
#import "OFData+CryptographicHashing.h"
#import "OFData+MessagePackParsing.h"







|

|

|







342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
 *
 * @param path The path of the file to write to
 */
- (void)writeToFile: (OFString *)path;
#endif

/**
 * @brief Writes the OFData to the specified URI.
 *
 * @param URI The URI to write to
 */
- (void)writeToURI: (OFURI *)URI;
@end

OF_ASSUME_NONNULL_END

#import "OFMutableData.h"
#import "OFData+CryptographicHashing.h"
#import "OFData+MessagePackParsing.h"

Modified src/OFData.m from [b6152aa57a] to [b0fcc38dae].

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifdef OF_HAVE_FILES
# import "OFFile.h"
# import "OFFileManager.h"
#endif
#import "OFStream.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURL.h"
#import "OFURLHandler.h"
#import "OFXMLElement.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"

/* References for static linking */
void







|
|




|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifdef OF_HAVE_FILES
# import "OFFile.h"
# import "OFFileManager.h"
#endif
#import "OFStream.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURI.h"
#import "OFURIHandler.h"
#import "OFXMLElement.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"

/* References for static linking */
void
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path
{
	return [[[self alloc] initWithContentsOfFile: path] autorelease];
}
#endif

+ (instancetype)dataWithContentsOfURL: (OFURL *)URL
{
	return [[[self alloc] initWithContentsOfURL: URL] autorelease];
}

+ (instancetype)dataWithStringRepresentation: (OFString *)string
{
	return [[[self alloc]
	    initWithStringRepresentation: string] autorelease];
}







|

|







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path
{
	return [[[self alloc] initWithContentsOfFile: path] autorelease];
}
#endif

+ (instancetype)dataWithContentsOfURI: (OFURI *)URI
{
	return [[[self alloc] initWithContentsOfURI: URI] autorelease];
}

+ (instancetype)dataWithStringRepresentation: (OFString *)string
{
	return [[[self alloc]
	    initWithStringRepresentation: string] autorelease];
}
171
172
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
	return self;
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	char *buffer = NULL;
	unsigned long long size;

	@try {

		OFFile *file;

		size = [[OFFileManager defaultManager]
		    attributesOfItemAtPath: path].fileSize;

# if ULLONG_MAX > SIZE_MAX
		if (size > 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];
		}

	} @catch (id e) {
		OFFreeMemory(buffer);
		[self release];

		@throw e;
	}

	@try {
		self = [self initWithItemsNoCopy: buffer
					   count: (size_t)size
				    freeWhenDone: true];
	} @catch (id e) {
		OFFreeMemory(buffer);
		@throw e;
	}

	return self;
}
#endif

- (instancetype)initWithContentsOfURL: (OFURL *)URL
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFURLHandler *URLHandler;
		OFStream *stream;
		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];
		buffer = OFAllocMemory(1, pageSize);








|


>
|
|
<
<

<
|

|
>

|
<
<
|
<
<
|
>









|










|





<
|



<
<
<
<
<
<







171
172
173
174
175
176
177
178
179
180
181
182
183


184

185
186
187
188
189
190


191


192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223






224
225
226
227
228
229
230
	return self;
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	char *buffer = NULL;
	OFStreamOffset fileSize;

	@try {
		void *pool = objc_autoreleasePoolPush();
		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];

		[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)fileSize
				    freeWhenDone: true];
	} @catch (id e) {
		OFFreeMemory(buffer);
		@throw e;
	}

	return self;
}
#endif

- (instancetype)initWithContentsOfURI: (OFURI *)URI
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		OFStream *stream = [OFURIHandler openItemAtURI: URI mode: @"r"];
		size_t pageSize;
		unsigned char *buffer;







		_count = 0;
		_itemSize = 1;
		_freeWhenDone = true;

		pageSize = [OFSystemInfo pageSize];
		buffer = OFAllocMemory(1, pageSize);

480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	for (size_t i = 0; i < _count * _itemSize; i++)
		OFHashAdd(&hash, ((uint8_t *)_items)[i]);

	OFHashFinalize(&hash);

	return hash;
}

- (OFData *)subdataWithRange: (OFRange)range







|







469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	for (size_t i = 0; i < _count * _itemSize; i++)
		OFHashAddByte(&hash, ((uint8_t *)_items)[i]);

	OFHashFinalize(&hash);

	return hash;
}

- (OFData *)subdataWithRange: (OFRange)range
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620

621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640

641
642
643
644
645
646
647
	    range.location + range.length > _count)
		@throw [OFOutOfRangeException exception];

	if (data == nil || data.itemSize != _itemSize)
		@throw [OFInvalidArgumentException exception];

	if ((searchLength = data.count) == 0)
		return OFRangeMake(0, 0);

	if (searchLength > range.length)
		return OFRangeMake(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);

			/* No match and we're at the last item */
			if (i == 0)
				break;
		}
	} 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 OFRangeMake(OFNotFound, 0);
}

#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
	OFFile *file = [[OFFile alloc] initWithPath: path mode: @"w"];
	@try {
		[file writeBuffer: _items length: _count * _itemSize];
	} @finally {
		[file release];
	}
}
#endif

- (void)writeToURL: (OFURL *)URL
{
	void *pool = objc_autoreleasePoolPush();
	OFURLHandler *URLHandler;

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	[[URLHandler openItemAtURL: URL mode: @"w"] writeData: self];

	objc_autoreleasePoolPop(pool);
}

- (OFXMLElement *)XMLElementBySerializing
{
	void *pool;
	OFXMLElement *element;

	if (_itemSize != 1)
		@throw [OFInvalidArgumentException exception];


	pool = objc_autoreleasePoolPush();
	element = [OFXMLElement
	    elementWithName: self.className
		  namespace: OFSerializationNS
		stringValue: OFBase64Encode(_items, _count * _itemSize)];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFData *)messagePackRepresentation
{
	OFMutableData *data;

	if (_itemSize != 1)
		@throw [OFInvalidArgumentException exception];


	if (_count <= UINT8_MAX) {
		uint8_t type = 0xC4;
		uint8_t tmp = (uint8_t)_count;

		data = [OFMutableData dataWithCapacity: _count + 2];
		[data addItem: &type];







|


|







|










|


|














|


<

<
<
<
|










|
>



















|
>







543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592

593



594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
	    range.location + range.length > _count)
		@throw [OFOutOfRangeException exception];

	if (data == nil || data.itemSize != _itemSize)
		@throw [OFInvalidArgumentException exception];

	if ((searchLength = data.count) == 0)
		return OFMakeRange(0, 0);

	if (searchLength > range.length)
		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 OFMakeRange(i, searchLength);

			/* No match and we're at the last item */
			if (i == 0)
				break;
		}
	} else {
		for (size_t i = range.location;
		    i <= range.length - searchLength; i++)
			if (memcmp(_items + i * _itemSize, search,
			    searchLength * _itemSize) == 0)
				return OFMakeRange(i, searchLength);
	}

	return OFMakeRange(OFNotFound, 0);
}

#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
	OFFile *file = [[OFFile alloc] initWithPath: path mode: @"w"];
	@try {
		[file writeBuffer: _items length: _count * _itemSize];
	} @finally {
		[file release];
	}
}
#endif

- (void)writeToURI: (OFURI *)URI
{
	void *pool = objc_autoreleasePoolPush();





	[[OFURIHandler openItemAtURI: URI mode: @"w"] writeData: self];

	objc_autoreleasePoolPop(pool);
}

- (OFXMLElement *)XMLElementBySerializing
{
	void *pool;
	OFXMLElement *element;

	if (_itemSize != 1)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	pool = objc_autoreleasePoolPush();
	element = [OFXMLElement
	    elementWithName: self.className
		  namespace: OFSerializationNS
		stringValue: OFBase64Encode(_items, _count * _itemSize)];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFData *)messagePackRepresentation
{
	OFMutableData *data;

	if (_itemSize != 1)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	if (_count <= UINT8_MAX) {
		uint8_t type = 0xC4;
		uint8_t tmp = (uint8_t)_count;

		data = [OFMutableData dataWithCapacity: _count + 2];
		[data addItem: &type];

Modified src/OFDatagramSocket.h from [c9d6b20376] to [81b28eb069].

113
114
115
116
117
118
119


120
121
122
123
124


125
126
127
128
129
130
131
	OF_RESERVE_IVARS(OFDatagramSocket, 4)
}

/**
 * @brief Whether the socket can block.
 *
 * By default, a socket can block.


 */
@property (nonatomic) bool canBlock;

/**
 * @brief Whether the socket can send to broadcast addresses.


 */
@property (nonatomic) bool canSendToBroadcastAddresses;

/**
 * @brief The delegate for asynchronous operations on the socket.
 *
 * @note The delegate is retained for as long as asynchronous operations are







>
>





>
>







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	OF_RESERVE_IVARS(OFDatagramSocket, 4)
}

/**
 * @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.
 *
 * @note The delegate is retained for as long as asynchronous operations are
147
148
149
150
151
152
153


154
155
156
157
158
159
160
161
162
163
164
 * If the buffer is too small, the datagram is truncated.
 *
 * @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


 */
- (size_t)receiveIntoBuffer: (void *)buffer
		     length: (size_t)length
		     sender: (OFSocketAddress *)sender;

/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the datagram is truncated.
 *







>
>



|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
 * If the buffer is too small, the datagram is truncated.
 *
 * @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: (nullable OFSocketAddress *)sender;

/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the datagram is truncated.
 *
226
227
228
229
230
231
232


233
234
235
236
237
238
239
/**
 * @brief Sends the specified datagram to the specified address.
 *
 * @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


 */
- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length
	  receiver: (const OFSocketAddress *)receiver;

/**
 * @brief Asynchronously sends the specified datagram to the specified address.







>
>







232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/**
 * @brief Sends the specified datagram to the specified address.
 *
 * @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;

/**
 * @brief Asynchronously sends the specified datagram to the specified address.
293
294
295
296
297
298
299


300
301
302
303
304
 * @brief Cancels all pending asynchronous requests on the socket.
 */
- (void)cancelAsyncRequests;

/**
 * @brief Closes the socket so that it can neither receive nor send any more
 *	  datagrams.


 */
- (void)close;
@end

OF_ASSUME_NONNULL_END







>
>





301
302
303
304
305
306
307
308
309
310
311
312
313
314
 * @brief Cancels all pending asynchronous requests on the socket.
 */
- (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

Modified src/OFDatagramSocket.m from [bbb2f277d7] to [ea19c6b109].

169
170
171
172
173
174
175

176
177
178
179
180

181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215





216
217
218

219
220
221
222
223
224
225
		     sender: (OFSocketAddress *)sender
{
	ssize_t ret;

	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];


	sender->length = (socklen_t)sizeof(sender->sockaddr);

#ifndef OF_WINDOWS
	if ((ret = recvfrom(_socket, buffer, length, 0,
	    (struct sockaddr *)&sender->sockaddr, &sender->length)) < 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)

		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];
#endif


	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;
#endif
#ifdef OF_HAVE_UNIX_SOCKETS
	case AF_UNIX:
		sender->family = OFSocketAddressFamilyUNIX;
		break;
#endif
#ifdef OF_HAVE_IPX
	case AF_IPX:
		sender->family = OFSocketAddressFamilyIPX;
		break;
#endif





	default:
		sender->family = OFSocketAddressFamilyUnknown;
		break;

	}

	return ret;
}

- (void)asyncReceiveIntoBuffer: (void *)buffer length: (size_t)length
{







>
|



|
>









|
>






>
|
|
|
|

|
|
|


|
|
|


|
|
|

>
>
>
>
>
|
|
|
>







169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
		     sender: (OFSocketAddress *)sender
{
	ssize_t ret;

	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (sender != NULL)
		sender->length = (socklen_t)sizeof(sender->sockaddr);

#ifndef OF_WINDOWS
	if ((ret = recvfrom(_socket, buffer, 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,
	    (sender != NULL ? (struct sockaddr *)&sender->sockaddr : NULL),
	    (sender != NULL ? &sender->length : NULL))) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];
#endif

	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;
#endif
#ifdef OF_HAVE_UNIX_SOCKETS
		case AF_UNIX:
			sender->family = OFSocketAddressFamilyUNIX;
			break;
#endif
#ifdef OF_HAVE_IPX
		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;
		}
	}

	return ret;
}

- (void)asyncReceiveIntoBuffer: (void *)buffer length: (size_t)length
{

Modified src/OFDate.h from [f33ded2414] to [ba59d767f1].

167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
 * @warning The format is currently limited to the following format specifiers:
 *	    %%a, %%b, %%d, %%e, %%H, %%m, %%M, %%S, %%y, %%Y, %%z, %%, %%n and
 *	    %%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

 */
+ (instancetype)dateWithDateString: (OFString *)string
			    format: (OFString *)format;

/**
 * @brief Creates a new OFDate with the specified string in the specified
 *	  format.
 *
 * See the man page for `strftime` for information on the format.
 *
 * @warning The format is currently limited to the following format specifiers:
 *	    %%a, %%b, %%d, %%e, %%H, %%m, %%M, %%S, %%y, %%Y, %%z, %%, %%n and
 *	    %%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

 */
+ (instancetype)dateWithLocalDateString: (OFString *)string
				 format: (OFString *)format;

/**
 * @brief Returns a date in the distant future.
 *







>

















>







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
 * @warning The format is currently limited to the following format specifiers:
 *	    %%a, %%b, %%d, %%e, %%H, %%m, %%M, %%S, %%y, %%Y, %%z, %%, %%n and
 *	    %%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;

/**
 * @brief Creates a new OFDate with the specified string in the specified
 *	  format.
 *
 * See the man page for `strftime` for information on the format.
 *
 * @warning The format is currently limited to the following format specifiers:
 *	    %%a, %%b, %%d, %%e, %%H, %%m, %%M, %%S, %%y, %%Y, %%z, %%, %%n and
 *	    %%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;

/**
 * @brief Returns a date in the distant future.
 *
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

285
286
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
 *
 * @warning The format is currently limited to the following format specifiers:
 *	    %%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

 */
- (instancetype)initWithDateString: (OFString *)string
			    format: (OFString *)format;

/**
 * @brief Initializes an already allocated OFDate with the specified string in
 *	  the specified format.
 *
 * If no time zone is specified, local time is assumed.
 *
 * See the man page for `strftime` for information on the format.
 *
 * @warning The format is currently limited to the following format specifiers:
 *	    %%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

 */
- (instancetype)initWithLocalDateString: (OFString *)string
				 format: (OFString *)format;

/**
 * @brief Compares the date to another date.
 *
 * @param date The date to compare the date to
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (OFDate *)date;

/**
 * @brief Creates a string of the date with the specified format.
 *
 * See the man page for `strftime` for information on the format.
 *
 * @param format The format for the date string
 * @return A new, autoreleased OFString

 */
- (OFString *)dateStringWithFormat: (OFConstantString *)format;

/**
 * @brief Creates a string of the local date with the specified format.
 *
 * See the man page for `strftime` for information on the format.
 *
 * @param format The format for the date string
 * @return A new, autoreleased OFString

 */
- (OFString *)localDateStringWithFormat: (OFConstantString *)format;

/**
 * @brief Returns the earlier of the two dates.
 *
 * If the argument is `nil`, it returns the receiver.







>


















>



















>










>







243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
 *
 * @warning The format is currently limited to the following format specifiers:
 *	    %%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;

/**
 * @brief Initializes an already allocated OFDate with the specified string in
 *	  the specified format.
 *
 * If no time zone is specified, local time is assumed.
 *
 * See the man page for `strftime` for information on the format.
 *
 * @warning The format is currently limited to the following format specifiers:
 *	    %%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;

/**
 * @brief Compares the date to another date.
 *
 * @param date The date to compare the date to
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (OFDate *)date;

/**
 * @brief Creates a string of the date with the specified format.
 *
 * 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.
 *
 * 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.
 *
 * If the argument is `nil`, it returns the receiver.

Modified src/OFDate.m from [ebc414dd12] to [692871b1cf].

29
30
31
32
33
34
35

36
37
38
39
40
41
42
#import "OFMessagePackExtension.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif
#import "OFStrPTime.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFXMLElement.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"







>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#import "OFMessagePackExtension.h"
#ifdef OF_HAVE_THREADS
# 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"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
502
503
504
505
506
507
508




509
510
511
512
513
514
515
	@try {
		void *pool = objc_autoreleasePoolPush();
		unsigned long long value;

		if (![element.name isEqual: @"OFDate"] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];





		value = [element unsignedLongLongValueWithBase: 16];

		if (value > UINT64_MAX)
			@throw [OFOutOfRangeException exception];

		seconds = OFFromBigEndianDouble(OFRawUInt64ToDouble(







>
>
>
>







503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
	@try {
		void *pool = objc_autoreleasePoolPush();
		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];

		seconds = OFFromBigEndianDouble(OFRawUInt64ToDouble(
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
	double tmp;

	OFHashInit(&hash);

	tmp = OFToLittleEndianDouble(self.timeIntervalSince1970);

	for (size_t i = 0; i < sizeof(double); i++)
		OFHashAdd(&hash, ((char *)&tmp)[i]);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy







|







553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
	double tmp;

	OFHashInit(&hash);

	tmp = OFToLittleEndianDouble(self.timeIntervalSince1970);

	for (size_t i = 0; i < sizeof(double); i++)
		OFHashAddByte(&hash, ((char *)&tmp)[i]);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
586
587
588
589
590
591
592

593
594
595
596
597
598
599
{
	void *pool = objc_autoreleasePoolPush();
	OFXMLElement *element;

	element = [OFXMLElement elementWithName: @"OFDate"
				      namespace: OFSerializationNS];


	element.stringValue = [OFString stringWithFormat: @"%016" PRIx64,
	    OFFromBigEndian64(OFDoubleToRawUInt64(OFToBigEndianDouble(
	    self.timeIntervalSince1970)))];

	[element retain];

	objc_autoreleasePoolPop(pool);







>







591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
{
	void *pool = objc_autoreleasePoolPush();
	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];

	objc_autoreleasePoolPop(pool);

Modified src/OFDictionary.h from [a605581e45] to [73af2a9ef3].

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
@property (readonly, nonatomic) OFArray OF_GENERIC(KeyType) *allKeys;

/**
 * @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
 */
+ (instancetype)dictionary;








<
<
<
<
<







89
90
91
92
93
94
95





96
97
98
99
100
101
102
@property (readonly, nonatomic) OFArray OF_GENERIC(KeyType) *allKeys;

/**
 * @brief An array of all objects.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(ObjectType) *allObjects;






/**
 * @brief Creates a new OFDictionary.
 *
 * @return A new autoreleased OFDictionary
 */
+ (instancetype)dictionary;

243
244
245
246
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
 * @return The value for the given key or `nil` if the key was not found
 */
- (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.
 *
 * @param key The key to set
 * @param value The value to set the key to

 */
- (void)setValue: (nullable id)value forKey: (OFString *)key;

/**
 * @brief Checks whether the dictionary contains an object equal to the
 *	  specified object.
 *







|
<



>







238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253
254
255
256
 * @return The value for the given key or `nil` if the key was not found
 */
- (nullable id)valueForKey: (OFString *)key;

/**
 * @brief Sets a value for a key.
 *
 * 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
 *	  specified object.
 *

Modified src/OFDictionary.m from [a48510c034] to [9689ae0f89].

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#import "OFOutOfRangeException.h"
#import "OFUndefinedKeyException.h"

static struct {
	Class isa;
} placeholder;

static OFCharacterSet *URLQueryPartAllowedCharacterSet = nil;

@interface OFDictionary ()
- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth;
@end








|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#import "OFOutOfRangeException.h"
#import "OFUndefinedKeyException.h"

static struct {
	Class isa;
} placeholder;

static OFCharacterSet *URIQueryPartAllowedCharacterSet = nil;

@interface OFDictionary ()
- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth;
@end

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
	OFEnumerator *_keyEnumerator;
}

- (instancetype)initWithDictionary: (OFDictionary *)dictionary;
@end

OF_DIRECT_MEMBERS
@interface OFURLQueryPartAllowedCharacterSet: OFCharacterSet
+ (OFCharacterSet *)URLQueryPartAllowedCharacterSet;
@end

@implementation OFDictionaryPlaceholder
- (instancetype)init
{
	return (id)[[OFMapTableDictionary alloc] init];
}







|
|







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
	OFEnumerator *_keyEnumerator;
}

- (instancetype)initWithDictionary: (OFDictionary *)dictionary;
@end

OF_DIRECT_MEMBERS
@interface OFURIQueryPartAllowedCharacterSet: OFCharacterSet
+ (OFCharacterSet *)URIQueryPartAllowedCharacterSet;
@end

@implementation OFDictionaryPlaceholder
- (instancetype)init
{
	return (id)[[OFMapTableDictionary alloc] init];
}
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}
@end

@implementation OFURLQueryPartAllowedCharacterSet
+ (void)initialize
{
	if (self != [OFURLQueryPartAllowedCharacterSet class])
		return;

	URLQueryPartAllowedCharacterSet =
	    [[OFURLQueryPartAllowedCharacterSet alloc] init];
}

+ (OFCharacterSet *)URLQueryPartAllowedCharacterSet
{
	return URLQueryPartAllowedCharacterSet;
}

- (instancetype)autorelease
{
	return self;
}








|


|


|
|


|

|







137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}
@end

@implementation OFURIQueryPartAllowedCharacterSet
+ (void)initialize
{
	if (self != [OFURIQueryPartAllowedCharacterSet class])
		return;

	URIQueryPartAllowedCharacterSet =
	    [[OFURIQueryPartAllowedCharacterSet alloc] init];
}

+ (OFCharacterSet *)URIQueryPartAllowedCharacterSet
{
	return URIQueryPartAllowedCharacterSet;
}

- (instancetype)autorelease
{
	return self;
}

628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
			[ret appendString: @";\n"];

		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;
}








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







628
629
630
631
632
633
634
































635
636
637
638
639
640
641
			[ret appendString: @";\n"];

		objc_autoreleasePoolPop(pool2);
	}
	[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];
	[ret appendString: @";\n}"];

































	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

Modified src/OFEmbeddedURIHandler.h from [b889d68d61] to [5163464dd8].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * 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







|



|



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * 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

Modified src/OFEmbeddedURIHandler.m from [10ad5c2607] to [bac2da914b].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#include "config.h"

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#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"







|

|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#include "config.h"

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#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"
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
	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







|
|



|
|
|



|



|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
	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
106
107
108
109
110
111
112
113
114
115
116
117
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		OFEnsure(OFPlainMutexUnlock(&mutex) == 0);
	}
#endif

	@throw [OFOpenItemFailedException exceptionWithURL: URL
						      mode: mode
						     errNo: ENOENT];
}
@end







|




106
107
108
109
110
111
112
113
114
115
116
117
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		OFEnsure(OFPlainMutexUnlock(&mutex) == 0);
	}
#endif

	@throw [OFOpenItemFailedException exceptionWithURI: URI
						      mode: mode
						     errNo: ENOENT];
}
@end

Modified src/OFEnumerator.h from [514f228b0c] to [bebf66139d].

78
79
80
81
82
83
84


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108


109
110
111
112
113
114
115
 *	  doing a fast enumeration.
 *
 * @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.


 */
- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id __unsafe_unretained _Nonnull *_Nonnull)
					objects
			     count: (int)count;
@end

/**
 * @class OFEnumerator OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief A class which provides methods to enumerate through collections.
 */
@interface OFEnumerator OF_GENERIC(ObjectType): OFObject <OFFastEnumeration>
#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


 */
- (nullable ObjectType)nextObject;

/**
 * @brief Returns an array of all remaining objects in the collection.
 *
 * @return An array of all remaining objects in the collection.







>
>
















<
<
<
<




>
>







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102




103
104
105
106
107
108
109
110
111
112
113
114
115
 *	  doing a fast enumeration.
 *
 * @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;
@end

/**
 * @class OFEnumerator OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief A class which provides methods to enumerate through collections.
 */
@interface OFEnumerator OF_GENERIC(ObjectType): OFObject <OFFastEnumeration>
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# define ObjectType id
#endif




/**
 * @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.
 *
 * @return An array of all remaining objects in the collection.

Modified src/OFEpollKernelEventObserver.m from [ddd2075d1e] to [a510ac5746].

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

#import "OFEpollKernelEventObserver.h"
#import "OFArray.h"
#import "OFMapTable.h"
#import "OFNull.h"

#import "OFInitializationFailedException.h"
#import "OFObserveFailedException.h"

#define eventListSize 64

static const OFMapTableFunctions mapFunctions = { NULL };

@implementation OFEpollKernelEventObserver
- (instancetype)init







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

#import "OFEpollKernelEventObserver.h"
#import "OFArray.h"
#import "OFMapTable.h"
#import "OFNull.h"

#import "OFInitializationFailedException.h"
#import "OFObserveKernelEventsFailedException.h"

#define eventListSize 64

static const OFMapTableFunctions mapFunctions = { NULL };

@implementation OFEpollKernelEventObserver
- (instancetype)init
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114
115

	memset(&event, 0, sizeof(event));
	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];

	[_FDToEvents setObject: (void *)(events | addEvents)
			forKey: (void *)((intptr_t)fd + 1)];
}

- (void)of_removeObject: (id)object
	 fileDescriptor: (int)fd







>
|
|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

	memset(&event, 0, sizeof(event));
	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 [OFObserveKernelEventsFailedException
		    exceptionWithObserver: self
				    errNo: errno];

	[_FDToEvents setObject: (void *)(events | addEvents)
			forKey: (void *)((intptr_t)fd + 1)];
}

- (void)of_removeObject: (id)object
	 fileDescriptor: (int)fd
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
		if (epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, NULL) == -1)
			/*
			 * 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
				    exceptionWithObserver: self
						    errNo: errno];

		[_FDToEvents removeObjectForKey: (void *)((intptr_t)fd + 1)];
	} else {
		struct epoll_event event;

		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
			    exceptionWithObserver: self
					    errNo: errno];

		[_FDToEvents setObject: (void *)events
				forKey: (void *)((intptr_t)fd + 1)];
	}
}







|












|







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
		if (epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, NULL) == -1)
			/*
			 * 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 [OFObserveKernelEventsFailedException
				    exceptionWithObserver: self
						    errNo: errno];

		[_FDToEvents removeObjectForKey: (void *)((intptr_t)fd + 1)];
	} else {
		struct epoll_event event;

		memset(&event, 0, sizeof(event));
		event.events = (int)events;
		event.data.ptr = object;

		if (epoll_ctl(_epfd, EPOLL_CTL_MOD, fd, &event) == -1)
			@throw [OFObserveKernelEventsFailedException
			    exceptionWithObserver: self
					    errNo: errno];

		[_FDToEvents setObject: (void *)events
				forKey: (void *)((intptr_t)fd + 1)];
	}
}
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
211
	if ([self of_processReadBuffers])
		return;

	events = epoll_wait(_epfd, eventList, eventListSize,
	    (timeInterval != -1 ? timeInterval * 1000 : -1));

	if (events < 0)

		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: errno];

	for (int i = 0; i < events; i++) {
		if (eventList[i].events & EPOLLIN) {
			void *pool = objc_autoreleasePoolPush();

			if (eventList[i].data.ptr == nullObject) {
				char buffer;







>
|
|







197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
	if ([self of_processReadBuffers])
		return;

	events = epoll_wait(_epfd, eventList, eventListSize,
	    (timeInterval != -1 ? timeInterval * 1000 : -1));

	if (events < 0)
		@throw [OFObserveKernelEventsFailedException
		    exceptionWithObserver: self
				    errNo: errno];

	for (int i = 0; i < events; i++) {
		if (eventList[i].events & EPOLLIN) {
			void *pool = objc_autoreleasePoolPush();

			if (eventList[i].data.ptr == nullObject) {
				char buffer;

Modified src/OFFile.h from [70f5f785c7] to [b56cdf66da].

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#else
typedef struct _OFFileHandle *OFFileHandle;
static const OFFileHandle OFInvalidFileHandle = NULL;
#endif

OF_ASSUME_NONNULL_BEGIN

@class OFURL;

/**
 * @class OFFile OFFile.h ObjFW/OFFile.h
 *
 * @brief A class which provides methods to read and write files.
 */
OF_SUBCLASSING_RESTRICTED







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#else
typedef struct _OFFileHandle *OFFileHandle;
static const OFFileHandle OFInvalidFileHandle = NULL;
#endif

OF_ASSUME_NONNULL_BEGIN

@class OFURI;

/**
 * @class OFFile OFFile.h ObjFW/OFFile.h
 *
 * @brief A class which provides methods to read and write files.
 */
OF_SUBCLASSING_RESTRICTED
58
59
60
61
62
63
64

65
66
67
68
69
70
71
 *	       `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
 * @return A new autoreleased OFFile

 */
+ (instancetype)fileWithPath: (OFString *)path mode: (OFString *)mode;

/**
 * @brief Creates a new OFFile with the specified native file handle.
 *
 * @param handle A native file handle. If OF_FILE_HANDLE_IS_FD is defined, this







>







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
 *	       `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
 * @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.
 *
 * @param handle A native file handle. If OF_FILE_HANDLE_IS_FD is defined, this
92
93
94
95
96
97
98

99
100
101
102
103
104
105
 *	       `w`            | read-write, create, truncate
 *	       `wb+` or `w+b` | read-write, create, truncate, binary
 *	       `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

 */
- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode;

/**
 * @brief Initializes an already allocated OFFile.
 *
 * @param handle A native file handle. If OF_FILE_HANDLE_IS_FD is defined, this







>







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
 *	       `w`            | read-write, create, truncate
 *	       `wb+` or `w+b` | read-write, create, truncate, binary
 *	       `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.
 *
 * @param handle A native file handle. If OF_FILE_HANDLE_IS_FD is defined, this

Modified src/OFFile.m from [6bda5c55f7] to [57c941bba1].

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# include <sys/stat.h>
#endif

#import "OFFile.h"
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURL.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotOpenException.h"
#import "OFOpenItemFailedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"







|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# include <sys/stat.h>
#endif

#import "OFFile.h"
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURI.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotOpenException.h"
#import "OFOpenItemFailedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
446
447
448
449
450
451
452
453

454
455
456
457
458
459
460


















461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
						      bytesWritten: 0
							     errNo: errno];
#endif

	return (size_t)bytesWritten;
}

- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence

{
	OFFileOffset ret;

	if (_handle == OFInvalidFileHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

#ifndef OF_AMIGAOS


















# if defined(OF_WINDOWS)
	ret = _lseeki64(_handle, offset, whence);
# elif defined(HAVE_LSEEK64)
	ret = lseek64(_handle, offset, whence);
# else
	ret = lseek(_handle, offset, whence);
# endif

	if (ret == -1)
		@throw [OFSeekFailedException exceptionWithStream: self
							   offset: offset
							   whence: whence
							    errNo: errno];
#else
	LONG translatedWhence;

	switch (whence) {
	case SEEK_SET:
		translatedWhence = OFFSET_BEGINNING;
		break;
	case SEEK_CUR:
		translatedWhence = OFFSET_CURRENT;
		break;
	case SEEK_END:
		translatedWhence = OFFSET_END;
		break;
	default:
		@throw [OFSeekFailedException exceptionWithStream: self
							   offset: offset
							   whence: whence
							    errNo: EINVAL];







|
>

|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|

|

|











|


|


|







446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
						      bytesWritten: 0
							     errNo: errno];
#endif

	return (size_t)bytesWritten;
}

- (OFStreamOffset)lowlevelSeekToOffset: (OFStreamOffset)offset
				whence: (OFSeekWhence)whence
{
	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, translatedWhence);
# elif defined(HAVE_LSEEK64)
	ret = lseek64(_handle, offset, translatedWhence);
# else
	ret = lseek(_handle, offset, translatedWhence);
# endif

	if (ret == -1)
		@throw [OFSeekFailedException exceptionWithStream: self
							   offset: offset
							   whence: whence
							    errNo: errno];
#else
	LONG translatedWhence;

	switch (whence) {
	case OFSeekSet:
		translatedWhence = OFFSET_BEGINNING;
		break;
	case OFSeekCurrent:
		translatedWhence = OFFSET_CURRENT;
		break;
	case OFSeekEnd:
		translatedWhence = OFFSET_END;
		break;
	default:
		@throw [OFSeekFailedException exceptionWithStream: self
							   offset: offset
							   whence: whence
							    errNo: EINVAL];

Modified src/OFFileManager.h from [e259c02d73] to [1cfc8cfdf6].

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# endif
#endif

@class OFArray OF_GENERIC(ObjectType);
@class OFConstantString;
@class OFDate;
@class OFString;
@class OFURL;

/**
 * @brief A key for a file attribute in the file attributes dictionary.
 *
 * Possible keys for file URLs are:
 *
 *  * @ref OFFileSize
 *  * @ref OFFileType
 *  * @ref OFFilePOSIXPermissions
 *  * @ref OFFileOwnerAccountID
 *  * @ref OFFileGroupOwnerAccountID
 *  * @ref OFFileOwnerAccountName
 *  * @ref OFFileGroupOwnerAccountName
 *  * @ref OFFileLastAccessDate
 *  * @ref OFFileModificationDate
 *  * @ref OFFileStatusChangeDate
 *  * @ref OFFileCreationDate
 *  * @ref OFFileSymbolicLinkDestination
 *
 * Other URL 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:
 *
 *  * @ref OFFileTypeRegular
 *  * @ref OFFileTypeDirectory
 *  * @ref OFFileTypeSymbolicLink
 *  * @ref OFFileTypeFIFO
 *  * @ref OFFileTypeCharacterSpecial
 *  * @ref OFFileTypeBlockSpecial
 *  * @ref OFFileTypeSocket
 *  * @ref OFFileTypeUnknown
 *
 * Other URL 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
 *	  attribute values.
 */







|




|














|






|










|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# endif
#endif

@class OFArray OF_GENERIC(ObjectType);
@class OFConstantString;
@class OFDate;
@class OFString;
@class OFURI;

/**
 * @brief A key for a file attribute in the file attributes dictionary.
 *
 * Possible keys for file URIs are:
 *
 *  * @ref OFFileSize
 *  * @ref OFFileType
 *  * @ref OFFilePOSIXPermissions
 *  * @ref OFFileOwnerAccountID
 *  * @ref OFFileGroupOwnerAccountID
 *  * @ref OFFileOwnerAccountName
 *  * @ref OFFileGroupOwnerAccountName
 *  * @ref OFFileLastAccessDate
 *  * @ref OFFileModificationDate
 *  * @ref OFFileStatusChangeDate
 *  * @ref OFFileCreationDate
 *  * @ref OFFileSymbolicLinkDestination
 *
 * 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 URIs are:
 *
 *  * @ref OFFileTypeRegular
 *  * @ref OFFileTypeDirectory
 *  * @ref OFFileTypeSymbolicLink
 *  * @ref OFFileTypeFIFO
 *  * @ref OFFileTypeCharacterSpecial
 *  * @ref OFFileTypeBlockSpecial
 *  * @ref OFFileTypeSocket
 *  * @ref OFFileTypeUnknown
 *
 * 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
 *	  attribute values.
 */
255
256
257
258
259
260
261


262
263
264
265
266


267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282


283
284
285
286
287
288
289
290
291
292




293
294
295
296
297
298
299
300
301
302
303





304
305
306
307
308
309
310
311
312
313
314
315







316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351


352
353
354
355
356
357
358
359

360
361
362
363
364
365
366
367


368
369
370
371
372
373
374
375
376



377
378
379
380
381
382
383
384




385
386
387
388
389
390
391
392
393
394
395


396
397
398
399
400
401
402
403
404
405
406
407




408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426


427
428
429
430
431
432
433


434
435
436
437
438
439
440
441
442
443
444
445
446
447
448



449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464





465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480








481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496










497
498
499
500
501
502
503
504
505
506

507
508
509
510
511
512
513
514
515
516



517
518
519
520
521
522
523
524
525
526
527
528
529
530



531
532
533
534
535
536
537
538
539
540
541
542
543
544





545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561



562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579


580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) OFFileManager *defaultManager;
#endif

#ifdef OF_HAVE_FILES
/**
 * @brief The path of the current working directory.


 */
@property (readonly, nonatomic) OFString *currentDirectoryPath;

/**
 * @brief The URL of the current working directory.


 */
@property (readonly, nonatomic) OFURL *currentDirectoryURL;
#endif

/**
 * @brief Returns the default file manager.
 */
+ (OFFileManager *)defaultManager;

#ifdef OF_HAVE_FILES
/**
 * @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


 */
- (OFFileAttributes)attributesOfItemAtPath: (OFString *)path;
#endif

/**
 * @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;

#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





 */
- (void)setAttributes: (OFFileAttributes)attributes
	 ofItemAtPath: (OFString *)path;
#endif

/**
 * @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;

#ifdef OF_HAVE_FILES
/**
 * @brief Checks whether a file exists at the specified path.
 *
 * @param path The path to check
 * @return A boolean whether there is a file at the specified path
 */
- (bool)fileExistsAtPath: (OFString *)path;
#endif

/**
 * @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;

#ifdef OF_HAVE_FILES
/**
 * @brief Checks whether a directory exists at the specified path.
 *
 * @param path The path to check
 * @return A boolean whether there is a directory at the specified path
 */
- (bool)directoryExistsAtPath: (OFString *)path;
#endif

/**
 * @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;

#ifdef OF_HAVE_FILES
/**
 * @brief Creates a directory at the specified path.
 *
 * @param path The path of the directory to create

 */
- (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


 */
- (void)createDirectoryAtPath: (OFString *)path
		createParents: (bool)createParents;
#endif

/**
 * @brief Creates a directory at the specified URL.
 *
 * @param URL The URL of the directory to create



 */
- (void)createDirectoryAtURL: (OFURL *)URL;

/**
 * @brief Creates a directory at the specified URL.
 *
 * @param URL The URL of the directory to create
 * @param createParents Whether to create the parents of the directory




 */
- (void)createDirectoryAtURL: (OFURL *)URL 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


 */
- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path;
#endif

/**
 * @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;

#ifdef OF_HAVE_FILES
/**
 * @brief Returns an array with all subpaths of the specified directory.
 *
 * @note `.` and `..` (of the directory itself or any subdirectory) are not
 * part of the returned array.
 *
 * @param path The path to the directory whose subpaths should be returned
 * @return An array of OFString with the subpaths of the specified directory
 */
- (OFArray OF_GENERIC(OFString *) *)subpathsOfDirectoryAtPath: (OFString *)path;

/**
 * @brief Changes the current working directory.
 *
 * @param path The new directory to change to


 */
- (void)changeCurrentDirectoryPath: (OFString *)path;

/**
 * @brief Changes the current working directory.
 *
 * @param URL The new directory to change to


 */
- (void)changeCurrentDirectoryURL: (OFURL *)URL;

/**
 * @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
 * 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 path



 */
- (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
 * 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





 */
- (void)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination;

#ifdef OF_HAVE_FILES
/**
 * @brief Moves an item.
 *
 * The destination path must be a full path, which means it must include the
 * name of the item.
 *
 * If the destination is on a different logical device, the source will be
 * 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








 */
- (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
 * 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:.
 *
 * @param source The item to rename
 * @param destination The new name for the item










 */
- (void)moveItemAtURL: (OFURL *)source toURL: (OFURL *)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

 */
- (void)removeItemAtPath: (OFString *)path;
#endif

/**
 * @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;

#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS
/**
 * @brief Creates a hard link for the specified item.
 *
 * The destination path must be a full path, which means it must include the
 * name of the item.
 *
 * 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



 */
- (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
 * 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;

#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
/**
 * @brief Creates a symbolic link for an item.
 *
 * The destination path must be a full path, which means it must include the
 * name of the item.
 *
 * This method is not available on some systems.
 *
 * @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



 */
- (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
 * 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;
@end

@interface OFDictionary (FileAttributes)
/**
 * @brief The @ref OFFileSize key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if 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.
 */
@property (readonly, nonatomic) OFFileAttributeType fileType;

/**
 * @brief The @ref OFFilePOSIXPermissions key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if 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.
 */
@property (readonly, nonatomic) unsigned long fileOwnerAccountID;

/**
 * @brief The @ref OFFileGroupOwnerAccountID key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if 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.
 */
@property (readonly, nonatomic) OFString *fileOwnerAccountName;

/**
 * @brief The @ref OFFileGroupOwnerAccountName key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if 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.
 */
@property (readonly, nonatomic) OFDate *fileLastAccessDate;

/**
 * @brief The @ref OFFileModificationDate key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if 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.
 */
@property (readonly, nonatomic) OFDate *fileStatusChangeDate;

/**
 * @brief The @ref OFFileCreationDate key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if 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.
 */
@property (readonly, nonatomic) OFString *fileSymbolicLinkDestination;
@end

OF_ASSUME_NONNULL_END







>
>




|
>
>

|














>
>





|

|
|

>
>
>
>

|









>
>
>
>
>






|



|
|
>
>
>
>
>
>
>

|












|

|
|
>
>

|












|

|
|
>
>

|






>








>
>






|

|
>
>
>

|


|

|

>
>
>
>

|









>
>





|




|
|
>
>
>
>

|

















>
>






|
>
>

|













>
>
>







|







|
>
>
>
>
>

|














>
>
>
>
>
>
>
>







|




|



>
>
>
>
>
>
>
>
>
>

|








>





|

|

|
>
>
>

|












>
>
>







|


|

|
|
>
>
>
>
>

|















>
>
>








|


|




|

>
>

|







|






|






|






|






|






|






|






|






|






|






|






|





255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) OFFileManager *defaultManager;
#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 URI of the current working directory.
 *
 * @throw OFGetCurrentDirectoryFailedException Couldn't get current directory
 */
@property (readonly, nonatomic) OFURI *currentDirectoryURI;
#endif

/**
 * @brief Returns the default file manager.
 */
+ (OFFileManager *)defaultManager;

#ifdef OF_HAVE_FILES
/**
 * @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 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
 * @throw OFGetItemAttributesFailedException Failed to get the attributes of
 *					     the item
 * @throw OFUnsupportedProtocolException No handler is registered for the URI's
 *					 scheme
 */
- (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 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
 * @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 ofItemAtURI: (OFURI *)URI;

#ifdef OF_HAVE_FILES
/**
 * @brief Checks whether a file exists at the specified path.
 *
 * @param path The path to check
 * @return A boolean whether there is a file at the specified path
 */
- (bool)fileExistsAtPath: (OFString *)path;
#endif

/**
 * @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
 * @throw OFUnsupportedProtocolException No handler is registered for the URI's
 *					 scheme
 */
- (bool)fileExistsAtURI: (OFURI *)URI;

#ifdef OF_HAVE_FILES
/**
 * @brief Checks whether a directory exists at the specified path.
 *
 * @param path The path to check
 * @return A boolean whether there is a directory at the specified path
 */
- (bool)directoryExistsAtPath: (OFString *)path;
#endif

/**
 * @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
 * @throw OFUnsupportedProtocolException No handler is registered for the URI's
 *					 scheme
 */
- (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 URI.
 *
 * @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)createDirectoryAtURI: (OFURI *)URI;

/**
 * @brief Creates a directory at the specified URI.
 *
 * @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)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 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
 * @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(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI;

#ifdef OF_HAVE_FILES
/**
 * @brief Returns an array with all subpaths of the specified directory.
 *
 * @note `.` and `..` (of the directory itself or any subdirectory) are not
 * part of the returned array.
 *
 * @param path The path to the directory whose subpaths should be returned
 * @return An array of OFString with the subpaths of the specified directory
 */
- (OFArray OF_GENERIC(OFString *) *)subpathsOfDirectoryAtPath: (OFString *)path;

/**
 * @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 URI The new directory to change to
 * @throw OFChangeCurrentDirectoryFailedException Changing the current working
 *						  directory failed
 */
- (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
 * 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 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 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
 * @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)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination;

#ifdef OF_HAVE_FILES
/**
 * @brief Moves an item.
 *
 * The destination path must be a full path, which means it must include the
 * name of the item.
 *
 * If the destination is on a different logical device, the source will be
 * 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 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 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)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 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
 * @throw OFRemoveItemFailedException Removing the item failed
 * @throw OFUnsupportedProtocolException No handler is registered for the URI's
 *					 scheme
 */
- (void)removeItemAtURI: (OFURI *)URI;

#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS
/**
 * @brief Creates a hard link for the specified item.
 *
 * The destination path must be a full path, which means it must include the
 * name of the item.
 *
 * 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 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
 * @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)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination;

#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
/**
 * @brief Creates a symbolic link for an item.
 *
 * The destination path must be a full path, which means it must include the
 * name of the item.
 *
 * This method is not available on some systems.
 *
 * @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 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
 * @throw OFUnsupportedProtocolException No handler is registered for the URI's
 *					 scheme
 */
- (void)createSymbolicLinkAtURI: (OFURI *)URI
	    withDestinationPath: (OFString *)target;
@end

@interface OFDictionary (FileAttributes)
/**
 * @brief The @ref OFFileSize key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) unsigned long long fileSize;

/**
 * @brief The @ref OFFileType key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFFileAttributeType fileType;

/**
 * @brief The @ref OFFilePOSIXPermissions key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) unsigned long filePOSIXPermissions;

/**
 * @brief The @ref OFFileOwnerAccountID key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) unsigned long fileOwnerAccountID;

/**
 * @brief The @ref OFFileGroupOwnerAccountID key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) unsigned long fileGroupOwnerAccountID;

/**
 * @brief The @ref OFFileOwnerAccountName key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFString *fileOwnerAccountName;

/**
 * @brief The @ref OFFileGroupOwnerAccountName key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFString *fileGroupOwnerAccountName;

/**
 * @brief The @ref OFFileLastAccessDate key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFDate *fileLastAccessDate;

/**
 * @brief The @ref OFFileModificationDate key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFDate *fileModificationDate;

/**
 * @brief The @ref OFFileStatusChangeDate key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFDate *fileStatusChangeDate;

/**
 * @brief The @ref OFFileCreationDate key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFDate *fileCreationDate;

/**
 * @brief The @ref OFFileSymbolicLinkDestination key from the dictionary.
 *
 * @throw OFUndefinedKeyException The key is missing
 */
@property (readonly, nonatomic) OFString *fileSymbolicLinkDestination;
@end

OF_ASSUME_NONNULL_END

Modified src/OFFileManager.m from [8b48822fc3] to [5dfeac045a].

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

#define OF_FILE_MANAGER_M

#include <errno.h>
#include <limits.h>
#include "unistd_wrapper.h"

#import "platform.h"

#ifdef OF_PSP
# include <sys/syslimits.h>
#endif

#import "OFArray.h"
#import "OFDate.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFFileManager.h"
#import "OFLocale.h"
#import "OFNumber.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURL.h"
#import "OFURLHandler.h"

#import "OFChangeCurrentDirectoryPathFailedException.h"
#import "OFCopyItemFailedException.h"
#import "OFCreateDirectoryFailedException.h"
#import "OFGetCurrentDirectoryPathFailedException.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 "OFUndefinedKeyException.h"
#import "OFUnsupportedProtocolException.h"

#ifdef OF_WINDOWS
# include <windows.h>
# include <direct.h>
# include <ntdef.h>







|

















|
|

|


|







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

#define OF_FILE_MANAGER_M

#include <errno.h>
#include <limits.h>
#include "unistd_wrapper.h"

#include "platform.h"

#ifdef OF_PSP
# include <sys/syslimits.h>
#endif

#import "OFArray.h"
#import "OFDate.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFFileManager.h"
#import "OFLocale.h"
#import "OFNumber.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURI.h"
#import "OFURIHandler.h"

#import "OFChangeCurrentDirectoryFailedException.h"
#import "OFCopyItemFailedException.h"
#import "OFCreateDirectoryFailedException.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 "OFGetItemAttributesFailedException.h"
#import "OFUndefinedKeyException.h"
#import "OFUnsupportedProtocolException.h"

#ifdef OF_WINDOWS
# include <windows.h>
# include <direct.h>
# include <ntdef.h>
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460

	return [OFString stringWithCString: buffer
				  encoding: [OFLocale encoding]];
# else
	char buffer[PATH_MAX];

	if ((getcwd(buffer, PATH_MAX)) == NULL)
		@throw [OFGetCurrentDirectoryPathFailedException
		    exceptionWithErrNo: errno];

#  ifdef OF_DJGPP
	/*
	 * For some reason, getcwd() returns forward slashes on DJGPP, even
	 * though the native format is to use backwards slashes.
	 */
	for (char *tmp = buffer; *tmp != '\0'; tmp++)
		if (*tmp == '/')
			*tmp = '\\';
#  endif

	return [OFString stringWithCString: buffer
				  encoding: [OFLocale encoding]];
# endif
}

- (OFURL *)currentDirectoryURL
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *ret;

	ret = [OFURL fileURLWithPath: self.currentDirectoryPath];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}
#endif

- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL
{
	OFURLHandler *URLHandler;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	return [URLHandler attributesOfItemAtURL: URL];
}

#ifdef OF_HAVE_FILES
- (OFFileAttributes)attributesOfItemAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFFileAttributes ret;

	ret = [self attributesOfItemAtURL: [OFURL fileURLWithPath: path]];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
#endif

- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL
{
	OFURLHandler *URLHandler;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	[URLHandler setAttributes: attributes ofItemAtURL: URL];
}

#ifdef OF_HAVE_FILES
- (void)setAttributes: (OFFileAttributes)attributes
	 ofItemAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	[self setAttributes: attributes
		ofItemAtURL: [OFURL fileURLWithPath: path]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (bool)fileExistsAtURL: (OFURL *)URL
{
	OFURLHandler *URLHandler;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	return [URLHandler fileExistsAtURL: URL];
}

#ifdef OF_HAVE_FILES
- (bool)fileExistsAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	bool ret;

	ret = [self fileExistsAtURL: [OFURL fileURLWithPath: path]];

	objc_autoreleasePoolPop(pool);

	return ret;
}
#endif

- (bool)directoryExistsAtURL: (OFURL *)URL
{
	OFURLHandler *URLHandler;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	return [URLHandler directoryExistsAtURL: URL];
}

#ifdef OF_HAVE_FILES
- (bool)directoryExistsAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	bool ret;

	ret = [self directoryExistsAtURL: [OFURL fileURLWithPath: path]];

	objc_autoreleasePoolPop(pool);

	return ret;
}
#endif

- (void)createDirectoryAtURL: (OFURL *)URL
{
	OFURLHandler *URLHandler;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	[URLHandler createDirectoryAtURL: URL];
}

- (void)createDirectoryAtURL: (OFURL *)URL
	       createParents: (bool)createParents
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableURL *mutableURL;
	OFArray OF_GENERIC(OFString *) *components;
	OFMutableArray OF_GENERIC(OFURL *) *componentURLs;
	size_t componentURLsCount;
	ssize_t i;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if (!createParents) {
		[self createDirectoryAtURL: URL];
		return;
	}

	/*
	 * Try blindly creating the directory first.
	 *
	 * 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])
		return;

	@try {
		[self createDirectoryAtURL: URL];
		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.
		 */
		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
	 * 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];

	for (OFString *component in components) {
		[mutableURL appendPathComponent: component];

		if (![mutableURL.URLEncodedPath isEqual: @"/"])
			[componentURLs addObject:
			    [[mutableURL copy] autorelease]];
	}

	componentURLsCount = componentURLs.count;
	for (i = componentURLsCount - 1; i > 0; i--) {
		if ([self directoryExistsAtURL:
		    [componentURLs objectAtIndex: i]])
			break;
	}

	if (++i == (ssize_t)componentURLsCount) {
		/*
		 * The URL 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]];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_FILES
- (void)createDirectoryAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();

	[self createDirectoryAtURL: [OFURL fileURLWithPath: path]];

	objc_autoreleasePoolPop(pool);
}

- (void)createDirectoryAtPath: (OFString *)path
		createParents: (bool)createParents
{
	void *pool = objc_autoreleasePoolPush();

	[self createDirectoryAtURL: [OFURL fileURLWithPath: path]
		     createParents: createParents];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL
{
	OFURLHandler *URLHandler;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	return [URLHandler contentsOfDirectoryAtURL: URL];
}

#ifdef OF_HAVE_FILES
- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFArray OF_GENERIC(OFURL *) *URLs;
	OFMutableArray OF_GENERIC(OFString *) *ret;

	URLs = [self contentsOfDirectoryAtURL: [OFURL fileURLWithPath: path]];
	ret = [OFMutableArray arrayWithCapacity: URLs.count];

	for (OFURL *URL in URLs)
		[ret addObject: URL.lastPathComponent];

	[ret makeImmutable];
	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];







|

















|


|

|







|

|

|


|
|

|








|









|

|

|


|
|

|








|




|

|

|


|
|

|








|







|

|

|


|
|

|








|







|

|

|


|
|

|


|
<


|

|
|


|



|










|



|











|
|



|
|
|
|


|

|
|
|


|
|
|
|



|

|







|
|









|









|






|

|

|


|
|

|






|


|
|

|
|







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459

	return [OFString stringWithCString: buffer
				  encoding: [OFLocale encoding]];
# else
	char buffer[PATH_MAX];

	if ((getcwd(buffer, PATH_MAX)) == NULL)
		@throw [OFGetCurrentDirectoryFailedException
		    exceptionWithErrNo: errno];

#  ifdef OF_DJGPP
	/*
	 * For some reason, getcwd() returns forward slashes on DJGPP, even
	 * though the native format is to use backwards slashes.
	 */
	for (char *tmp = buffer; *tmp != '\0'; tmp++)
		if (*tmp == '/')
			*tmp = '\\';
#  endif

	return [OFString stringWithCString: buffer
				  encoding: [OFLocale encoding]];
# endif
}

- (OFURI *)currentDirectoryURI
{
	void *pool = objc_autoreleasePoolPush();
	OFURI *ret;

	ret = [OFURI fileURIWithPath: self.currentDirectoryPath];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}
#endif

- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI
{
	OFURIHandler *URIHandler;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	return [URIHandler attributesOfItemAtURI: URI];
}

#ifdef OF_HAVE_FILES
- (OFFileAttributes)attributesOfItemAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFFileAttributes ret;

	ret = [self attributesOfItemAtURI: [OFURI fileURIWithPath: path]];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
#endif

- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI
{
	OFURIHandler *URIHandler;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	[URIHandler setAttributes: attributes ofItemAtURI: URI];
}

#ifdef OF_HAVE_FILES
- (void)setAttributes: (OFFileAttributes)attributes
	 ofItemAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	[self setAttributes: attributes
		ofItemAtURI: [OFURI fileURIWithPath: path]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (bool)fileExistsAtURI: (OFURI *)URI
{
	OFURIHandler *URIHandler;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	return [URIHandler fileExistsAtURI: URI];
}

#ifdef OF_HAVE_FILES
- (bool)fileExistsAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	bool ret;

	ret = [self fileExistsAtURI: [OFURI fileURIWithPath: path]];

	objc_autoreleasePoolPop(pool);

	return ret;
}
#endif

- (bool)directoryExistsAtURI: (OFURI *)URI
{
	OFURIHandler *URIHandler;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	return [URIHandler directoryExistsAtURI: URI];
}

#ifdef OF_HAVE_FILES
- (bool)directoryExistsAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	bool ret;

	ret = [self directoryExistsAtURI: [OFURI fileURIWithPath: path]];

	objc_autoreleasePoolPop(pool);

	return ret;
}
#endif

- (void)createDirectoryAtURI: (OFURI *)URI
{
	OFURIHandler *URIHandler;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	[URIHandler createDirectoryAtURI: URI];
}

- (void)createDirectoryAtURI: (OFURI *)URI createParents: (bool)createParents

{
	void *pool = objc_autoreleasePoolPush();
	OFMutableURI *mutableURI;
	OFArray OF_GENERIC(OFString *) *components;
	OFMutableArray OF_GENERIC(OFURI *) *componentURIs;
	size_t componentURIsCount;
	ssize_t i;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if (!createParents) {
		[self createDirectoryAtURI: URI];
		return;
	}

	/*
	 * Try blindly creating the directory first.
	 *
	 * 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 directoryExistsAtURI: URI])
		return;

	@try {
		[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.
		 */
		if (e.errNo != ENOENT)
			@throw e;
	}

	/*
	 * 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.
	 */
	mutableURI = [[URI mutableCopy] autorelease];
	mutableURI.percentEncodedPath = @"/";
	components = URI.pathComponents;
	componentURIs = [OFMutableArray arrayWithCapacity: components.count];

	for (OFString *component in components) {
		[mutableURI appendPathComponent: component];

		if (![mutableURI.percentEncodedPath isEqual: @"/"])
			[componentURIs addObject:
			    [[mutableURI copy] autorelease]];
	}

	componentURIsCount = componentURIs.count;
	for (i = componentURIsCount - 1; i > 0; i--) {
		if ([self directoryExistsAtURI:
		    [componentURIs objectAtIndex: i]])
			break;
	}

	if (++i == (ssize_t)componentURIsCount) {
		/*
		 * 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)componentURIsCount; i++)
		[self createDirectoryAtURI: [componentURIs objectAtIndex: i]];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_FILES
- (void)createDirectoryAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();

	[self createDirectoryAtURI: [OFURI fileURIWithPath: path]];

	objc_autoreleasePoolPop(pool);
}

- (void)createDirectoryAtPath: (OFString *)path
		createParents: (bool)createParents
{
	void *pool = objc_autoreleasePoolPush();

	[self createDirectoryAtURI: [OFURI fileURIWithPath: path]
		     createParents: createParents];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI
{
	OFURIHandler *URIHandler;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	return [URIHandler contentsOfDirectoryAtURI: URI];
}

#ifdef OF_HAVE_FILES
- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFArray OF_GENERIC(OFURI *) *URIs;
	OFMutableArray OF_GENERIC(OFString *) *ret;

	URIs = [self contentsOfDirectoryAtURI: [OFURI fileURIWithPath: path]];
	ret = [OFMutableArray arrayWithCapacity: URIs.count];

	for (OFURI *URI in URIs)
		[ret addObject: URI.lastPathComponent];

	[ret makeImmutable];
	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
			errNo = ENOENT;
			break;
		default:
			errNo = 0;
			break;
		}

		@throw [OFChangeCurrentDirectoryPathFailedException
		    exceptionWithPath: path
				errNo: errNo];
	}

	oldLock = CurrentDir(lock);

	if (!dirChanged)







|







510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
			errNo = ENOENT;
			break;
		default:
			errNo = 0;
			break;
		}

		@throw [OFChangeCurrentDirectoryFailedException
		    exceptionWithPath: path
				errNo: errNo];
	}

	oldLock = CurrentDir(lock);

	if (!dirChanged)
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
		status = _wchdir(path.UTF16String);
	else
#  endif
		status = chdir(
		    [path cStringWithEncoding: [OFLocale encoding]]);

	if (status != 0)
		@throw [OFChangeCurrentDirectoryPathFailedException
		    exceptionWithPath: path
				errNo: errno];
# endif
}

- (void)changeCurrentDirectoryURL: (OFURL *)URL
{
	void *pool = objc_autoreleasePoolPush();

	[self changeCurrentDirectoryPath: URL.fileSystemRepresentation];

	objc_autoreleasePoolPop(pool);
}

- (void)copyItemAtPath: (OFString *)source toPath: (OFString *)destination
{
	void *pool = objc_autoreleasePoolPush();

	[self copyItemAtURL: [OFURL fileURLWithPath: source]
		      toURL: [OFURL fileURLWithPath: destination]];

	objc_autoreleasePoolPop(pool);
}
#endif

- (void)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination
{
	void *pool;
	OFURLHandler *URLHandler;
	OFFileAttributes attributes;
	OFFileAttributeType type;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	if ((URLHandler = [OFURLHandler handlerForURL: source]) == nil)
		@throw [OFUnsupportedProtocolException
		    exceptionWithURL: source];

	if ([URLHandler copyItemAtURL: source toURL: destination])
		return;

	if ([self fileExistsAtURL: destination])
		@throw [OFCopyItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: EEXIST];

	@try {
		attributes = [self attributesOfItemAtURL: source];
	} @catch (OFRetrieveItemAttributesFailedException *e) {
		@throw [OFCopyItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: e.errNo];
	}

	type = attributes.fileType;

	if ([type isEqual: OFFileTypeDirectory]) {
		OFArray OF_GENERIC(OFURL *) *contents;

		@try {
			[self createDirectoryAtURL: destination];

			@try {
				OFFileAttributeKey key = OFFilePOSIXPermissions;
				OFNumber *permissions =
				    [attributes objectForKey: key];
				OFFileAttributes destinationAttributes;

				if (permissions != nil) {
					destinationAttributes = [OFDictionary
					    dictionaryWithObject: permissions
							  forKey: key];
					[self
					    setAttributes: destinationAttributes
					      ofItemAtURL: destination];
				}
			} @catch (OFNotImplementedException *e) {
			}

			contents = [self contentsOfDirectoryAtURL: 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
						     errNo: [e errNo]];

			@throw e;
		}

		for (OFURL *item in contents) {
			void *pool2 = objc_autoreleasePoolPush();
			OFURL *destinationURL = [destination
			    URLByAppendingPathComponent:
			    item.lastPathComponent];

			[self copyItemAtURL: item toURL: destinationURL];

			objc_autoreleasePoolPop(pool2);
		}
	} else if ([type isEqual: OFFileTypeRegular]) {
		size_t pageSize = [OFSystemInfo pageSize];
		OFStream *sourceStream = nil;
		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"];

			while (!sourceStream.atEndOfStream) {
				size_t length;

				length = [sourceStream
				    readIntoBuffer: buffer
					    length: pageSize];







|





|



|








|
|





|


|








|

|

|


|

|
|



|
|

|
|






|


|













|




|









|
|





|

|
|


|











<
|
|
|
|
|







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661

662
663
664
665
666
667
668
669
670
671
672
673
		status = _wchdir(path.UTF16String);
	else
#  endif
		status = chdir(
		    [path cStringWithEncoding: [OFLocale encoding]]);

	if (status != 0)
		@throw [OFChangeCurrentDirectoryFailedException
		    exceptionWithPath: path
				errNo: errno];
# endif
}

- (void)changeCurrentDirectoryURI: (OFURI *)URI
{
	void *pool = objc_autoreleasePoolPush();

	[self changeCurrentDirectoryPath: URI.fileSystemRepresentation];

	objc_autoreleasePoolPop(pool);
}

- (void)copyItemAtPath: (OFString *)source toPath: (OFString *)destination
{
	void *pool = objc_autoreleasePoolPush();

	[self copyItemAtURI: [OFURI fileURIWithPath: source]
		      toURI: [OFURI fileURIWithPath: destination]];

	objc_autoreleasePoolPop(pool);
}
#endif

- (void)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination
{
	void *pool;
	OFURIHandler *URIHandler;
	OFFileAttributes attributes;
	OFFileAttributeType type;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	if ((URIHandler = [OFURIHandler handlerForURI: source]) == nil)
		@throw [OFUnsupportedProtocolException
		    exceptionWithURI: source];

	if ([URIHandler copyItemAtURI: source toURI: destination])
		return;

	if ([self fileExistsAtURI: destination])
		@throw [OFCopyItemFailedException
		    exceptionWithSourceURI: source
			    destinationURI: destination
				     errNo: EEXIST];

	@try {
		attributes = [self attributesOfItemAtURI: source];
	} @catch (OFGetItemAttributesFailedException *e) {
		@throw [OFCopyItemFailedException
		    exceptionWithSourceURI: source
			    destinationURI: destination
				     errNo: e.errNo];
	}

	type = attributes.fileType;

	if ([type isEqual: OFFileTypeDirectory]) {
		OFArray OF_GENERIC(OFURI *) *contents;

		@try {
			[self createDirectoryAtURI: destination];

			@try {
				OFFileAttributeKey key = OFFilePOSIXPermissions;
				OFNumber *permissions =
				    [attributes objectForKey: key];
				OFFileAttributes destinationAttributes;

				if (permissions != nil) {
					destinationAttributes = [OFDictionary
					    dictionaryWithObject: permissions
							  forKey: key];
					[self
					    setAttributes: destinationAttributes
					      ofItemAtURI: destination];
				}
			} @catch (OFNotImplementedException *e) {
			}

			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
				    exceptionWithSourceURI: source
					    destinationURI: destination
						     errNo: [e errNo]];

			@throw e;
		}

		for (OFURI *item in contents) {
			void *pool2 = objc_autoreleasePoolPush();
			OFURI *destinationURI = [destination
			    URIByAppendingPathComponent:
			    item.lastPathComponent];

			[self copyItemAtURI: item toURI: destinationURI];

			objc_autoreleasePoolPop(pool2);
		}
	} else if ([type isEqual: OFFileTypeRegular]) {
		size_t pageSize = [OFSystemInfo pageSize];
		OFStream *sourceStream = nil;
		OFStream *destinationStream = nil;
		char *buffer;

		buffer = OFAllocMemory(1, pageSize);
		@try {

			sourceStream = [OFURIHandler openItemAtURI: source
							      mode: @"r"];
			destinationStream = [OFURIHandler
			    openItemAtURI: destination
				     mode: @"w"];

			while (!sourceStream.atEndOfStream) {
				size_t length;

				length = [sourceStream
				    readIntoBuffer: buffer
					    length: pageSize];
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892

				if (permissions != nil) {
					destinationAttributes = [OFDictionary
					    dictionaryWithObject: permissions
							  forKey: key];
					[self
					    setAttributes: destinationAttributes
					      ofItemAtURL: destination];
				}
			} @catch (OFNotImplementedException *e) {
			}
		} @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
						     errNo: [e errNo]];

			@throw e;
		} @finally {
			[sourceStream close];
			[destinationStream close];
			OFFreeMemory(buffer);
		}
	} else if ([type isEqual: OFFileTypeSymbolicLink]) {
		@try {
			OFString *linkDestination =
			    attributes.fileSymbolicLinkDestination;

			[self createSymbolicLinkAtURL: destination
				  withDestinationPath: linkDestination];
		} @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
						     errNo: [e errNo]];

			@throw e;
		}
	} else
		@throw [OFCopyItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: 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]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (void)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination
{
	void *pool;
	OFURLHandler *URLHandler;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	if ((URLHandler = [OFURLHandler handlerForURL: source]) == nil)
		@throw [OFUnsupportedProtocolException
		    exceptionWithURL: source];

	@try {
		if ([URLHandler moveItemAtURL: source toURL: destination])
			return;
	} @catch (OFMoveItemFailedException *e) {
		if (e.errNo != EXDEV)
			@throw e;
	}

	if ([self fileExistsAtURL: destination])
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: EEXIST];

	@try {
		[self copyItemAtURL: source toURL: destination];
	} @catch (OFCopyItemFailedException *e) {
		[self removeItemAtURL: destination];

		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: e.errNo];
	}

	@try {
		[self removeItemAtURL: source];
	} @catch (OFRemoveItemFailedException *e) {
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: e.errNo];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)removeItemAtURL: (OFURL *)URL
{
	OFURLHandler *URLHandler;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	[URLHandler removeItemAtURL: URL];
}

#ifdef OF_HAVE_FILES
- (void)removeItemAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	[self removeItemAtURL: [OFURL fileURLWithPath: path]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination
{
	void *pool = objc_autoreleasePoolPush();
	OFURLHandler *URLHandler;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	if (![destination.scheme isEqual: source.scheme])
		@throw [OFInvalidArgumentException exception];

	URLHandler = [OFURLHandler handlerForURL: source];

	if (URLHandler == nil)
		@throw [OFUnsupportedProtocolException
		    exceptionWithURL: source];

	[URLHandler linkItemAtURL: source toURL: 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]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (void)createSymbolicLinkAtURL: (OFURL *)URL
	    withDestinationPath: (OFString *)target
{
	void *pool = objc_autoreleasePoolPush();
	OFURLHandler *URLHandler;

	if (URL == nil || target == nil)
		@throw [OFInvalidArgumentException exception];

	URLHandler = [OFURLHandler handlerForURL: URL];

	if (URLHandler == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	[URLHandler createSymbolicLinkAtURL: URL 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]
		  withDestinationPath: target];
	objc_autoreleasePoolPop(pool);
}
#endif
@end

@implementation OFDefaultFileManager







|












|
|













|










|
|






|
|









|
|




|


|






|

|


|






|

|
|



|

|


|
|




|


|
|






|

|

|


|
|

|






|




|


|







|

|

|

|








|
|




|



|

|


|

|
|

|









|







683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890

				if (permissions != nil) {
					destinationAttributes = [OFDictionary
					    dictionaryWithObject: permissions
							  forKey: key];
					[self
					    setAttributes: destinationAttributes
					      ofItemAtURI: destination];
				}
			} @catch (OFNotImplementedException *e) {
			}
		} @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
				    exceptionWithSourceURI: source
					    destinationURI: destination
						     errNo: [e errNo]];

			@throw e;
		} @finally {
			[sourceStream close];
			[destinationStream close];
			OFFreeMemory(buffer);
		}
	} else if ([type isEqual: OFFileTypeSymbolicLink]) {
		@try {
			OFString *linkDestination =
			    attributes.fileSymbolicLinkDestination;

			[self createSymbolicLinkAtURI: destination
				  withDestinationPath: linkDestination];
		} @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
				    exceptionWithSourceURI: source
					    destinationURI: destination
						     errNo: [e errNo]];

			@throw e;
		}
	} else
		@throw [OFCopyItemFailedException
		    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 moveItemAtURI: [OFURI fileURIWithPath: source]
		      toURI: [OFURI fileURIWithPath: destination]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (void)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination
{
	void *pool;
	OFURIHandler *URIHandler;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	if ((URIHandler = [OFURIHandler handlerForURI: source]) == nil)
		@throw [OFUnsupportedProtocolException
		    exceptionWithURI: source];

	@try {
		if ([URIHandler moveItemAtURI: source toURI: destination])
			return;
	} @catch (OFMoveItemFailedException *e) {
		if (e.errNo != EXDEV)
			@throw e;
	}

	if ([self fileExistsAtURI: destination])
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURI: source
			    destinationURI: destination
				     errNo: EEXIST];

	@try {
		[self copyItemAtURI: source toURI: destination];
	} @catch (OFCopyItemFailedException *e) {
		[self removeItemAtURI: destination];

		@throw [OFMoveItemFailedException
		    exceptionWithSourceURI: source
			    destinationURI: destination
				     errNo: e.errNo];
	}

	@try {
		[self removeItemAtURI: source];
	} @catch (OFRemoveItemFailedException *e) {
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURI: source
			    destinationURI: destination
				     errNo: e.errNo];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)removeItemAtURI: (OFURI *)URI
{
	OFURIHandler *URIHandler;

	if (URI == nil)
		@throw [OFInvalidArgumentException exception];

	if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	[URIHandler removeItemAtURI: URI];
}

#ifdef OF_HAVE_FILES
- (void)removeItemAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	[self removeItemAtURI: [OFURI fileURIWithPath: path]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination
{
	void *pool = objc_autoreleasePoolPush();
	OFURIHandler *URIHandler;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	if (![destination.scheme isEqual: source.scheme])
		@throw [OFInvalidArgumentException exception];

	URIHandler = [OFURIHandler handlerForURI: source];

	if (URIHandler == nil)
		@throw [OFUnsupportedProtocolException
		    exceptionWithURI: source];

	[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 linkItemAtURI: [OFURI fileURIWithPath: source]
		      toURI: [OFURI fileURIWithPath: destination]];
	objc_autoreleasePoolPop(pool);
}
#endif

- (void)createSymbolicLinkAtURI: (OFURI *)URI
	    withDestinationPath: (OFString *)target
{
	void *pool = objc_autoreleasePoolPush();
	OFURIHandler *URIHandler;

	if (URI == nil || target == nil)
		@throw [OFInvalidArgumentException exception];

	URIHandler = [OFURIHandler handlerForURI: URI];

	if (URIHandler == nil)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	[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 createSymbolicLinkAtURI: [OFURI fileURIWithPath: path]
		  withDestinationPath: target];
	objc_autoreleasePoolPop(pool);
}
#endif
@end

@implementation OFDefaultFileManager

Modified src/OFFileURIHandler.h from [bc6fd1985b] to [24ec18a27d].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * 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







|



|




9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * 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

Modified src/OFFileURIHandler.m from [50a5ea9a56] to [d74cbf6f89].

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <math.h>

#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
#include "unistd_wrapper.h"

#import "platform.h"
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <sys/time.h>
#ifdef OF_WINDOWS
# include <utime.h>
#endif

#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#ifdef HAVE_GRP_H
# include <grp.h>
#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 <windows.h>
# include <direct.h>
# include <ntdef.h>
# include <wchar.h>
#endif

#ifdef OF_AMIGAOS
# include <proto/exec.h>
# include <proto/dos.h>
# include <proto/locale.h>
# 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







|















|







|







>


|






<




















|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <math.h>

#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
#include "unistd_wrapper.h"

#include "platform.h"
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <sys/time.h>
#ifdef OF_WINDOWS
# include <utime.h>
#endif

#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#ifdef HAVE_GRP_H
# include <grp.h>
#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 <windows.h>
# include <direct.h>
# include <ntdef.h>
# include <wchar.h>
#endif

#ifdef OF_AMIGAOS
# include <proto/exec.h>
# include <proto/dos.h>
# include <proto/locale.h>
# 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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:







|




















|







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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:
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
		    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;








|
















|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
		    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;

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# 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)







|








|







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# 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)
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# 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







|

|










|
|


















|
|
|












|
|
|


|
|
|







453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# 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
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
	} @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)







|






|







531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
	} @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)
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700

	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], &times) != 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], &times);
		else
			status = _utime(
			    [path cStringWithEncoding: [OFLocale encoding]],
			    &times);

		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 =







|



|







|







|


|


|


|
|


















|









|


|



















|




















|







583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700

	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], &times) != 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], &times);
		else
			status = _utime(
			    [path cStringWithEncoding: [OFLocale encoding]],
			    &times);

		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 =
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
	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) {







|


|



















|







|




|












|










|




|



















|













|














|








|








|


|












|




|





|














|





|





|


|


|











|





|


|


|











|




|


|


|












|







|
|





|






|

|



|


|


|












|

|












|
|









|











|

|













|
|









|











|

|










|

|








|
|












|

|






|
|










|







|







724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
	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) {
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
			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);







|
|












|



|


|






|


|


|


|



|


|










|





|


|

















|














|







|
|






|



















|
|
|








|
|
|
|







|





|


|


|







|










|

|






|







|

|
|












|
|
|







1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
			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);
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
		    cStringWithEncoding: encoding]);
# ifdef OF_WINDOWS
	}
# endif

	if (status != 0)
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: errno];
#endif

	objc_autoreleasePoolPop(pool);

	return true;
}
@end







|
|








1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
		    cStringWithEncoding: encoding]);
# ifdef OF_WINDOWS
	}
# endif

	if (status != 0)
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURI: source
			    destinationURI: destination
				     errNo: errno];
#endif

	objc_autoreleasePoolPop(pool);

	return true;
}
@end

Modified src/OFHMAC.h from [618167fc85] to [2d8e8879fb].

44
45
46
47
48
49
50


51
52
53
54
55
56
57
@property (readonly, nonatomic) bool allowsSwappableMemory;

/**
 * @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.


 */
@property (readonly, nonatomic) const unsigned char *digest
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The size of the digest.
 */







>
>







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@property (readonly, nonatomic) bool allowsSwappableMemory;

/**
 * @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;

/**
 * @brief The size of the digest.
 */
96
97
98
99
100
101
102

103
104
105
106
107


108
109
110
111
112
113
114
- (void)setKey: (const void *)key length: (size_t)length;

/**
 * @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

 */
- (void)updateWithBuffer: (const void *)buffer length: (size_t)length;

/**
 * @brief Performs the final calculation of the HMAC.


 */
- (void)calculate;

/**
 * @brief Resets the HMAC so that it can be calculated for a new message.
 *
 * @note This does not reset the key so that a new HMAC with the same key can







>





>
>







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
- (void)setKey: (const void *)key length: (size_t)length;

/**
 * @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.
 *
 * @note This does not reset the key so that a new HMAC with the same key can

Modified src/OFHTTPClient.h from [4532ba7b26] to [d5da950339].

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFHTTPClient;
@class OFHTTPRequest;
@class OFHTTPResponse;
@class OFStream;
@class OFTCPSocket;
@class OFTLSStream;
@class OFURL;

/**
 * @protocol OFHTTPClientDelegate OFHTTPClient.h ObjFW/OFHTTPClient.h
 *
 * @brief A delegate for OFHTTPClient.
 */
@protocol OFHTTPClientDelegate <OFObject>







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFHTTPClient;
@class OFHTTPRequest;
@class OFHTTPResponse;
@class OFStream;
@class OFTCPSocket;
@class OFTLSStream;
@class OFURI;

/**
 * @protocol OFHTTPClientDelegate OFHTTPClient.h ObjFW/OFHTTPClient.h
 *
 * @brief A delegate for OFHTTPClient.
 */
@protocol OFHTTPClientDelegate <OFObject>
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
 * to detect a redirect.
 *
 * 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 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
 *		  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;
@end

/**
 * @class OFHTTPClient OFHTTPClient.h ObjFW/OFHTTPClient.h
 *
 * @brief A class for performing HTTP requests.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHTTPClient: OFObject
{
#ifdef OF_HTTPCLIENT_M
@public
#endif
	OFObject <OFHTTPClientDelegate> *_Nullable _delegate;
	bool _allowsInsecureRedirects, _inProgress;
	OFStream *_Nullable _stream;
	OFURL *_Nullable _lastURL;
	bool _lastWasHEAD;
	OFHTTPResponse *_Nullable _lastResponse;
}

/**
 * @brief The delegate of the HTTP request.
 */







|




|




|
|
|
|
|










|





|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
 * to detect a redirect.
 *
 * 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 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 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
  shouldFollowRedirectToURI: (OFURI *)URI
		 statusCode: (short)statusCode
		    request: (OFHTTPRequest *)request
		   response: (OFHTTPResponse *)response;
@end

/**
 * @class OFHTTPClient OFHTTPClient.h ObjFW/OFHTTPClient.h
 *
 * @brief A class for performing HTTP requests.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHTTPClient: OFObject
{
#ifdef OF_HTTP_CLIENT_M
@public
#endif
	OFObject <OFHTTPClientDelegate> *_Nullable _delegate;
	bool _allowsInsecureRedirects, _inProgress;
	OFStream *_Nullable _stream;
	OFURI *_Nullable _lastURI;
	bool _lastWasHEAD;
	OFHTTPResponse *_Nullable _lastResponse;
}

/**
 * @brief The delegate of the HTTP request.
 */
178
179
180
181
182
183
184





185
186
187
188
189
190
191
192
193
194
195
196
197
198
199





200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226
227
228
 *
 * @note You must not change the delegate while a synchronous request is
 *	 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





 */
- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request;

/**
 * @brief Synchronously performs the specified HTTP request.
 *
 * @note You must not change the delegate while a synchronous request is
 *	 running! If you want to change the delegate during the request,
 *	 perform an asynchronous request instead!
 *
 * @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





 */
- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (unsigned int)redirects;

/**
 * @brief Asynchronously performs the specified HTTP request.
 *
 * @param request The request to perform

 */
- (void)asyncPerformRequest: (OFHTTPRequest *)request;

/**
 * @brief Asynchronously performs the specified HTTP request.
 *
 * @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

 */
- (void)asyncPerformRequest: (OFHTTPRequest *)request
		  redirects: (unsigned int)redirects;

/**
 * @brief Closes connections that are still open due to keep-alive.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END







>
>
>
>
>















>
>
>
>
>








>










>











178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
 *
 * @note You must not change the delegate while a synchronous request is
 *	 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.
 *
 * @note You must not change the delegate while a synchronous request is
 *	 running! If you want to change the delegate during the request,
 *	 perform an asynchronous request instead!
 *
 * @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.
 *
 * @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;

/**
 * @brief Closes connections that are still open due to keep-alive.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Modified src/OFHTTPClient.m from [74f8b0c30d] to [e887f99bf5].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 *
 * 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_HTTPCLIENT_M

#include "config.h"

#include <errno.h>
#include <string.h>

#import "OFHTTPClient.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFKernelEventObserver.h"
#import "OFNumber.h"
#import "OFRunLoop.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFTLSStream.h"
#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"







|

















|






|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 *
 * 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_HTTP_CLIENT_M

#include "config.h"

#include <errno.h>
#include <string.h>

#import "OFHTTPClient.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFKernelEventObserver.h"
#import "OFNumber.h"
#import "OFRunLoop.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFTLSStream.h"
#import "OFURI.h"

#import "OFAlreadyConnectedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerResponseException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
@end

static OFString *
constructRequestString(OFHTTPRequest *request)
{
	void *pool = objc_autoreleasePoolPush();
	OFHTTPRequestMethod method = request.method;
	OFURL *URL = request.URL;
	OFString *path;
	OFString *user = URL.user, *password = URL.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;
	else
		path = @"/";

	requestString = [OFMutableString stringWithFormat:
	    @"%s %@", OFHTTPRequestMethodName(method), path];

	if (URL.query != nil) {
		[requestString appendString: @"?"];
		[requestString appendString: URL.URLEncodedQuery];
	}

	[requestString appendString: @" HTTP/"];
	[requestString appendString: request.protocolVersionString];
	[requestString appendString: @"\r\n"];

	headers = [[request.headers mutableCopy] autorelease];
	if (headers == nil)
		headers = [OFMutableDictionary dictionary];

	if ([headers objectForKey: @"Host"] == nil) {
		OFNumber *port = URL.port;

		if (port != nil) {
			OFString *host = [OFString stringWithFormat:
			    @"%@:%@", URL.URLEncodedHost, port];

			[headers setObject: host forKey: @"Host"];
		} else
			[headers setObject: URL.URLEncodedHost forKey: @"Host"];

	}

	if ((user.length > 0 || password.length > 0) &&
	    [headers objectForKey: @"Authorization"] == nil) {
		OFMutableData *authorizationData = [OFMutableData data];
		OFString *authorization;








|

|






|
|






|

|











|



|



|
>







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
@end

static OFString *
constructRequestString(OFHTTPRequest *request)
{
	void *pool = objc_autoreleasePoolPush();
	OFHTTPRequestMethod method = request.method;
	OFURI *URI = request.URI;
	OFString *path;
	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 (URI.path.length > 0)
		path = URI.percentEncodedPath;
	else
		path = @"/";

	requestString = [OFMutableString stringWithFormat:
	    @"%s %@", OFHTTPRequestMethodName(method), path];

	if (URI.query != nil) {
		[requestString appendString: @"?"];
		[requestString appendString: URI.percentEncodedQuery];
	}

	[requestString appendString: @" HTTP/"];
	[requestString appendString: request.protocolVersionString];
	[requestString appendString: @"\r\n"];

	headers = [[request.headers mutableCopy] autorelease];
	if (headers == nil)
		headers = [OFMutableDictionary dictionary];

	if ([headers objectForKey: @"Host"] == nil) {
		OFNumber *port = URI.port;

		if (port != nil) {
			OFString *host = [OFString stringWithFormat:
			    @"%@:%@", URI.percentEncodedHost, port];

			[headers setObject: host forKey: @"Host"];
		} else
			[headers setObject: URI.percentEncodedHost
				    forKey: @"Host"];
	}

	if ((user.length > 0 || password.length > 0) &&
	    [headers objectForKey: @"Authorization"] == nil) {
		OFMutableData *authorizationData = [OFMutableData data];
		OFString *authorization;

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
		 didPerformRequest: _request
			  response: nil
			 exception: exception];
}

- (void)createResponseWithStreamOrThrow: (OFStream *)stream
{
	OFURL *URL = _request.URL;
	OFHTTPClientResponse *response;
	OFString *connectionHeader;
	bool keepAlive;
	OFString *location;
	id exception;

	response = [[[OFHTTPClientResponse alloc] initWithStream: stream]







|







295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
		 didPerformRequest: _request
			  response: nil
			 exception: exception];
}

- (void)createResponseWithStreamOrThrow: (OFStream *)stream
{
	OFURI *URI = _request.URI;
	OFHTTPClientResponse *response;
	OFString *connectionHeader;
	bool keepAlive;
	OFString *location;
	id exception;

	response = [[[OFHTTPClientResponse alloc] initWithStream: stream]
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362

363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
			keepAlive = false;
	}

	if (keepAlive) {
		response.of_keepAlive = true;

		_client->_stream = [stream retain];
		_client->_lastURL = [URL 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"] !=
		    OFOrderedSame &&
		    [newURLScheme caseInsensitiveCompare: @"https"] !=
		    OFOrderedSame)
			follow = false;

		if (!_client->_allowsInsecureRedirects &&
		    [URL.scheme caseInsensitiveCompare: @"https"] ==
		    OFOrderedSame &&
		    [newURLScheme caseInsensitiveCompare: @"http"] ==
		    OFOrderedSame)
			follow = false;

		if (follow && [_client->_delegate respondsToSelector: @selector(

		    client:shouldFollowRedirect:statusCode:request:response:)])
			follow = [_client->_delegate client: _client
				       shouldFollowRedirect: newURL
						 statusCode: _status
						    request: _request
						   response: response];
		else if (follow)
			follow = defaultShouldFollow(_request.method, _status);

		if (follow) {
			OFDictionary OF_GENERIC(OFString *, OFString *)
			    *headers = _request.headers;
			OFHTTPRequest *newRequest =
			    [[_request copy] autorelease];
			OFMutableDictionary *newHeaders =
			    [[headers mutableCopy] autorelease];

			if (![newURL.host isEqual: URL.host])
				[newHeaders removeObjectForKey: @"Host"];

			/*
			 * 303 means the request should be converted to a GET
			 * request before redirection. This also means stripping
			 * the entity of the request.
			 */







|









|
|

|
<
|

|

|




|

|



|
>
|

|














|







326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
			keepAlive = false;
	}

	if (keepAlive) {
		response.of_keepAlive = true;

		_client->_stream = [stream retain];
		_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;
		OFURI *newURI;
		OFString *newURIScheme;

		newURI = [OFURI URIWithString: location relativeToURI: URI];

		newURIScheme = newURI.scheme;

		if ([newURIScheme caseInsensitiveCompare: @"http"] !=
		    OFOrderedSame &&
		    [newURIScheme caseInsensitiveCompare: @"https"] !=
		    OFOrderedSame)
			follow = false;

		if (!_client->_allowsInsecureRedirects &&
		    [URI.scheme caseInsensitiveCompare: @"https"] ==
		    OFOrderedSame &&
		    [newURIScheme caseInsensitiveCompare: @"http"] ==
		    OFOrderedSame)
			follow = false;

		if (follow && [_client->_delegate respondsToSelector:
		    @selector(client:shouldFollowRedirectToURI:statusCode:
		    request:response:)])
			follow = [_client->_delegate client: _client
				  shouldFollowRedirectToURI: newURI
						 statusCode: _status
						    request: _request
						   response: response];
		else if (follow)
			follow = defaultShouldFollow(_request.method, _status);

		if (follow) {
			OFDictionary OF_GENERIC(OFString *, OFString *)
			    *headers = _request.headers;
			OFHTTPRequest *newRequest =
			    [[_request copy] autorelease];
			OFMutableDictionary *newHeaders =
			    [[headers mutableCopy] autorelease];

			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
			 * the entity of the request.
			 */
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
					    [key hasPrefix: @"Transfer-"])
						[newHeaders
						    removeObjectForKey: key];

				newRequest.method = OFHTTPRequestMethodGet;
			}

			newRequest.URL = newURL;
			newRequest.headers = newHeaders;

			_client->_inProgress = false;

			[_client asyncPerformRequest: newRequest
					   redirects: _redirects - 1];
			return;







|







400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
					    [key hasPrefix: @"Transfer-"])
						[newHeaders
						    removeObjectForKey: key];

				newRequest.method = OFHTTPRequestMethodGet;
			}

			newRequest.URI = newURI;
			newRequest.headers = newHeaders;

			_client->_inProgress = false;

			[_client asyncPerformRequest: newRequest
					   redirects: _redirects - 1];
			return;
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
	if (line == nil) {
		[self closeAndReconnect];
		return false;
	}

	if (![line hasPrefix: @"HTTP/"] || line.length < 9 ||
	    [line characterAtIndex: 8] != ' ')
		@throw [OFInvalidServerReplyException exception];

	_version = [[line substringWithRange: OFRangeMake(5, 3)] copy];
	if (![_version isEqual: @"1.0"] && ![_version isEqual: @"1.1"])
		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: _version];

	status = [line substringWithRange: OFRangeMake(9, 3)].longLongValue;

	if (status < 0 || status > 599)
		@throw [OFInvalidServerReplyException exception];

	_status = (short)status;

	return true;
}

- (bool)handleServerHeader: (OFString *)line stream: (OFStream *)stream
{
	OFString *key, *value, *old;
	const char *lineC, *tmp;
	char *keyC;

	if (line == nil)
		@throw [OFInvalidServerReplyException exception];

	if (line.length == 0) {
		[_serverHeaders makeImmutable];

		if ([_client->_delegate respondsToSelector: @selector(client:
		    didReceiveHeaders:statusCode:request:)])
			[_client->_delegate client: _client







|

|




|


|













|







454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
	if (line == nil) {
		[self closeAndReconnect];
		return false;
	}

	if (![line hasPrefix: @"HTTP/"] || line.length < 9 ||
	    [line characterAtIndex: 8] != ' ')
		@throw [OFInvalidServerResponseException exception];

	_version = [[line substringWithRange: OFMakeRange(5, 3)] copy];
	if (![_version isEqual: @"1.0"] && ![_version isEqual: @"1.1"])
		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: _version];

	status = [line substringWithRange: OFMakeRange(9, 3)].longLongValue;

	if (status < 0 || status > 599)
		@throw [OFInvalidServerResponseException exception];

	_status = (short)status;

	return true;
}

- (bool)handleServerHeader: (OFString *)line stream: (OFStream *)stream
{
	OFString *key, *value, *old;
	const char *lineC, *tmp;
	char *keyC;

	if (line == nil)
		@throw [OFInvalidServerResponseException exception];

	if (line.length == 0) {
		[_serverHeaders makeImmutable];

		if ([_client->_delegate respondsToSelector: @selector(client:
		    didReceiveHeaders:statusCode:request:)])
			[_client->_delegate client: _client
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515

		return false;
	}

	lineC = line.UTF8String;

	if ((tmp = strchr(lineC, ':')) == NULL)
		@throw [OFInvalidServerReplyException exception];

	keyC = OFAllocMemory(tmp - lineC + 1, 1);
	memcpy(keyC, lineC, tmp - lineC);
	keyC[tmp - lineC] = '\0';
	normalizeKey(keyC);

	@try {







|







502
503
504
505
506
507
508
509
510
511
512
513
514
515
516

		return false;
	}

	lineC = line.UTF8String;

	if ((tmp = strchr(lineC, ':')) == NULL)
		@throw [OFInvalidServerResponseException exception];

	keyC = OFAllocMemory(tmp - lineC + 1, 1);
	memcpy(keyC, lineC, tmp - lineC);
	keyC[tmp - lineC] = '\0';
	normalizeKey(keyC);

	@try {
540
541
542
543
544
545
546
547

548
549
550
551
552
553
554
     exception: (id)exception
{
	bool ret;

	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFInvalidEncodingException class]])
			exception = [OFInvalidServerReplyException exception];


		[self raiseException: exception];
		return false;
	}

	@try {
		if (_firstLine) {







|
>







541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
     exception: (id)exception
{
	bool ret;

	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFInvalidEncodingException class]])
			exception =
			    [OFInvalidServerResponseException exception];

		[self raiseException: exception];
		return false;
	}

	@try {
		if (_firstLine) {
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675

	if ([_client->_delegate respondsToSelector:
	    @selector(client:didCreateTCPSocket:request:)])
		[_client->_delegate client: _client
			didCreateTCPSocket: sock
				   request: _request];

	if ([_request.URL.scheme caseInsensitiveCompare: @"https"] ==
	    OFOrderedSame) {
		OFTLSStream *stream;
		@try {
			stream = [OFTLSStream streamWithStream: sock];
		} @catch (OFNotImplementedException *e) {
			[self raiseException:
			    [OFUnsupportedProtocolException
			    exceptionWithURL: _request.URL]];
			return;
		}

		if ([_client->_delegate respondsToSelector:
		    @selector(client:didCreateTLSStream:request:)])
			[_client->_delegate client: _client
				didCreateTLSStream: stream
					   request: _request];

		stream.delegate = self;
		[stream asyncPerformClientHandshakeWithHost: _request.URL.host];
	} else {
		sock.delegate = self;
		[self performSelector: @selector(handleStream:)
			   withObject: sock
			   afterDelay: 0];
	}
}







|







|










|







644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677

	if ([_client->_delegate respondsToSelector:
	    @selector(client:didCreateTCPSocket:request:)])
		[_client->_delegate client: _client
			didCreateTCPSocket: sock
				   request: _request];

	if ([_request.URI.scheme caseInsensitiveCompare: @"https"] ==
	    OFOrderedSame) {
		OFTLSStream *stream;
		@try {
			stream = [OFTLSStream streamWithStream: sock];
		} @catch (OFNotImplementedException *e) {
			[self raiseException:
			    [OFUnsupportedProtocolException
			    exceptionWithURI: _request.URI]];
			return;
		}

		if ([_client->_delegate respondsToSelector:
		    @selector(client:didCreateTLSStream:request:)])
			[_client->_delegate client: _client
				didCreateTLSStream: stream
					   request: _request];

		stream.delegate = self;
		[stream asyncPerformClientHandshakeWithHost: _request.URI.host];
	} else {
		sock.delegate = self;
		[self performSelector: @selector(handleStream:)
			   withObject: sock
			   afterDelay: 0];
	}
}
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
	[self performSelector: @selector(handleStream:)
		   withObject: stream
		   afterDelay: 0];
}

- (void)start
{
	OFURL *URL = _request.URL;
	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->_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->_lastResponse release];
		_client->_lastResponse = nil;

		stream.delegate = self;

		[self performSelector: @selector(handleStream:)
			   withObject: stream
			   afterDelay: 0];
	} else
		[self closeAndReconnect];
}

- (void)closeAndReconnect
{
	@try {
		OFURL *URL = _request.URL;
		OFTCPSocket *sock;
		uint16_t port;
		OFNumber *URLPort;

		[_client close];

		sock = [OFTCPSocket socket];

		if ([URL.scheme caseInsensitiveCompare: @"https"] ==
		    OFOrderedSame)
			port = 443;
		else
			port = 80;

		URLPort = URL.port;
		if (URLPort != nil)
			port = URLPort.unsignedShortValue;

		sock.delegate = self;
		[sock asyncConnectToHost: URL.host port: port];
	} @catch (id e) {
		[self raiseException: e];
	}
}
@end

@implementation OFHTTPClientRequestBodyStream







|




|
|
|
|









|
|
















|


|





|





|
|
|


|







688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
	[self performSelector: @selector(handleStream:)
		   withObject: stream
		   afterDelay: 0];
}

- (void)start
{
	OFURI *URI = _request.URI;
	OFStream *stream;

	/* Can we reuse the last socket? */
	if (_client->_stream != nil && !_client->_stream.atEndOfStream &&
	    [_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->_lastURI release];
		_client->_lastURI = nil;

		[_client->_lastResponse release];
		_client->_lastResponse = nil;

		stream.delegate = self;

		[self performSelector: @selector(handleStream:)
			   withObject: stream
			   afterDelay: 0];
	} else
		[self closeAndReconnect];
}

- (void)closeAndReconnect
{
	@try {
		OFURI *URI = _request.URI;
		OFTCPSocket *sock;
		uint16_t port;
		OFNumber *URIPort;

		[_client close];

		sock = [OFTCPSocket socket];

		if ([URI.scheme caseInsensitiveCompare: @"https"] ==
		    OFOrderedSame)
			port = 443;
		else
			port = 80;

		URIPort = URI.port;
		if (URIPort != nil)
			port = URIPort.unsignedShortValue;

		sock.delegate = self;
		[sock asyncConnectToHost: URI.host port: port];
	} @catch (id e) {
		[self raiseException: e];
	}
}
@end

@implementation OFHTTPClientRequestBodyStream
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924

	_chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	contentLength = [headers objectForKey: @"Content-Length"];
	if (contentLength != nil) {
		if (_chunked || contentLength.length == 0)
			@throw [OFInvalidServerReplyException exception];

		_hasContentLength = true;

		@try {
			unsigned long long toRead =
			    contentLength.unsignedLongLongValue;

			if (toRead > LLONG_MAX)
				@throw [OFOutOfRangeException exception];

			_toRead = (long long)toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerReplyException exception];
		}
	}
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
	if (_stream == nil)







|












|







899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926

	_chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	contentLength = [headers objectForKey: @"Content-Length"];
	if (contentLength != nil) {
		if (_chunked || contentLength.length == 0)
			@throw [OFInvalidServerResponseException exception];

		_hasContentLength = true;

		@try {
			unsigned long long toRead =
			    contentLength.unsignedLongLongValue;

			if (toRead > LLONG_MAX)
				@throw [OFOutOfRangeException exception];

			_toRead = (long long)toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerResponseException exception];
		}
	}
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
	if (_stream == nil)
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
	if (_toRead == -2) {
		char tmp[2];

		switch ([_stream readIntoBuffer: tmp length: 2]) {
		case 2:
			_toRead++;
			if (tmp[1] != '\n')
				@throw [OFInvalidServerReplyException
				    exception];
		case 1:
			_toRead++;
			if (tmp[0] != '\r')
				@throw [OFInvalidServerReplyException
				    exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
	} else if (_toRead == -1) {
		char tmp;

		if ([_stream readIntoBuffer: &tmp length: 1] == 1) {
			_toRead++;
			if (tmp != '\n')
				@throw [OFInvalidServerReplyException
				    exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;







|




|













|







958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
	if (_toRead == -2) {
		char tmp[2];

		switch ([_stream readIntoBuffer: tmp length: 2]) {
		case 2:
			_toRead++;
			if (tmp[1] != '\n')
				@throw [OFInvalidServerResponseException
				    exception];
		case 1:
			_toRead++;
			if (tmp[0] != '\r')
				@throw [OFInvalidServerResponseException
				    exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
	} else if (_toRead == -1) {
		char tmp;

		if ([_stream readIntoBuffer: &tmp length: 1] == 1) {
			_toRead++;
			if (tmp != '\n')
				@throw [OFInvalidServerResponseException
				    exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
		void *pool = objc_autoreleasePoolPush();
		OFString *line;
		size_t pos;

		@try {
			line = [_stream tryReadLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidServerReplyException exception];
		}

		if (line == nil)
			return 0;

		pos = [line rangeOfString: @";"].location;
		if (pos != OFNotFound)
			line = [line substringToIndex: pos];

		if (line.length < 1) {
			/*
			 * We have read the empty string because the stream is
			 * at end of stream.
			 */
			if (_stream.atEndOfStream && pos == OFNotFound)
				@throw [OFTruncatedDataException exception];
			else
				@throw [OFInvalidServerReplyException
				    exception];
		}

		@try {
			unsigned long long toRead =
			    [line unsignedLongLongValueWithBase: 16];

			if (toRead > LLONG_MAX)
				@throw [OFOutOfRangeException exception];

			_toRead = (long long)toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerReplyException exception];
		}

		if (_toRead == 0) {
			_setAtEndOfStream = true;
			_toRead = -2;
		}








|

















|












|







1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
		void *pool = objc_autoreleasePoolPush();
		OFString *line;
		size_t pos;

		@try {
			line = [_stream tryReadLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidServerResponseException exception];
		}

		if (line == nil)
			return 0;

		pos = [line rangeOfString: @";"].location;
		if (pos != OFNotFound)
			line = [line substringToIndex: pos];

		if (line.length < 1) {
			/*
			 * We have read the empty string because the stream is
			 * at end of stream.
			 */
			if (_stream.atEndOfStream && pos == OFNotFound)
				@throw [OFTruncatedDataException exception];
			else
				@throw [OFInvalidServerResponseException
				    exception];
		}

		@try {
			unsigned long long toRead =
			    [line unsignedLongLongValueWithBase: 16];

			if (toRead > LLONG_MAX)
				@throw [OFOutOfRangeException exception];

			_toRead = (long long)toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerResponseException exception];
		}

		if (_toRead == 0) {
			_setAtEndOfStream = true;
			_toRead = -2;
		}

1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
	    @selector(client:didReceiveHeaders:statusCode:request:)])
		[_delegate     client: client
		    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];
	else
		return defaultShouldFollow(request.method, statusCode);
}
@end

@implementation OFHTTPClient
@synthesize delegate = _delegate;







|
|
|
|
|

|
|
|
|
|
|
|







1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
	    @selector(client:didReceiveHeaders:statusCode:request:)])
		[_delegate     client: client
		    didReceiveHeaders: headers
			   statusCode: statusCode
			      request: request];
}

-	       (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

@implementation OFHTTPClient
@synthesize delegate = _delegate;
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
	[self asyncPerformRequest: request redirects: defaultRedirects];
}

- (void)asyncPerformRequest: (OFHTTPRequest *)request
		  redirects: (unsigned int)redirects
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *URL = request.URL;
	OFString *scheme = URL.scheme;

	if ([scheme caseInsensitiveCompare: @"http"] != OFOrderedSame &&
	    [scheme caseInsensitiveCompare: @"https"] != OFOrderedSame)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	if (_inProgress)
		/* TODO: Find a better exception */
		@throw [OFAlreadyConnectedException exception];

	_inProgress = true;

	[[[[OFHTTPClientRequestHandler alloc]
	    initWithClient: self
		   request: request
		 redirects: redirects] autorelease] start];

	objc_autoreleasePoolPop(pool);
}

- (void)close
{
	[_stream release];
	_stream = nil;

	[_lastURL release];
	_lastURL = nil;

	[_lastResponse release];
	_lastResponse = nil;
}
@end







|
|



|


<

















|
|





1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277

1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
	[self asyncPerformRequest: request redirects: defaultRedirects];
}

- (void)asyncPerformRequest: (OFHTTPRequest *)request
		  redirects: (unsigned int)redirects
{
	void *pool = objc_autoreleasePoolPush();
	OFURI *URI = request.URI;
	OFString *scheme = URI.scheme;

	if ([scheme caseInsensitiveCompare: @"http"] != OFOrderedSame &&
	    [scheme caseInsensitiveCompare: @"https"] != OFOrderedSame)
		@throw [OFUnsupportedProtocolException exceptionWithURI: URI];

	if (_inProgress)

		@throw [OFAlreadyConnectedException exception];

	_inProgress = true;

	[[[[OFHTTPClientRequestHandler alloc]
	    initWithClient: self
		   request: request
		 redirects: redirects] autorelease] start];

	objc_autoreleasePoolPop(pool);
}

- (void)close
{
	[_stream release];
	_stream = nil;

	[_lastURI release];
	_lastURI = nil;

	[_lastResponse release];
	_lastResponse = nil;
}
@end

Modified src/OFHTTPCookie.h from [c915720e1d] to [ff53970b1e].

18
19
20
21
22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFDate;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFURL;

/**
 * @class OFHTTPCookie OFHTTPCookie.h ObjFW/OFHTTPCookie.h
 *
 * @brief A class for storing and manipulating HTTP cookies.
 */

@interface OFHTTPCookie: OFObject <OFCopying>
{
	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.
 */
@property (copy, nonatomic) OFString *name;








|






>






<







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFDate;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFURI;

/**
 * @class OFHTTPCookie OFHTTPCookie.h ObjFW/OFHTTPCookie.h
 *
 * @brief A class for storing and manipulating HTTP cookies.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHTTPCookie: OFObject <OFCopying>
{
	OFString *_name, *_value, *_domain, *_path;
	OFDate *_Nullable _expires;
	bool _secure, _HTTPOnly;
	OFMutableArray OF_GENERIC(OFString *) *_extensions;

}

/**
 * @brief The name of the cookie.
 */
@property (copy, nonatomic) OFString *name;

76
77
78
79
80
81
82
83
84
85
86
87
88


89
90
91
92
93
94
95
96
97
98
99
/**
 * @brief An array of other attributes.
 */
@property (readonly, nonatomic)
    OFMutableArray OF_GENERIC(OFString *) *extensions;

/**
 * @brief Parses the specified response header fields for the specified URL 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
 * @return An array of cookies


 */
+ (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields:
    (OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields
    forURL: (OFURL *)URL;

/**
 * @brief Returns the request header fields for the specified cookies.
 *
 * @param cookies The cookies to return the request header fields for
 * @return The request header fields for the specified cookies
 */







|



|

>
>



|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
 * @brief An array of other attributes.
 */
@property (readonly, nonatomic)
    OFMutableArray OF_GENERIC(OFString *) *extensions;

/**
 * @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 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
    forURI: (OFURI *)URI;

/**
 * @brief Returns the request header fields for the specified cookies.
 *
 * @param cookies The cookies to return the request header fields for
 * @return The request header fields for the specified cookies
 */

Modified src/OFHTTPCookie.m from [30659cca3c] to [a7d6a07918].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include "config.h"

#import "OFHTTPCookie.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFURL.h"

#import "OFInvalidFormatException.h"

static void
handleAttribute(OFHTTPCookie *cookie, OFString *name, OFString *value)
{
	OFString *lowercaseName = name.lowercaseString;







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include "config.h"

#import "OFHTTPCookie.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFURI.h"

#import "OFInvalidFormatException.h"

static void
handleAttribute(OFHTTPCookie *cookie, OFString *name, OFString *value)
{
	OFString *lowercaseName = name.lowercaseString;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@implementation OFHTTPCookie
@synthesize name = _name, value = _value, domain = _domain, path = _path;
@synthesize expires = _expires, secure = _secure, HTTPOnly = _HTTPOnly;
@synthesize extensions = _extensions;

+ (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields:
    (OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields
    forURL: (OFURL *)URL
{
	OFMutableArray OF_GENERIC(OFHTTPCookie *) *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();
	OFString *string = [headerFields objectForKey: @"Set-Cookie"];
	OFString *domain = URL.host;
	const OFUnichar *characters = string.characters;
	size_t length = string.length, last = 0;
	enum {
		statePreName,
		stateName,
		stateExpectValue,
		stateValue,







|




|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@implementation OFHTTPCookie
@synthesize name = _name, value = _value, domain = _domain, path = _path;
@synthesize expires = _expires, secure = _secure, HTTPOnly = _HTTPOnly;
@synthesize extensions = _extensions;

+ (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields:
    (OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields
    forURI: (OFURI *)URI
{
	OFMutableArray OF_GENERIC(OFHTTPCookie *) *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();
	OFString *string = [headerFields objectForKey: @"Set-Cookie"];
	OFString *domain = URI.host;
	const OFUnichar *characters = string.characters;
	size_t length = string.length, last = 0;
	enum {
		statePreName,
		stateName,
		stateExpectValue,
		stateValue,
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
				last = i;
				i--;
			}
			break;
		case stateName:
			if (characters[i] == '=') {
				name = [string substringWithRange:
				    OFRangeMake(last, i - last)];
				state = stateExpectValue;
			}
			break;
		case stateExpectValue:
			if (characters[i] == '"') {
				state = stateQuotedValue;
				last = i + 1;
			} else {
				state = stateValue;
				last = i;
			}

			i--;
			break;
		case stateValue:
			if (characters[i] == ';' || characters[i] == ',') {
				value = [string substringWithRange:
				    OFRangeMake(last, i - last)];

				[ret addObject:
				    [OFHTTPCookie cookieWithName: name
							   value: value
							  domain: domain]];

				state = (characters[i] == ';'
				    ? statePreAttrName : statePreName);
			}
			break;
		case stateQuotedValue:
			if (characters[i] == '"') {
				value = [string substringWithRange:
				    OFRangeMake(last, i - last)];
				[ret addObject:
				    [OFHTTPCookie cookieWithName: name
							   value: value
							  domain: domain]];

				state = statePostQuotedValue;
			}







|

















|













|







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
				last = i;
				i--;
			}
			break;
		case stateName:
			if (characters[i] == '=') {
				name = [string substringWithRange:
				    OFMakeRange(last, i - last)];
				state = stateExpectValue;
			}
			break;
		case stateExpectValue:
			if (characters[i] == '"') {
				state = stateQuotedValue;
				last = i + 1;
			} else {
				state = stateValue;
				last = i;
			}

			i--;
			break;
		case stateValue:
			if (characters[i] == ';' || characters[i] == ',') {
				value = [string substringWithRange:
				    OFMakeRange(last, i - last)];

				[ret addObject:
				    [OFHTTPCookie cookieWithName: name
							   value: value
							  domain: domain]];

				state = (characters[i] == ';'
				    ? statePreAttrName : statePreName);
			}
			break;
		case stateQuotedValue:
			if (characters[i] == '"') {
				value = [string substringWithRange:
				    OFMakeRange(last, i - last)];
				[ret addObject:
				    [OFHTTPCookie cookieWithName: name
							   value: value
							  domain: domain]];

				state = statePostQuotedValue;
			}
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
				last = i;
				i--;
			}
			break;
		case stateAttrName:
			if (characters[i] == '=') {
				name = [string substringWithRange:
				    OFRangeMake(last, i - last)];

				state = stateAttrValue;
				last = i + 1;
			} else if (characters[i] == ';' ||
			    characters[i] == ',') {
				name = [string substringWithRange:
				    OFRangeMake(last, i - last)];

				handleAttribute(ret.lastObject, name, nil);

				state = (characters[i] == ';'
				    ? statePreAttrName : statePreName);
			}

			break;
		case stateAttrValue:
			if (characters[i] == ';' || characters[i] == ',') {
				value = [string substringWithRange:
				    OFRangeMake(last, i - last)];

				/*
				 * Expires often contains a comma, even though
				 * the comma is used as a separator for
				 * concatenating headers as per RFC 2616,
				 * meaning RFC 6265 contradicts RFC 2616.
				 * Solve this by special casing this.







|






|











|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
				last = i;
				i--;
			}
			break;
		case stateAttrName:
			if (characters[i] == '=') {
				name = [string substringWithRange:
				    OFMakeRange(last, i - last)];

				state = stateAttrValue;
				last = i + 1;
			} else if (characters[i] == ';' ||
			    characters[i] == ',') {
				name = [string substringWithRange:
				    OFMakeRange(last, i - last)];

				handleAttribute(ret.lastObject, name, nil);

				state = (characters[i] == ';'
				    ? statePreAttrName : statePreName);
			}

			break;
		case stateAttrValue:
			if (characters[i] == ';' || characters[i] == ',') {
				value = [string substringWithRange:
				    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,
				 * meaning RFC 6265 contradicts RFC 2616.
				 * Solve this by special casing this.
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
		break;
	case stateName:
	case stateQuotedValue:
		@throw [OFInvalidFormatException exception];
		break;
	case stateValue:
		value = [string substringWithRange:
		    OFRangeMake(last, length - last)];
		[ret addObject: [OFHTTPCookie cookieWithName: name
						       value: value
						      domain: domain]];
		break;
	/* We end up here if the cookie is just foo= */
	case stateExpectValue:
		[ret addObject: [OFHTTPCookie cookieWithName: name
						       value: @""
						      domain: domain]];
		break;
	case stateAttrName:
		if (last != length) {
			name = [string substringWithRange:
			    OFRangeMake(last, length - last)];

			handleAttribute(ret.lastObject, name, nil);
		}
		break;
	case stateAttrValue:
		value = [string substringWithRange:
		    OFRangeMake(last, length - last)];

		handleAttribute(ret.lastObject, name, value);

		break;
	}

	objc_autoreleasePoolPop(pool);







|













|






|







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
		break;
	case stateName:
	case stateQuotedValue:
		@throw [OFInvalidFormatException exception];
		break;
	case stateValue:
		value = [string substringWithRange:
		    OFMakeRange(last, length - last)];
		[ret addObject: [OFHTTPCookie cookieWithName: name
						       value: value
						      domain: domain]];
		break;
	/* We end up here if the cookie is just foo= */
	case stateExpectValue:
		[ret addObject: [OFHTTPCookie cookieWithName: name
						       value: @""
						      domain: domain]];
		break;
	case stateAttrName:
		if (last != length) {
			name = [string substringWithRange:
			    OFMakeRange(last, length - last)];

			handleAttribute(ret.lastObject, name, nil);
		}
		break;
	case stateAttrValue:
		value = [string substringWithRange:
		    OFMakeRange(last, length - last)];

		handleAttribute(ret.lastObject, name, value);

		break;
	}

	objc_autoreleasePoolPop(pool);
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382

	OFHashInit(&hash);
	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);
	OFHashAddHash(&hash, _extensions.hash);
	OFHashFinalize(&hash);

	return hash;
}

- (id)copy







|
|







367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382

	OFHashInit(&hash);
	OFHashAddHash(&hash, _name.hash);
	OFHashAddHash(&hash, _value.hash);
	OFHashAddHash(&hash, _domain.hash);
	OFHashAddHash(&hash, _path.hash);
	OFHashAddHash(&hash, _expires.hash);
	OFHashAddByte(&hash, _secure);
	OFHashAddByte(&hash, _HTTPOnly);
	OFHashAddHash(&hash, _extensions.hash);
	OFHashFinalize(&hash);

	return hash;
}

- (id)copy

Modified src/OFHTTPCookieManager.h from [776c67cc36] to [3cc0432376].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#import "OFObject.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFHTTPCookie;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFURL;

/**
 * @class OFHTTPCookieManager OFHTTPCookieManager.h ObjFW/OFHTTPCookieManager.h
 *
 * @brief A class for managing cookies for multiple domains.
 */
OF_SUBCLASSING_RESTRICTED







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#import "OFObject.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFHTTPCookie;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFURI;

/**
 * @class OFHTTPCookieManager OFHTTPCookieManager.h ObjFW/OFHTTPCookieManager.h
 *
 * @brief A class for managing cookies for multiple domains.
 */
OF_SUBCLASSING_RESTRICTED
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
 * @brief Create a new cookie manager.
 *
 * @return A new, autoreleased OFHTTPCookieManager
 */
+ (instancetype)manager;

/**
 * @brief Adds the specified cookie for the specified URL.
 *
 * @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
 */
- (void)addCookie: (OFHTTPCookie *)cookie forURL: (OFURL *)URL;

/**
 * @brief Adds the specified cookies for the specified URL.
 *
 * @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
 */
- (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
	    forURL: (OFURL *)URL;

/**
 * @brief Returns the cookies for the specified URL.
 *
 * @param URL The URL for which the cookies should be returned
 * @return The cookies for the specified URL
 */
- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURL: (OFURL *)URL;

/**
 * @brief Purges all expired cookies.
 */
- (void)purgeExpiredCookies;
@end

OF_ASSUME_NONNULL_END







|





|

|


|





|


|


|

|
|

|








42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
 * @brief Create a new cookie manager.
 *
 * @return A new, autoreleased OFHTTPCookieManager
 */
+ (instancetype)manager;

/**
 * @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 URI The URI for which the cookie should be added
 */
- (void)addCookie: (OFHTTPCookie *)cookie forURI: (OFURI *)URI;

/**
 * @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 URI The URI for which the cookies should be added
 */
- (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
	    forURI: (OFURI *)URI;

/**
 * @brief Returns the cookies for the specified URI.
 *
 * @param URI The URI for which the cookies should be returned
 * @return The cookies for the specified URI
 */
- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURI: (OFURI *)URI;

/**
 * @brief Purges all expired cookies.
 */
- (void)purgeExpiredCookies;
@end

OF_ASSUME_NONNULL_END

Modified src/OFHTTPCookieManager.m from [ab6e7fb16a] to [1dd74fe07e].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include "config.h"

#import "OFHTTPCookieManager.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFHTTPCookie.h"
#import "OFURL.h"

@implementation OFHTTPCookieManager
+ (instancetype)manager
{
	return [[[self alloc] init] autorelease];
}








|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include "config.h"

#import "OFHTTPCookieManager.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFHTTPCookie.h"
#import "OFURI.h"

@implementation OFHTTPCookieManager
+ (instancetype)manager
{
	return [[[self alloc] init] autorelease];
}

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
}

- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
{
	return [[_cookies copy] autorelease];
}

- (void)addCookie: (OFHTTPCookie *)cookie forURL: (OFURL *)URL
{
	void *pool = objc_autoreleasePoolPush();
	OFString *cookieDomain, *URLHost;
	size_t i;

	if (![cookie.path hasPrefix: @"/"])
		cookie.path = @"/";

	if (cookie.secure &&
	    [URL.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];

		if (![cookieDomain hasSuffix: URLHost]) {
			objc_autoreleasePoolPop(pool);
			return;
		}
	}

	i = 0;
	for (OFHTTPCookie *iter in _cookies) {







|


|






|







|
|
|

|







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
}

- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
{
	return [[_cookies copy] autorelease];
}

- (void)addCookie: (OFHTTPCookie *)cookie forURI: (OFURI *)URI
{
	void *pool = objc_autoreleasePoolPush();
	OFString *cookieDomain, *URIHost;
	size_t i;

	if (![cookie.path hasPrefix: @"/"])
		cookie.path = @"/";

	if (cookie.secure &&
	    [URI.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	cookieDomain = cookie.domain.lowercaseString;
	cookie.domain = cookieDomain;

	URIHost = URI.host.lowercaseString;
	if (![cookieDomain isEqual: URIHost]) {
		URIHost = [@"." stringByAppendingString: URIHost];

		if (![cookieDomain hasSuffix: URIHost]) {
			objc_autoreleasePoolPop(pool);
			return;
		}
	}

	i = 0;
	for (OFHTTPCookie *iter in _cookies) {
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

	[_cookies addObject: cookie];

	objc_autoreleasePoolPop(pool);
}

- (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
	    forURL: (OFURL *)URL
{
	for (OFHTTPCookie *cookie in cookies)
		[self addCookie: cookie forURL: URL];
}

- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURL: (OFURL *)URL
{
	OFMutableArray *ret = [OFMutableArray array];

	for (OFHTTPCookie *cookie in _cookies) {
		void *pool;
		OFDate *expires;
		OFString *cookieDomain, *URLHost, *cookiePath, *URLPath;
		bool match;

		expires = cookie.expires;
		if (expires != nil && expires.timeIntervalSinceNow <= 0)
			continue;

		if (cookie.secure && [URL.scheme caseInsensitiveCompare:
		    @"https"] != OFOrderedSame)
			continue;

		pool = objc_autoreleasePoolPush();

		cookieDomain = cookie.domain.lowercaseString;
		URLHost = URL.host.lowercaseString;
		if ([cookieDomain hasPrefix: @"."]) {
			if ([URLHost hasSuffix: cookieDomain])
				match = true;
			else {
				cookieDomain =
				    [cookieDomain substringFromIndex: 1];

				match = [cookieDomain isEqual: URLHost];
			}
		} else
			match = [cookieDomain isEqual: URLHost];

		if (!match) {
			objc_autoreleasePoolPop(pool);
			continue;
		}

		cookiePath = cookie.path;
		URLPath = URL.path;
		if (![cookiePath isEqual: @"/"]) {
			if ([cookiePath isEqual: URLPath])
				match = true;
			else {
				if (![cookiePath hasSuffix: @"/"])
					cookiePath = [cookiePath
					    stringByAppendingString: @"/"];

				match = [URLPath hasPrefix: cookiePath];
			}

			if (!match) {
				objc_autoreleasePoolPop(pool);
				continue;
			}
		}







|


|


|






|






|






|

|





|


|







|

|






|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

	[_cookies addObject: cookie];

	objc_autoreleasePoolPop(pool);
}

- (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
	    forURI: (OFURI *)URI
{
	for (OFHTTPCookie *cookie in cookies)
		[self addCookie: cookie forURI: URI];
}

- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURI: (OFURI *)URI
{
	OFMutableArray *ret = [OFMutableArray array];

	for (OFHTTPCookie *cookie in _cookies) {
		void *pool;
		OFDate *expires;
		OFString *cookieDomain, *URIHost, *cookiePath, *URIPath;
		bool match;

		expires = cookie.expires;
		if (expires != nil && expires.timeIntervalSinceNow <= 0)
			continue;

		if (cookie.secure && [URI.scheme caseInsensitiveCompare:
		    @"https"] != OFOrderedSame)
			continue;

		pool = objc_autoreleasePoolPush();

		cookieDomain = cookie.domain.lowercaseString;
		URIHost = URI.host.lowercaseString;
		if ([cookieDomain hasPrefix: @"."]) {
			if ([URIHost hasSuffix: cookieDomain])
				match = true;
			else {
				cookieDomain =
				    [cookieDomain substringFromIndex: 1];

				match = [cookieDomain isEqual: URIHost];
			}
		} else
			match = [cookieDomain isEqual: URIHost];

		if (!match) {
			objc_autoreleasePoolPop(pool);
			continue;
		}

		cookiePath = cookie.path;
		URIPath = URI.path;
		if (![cookiePath isEqual: @"/"]) {
			if ([cookiePath isEqual: URIPath])
				match = true;
			else {
				if (![cookiePath hasSuffix: @"/"])
					cookiePath = [cookiePath
					    stringByAppendingString: @"/"];

				match = [URIPath hasPrefix: cookiePath];
			}

			if (!match) {
				objc_autoreleasePoolPop(pool);
				continue;
			}
		}

Modified src/OFHTTPRequest.h from [cc3f0d316f] to [1278addd47].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#import "OFObject.h"
#import "OFSocket.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

@class OFURL;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFData;
@class OFString;

/** @file */

/**







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#import "OFObject.h"
#import "OFSocket.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

@class OFURI;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFData;
@class OFString;

/** @file */

/**
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85



86
87
88
89
90





91
92
93
94
95
96
97
} OFHTTPRequestProtocolVersion;

/**
 * @class OFHTTPRequest OFHTTPRequest.h ObjFW/OFHTTPRequest.h
 *
 * @brief A class for storing HTTP requests.
 */

@interface OFHTTPRequest: OFObject <OFCopying>
{
	OFURL *_URL;
	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.
 */
@property (copy, nonatomic) OFURL *URL;

/**
 * @brief The protocol version of the HTTP request.



 */
@property (nonatomic) OFHTTPRequestProtocolVersion protocolVersion;

/**
 * @brief The protocol version of the HTTP request as a string.





 */
@property (copy, nonatomic) OFString *protocolVersionString;

/**
 * @brief The request method of the HTTP request.
 */
@property (nonatomic) OFHTTPRequestMethod method;







>


|





<



|

|



>
>
>





>
>
>
>
>







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
} OFHTTPRequestProtocolVersion;

/**
 * @class OFHTTPRequest OFHTTPRequest.h ObjFW/OFHTTPRequest.h
 *
 * @brief A class for storing HTTP requests.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHTTPRequest: OFObject <OFCopying>
{
	OFURI *_URI;
	OFHTTPRequestMethod _method;
	OFHTTPRequestProtocolVersion _protocolVersion;
	OFDictionary OF_GENERIC(OFString *, OFString *) *_Nullable _headers;
	OFSocketAddress _remoteAddress;
	bool _hasRemoteAddress;

}

/**
 * @brief The URI of the HTTP request.
 */
@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.
 */
@property (nonatomic) OFHTTPRequestMethod method;
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 * @brief The remote address from which the request originates.
 *
 * @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.
 *
 * @param URL The URL for the request
 * @return A new, autoreleased OFHTTPRequest
 */
+ (instancetype)requestWithURL: (OFURL *)URL;

/**
 * @brief Initializes an already allocated OFHTTPRequest with the specified URL.
 *
 * @param URL The URL for the request
 * @return An initialized OFHTTPRequest
 */
- (instancetype)initWithURL: (OFURL *)URL;

- (instancetype)init OF_UNAVAILABLE;
@end

#ifdef __cplusplus
extern "C" {
#endif







|

|


|


|

|


|







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
 * @brief The remote address from which the request originates.
 *
 * @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 URI.
 *
 * @param URI The URI for the request
 * @return A new, autoreleased OFHTTPRequest
 */
+ (instancetype)requestWithURI: (OFURI *)URI;

/**
 * @brief Initializes an already allocated OFHTTPRequest with the specified URI.
 *
 * @param URI The URI for the request
 * @return An initialized OFHTTPRequest
 */
- (instancetype)initWithURI: (OFURI *)URI;

- (instancetype)init OF_UNAVAILABLE;
@end

#ifdef __cplusplus
extern "C" {
#endif
141
142
143
144
145
146
147


148
149
150
151
152
153
154
    OFHTTPRequestMethod method);

/**
 * @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


 */
extern OFHTTPRequestMethod OFHTTPRequestMethodParseName(OFString *string);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







>
>







149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
    OFHTTPRequestMethod method);

/**
 * @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

Modified src/OFHTTPRequest.m from [723c374a89] to [c85955ea02].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include "config.h"

#include <string.h>

#import "OFHTTPRequest.h"
#import "OFString.h"
#import "OFURL.h"
#import "OFDictionary.h"
#import "OFData.h"
#import "OFArray.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include "config.h"

#include <string.h>

#import "OFHTTPRequest.h"
#import "OFString.h"
#import "OFURI.h"
#import "OFDictionary.h"
#import "OFData.h"
#import "OFArray.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
	if ([string isEqual: @"DELETE"])
		return OFHTTPRequestMethodDelete;
	if ([string isEqual: @"TRACE"])
		return OFHTTPRequestMethodTrace;
	if ([string isEqual: @"CONNECT"])
		return OFHTTPRequestMethodConnect;

	@throw [OFInvalidArgumentException exception];
}

@implementation OFHTTPRequest
@synthesize URL = _URL, method = _method, headers = _headers;

+ (instancetype)requestWithURL: (OFURL *)URL
{
	return [[[self alloc] initWithURL: URL] autorelease];
}

- (instancetype)initWithURL: (OFURL *)URL
{
	self = [super init];

	@try {
		_URL = [URL copy];
		_method = OFHTTPRequestMethodGet;
		_protocolVersion.major = 1;
		_protocolVersion.minor = 1;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_URL release];
	[_headers release];

	[super dealloc];
}

- (void)setRemoteAddress: (const OFSocketAddress *)remoteAddress
{







|



|

|

|


|




|


















|







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
	if ([string isEqual: @"DELETE"])
		return OFHTTPRequestMethodDelete;
	if ([string isEqual: @"TRACE"])
		return OFHTTPRequestMethodTrace;
	if ([string isEqual: @"CONNECT"])
		return OFHTTPRequestMethodConnect;

	@throw [OFInvalidFormatException exception];
}

@implementation OFHTTPRequest
@synthesize URI = _URI, method = _method, headers = _headers;

+ (instancetype)requestWithURI: (OFURI *)URI
{
	return [[[self alloc] initWithURI: URI] autorelease];
}

- (instancetype)initWithURI: (OFURI *)URI
{
	self = [super init];

	@try {
		_URI = [URI copy];
		_method = OFHTTPRequestMethodGet;
		_protocolVersion.major = 1;
		_protocolVersion.minor = 1;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_URI release];
	[_headers release];

	[super dealloc];
}

- (void)setRemoteAddress: (const OFSocketAddress *)remoteAddress
{
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
		return &_remoteAddress;

	return NULL;
}

- (id)copy
{
	OFHTTPRequest *copy = [[OFHTTPRequest alloc] initWithURL: _URL];

	@try {
		copy->_method = _method;
		copy->_protocolVersion = _protocolVersion;
		copy.headers = _headers;
		copy.remoteAddress = self.remoteAddress;
	} @catch (id e) {







|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
		return &_remoteAddress;

	return NULL;
}

- (id)copy
{
	OFHTTPRequest *copy = [[OFHTTPRequest alloc] initWithURI: _URI];

	@try {
		copy->_method = _method;
		copy->_protocolVersion = _protocolVersion;
		copy.headers = _headers;
		copy.remoteAddress = self.remoteAddress;
	} @catch (id e) {
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
		return false;

	request = object;

	if (request->_method != _method ||
	    request->_protocolVersion.major != _protocolVersion.major ||
	    request->_protocolVersion.minor != _protocolVersion.minor ||
	    ![request->_URL isEqual: _URL] ||
	    ![request->_headers isEqual: _headers])
		return false;

	if (request.remoteAddress != self.remoteAddress &&
	    !OFSocketAddressEqual(request.remoteAddress, self.remoteAddress))
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAdd(&hash, _method);
	OFHashAdd(&hash, _protocolVersion.major);
	OFHashAdd(&hash, _protocolVersion.minor);
	OFHashAddHash(&hash, _URL.hash);
	OFHashAddHash(&hash, _headers.hash);
	if (_hasRemoteAddress)
		OFHashAddHash(&hash, OFSocketAddressHash(&_remoteAddress));

	OFHashFinalize(&hash);

	return hash;







|
















|
|
|
|







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
		return false;

	request = object;

	if (request->_method != _method ||
	    request->_protocolVersion.major != _protocolVersion.major ||
	    request->_protocolVersion.minor != _protocolVersion.minor ||
	    ![request->_URI isEqual: _URI] ||
	    ![request->_headers isEqual: _headers])
		return false;

	if (request.remoteAddress != self.remoteAddress &&
	    !OFSocketAddressEqual(request.remoteAddress, self.remoteAddress))
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&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);

	return hash;
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270

	if (_hasRemoteAddress)
		remoteAddress = OFSocketAddressString(&_remoteAddress);
	else
		remoteAddress = nil;

	ret = [[OFString alloc] initWithFormat:
	    @"<%@:\n\tURL = %@\n"
	    @"\tMethod = %s\n"
	    @"\tHeaders = %@\n"
	    @"\tRemote address = %@\n"
	    @">",
	    self.class, _URL, method, indentedHeaders, remoteAddress];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end







|




|






252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270

	if (_hasRemoteAddress)
		remoteAddress = OFSocketAddressString(&_remoteAddress);
	else
		remoteAddress = nil;

	ret = [[OFString alloc] initWithFormat:
	    @"<%@:\n\tURI = %@\n"
	    @"\tMethod = %s\n"
	    @"\tHeaders = %@\n"
	    @"\tRemote address = %@\n"
	    @">",
	    self.class, _URI, method, indentedHeaders, remoteAddress];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

Modified src/OFHTTPResponse.h from [b3d8087036] to [3c482db81b].

22
23
24
25
26
27
28



29
30
31
32
33
34
35
36
37
38



39
40
41
42
43





44
45
46
47
48
49
50
@class OFArray OF_GENERIC(ObjectType);

/**
 * @class OFHTTPResponse OFHTTPResponse.h ObjFW/OFHTTPResponse.h
 *
 * @brief A class for representing an HTTP request response as a stream.
 */



@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.



 */
@property (nonatomic) OFHTTPRequestProtocolVersion protocolVersion;

/**
 * @brief The protocol version of the HTTP request response as a string.





 */
@property (copy, nonatomic) OFString *protocolVersionString;

/**
 * @brief The status code of the response to the HTTP request.
 */
@property (nonatomic) short statusCode;







>
>
>





<




>
>
>





>
>
>
>
>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@class OFArray OF_GENERIC(ObjectType);

/**
 * @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;

}

/**
 * @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.
 */
@property (nonatomic) short statusCode;

Modified src/OFHTTPServer.h from [a2fd4b8141] to [ba5fc5aae1].

102
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

139
140
141
142
143
144
145
	OFArray *_threadPool;
#endif
}

/**
 * @brief The host on which the HTTP server will listen.
 *
 * Setting this after @ref start has been called raises an
 * @ref OFAlreadyConnectedException.

 */
@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.

 */
@property (nonatomic) uint16_t port;

/**
 * @brief The delegate for the HTTP server.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFHTTPServerDelegate> delegate;

#ifdef OF_HAVE_THREADS
/**
 * @brief The number of threads the OFHTTPServer should use.
 *
 * 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.

 */
@property (nonatomic) size_t numberOfThreads;
#endif

/**
 * @brief The server name the server presents to clients.
 *







<
|
>






<
|
>


















<
|
>







102
103
104
105
106
107
108

109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
	OFArray *_threadPool;
#endif
}

/**
 * @brief The host on which the HTTP server will listen.
 *

 * @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.
 *

 * @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.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFHTTPServerDelegate> delegate;

#ifdef OF_HAVE_THREADS
/**
 * @brief The number of threads the OFHTTPServer should use.
 *
 * 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`.
 *

 * @throw OFAlreadyConnectedException The number of threads could not be set
 *				      because @ref start had already been called
 */
@property (nonatomic) size_t numberOfThreads;
#endif

/**
 * @brief The server name the server presents to clients.
 *
153
154
155
156
157
158
159


160
161
162
163
164
165
166
167
168
169
170
171
 *
 * @return A new HTTP server
 */
+ (instancetype)server;

/**
 * @brief Starts the HTTP server in the current thread's run loop.


 */
- (void)start;

/**
 * @brief Stops the HTTP server, meaning it will not accept any new incoming
 *	  connections, but still handle existing connections until they are
 *	  finished or timed out.
 */
- (void)stop;
@end

OF_ASSUME_NONNULL_END







>
>












153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
 *
 * @return A new HTTP server
 */
+ (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
 *	  connections, but still handle existing connections until they are
 *	  finished or timed out.
 */
- (void)stop;
@end

OF_ASSUME_NONNULL_END

Modified src/OFHTTPServer.m from [56dd1ea76e] to [e75cf57b42].

9
10
11
12
13
14
15


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 *
 * 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 <errno.h>
#include <stdlib.h>
#include <string.h>

#import "OFHTTPServer.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFNumber.h"
#import "OFSocket+Private.h"
#import "OFTCPSocket.h"
#import "OFThread.h"
#import "OFTimer.h"
#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"







>
>


















|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 *
 * 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_HTTP_SERVER_M

#include "config.h"

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#import "OFHTTPServer.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFNumber.h"
#import "OFSocket+Private.h"
#import "OFTCPSocket.h"
#import "OFThread.h"
#import "OFTimer.h"
#import "OFURI.h"

#import "OFAlreadyConnectedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
{
	OFString *method;
	OFMutableString *path;
	size_t pos;

	@try {
		OFString *version = [line
		    substringWithRange: OFRangeMake(line.length - 9, 9)];
		OFUnichar tmp;

		if (![version hasPrefix: @" HTTP/1."])
			return [self sendErrorAndClose: 505];

		tmp = [version characterAtIndex: 8];
		if (tmp < '0' || tmp > '9')







|







357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
{
	OFString *method;
	OFMutableString *path;
	size_t pos;

	@try {
		OFString *version = [line
		    substringWithRange: OFMakeRange(line.length - 9, 9)];
		OFUnichar tmp;

		if (![version hasPrefix: @" HTTP/1."])
			return [self sendErrorAndClose: 505];

		tmp = [version characterAtIndex: 8];
		if (tmp < '0' || tmp > '9')
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
	@try {
		_method = OFHTTPRequestMethodParseName(method);
	} @catch (OFInvalidArgumentException *e) {
		return [self sendErrorAndClose: 405];
	}

	@try {
		OFRange range = OFRangeMake(pos + 1, line.length - pos - 10);

		path = [[[line substringWithRange:
		    range] mutableCopy] autorelease];
	} @catch (OFOutOfRangeException *e) {
		return [self sendErrorAndClose: 400];
	}








|







384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
	@try {
		_method = OFHTTPRequestMethodParseName(method);
	} @catch (OFInvalidArgumentException *e) {
		return [self sendErrorAndClose: 405];
	}

	@try {
		OFRange range = OFMakeRange(pos + 1, line.length - pos - 10);

		path = [[[line substringWithRange:
		    range] mutableCopy] autorelease];
	} @catch (OFOutOfRangeException *e) {
		return [self sendErrorAndClose: 400];
	}

508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
			      date, _server.name];
	return false;
}

- (void)createResponse
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableURL *URL;
	OFHTTPRequest *request;
	OFHTTPServerResponse *response;
	size_t pos;

	[_timer invalidate];
	[_timer release];
	_timer = nil;

	if (_host == nil || _port == 0) {
		if (_HTTPMinorVersion > 0) {
			[self sendErrorAndClose: 400];
			return;
		}

		[_host release];
		_host = [_server.host copy];
		_port = [_server port];
	}

	URL = [OFMutableURL URL];
	URL.scheme = @"http";
	URL.host = _host;
	if (_port != 80)
		URL.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;
		} else
			URL.URLEncodedPath = _path;
	} @catch (OFInvalidFormatException *e) {
		objc_autoreleasePoolPop(pool);
		[self sendErrorAndClose: 400];
		return;
	}

	[URL makeImmutable];

	request = [OFHTTPRequest requestWithURL: URL];
	request.method = _method;
	request.protocolVersion =
	    (OFHTTPRequestProtocolVersion){ 1, _HTTPMinorVersion };
	request.headers = _headers;
	request.remoteAddress = _socket.remoteAddress;

	response = [[[OFHTTPServerResponse alloc]







|



















|
<
|

|









|
|

|






|

|







510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537

538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
			      date, _server.name];
	return false;
}

- (void)createResponse
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableURI *URI;
	OFHTTPRequest *request;
	OFHTTPServerResponse *response;
	size_t pos;

	[_timer invalidate];
	[_timer release];
	_timer = nil;

	if (_host == nil || _port == 0) {
		if (_HTTPMinorVersion > 0) {
			[self sendErrorAndClose: 400];
			return;
		}

		[_host release];
		_host = [_server.host copy];
		_port = [_server port];
	}

	URI = [OFMutableURI URIWithScheme: @"http"];

	URI.host = _host;
	if (_port != 80)
		URI.port = [OFNumber numberWithUnsignedShort: _port];

	@try {
		if ((pos = [_path rangeOfString: @"?"].location) !=
		    OFNotFound) {
			OFString *path, *query;

			path = [_path substringToIndex: pos];
			query = [_path substringFromIndex: pos + 1];

			URI.percentEncodedPath = path;
			URI.percentEncodedQuery = query;
		} else
			URI.percentEncodedPath = _path;
	} @catch (OFInvalidFormatException *e) {
		objc_autoreleasePoolPop(pool);
		[self sendErrorAndClose: 400];
		return;
	}

	[URI makeImmutable];

	request = [OFHTTPRequest requestWithURI: URI];
	request.method = _method;
	request.protocolVersion =
	    (OFHTTPRequestProtocolVersion){ 1, _HTTPMinorVersion };
	request.headers = _headers;
	request.remoteAddress = _socket.remoteAddress;

	response = [[[OFHTTPServerResponse alloc]

Modified src/OFHTTPURIHandler.h from [6b4755acdc] to [1fbd933246].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * 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







|



|



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * 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

Modified src/OFHTTPURIHandler.m from [c4a43aad17] to [9e6fa9d3fe].

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * 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







|




|
|
<



|









11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36
37
 * 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

Modified src/OFINICategory.h from [e194c2c9e1] to [8e0c968abf].

74
75
76
77
78
79
80


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95


96
97
98
99
100
101
102
103
104
105
106
107
108
109


110
111
112
113
114
115
116
117
118
119
120
121
122
123


124
125
126
127
128
129
130
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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


 */
- (long long)longLongForKey: (OFString *)key
	       defaultValue: (long long)defaultValue;

/**
 * @brief Returns the bool for the specified key or the specified default value
 *	  if it does not exist.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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


 */
- (bool)boolForKey: (OFString *)key defaultValue: (bool)defaultValue;

/**
 * @brief Returns the float for the specified key or the specified default
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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


 */
- (float)floatForKey: (OFString *)key defaultValue: (float)defaultValue;

/**
 * @brief Returns the double for the specified key or the specified default
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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


 */
- (double)doubleForKey: (OFString *)key defaultValue: (double)defaultValue;

/**
 * @brief Returns an array of strings for the specified multi-key, or an empty
 *	  array if the key does not exist.
 *







>
>















>
>














>
>














>
>







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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;

/**
 * @brief Returns the bool for the specified key or the specified default value
 *	  if it does not exist.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @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
 *	  array if the key does not exist.
 *

Modified src/OFINICategory.m from [0938799665] to [1db2d24df3].

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

	[mutableString replaceOccurrencesOfString: @"\\" withString: @"\\\\"];
	[mutableString replaceOccurrencesOfString: @"\f" withString: @"\\f"];
	[mutableString replaceOccurrencesOfString: @"\r" withString: @"\\r"];
	[mutableString replaceOccurrencesOfString: @"\n" withString: @"\\n"];
	[mutableString replaceOccurrencesOfString: @"\"" withString: @"\\\""];

	[mutableString prependString: @"\""];
	[mutableString appendString: @"\""];

	[mutableString makeImmutable];

	return mutableString;
}

static OFString *
unescapeString(OFString *string)
{
	OFMutableString *mutableString;

	if (![string hasPrefix: @"\""] || ![string hasSuffix: @"\""])
		return string;

	string = [string substringWithRange: OFRangeMake(1, string.length - 2)];
	mutableString = [[string mutableCopy] autorelease];

	[mutableString replaceOccurrencesOfString: @"\\f" withString: @"\f"];
	[mutableString replaceOccurrencesOfString: @"\\r" withString: @"\r"];
	[mutableString replaceOccurrencesOfString: @"\\n" withString: @"\n"];
	[mutableString replaceOccurrencesOfString: @"\\\"" withString: @"\""];
	[mutableString replaceOccurrencesOfString: @"\\\\" withString: @"\\"];







|















|







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

	[mutableString replaceOccurrencesOfString: @"\\" withString: @"\\\\"];
	[mutableString replaceOccurrencesOfString: @"\f" withString: @"\\f"];
	[mutableString replaceOccurrencesOfString: @"\r" withString: @"\\r"];
	[mutableString replaceOccurrencesOfString: @"\n" withString: @"\\n"];
	[mutableString replaceOccurrencesOfString: @"\"" withString: @"\\\""];

	[mutableString insertString: @"\"" atIndex: 0];
	[mutableString appendString: @"\""];

	[mutableString makeImmutable];

	return mutableString;
}

static OFString *
unescapeString(OFString *string)
{
	OFMutableString *mutableString;

	if (![string hasPrefix: @"\""] || ![string hasSuffix: @"\""])
		return string;

	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"];
	[mutableString replaceOccurrencesOfString: @"\\\"" withString: @"\""];
	[mutableString replaceOccurrencesOfString: @"\\\\" withString: @"\\"];

Modified src/OFINIFile.h from [de47c3e875] to [f35fd21473].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46




47
48
49
50
51
52
53
54
55
56
57




58
59
60
61
62
63
64
65
66
67
68
69




70
71
72
73
74
75
76
77
78
79
80




81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#import "OFObject.h"
#import "OFString.h"
#import "OFINICategory.h"

OF_ASSUME_NONNULL_BEGIN

@class OFMutableArray OF_GENERIC(ObjectType);
@class OFURL;

/**
 * @class OFINIFile OFINIFile.h ObjFW/OFINIFile.h
 *
 * @brief A class for reading, creating and modifying INI files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFINIFile: OFObject
{
	OFMutableArray OF_GENERIC(OFINICategory *) *_categories;
}

/**
 * @brief All categories in the INI file.
 */
@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
 *
 * @return A new, autoreleased OFINIFile with the contents of the specified file




 */
+ (instancetype)fileWithURL: (OFURL *)URL;

/**
 * @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 encoding The encoding of the specified file
 *
 * @return A new, autoreleased OFINIFile with the contents of the specified file




 */
+ (instancetype)fileWithURL: (OFURL *)URL 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
 *
 * @return An initialized OFINIFile with the contents of the specified file




 */
- (instancetype)initWithURL: (OFURL *)URL;

/**
 * @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 encoding The encoding of the specified file
 *
 * @return An initialized OFINIFile with the contents of the specified file




 */
- (instancetype)initWithURL: (OFURL *)URL
		   encoding: (OFStringEncoding)encoding
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns an @ref OFINICategory for the category with the specified
 *	  name.
 *
 * @param name The name of the category for which an @ref OFINICategory should
 *	       be returned
 *
 * @return An @ref OFINICategory for the category with the specified name
 */
- (OFINICategory *)categoryForName: (OFString *)name;

/**
 * @brief Writes the contents of the OFINIFile to a file.
 *
 * @param URL The URL of the file to write to
 */
- (void)writeToURL: (OFURL *)URL;

/**
 * @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 encoding The encoding to use
 */
- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding;
@end

OF_ASSUME_NONNULL_END







|




















|


>
>
>
>

|





|

<

>
>
>
>

|







|


>
>
>
>

|





|

<

>
>
>
>

|

















|

|





|


|



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#import "OFObject.h"
#import "OFString.h"
#import "OFINICategory.h"

OF_ASSUME_NONNULL_BEGIN

@class OFMutableArray OF_GENERIC(ObjectType);
@class OFURI;

/**
 * @class OFINIFile OFINIFile.h ObjFW/OFINIFile.h
 *
 * @brief A class for reading, creating and modifying INI files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFINIFile: OFObject
{
	OFMutableArray OF_GENERIC(OFINICategory *) *_categories;
}

/**
 * @brief All categories in the INI file.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFINICategory *) *categories;

/**
 * @brief Creates a new OFINIFile with the contents of the specified file.
 *
 * @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)fileWithURI: (OFURI *)URI;

/**
 * @brief Creates a new OFINIFile with the contents of the specified file in
 *	  the specified encoding.
 *
 * @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)fileWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFINIFile with the contents of the
 *	  specified file.
 *
 * @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)initWithURI: (OFURI *)URI;

/**
 * @brief Initializes an already allocated OFINIFile with the contents of the
 *	  specified file in the specified encoding.
 *
 * @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)initWithURI: (OFURI *)URI
		   encoding: (OFStringEncoding)encoding
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns an @ref OFINICategory for the category with the specified
 *	  name.
 *
 * @param name The name of the category for which an @ref OFINICategory should
 *	       be returned
 *
 * @return An @ref OFINICategory for the category with the specified name
 */
- (OFINICategory *)categoryForName: (OFString *)name;

/**
 * @brief Writes the contents of the OFINIFile to a file.
 *
 * @param URI The URI of the file to write to
 */
- (void)writeToURI: (OFURI *)URI;

/**
 * @brief Writes the contents of the OFINIFile to a file in the specified
 *	  encoding.
 *
 * @param URI The URI of the file to write to
 * @param encoding The encoding to use
 */
- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding;
@end

OF_ASSUME_NONNULL_END

Modified src/OFINIFile.m from [5568ad8169] to [7850530cf1].

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

#import "OFINIFile.h"
#import "OFArray.h"
#import "OFINICategory+Private.h"
#import "OFINICategory.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFURL.h"
#import "OFURLHandler.h"

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"

OF_DIRECT_MEMBERS
@interface OFINIFile ()
- (void)of_parseURL: (OFURL *)URL encoding: (OFStringEncoding)encoding;
@end

static bool
isWhitespaceLine(OFString *line)
{
	const char *cString = line.UTF8String;
	size_t length = line.UTF8StringLength;

	for (size_t i = 0; i < length; i++)
		if (!OFASCIIIsSpace(cString[i]))
			return false;

	return true;
}

@implementation OFINIFile
@synthesize categories = _categories;

+ (instancetype)fileWithURL: (OFURL *)URL
{
	return [[[self alloc] initWithURL: URL] autorelease];
}

+ (instancetype)fileWithURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithURL: URL encoding: encoding] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL
{
	return [self initWithURL: URL encoding: OFStringEncodingAutodetect];
}

- (instancetype)initWithURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_categories = [[OFMutableArray alloc] init];

		[self of_parseURL: URL encoding: encoding];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







|
|






|


















|

|


|

|







|

|


|






|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

#import "OFINIFile.h"
#import "OFArray.h"
#import "OFINICategory+Private.h"
#import "OFINICategory.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFURI.h"
#import "OFURIHandler.h"

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"

OF_DIRECT_MEMBERS
@interface OFINIFile ()
- (void)of_parseURI: (OFURI *)URI encoding: (OFStringEncoding)encoding;
@end

static bool
isWhitespaceLine(OFString *line)
{
	const char *cString = line.UTF8String;
	size_t length = line.UTF8StringLength;

	for (size_t i = 0; i < length; i++)
		if (!OFASCIIIsSpace(cString[i]))
			return false;

	return true;
}

@implementation OFINIFile
@synthesize categories = _categories;

+ (instancetype)fileWithURI: (OFURI *)URI
{
	return [[[self alloc] initWithURI: URI] autorelease];
}

+ (instancetype)fileWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithURI: URI encoding: encoding] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURI: (OFURI *)URI
{
	return [self initWithURI: URI encoding: OFStringEncodingAutodetect];
}

- (instancetype)initWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_categories = [[OFMutableArray alloc] init];

		[self of_parseURI: URI encoding: encoding];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
106
107
108
109
110
111
112
113
114
115
116
117
118
119



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
	[_categories addObject: category];

	objc_autoreleasePoolPop(pool);

	return category;
}

- (void)of_parseURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *file;
	OFINICategory *category = nil;
	OFString *line;




	@try {
		file = [[OFURLHandler handlerForURL: URL] openItemAtURL: URL
								   mode: @"r"];
	} @catch (OFOpenItemFailedException *e) {
		/* Handle missing file like an empty file */
		if (e.errNo == ENOENT)
			return;

		@throw e;
	}

	while ((line = [file readLineWithEncoding: encoding]) != nil) {
		if (isWhitespaceLine(line))
			continue;

		if ([line hasPrefix: @"["]) {
			OFString *categoryName;

			if (![line hasSuffix: @"]"])
				@throw [OFInvalidFormatException exception];

			categoryName = [line substringWithRange:
			    OFRangeMake(1, line.length - 2)];
			category = [[[OFINICategory alloc]
			    of_initWithName: categoryName] autorelease];
			[_categories addObject: category];
		} else {
			if (category == nil)
				@throw [OFInvalidFormatException exception];

			[category of_parseLine: line];
		}
	}

	objc_autoreleasePoolPop(pool);
}

- (void)writeToURL: (OFURL *)URL
{
	[self writeToURL: URL encoding: OFStringEncodingUTF8];
}

- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *file = [[OFURLHandler handlerForURL: URL]
	    openItemAtURL: URL
		     mode: @"w"];
	bool first = true;

	for (OFINICategory *category in _categories)
		if ([category of_writeToStream: file
				      encoding: encoding
					 first: first])
			first = false;







|






>
>
>

<
|



















|














|

|


|


|
<
<







106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167


168
169
170
171
172
173
174
	[_categories addObject: category];

	objc_autoreleasePoolPop(pool);

	return category;
}

- (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 = [OFURIHandler openItemAtURI: URI mode: @"r"];
	} @catch (OFOpenItemFailedException *e) {
		/* Handle missing file like an empty file */
		if (e.errNo == ENOENT)
			return;

		@throw e;
	}

	while ((line = [file readLineWithEncoding: encoding]) != nil) {
		if (isWhitespaceLine(line))
			continue;

		if ([line hasPrefix: @"["]) {
			OFString *categoryName;

			if (![line hasSuffix: @"]"])
				@throw [OFInvalidFormatException exception];

			categoryName = [line substringWithRange:
			    OFMakeRange(1, line.length - 2)];
			category = [[[OFINICategory alloc]
			    of_initWithName: categoryName] autorelease];
			[_categories addObject: category];
		} else {
			if (category == nil)
				@throw [OFInvalidFormatException exception];

			[category of_parseLine: line];
		}
	}

	objc_autoreleasePoolPop(pool);
}

- (void)writeToURI: (OFURI *)URI
{
	[self writeToURI: URI encoding: OFStringEncodingUTF8];
}

- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"w"];


	bool first = true;

	for (OFINICategory *category in _categories)
		if ([category of_writeToStream: file
				      encoding: encoding
					 first: first])
			first = false;

Modified src/OFINIFileSettings.h from [e6622a40c7] to [125e92a3e8].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#import "OFSettings.h"

OF_ASSUME_NONNULL_BEGIN

@class OFINIFile;
@class OFString;
@class OFURL;

@interface OFINIFileSettings: OFSettings
{
	OFURL *_fileURL;
	OFINIFile *_INIFile;
}
@end

OF_ASSUME_NONNULL_END







|



|





15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#import "OFSettings.h"

OF_ASSUME_NONNULL_BEGIN

@class OFINIFile;
@class OFString;
@class OFURI;

@interface OFINIFileSettings: OFSettings
{
	OFURI *_fileURI;
	OFINIFile *_INIFile;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFINIFileSettings.m from [06a12ac82c] to [b3d1a39e2b].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "config.h"

#import "OFINIFileSettings.h"
#import "OFArray.h"
#import "OFINIFile.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURL.h"

@implementation OFINIFileSettings
- (instancetype)initWithApplicationName: (OFString *)applicationName
{
	self = [super initWithApplicationName: applicationName];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFString *fileName;

		fileName = [applicationName stringByAppendingString: @".ini"];
		_fileURL = [[[OFSystemInfo userConfigURL]
		    URLByAppendingPathComponent: fileName] copy];
		_INIFile = [[OFINIFile alloc] initWithURL: _fileURL];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_fileURL release];
	[_INIFile release];

	[super dealloc];
}

- (void)of_getCategory: (OFString **)category
		andKey: (OFString **)key







|











|
|
|












|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "config.h"

#import "OFINIFileSettings.h"
#import "OFArray.h"
#import "OFINIFile.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURI.h"

@implementation OFINIFileSettings
- (instancetype)initWithApplicationName: (OFString *)applicationName
{
	self = [super initWithApplicationName: applicationName];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFString *fileName;

		fileName = [applicationName stringByAppendingString: @".ini"];
		_fileURI = [[[OFSystemInfo userConfigURI]
		    URIByAppendingPathComponent: fileName] copy];
		_INIFile = [[OFINIFile alloc] initWithURI: _fileURI];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_fileURI release];
	[_INIFile release];

	[super dealloc];
}

- (void)of_getCategory: (OFString **)category
		andKey: (OFString **)key
239
240
241
242
243
244
245
246
247
248
	[[_INIFile categoryForName: category] removeValueForKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)save
{
	[_INIFile writeToURL: _fileURL];
}
@end







|


239
240
241
242
243
244
245
246
247
248
	[[_INIFile categoryForName: category] removeValueForKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)save
{
	[_INIFile writeToURI: _fileURI];
}
@end

Modified src/OFIPXSocket.h from [a2ca3d4d82] to [8dc615311a].

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 *
 * @brief A class which provides methods to create and use IPX sockets.
 *
 * 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
 * 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,
 *	    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







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 *
 * @brief A class which provides methods to create and use IPX sockets.
 *
 * 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 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,
 *	    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
63
64
65
66
67
68
69



70
71
72
73


74
75




76
77
78
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFIPXSocketDelegate> delegate;

/**
 * @brief Bind the socket to the specified network, node and port with the
 *	  specified packet type.
 *



 * @param port The port (sometimes called socket number) to bind to. 0 means to
 *	       pick one and return it.
 * @param packetType The packet type to use on the socket
 * @return The address on which this socket can be reached


 */
- (OFSocketAddress)bindToPort: (uint16_t)port packetType: (uint8_t)packetType;




@end

OF_ASSUME_NONNULL_END







>
>
>

|


>
>

|
>
>
>
>



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFIPXSocketDelegate> delegate;

/**
 * @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 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)
    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

Modified src/OFIPXSocket.m from [f0059481b5] to [1dc137a6b3].

22
23
24
25
26
27
28
29
30
31
32
33



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

93


94
95
96
97
98
99
100
101
102

103


104
105
106
107
108
109
110
111
112
113
#endif

#import "OFIPXSocket.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"

@implementation OFIPXSocket
@dynamic delegate;




- (OFSocketAddress)bindToPort: (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);

#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()];

	_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 [OFBindFailedException exceptionWithPort: port


						     packetType: packetType
							 socket: self
							  errNo: errNo];
	}

	memset(&address, 0, sizeof(address));
	address.family = OFSocketAddressFamilyIPX;
	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 [OFBindFailedException exceptionWithPort: 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];
	}

	return address;
}

#ifndef OF_WINDOWS
- (void)sendBuffer: (const void *)buffer







|




>
>
>
|

<









|









|
|
>
>
|
|
|















>
|
>
>
|
|
|













>
|
>
>
|
|
|






>
|
>
>
|
|
|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#endif

#import "OFIPXSocket.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindIPXSocketFailedException.h"

@implementation OFIPXSocket
@dynamic delegate;

- (OFSocketAddress)bindToNetwork: (uint32_t)network
			    node: (const unsigned char [IPX_NODE_LEN])node
			    port: (uint16_t)port
		      packetType: (uint8_t)packetType
{

	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(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 [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)
		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 [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);

	if (OFGetSockName(_socket, (struct sockaddr *)&address.sockaddr,
	    &address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@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 [OFBindIPXSocketFailedException
		    exceptionWithNetwork: network
				    node: node
				    port: port
			      packetType: packetType
				  socket: self
				   errNo: EAFNOSUPPORT];
	}

	return address;
}

#ifndef OF_WINDOWS
- (void)sendBuffer: (const void *)buffer

Modified src/OFInvocation.h from [7cc36c2eaa] to [76f156403c].

34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53
@class OFMutableData;

/**
 * @class OFInvocation OFInvocation.h ObjFW/OFInvocation.h
 *
 * @brief A class for storing and accessing invocations, and invoking them.
 */

@interface OFInvocation: OFObject
{
	OFMethodSignature *_methodSignature;
	OFMutableArray OF_GENERIC(OFMutableData *) *_arguments;
	OFMutableData *_returnValue;
	OF_RESERVE_IVARS(OFInvocation, 4)
}

/**
 * @brief The method signature for the invocation.
 */
@property (readonly, nonatomic) OFMethodSignature *methodSignature;








>





<







34
35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
@class OFMutableData;

/**
 * @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;

}

/**
 * @brief The method signature for the invocation.
 */
@property (readonly, nonatomic) OFMethodSignature *methodSignature;

Modified src/OFKernelEventObserver.h from [21aebcf8a0] to [81efdafa0c].

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 */
@protocol OFKernelEventObserverDelegate <OFObject>
@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
 *	 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.
 *
 * @param object The object which did become ready for reading







|
|







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 */
@protocol OFKernelEventObserverDelegate <OFObject>
@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::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.
 *
 * @param object The object which did become ready for reading
164
165
166
167
168
169
170


171
172
173
174
175
176
177
178
179
180


181
182
183
184
185
186
187
188
189
190


191
192
193
194
195
196
197
198
199
200


201
202
203
204
205



206
207
208
209
210
211
212
213


214
215
216
217
218
219
220
221


222
223
224
225
226
227
228
 * This is also used to observe a listening socket for incoming connections,
 * which then triggers a read event for the observed object.
 *
 * 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


 */
- (void)addObjectForReading: (id <OFReadyForReadingObserving>)object;

/**
 * @brief Adds an object to observe for writing.
 *
 * 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


 */
- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object;

/**
 * @brief Removes an object to observe for reading.
 *
 * 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


 */
- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object;

/**
 * @brief Removes an object to observe for writing.
 *
 * 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


 */
- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object;

/**
 * @brief Observes all objects and blocks until an event happens on an object.



 */
- (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


 */
- (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


 */
- (void)observeUntilDate: (OFDate *)date;

/**
 * @brief Cancels the currently blocking observe call.
 *
 * This is the only method that can and should be called from another thread







>
>










>
>










>
>










>
>





>
>
>








>
>








>
>







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
 * This is also used to observe a listening socket for incoming connections,
 * which then triggers a read event for the observed object.
 *
 * 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 <OFReadyForReadingObserving>)object;

/**
 * @brief Adds an object to observe for writing.
 *
 * 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 <OFReadyForWritingObserving>)object;

/**
 * @brief Removes an object to observe for reading.
 *
 * 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 <OFReadyForReadingObserving>)object;

/**
 * @brief Removes an object to observe for writing.
 *
 * 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 <OFReadyForWritingObserving>)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.
 *
 * This is the only method that can and should be called from another thread

Modified src/OFKeyValueCoding.h from [06cc2c1de6] to [3568d90874].

29
30
31
32
33
34
35


36
37
38
39
40
41
42
43


44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62



63
64
65
66
67
68
69
70



71
72
73
74
75
76
77
78
79
80
81

82
83
84
85
86
87
88
 */
@protocol OFKeyValueCoding
/**
 * @brief Returns the value for the specified key.
 *
 * @param key The key of the value to return
 * @return The value for the specified key


 */
- (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


 */
- (nullable id)valueForKeyPath: (OFString *)keyPath;

/**
 * @brief This is called by @ref valueForKey: if the specified key does not
 *	  exist.
 *
 * 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

 */
- (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



 */
- (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



 */
- (void)setValue: (nullable id)value forKeyPath: (OFString *)keyPath;

/**
 * @brief This is called by @ref setValue:forKey: if the specified key does not
 *	  exist.
 *
 * 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

 */
-  (void)setValue: (nullable id)value forUndefinedKey: (OFString *)key;

/**
 * @brief This is called by @ref setValue:forKey: if the specified key is a
 *	  scalar, but the value specified is `nil`.
 *







>
>








>
>











>








>
>
>








>
>
>











>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
 */
@protocol OFKeyValueCoding
/**
 * @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
 *	  exist.
 *
 * 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
 *	  exist.
 *
 * 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
 *	  scalar, but the value specified is `nil`.
 *

Modified src/OFKqueueKernelEventObserver.m from [bcb05813e9] to [ef6117c8da].

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <sys/event.h>
#include <sys/time.h>

#import "OFKqueueKernelEventObserver.h"
#import "OFArray.h"

#import "OFInitializationFailedException.h"
#import "OFObserveFailedException.h"
#import "OFOutOfRangeException.h"

#define eventListSize 64

@implementation OFKqueueKernelEventObserver
- (instancetype)init
{







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <sys/event.h>
#include <sys/time.h>

#import "OFKqueueKernelEventObserver.h"
#import "OFArray.h"

#import "OFInitializationFailedException.h"
#import "OFObserveKernelEventsFailedException.h"
#import "OFOutOfRangeException.h"

#define eventListSize 64

@implementation OFKqueueKernelEventObserver
- (instancetype)init
{
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
	/*
	 * Ugly hack required for NetBSD: NetBSD used `intptr_t` for udata, but
	 * 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];

	[super addObjectForReading: object];
}

- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	event.ident = object.fileDescriptorForWriting;
	event.filter = EVFILT_WRITE;
	event.flags = EV_ADD;
	/*
	 * Ugly hack required for NetBSD: NetBSD used `intptr_t` for udata, but
	 * 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];

	[super addObjectForWriting: object];
}

- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	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];

	[super removeObjectForReading: object];
}

- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	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];

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (OFTimeInterval)timeInterval
{
	struct timespec timeout;
	struct kevent eventList[eventListSize];
	int events;

	if ([self of_processReadBuffers])
		return;

	timeout.tv_sec = (time_t)timeInterval;
	timeout.tv_nsec = (long)((timeInterval - timeout.tv_sec) * 1000000000);

	events = kevent(_kernelQueue, NULL, 0, eventList, eventListSize,
	    (timeInterval != -1 ? &timeout : NULL));

	if (events < 0)

		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: errno];

	for (int i = 0; i < events; i++) {
		void *pool;

		if (eventList[i].flags & EV_ERROR)
			@throw [OFObserveFailedException
			    exceptionWithObserver: self
					    errNo: (int)eventList[i].data];

		if (eventList[i].ident == (uintptr_t)_cancelFD[0]) {
			char buffer;

			assert(eventList[i].filter == EVFILT_READ);







>
|
|



















>
|
|














>
|
|














>
|
|




















>
|
|





|







90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
	/*
	 * Ugly hack required for NetBSD: NetBSD used `intptr_t` for udata, but
	 * switched this to `void *` in NetBSD 10.
	 */
	event.udata = (__typeof__(event.udata))object;

	if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
		@throw [OFObserveKernelEventsFailedException
		    exceptionWithObserver: self
				    errNo: errno];

	[super addObjectForReading: object];
}

- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	event.ident = object.fileDescriptorForWriting;
	event.filter = EVFILT_WRITE;
	event.flags = EV_ADD;
	/*
	 * Ugly hack required for NetBSD: NetBSD used `intptr_t` for udata, but
	 * switched this to `void *` in NetBSD 10.
	 */
	event.udata = (__typeof__(event.udata))object;

	if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
		@throw [OFObserveKernelEventsFailedException
		    exceptionWithObserver: self
				    errNo: errno];

	[super addObjectForWriting: object];
}

- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	event.ident = object.fileDescriptorForReading;
	event.filter = EVFILT_READ;
	event.flags = EV_DELETE;

	if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
		@throw [OFObserveKernelEventsFailedException
		    exceptionWithObserver: self
				    errNo: errno];

	[super removeObjectForReading: object];
}

- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	event.ident = object.fileDescriptorForWriting;
	event.filter = EVFILT_WRITE;
	event.flags = EV_DELETE;

	if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
		@throw [OFObserveKernelEventsFailedException
		    exceptionWithObserver: self
				    errNo: errno];

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (OFTimeInterval)timeInterval
{
	struct timespec timeout;
	struct kevent eventList[eventListSize];
	int events;

	if ([self of_processReadBuffers])
		return;

	timeout.tv_sec = (time_t)timeInterval;
	timeout.tv_nsec = (long)((timeInterval - timeout.tv_sec) * 1000000000);

	events = kevent(_kernelQueue, NULL, 0, eventList, eventListSize,
	    (timeInterval != -1 ? &timeout : NULL));

	if (events < 0)
		@throw [OFObserveKernelEventsFailedException
		    exceptionWithObserver: self
				    errNo: errno];

	for (int i = 0; i < events; i++) {
		void *pool;

		if (eventList[i].flags & EV_ERROR)
			@throw [OFObserveKernelEventsFailedException
			    exceptionWithObserver: self
					    errNo: (int)eventList[i].data];

		if (eventList[i].ident == (uintptr_t)_cancelFD[0]) {
			char buffer;

			assert(eventList[i].filter == EVFILT_READ);

Modified src/OFLHAArchive.h from [eaf15d177e] to [038ec719a6].

17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35




36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77










78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122




123
124
125












126
127
128
129
130
131
132
#import "OFKernelEventObserver.h"
#import "OFLHAArchiveEntry.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

@class OFStream;


/**
 * @class OFLHAArchive OFLHAArchive.h ObjFW/OFLHAArchive.h
 *
 * @brief A class for accessing and manipulating LHA files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFLHAArchive: OFObject
{
	OFStream *_stream;
	uint_least8_t _mode;
	OFStringEncoding _encoding;




	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.
 * @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)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 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)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated 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.
 * @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)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 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

/**
 * @brief Returns the next entry from the LHA archive or `nil` if all entries
 *	  have been read.
 *
 * @note This is only available in read mode.
 *
 * @warning Calling @ref nextEntry will invalidate all streams returned by
 *	    @ref streamForReadingCurrentEntry or
 *	    @ref streamForWritingEntry:! Reading from or writing to an
 *	    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




 */
- (nullable OFLHAArchiveEntry *)nextEntry;













/**
 * @brief Returns a stream for writing the specified entry.
 *
 * @note This is only available in write and append mode.
 *
 * @note The uncompressed size, compressed size and CRC16 of the specified
 *	 entry are ignored.







>












>
>
>
>








<
<
<
<
<
<
<
<
<
<












<



|





|
|
>
>
>
>
>
>
>
>
>
>

















<




|





|
<















>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48










49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#import "OFKernelEventObserver.h"
#import "OFLHAArchiveEntry.h"
#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.
 */
OF_SUBCLASSING_RESTRICTED
@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 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.
 * @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)archiveWithStream: (OFStream *)stream mode: (OFString *)mode;


/**
 * @brief Creates a new OFLHAArchive object with the specified 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)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
 *	  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.
 * @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)initWithStream: (OFStream *)stream
			  mode: (OFString *)mode OF_DESIGNATED_INITIALIZER;


/**
 * @brief Initializes an already allocated OFLHAArchive object with the
 *	  specified 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)initWithURI: (OFURI *)URI mode: (OFString *)mode;


/**
 * @brief Returns the next entry from the LHA archive or `nil` if all entries
 *	  have been read.
 *
 * @note This is only available in read mode.
 *
 * @warning Calling @ref nextEntry will invalidate all streams returned by
 *	    @ref streamForReadingCurrentEntry or
 *	    @ref streamForWritingEntry:! Reading from or writing to an
 *	    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.
 *
 * @note The uncompressed size, compressed size and CRC16 of the specified
 *	 entry are ignored.
143
144
145
146
147
148
149


150
151
152
153
154
 * @param entry The entry for which a stream for writing should be returned
 * @return A stream for writing the specified entry
 */
- (OFStream *)streamForWritingEntry: (OFLHAArchiveEntry *)entry;

/**
 * @brief Closes the OFLHAArchive.


 */
- (void)close;
@end

OF_ASSUME_NONNULL_END







>
>





161
162
163
164
165
166
167
168
169
170
171
172
173
174
 * @param entry The entry for which a stream for writing should be returned
 * @return A stream for writing the specified entry
 */
- (OFStream *)streamForWritingEntry: (OFLHAArchiveEntry *)entry;

/**
 * @brief Closes the OFLHAArchive.
 *
 * @throw OFNotOpenException The archive is not open
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Modified src/OFLHAArchive.m from [5dc375ab00] to [fa04eb799a].

9
10
11
12
13
14
15


16
17
18
19
20
21
22
23
24
25
26
27

28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56

57
58
59
60
61
62
63

64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90




91
92
93
94
95
96
97
 *
 * 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 <errno.h>

#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"
#import "OFCRC16.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFLHADecompressingStream.h"

#import "OFStream.h"
#import "OFSeekableStream.h"
#import "OFString.h"


#import "OFChecksumMismatchException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFWriteFailedException.h"

enum {
	modeRead,
	modeWrite,
	modeAppend
};

OF_DIRECT_MEMBERS
@interface OFLHAArchiveFileReadStream: OFStream <OFReadyForReadingObserving>
{

	OFStream *_stream, *_decompressedStream;
	OFLHAArchiveEntry *_entry;
	uint32_t _toRead, _bytesConsumed;
	uint16_t _CRC16;
	bool _atEndOfStream, _skipped;
}

- (instancetype)of_initWithStream: (OFStream *)stream

			    entry: (OFLHAArchiveEntry *)entry;
- (void)of_skip;
@end

OF_DIRECT_MEMBERS
@interface OFLHAArchiveFileWriteStream: OFStream <OFReadyForWritingObserving>
{

	OFMutableLHAArchiveEntry *_entry;
	OFStringEncoding _encoding;
	OFSeekableStream *_stream;
	OFFileOffset _headerOffset;
	uint32_t _bytesWritten;
	uint16_t _CRC16;
}


- (instancetype)of_initWithStream: (OFSeekableStream *)stream
			    entry: (OFLHAArchiveEntry *)entry
			 encoding: (OFStringEncoding)encoding;
@end

@implementation OFLHAArchive
@synthesize encoding = _encoding;

+ (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
{
	return [[[self alloc] initWithPath: path mode: mode] autorelease];
}
#endif





- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFStream *)stream mode: (OFString *)mode







>
>







|
<
|
<

>

|
|
>


















>


|




|
>
|






>



|




>
|
|
|










<
|

|

|
>
>
>
>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 *
 * 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_LHA_ARCHIVE_M

#include "config.h"

#include <errno.h>

#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"
#import "OFArchiveURIHandler.h"

#import "OFCRC16.h"

#import "OFLHADecompressingStream.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"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFWriteFailedException.h"

enum {
	modeRead,
	modeWrite,
	modeAppend
};

OF_DIRECT_MEMBERS
@interface OFLHAArchiveFileReadStream: OFStream <OFReadyForReadingObserving>
{
	OFLHAArchive *_archive;
	OFStream *_stream, *_decompressedStream;
	OFLHAArchiveEntry *_entry;
	unsigned long long _toRead;
	uint16_t _CRC16;
	bool _atEndOfStream, _skipped;
}

- (instancetype)of_initWithArchive: (OFLHAArchive *)archive
			    stream: (OFStream *)stream
			     entry: (OFLHAArchiveEntry *)entry;
- (void)of_skip;
@end

OF_DIRECT_MEMBERS
@interface OFLHAArchiveFileWriteStream: OFStream <OFReadyForWritingObserving>
{
	OFLHAArchive *_archive;
	OFMutableLHAArchiveEntry *_entry;
	OFStringEncoding _encoding;
	OFSeekableStream *_stream;
	OFStreamOffset _headerOffset;
	uint32_t _bytesWritten;
	uint16_t _CRC16;
}

- (instancetype)of_initWithArchive: (OFLHAArchive *)archive
			    stream: (OFSeekableStream *)stream
			     entry: (OFLHAArchiveEntry *)entry
			  encoding: (OFStringEncoding)encoding;
@end

@implementation OFLHAArchive
@synthesize encoding = _encoding;

+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream mode: mode] autorelease];
}


+ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode
{
	return [[[self alloc] initWithURI: URI mode: mode] autorelease];
}

+ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI
{
	return OFArchiveURIHandlerURIForFileInArchive(@"lha", path, URI);
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFStream *)stream mode: (OFString *)mode
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

133
134

135
136
137
138




139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154


155
156
157
158
159
160
161
162
163
164
165



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211


212




213
214
215
216
217
218
219

		if ((_mode == modeWrite || _mode == modeAppend) &&
		    ![_stream isKindOfClass: [OFSeekableStream class]])
			@throw [OFInvalidArgumentException exception];

		if (_mode == modeAppend)
			[(OFSeekableStream *)_stream seekToOffset: 0
							   whence: SEEK_END];

		_encoding = OFStringEncodingISO8859_1;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	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];





	@try {
		self = [self initWithStream: file mode: mode];
	} @finally {
		[file release];
	}


	return self;
}
#endif

- (void)dealloc
{
	if (_stream != nil)
		[self close];



	[super dealloc];
}

- (OFLHAArchiveEntry *)nextEntry
{
	OFLHAArchiveEntry *entry;
	char header[21];
	size_t headerLen;

	if (_mode != modeRead)
		@throw [OFInvalidArgumentException exception];




	[(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)
				return nil;

			if (headerLen == 1 && header[0] == 0)
				return nil;

			@throw [OFTruncatedDataException exception];
		}

		headerLen += [_stream readIntoBuffer: header + headerLen
					      length: 21 - headerLen];
	}

	entry = [[[OFLHAArchiveEntry alloc]
	    of_initWithHeader: header
		       stream: _stream
		     encoding: _encoding] autorelease];

	_lastReturnedStream = [[OFLHAArchiveFileReadStream alloc]
	    of_initWithStream: _stream
			entry: entry];

	return entry;
}

- (OFStream *)streamForReadingCurrentEntry
{
	if (_mode != modeRead)
		@throw [OFInvalidArgumentException exception];

	if (_lastReturnedStream == nil)
		@throw [OFInvalidArgumentException exception];

	return [[(OFLHAArchiveFileReadStream *)_lastReturnedStream


	    retain] autorelease];




}

- (OFStream *)streamForWritingEntry: (OFLHAArchiveEntry *)entry
{
	OFString *compressionMethod;

	if (_mode != modeWrite && _mode != modeAppend)







|










<
|

>
|

>
|
|
|
<
>
>
>
>
|
|
|
<
<
|
>



<






>
>





<





>
>
>







<

















|


|

<
<
<
<
|







|


|
>
>
|
>
>
>
>







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154


155
156
157
158
159

160
161
162
163
164
165
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209




210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235

		if ((_mode == modeWrite || _mode == modeAppend) &&
		    ![_stream isKindOfClass: [OFSeekableStream class]])
			@throw [OFInvalidArgumentException exception];

		if (_mode == modeAppend)
			[(OFSeekableStream *)_stream seekToOffset: 0
							   whence: OFSeekEnd];

		_encoding = OFStringEncodingISO8859_1;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}


- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *stream;

	@try {
		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;
}


- (void)dealloc
{
	if (_stream != nil)
		[self close];

	[_currentEntry release];

	[super dealloc];
}

- (OFLHAArchiveEntry *)nextEntry
{

	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 = nil;

	for (headerLen = 0; headerLen < 21;) {
		if (_stream.atEndOfStream) {
			if (headerLen == 0)
				return nil;

			if (headerLen == 1 && header[0] == 0)
				return nil;

			@throw [OFTruncatedDataException exception];
		}

		headerLen += [_stream readIntoBuffer: header + headerLen
					      length: 21 - headerLen];
	}

	_currentEntry= [[OFLHAArchiveEntry alloc]
	    of_initWithHeader: header
		       stream: _stream
		     encoding: _encoding];





	return _currentEntry;
}

- (OFStream *)streamForReadingCurrentEntry
{
	if (_mode != modeRead)
		@throw [OFInvalidArgumentException exception];

	if (_currentEntry == nil)
		@throw [OFInvalidArgumentException exception];

	_lastReturnedStream = [[[OFLHAArchiveFileReadStream alloc]
	    of_initWithArchive: self
			stream: _stream
			 entry: _currentEntry] autorelease];
	[_currentEntry release];
	_currentEntry = nil;

	return _lastReturnedStream;
}

- (OFStream *)streamForWritingEntry: (OFLHAArchiveEntry *)entry
{
	OFString *compressionMethod;

	if (_mode != modeWrite && _mode != modeAppend)
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272

273
274
275
276
277
278
279
								 object: self];

	@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];

	return [[(OFLHAArchiveFileWriteStream *)_lastReturnedStream
	    retain] autorelease];
}

- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	@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
{
	self = [super init];

	@try {
		OFString *compressionMethod;


		_stream = [stream retain];

		compressionMethod = entry.compressionMethod;

		if ([compressionMethod isEqual: @"-lh4-"] ||
		    [compressionMethod isEqual: @"-lh5-"])
			_decompressedStream = [[OFLHADecompressingStream alloc]







<


|
>
|
|
|

|
<












<








|
>
|






>







243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258

259
260
261
262
263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
								 object: self];

	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}

	_lastReturnedStream = nil;

	_lastReturnedStream = [[[OFLHAArchiveFileWriteStream alloc]
	    of_initWithArchive: self
			stream: (OFSeekableStream *)_stream
			 entry: entry
		      encoding: _encoding] autorelease];

	return _lastReturnedStream;

}

- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}

	_lastReturnedStream = nil;

	[_stream release];
	_stream = nil;
}
@end

@implementation OFLHAArchiveFileReadStream
- (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-"] ||
		    [compressionMethod isEqual: @"-lh5-"])
			_decompressedStream = [[OFLHADecompressingStream alloc]
306
307
308
309
310
311
312





313
314
315
316
317
318
319
- (void)dealloc
{
	if (_stream != nil || _decompressedStream != nil)
		[self close];

	[_entry release];






	[super dealloc];
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];







>
>
>
>
>







322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
- (void)dealloc
{
	if (_stream != nil || _decompressedStream != nil)
		[self close];

	[_entry release];

	if (_archive->_lastReturnedStream == self)
		_archive->_lastReturnedStream = nil;

	[_archive release];

	[super dealloc];
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
	if (_atEndOfStream)
		return 0;

	if (_stream.atEndOfStream && !_decompressedStream.hasDataInReadBuffer)
		@throw [OFTruncatedDataException exception];

	if (length > _toRead)
		length = _toRead;

	ret = [_decompressedStream readIntoBuffer: buffer length: length];

	_toRead -= ret;
	_CRC16 = OFCRC16(_CRC16, buffer, ret);

	if (_toRead == 0) {







|







352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
	if (_atEndOfStream)
		return 0;

	if (_stream.atEndOfStream && !_decompressedStream.hasDataInReadBuffer)
		@throw [OFTruncatedDataException exception];

	if (length > _toRead)
		length = (size_t)_toRead;

	ret = [_decompressedStream readIntoBuffer: buffer length: length];

	_toRead -= ret;
	_CRC16 = OFCRC16(_CRC16, buffer, ret);

	if (_toRead == 0) {
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
	return ((id <OFReadyForReadingObserving>)_decompressedStream)
	    .fileDescriptorForReading;
}

- (void)of_skip
{
	OFStream *stream;
	uint32_t toRead;

	if (_stream == nil || _skipped)
		return;

	stream = _stream;
	toRead = _toRead;








|







392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
	return ((id <OFReadyForReadingObserving>)_decompressedStream)
	    .fileDescriptorForReading;
}

- (void)of_skip
{
	OFStream *stream;
	unsigned long long toRead;

	if (_stream == nil || _skipped)
		return;

	stream = _stream;
	toRead = _toRead;

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
		toRead =
		    _entry.compressedSize - decompressingStream.bytesConsumed;

		stream = _stream;
	}

	if ([stream isKindOfClass: [OFSeekableStream class]] &&
	    (sizeof(OFFileOffset) > 4 || toRead < INT32_MAX))
		[(OFSeekableStream *)stream seekToOffset: (OFFileOffset)toRead
						  whence: SEEK_CUR];
	else {
		while (toRead > 0) {
			char buffer[512];
			size_t min = toRead;

			if (min > 512)
				min = 512;

			toRead -= [stream readIntoBuffer: buffer length: min];

		}
	}

	_toRead = 0;
	_skipped = true;
}








|
|
|



|




|
>







417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
		toRead =
		    _entry.compressedSize - decompressingStream.bytesConsumed;

		stream = _stream;
	}

	if ([stream isKindOfClass: [OFSeekableStream class]] &&
	    toRead < LLONG_MAX && (long long)toRead == (OFStreamOffset)toRead)
		[(OFSeekableStream *)stream seekToOffset: (OFStreamOffset)toRead
						  whence: OFSeekCurrent];
	else {
		while (toRead > 0) {
			char buffer[512];
			unsigned long long min = toRead;

			if (min > 512)
				min = 512;

			toRead -= [stream readIntoBuffer: buffer
						  length: (size_t)min];
		}
	}

	_toRead = 0;
	_skipped = true;
}

433
434
435
436
437
438
439

440
441
442
443
444
445
446

447
448
449
450
451
452
453
454
455
456
457
	_decompressedStream = nil;

	[super close];
}
@end

@implementation OFLHAArchiveFileWriteStream

- (instancetype)of_initWithStream: (OFSeekableStream *)stream
			    entry: (OFLHAArchiveEntry *)entry
			 encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {

		_entry = [entry mutableCopy];
		_encoding = encoding;

		_headerOffset = [stream seekToOffset: 0 whence: SEEK_CUR];
		[_entry of_writeToStream: stream encoding: _encoding];

		/*
		 * Retain stream last, so that -[close] called by -[dealloc]
		 * doesn't write in case of an error.
		 */
		_stream = [stream retain];







>
|
|
|




>



|







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
	_decompressedStream = nil;

	[super close];
}
@end

@implementation OFLHAArchiveFileWriteStream
- (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: OFSeekCurrent];
		[_entry of_writeToStream: stream encoding: _encoding];

		/*
		 * Retain stream last, so that -[close] called by -[dealloc]
		 * doesn't write in case of an error.
		 */
		_stream = [stream retain];
465
466
467
468
469
470
471





472
473
474
475
476
477
478

- (void)dealloc
{
	if (_stream != nil)
		[self close];

	[_entry release];






	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
	if (_stream == nil)







>
>
>
>
>







489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507

- (void)dealloc
{
	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
{
	if (_stream == nil)
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
{
	return ((id <OFReadyForWritingObserving>)_stream)
	    .fileDescriptorForWriting;
}

- (void)close
{
	OFFileOffset 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];
	[_entry of_writeToStream: _stream encoding: _encoding];
	[_stream seekToOffset: offset whence: SEEK_SET];

	[_stream release];
	_stream = nil;

	[super close];
}
@end







|








|
|

|







542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
{
	return ((id <OFReadyForWritingObserving>)_stream)
	    .fileDescriptorForWriting;
}

- (void)close
{
	OFStreamOffset offset;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	_entry.uncompressedSize = _bytesWritten;
	_entry.compressedSize = _bytesWritten;
	_entry.CRC16 = _CRC16;

	offset = [_stream seekToOffset: 0 whence: OFSeekCurrent];
	[_stream seekToOffset: _headerOffset whence: OFSeekSet];
	[_entry of_writeToStream: _stream encoding: _encoding];
	[_stream seekToOffset: offset whence: OFSeekSet];

	[_stream release];
	_stream = nil;

	[super close];
}
@end

Modified src/OFLHAArchiveEntry+Private.h from [dc492f96c5] to [521b95b2fc].

13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
 * file.
 */

#import "OFLHAArchive.h"

OF_ASSUME_NONNULL_BEGIN

OF_DIRECT_MEMBERS
@interface OFLHAArchiveEntry ()

- (instancetype)of_initWithHeader: (char [_Nonnull 21])header
			   stream: (OFStream *)stream
			 encoding: (OFStringEncoding)encoding
    OF_METHOD_FAMILY(init);
- (void)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding;
@end

OF_ASSUME_NONNULL_END







<

>



|

|



13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
28
29
30
 * file.
 */

#import "OFLHAArchive.h"

OF_ASSUME_NONNULL_BEGIN


@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_DIRECT;
- (void)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding OF_DIRECT;
@end

OF_ASSUME_NONNULL_END

Modified src/OFLHAArchiveEntry.h from [e47bb8e429] to [89ea23a8c1].

10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
 * 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"


OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFData;
@class OFDate;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFNumber;
@class OFString;

/**
 * @class OFLHAArchiveEntry OFLHAArchiveEntry.h ObjFW/OFLHAArchiveEntry.h
 *
 * @brief A class which represents an entry in an LHA archive.
 */
@interface OFLHAArchiveEntry: OFObject <OFCopying, OFMutableCopying>

{
	OFString *_fileName, *_Nullable _directoryName, *_compressionMethod;
	uint32_t _compressedSize, _uncompressedSize;
	OFDate *_date;
	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;
	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;

/**
 * @brief The CRC16 of the file.
 */
@property (readonly, nonatomic) uint16_t CRC16;

/**
 * @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"







>















|
>


|
|




>
|
|
|




<
<
<
<
<





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<





10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50





51
52
53
54
55















56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71





































72
73
74
75








76









77
78
79
80
81
 * 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 "OFArchiveEntry.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFData;
@class OFDate;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFNumber;
@class OFString;

/**
 * @class OFLHAArchiveEntry OFLHAArchiveEntry.h ObjFW/OFLHAArchiveEntry.h
 *
 * @brief A class which represents an entry in an LHA archive.
 */
@interface OFLHAArchiveEntry: OFObject <OFArchiveEntry, OFCopying,
    OFMutableCopying>
{
	OFString *_fileName, *_Nullable _directoryName, *_compressionMethod;
	unsigned long long _compressedSize, _uncompressedSize;
	OFDate *_modificationDate;
	uint8_t _headerLevel;
	uint16_t _CRC16;
	uint8_t _operatingSystemIdentifier;
	OFString *_Nullable _fileComment;
	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 compression method of the entry.
 */
@property (readonly, copy, nonatomic) OFString *compressionMethod;
















/**
 * @brief The LHA level of the file.
 */
@property (readonly, nonatomic) uint8_t headerLevel;

/**
 * @brief The CRC16 of the file.
 */
@property (readonly, nonatomic) uint16_t CRC16;

/**
 * @brief The operating system identifier of the file.
 */
@property (readonly, nonatomic) uint8_t operatingSystemIdentifier;

/**





































 * @brief The LHA extensions of the file.
 */
@property (readonly, copy, nonatomic) OFArray OF_GENERIC(OFData *) *extensions;









- (instancetype)init OF_UNAVAILABLE;









@end

OF_ASSUME_NONNULL_END

#import "OFMutableLHAArchiveEntry.h"

Modified src/OFLHAArchiveEntry.m from [45c1be7d3b] to [b338459827].

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
		     length: extension.count - 1];
}

static void
parsePermissionsExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	uint16_t mode;

	if (extension.count != 3)
		@throw [OFInvalidFormatException exception];

	memcpy(&mode, (char *)extension.items + 1, 2);
	mode = OFFromLittleEndian16(mode);

	[entry->_mode release];
	entry->_mode = nil;


	entry->_mode = [[OFNumber alloc] initWithUnsignedShort: mode];
}

static void
parseGIDUIDExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	uint16_t UID, GID;

	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];
}

static void
parseGroupExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	[entry->_group release];
	entry->_group = nil;

	entry->_group = [[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->_owner = [[OFString alloc]
	    initWithCString: (char *)extension.items + 1
		   encoding: encoding
		     length: extension.count - 1];
}

static void
parseModificationDateExtension(OFLHAArchiveEntry *entry, OFData *extension,







|




|
|

|
|

>
|






|




|
|

|
|

|
|

|
|

>
|
>
|






|
|

|









|
|

|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
		     length: extension.count - 1];
}

static void
parsePermissionsExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	uint16_t POSIXPermissions;

	if (extension.count != 3)
		@throw [OFInvalidFormatException exception];

	memcpy(&POSIXPermissions, (char *)extension.items + 1, 2);
	POSIXPermissions = OFFromLittleEndian16(POSIXPermissions);

	[entry->_POSIXPermissions release];
	entry->_POSIXPermissions = nil;

	entry->_POSIXPermissions =
	    [[OFNumber alloc] initWithUnsignedShort: POSIXPermissions];
}

static void
parseGIDUIDExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	uint16_t ownerAccountID, groupOwnerAccountID;

	if (extension.count != 5)
		@throw [OFInvalidFormatException exception];

	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->_groupOwnerAccountName release];
	entry->_groupOwnerAccountName = nil;

	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->_ownerAccountName release];
	entry->_ownerAccountName = nil;

	entry->_ownerAccountName = [[OFString alloc]
	    initWithCString: (char *)extension.items + 1
		   encoding: encoding
		     length: extension.count - 1];
}

static void
parseModificationDateExtension(OFLHAArchiveEntry *entry, OFData *extension,
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

	*fileName = cString + pos;
	*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
{
	self = [super init];

	@try {
		_fileName = [fileName copy];
		_compressionMethod = @"-lh0-";
		_date = [[OFDate alloc] initWithTimeIntervalSince1970: 0];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







<
<
<
<
<





|




<

|







302
303
304
305
306
307
308





309
310
311
312
313
314
315
316
317
318

319
320
321
322
323
324
325
326
327

	*fileName = cString + pos;
	*fileNameLength = length - pos;
	*directoryName = cString;
	*directoryNameLength = pos;
}






- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_init
{
	self = [super init];

	@try {

		_compressionMethod = @"-lh0-";
		_modificationDate = [[OFDate alloc] init];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
339
340
341
342
343
344
345




346
347

348
349
350

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
		uint32_t date;

		_compressionMethod = [[OFString alloc]
		    initWithCString: header + 2
			   encoding: OFStringEncodingASCII
			     length: 5];





		memcpy(&_compressedSize, header + 7, 4);
		_compressedSize = OFFromLittleEndian32(_compressedSize);


		memcpy(&_uncompressedSize, header + 11, 4);
		_uncompressedSize = OFFromLittleEndian32(_uncompressedSize);


		memcpy(&date, header + 15, 4);
		date = OFFromLittleEndian32(date);

		_headerLevel = header[20];
		_extensions = [[OFMutableArray alloc] init];

		switch (_headerLevel) {
		case 0:
		case 1:;
			void *pool = objc_autoreleasePoolPush();
			uint8_t fileNameLength;
			OFString *tmp;

			_date = [parseMSDOSDate(date) retain];

			fileNameLength = [stream readInt8];
			tmp = [stream readStringWithLength: fileNameLength
						  encoding: encoding];
			tmp = [tmp stringByReplacingOccurrencesOfString: @"\\"
							     withString: @"/"];
			_fileName = [tmp copy];

			_CRC16 = [stream readLittleEndianInt16];

			if (_headerLevel == 1) {
				_operatingSystemIdentifier = [stream readInt8];

				readExtensions(self, stream, encoding, false);
			}

			objc_autoreleasePoolPop(pool);
			break;
		case 2:
			_date = [[OFDate alloc]
			    initWithTimeIntervalSince1970: date];

			_CRC16 = [stream readLittleEndianInt16];
			_operatingSystemIdentifier = [stream readInt8];

			readExtensions(self, stream, encoding, true);








>
>
>
>

|
>


|
>














|



















|







336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
		uint32_t date;

		_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((uint32_t)_compressedSize);

		memcpy(&_uncompressedSize, header + 11, 4);
		_uncompressedSize =
		    OFFromLittleEndian32((uint32_t)_uncompressedSize);

		memcpy(&date, header + 15, 4);
		date = OFFromLittleEndian32(date);

		_headerLevel = header[20];
		_extensions = [[OFMutableArray alloc] init];

		switch (_headerLevel) {
		case 0:
		case 1:;
			void *pool = objc_autoreleasePoolPush();
			uint8_t fileNameLength;
			OFString *tmp;

			_modificationDate = [parseMSDOSDate(date) retain];

			fileNameLength = [stream readInt8];
			tmp = [stream readStringWithLength: fileNameLength
						  encoding: encoding];
			tmp = [tmp stringByReplacingOccurrencesOfString: @"\\"
							     withString: @"/"];
			_fileName = [tmp copy];

			_CRC16 = [stream readLittleEndianInt16];

			if (_headerLevel == 1) {
				_operatingSystemIdentifier = [stream readInt8];

				readExtensions(self, stream, encoding, false);
			}

			objc_autoreleasePoolPop(pool);
			break;
		case 2:
			_modificationDate = [[OFDate alloc]
			    initWithTimeIntervalSince1970: date];

			_CRC16 = [stream readLittleEndianInt16];
			_operatingSystemIdentifier = [stream readInt8];

			readExtensions(self, stream, encoding, true);

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
}

- (void)dealloc
{
	[_compressionMethod release];
	[_fileName release];
	[_directoryName release];
	[_date release];
	[_fileComment release];
	[_mode release];
	[_UID release];
	[_GID release];
	[_owner release];
	[_group release];
	[_extensions release];

	[super dealloc];
}

- (id)copy
{
	return [self retain];
}

- (id)mutableCopy
{
	OFLHAArchiveEntry *copy = [[OFMutableLHAArchiveEntry alloc]
	    initWithFileName: _fileName];

	@try {
		[copy->_compressionMethod release];
		copy->_compressionMethod = nil;

		[copy->_date release];
		copy->_date = nil;

		copy->_directoryName = [_directoryName copy];
		copy->_compressionMethod = [_compressionMethod copy];
		copy->_compressedSize = _compressedSize;
		copy->_uncompressedSize = _uncompressedSize;
		copy->_date = [_date 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->_extensions = [_extensions copy];
	} @catch (id e) {
		[copy release];
		@throw e;
	}

	return copy;







|

|
|
|
|
|



















|
|





|




|
|
|
|
|
<







415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

465
466
467
468
469
470
471
}

- (void)dealloc
{
	[_compressionMethod release];
	[_fileName release];
	[_directoryName release];
	[_modificationDate release];
	[_fileComment release];
	[_POSIXPermissions release];
	[_ownerAccountID release];
	[_groupOwnerAccountID release];
	[_ownerAccountName release];
	[_groupOwnerAccountName release];
	[_extensions release];

	[super dealloc];
}

- (id)copy
{
	return [self retain];
}

- (id)mutableCopy
{
	OFLHAArchiveEntry *copy = [[OFMutableLHAArchiveEntry alloc]
	    initWithFileName: _fileName];

	@try {
		[copy->_compressionMethod release];
		copy->_compressionMethod = nil;

		[copy->_modificationDate release];
		copy->_modificationDate = nil;

		copy->_directoryName = [_directoryName copy];
		copy->_compressionMethod = [_compressionMethod copy];
		copy->_compressedSize = _compressedSize;
		copy->_uncompressedSize = _uncompressedSize;
		copy->_modificationDate = [_modificationDate copy];
		copy->_headerLevel = _headerLevel;
		copy->_CRC16 = _CRC16;
		copy->_operatingSystemIdentifier = _operatingSystemIdentifier;
		copy->_fileComment = [_fileComment copy];
		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;
	}

	return copy;
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
}

- (OFString *)compressionMethod
{
	return _compressionMethod;
}

- (uint32_t)compressedSize
{
	return _compressedSize;
}

- (uint32_t)uncompressedSize
{
	return _uncompressedSize;
}

- (OFDate *)date
{
	return _date;
}

- (uint8_t)headerLevel
{
	return _headerLevel;
}








|




|




|

|







480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
}

- (OFString *)compressionMethod
{
	return _compressionMethod;
}

- (unsigned long long)compressedSize
{
	return _compressedSize;
}

- (unsigned long long)uncompressedSize
{
	return _uncompressedSize;
}

- (OFDate *)modificationDate
{
	return _modificationDate;
}

- (uint8_t)headerLevel
{
	return _headerLevel;
}

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
}

- (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;
}

- (OFArray OF_GENERIC(OFData *) *)extensions
{
	return _extensions;
}








|

|


|

|


|

|


|

|


|

|
<
<
<
<
<







515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544





545
546
547
548
549
550
551
}

- (OFString *)fileComment
{
	return _fileComment;
}

- (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;
}

567
568
569
570
571
572
573
574

575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590

591
592
593
594
595
596
597
	    OFStringEncodingASCII] != 5)
		@throw [OFInvalidArgumentException exception];

	getFileNameAndDirectoryName(self, encoding, &fileName, &fileNameLength,
	    &directoryName, &directoryNameLength);

	if (fileNameLength > UINT16_MAX - 3 ||
	    directoryNameLength > UINT16_MAX - 3)

		@throw [OFOutOfRangeException exception];

	/* Length. Filled in after we're done. */
	[data increaseCountBy: 2];

	[data addItems: [_compressionMethod
			    cStringWithEncoding: OFStringEncodingASCII]
		 count: 5];

	tmp32 = OFToLittleEndian32(_compressedSize);
	[data addItems: &tmp32 count: sizeof(tmp32)];

	tmp32 = OFToLittleEndian32(_uncompressedSize);
	[data addItems: &tmp32 count: sizeof(tmp32)];

	tmp32 = OFToLittleEndian32((uint32_t)_date.timeIntervalSince1970);

	[data addItems: &tmp32 count: sizeof(tmp32)];

	/* Reserved */
	[data increaseCountBy: 1];

	/* Header level */
	[data addItem: "\x02"];







|
>









|


|


|
>







564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
	    OFStringEncodingASCII] != 5)
		@throw [OFInvalidArgumentException exception];

	getFileNameAndDirectoryName(self, encoding, &fileName, &fileNameLength,
	    &directoryName, &directoryNameLength);

	if (fileNameLength > 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((uint32_t)_compressedSize);
	[data addItems: &tmp32 count: sizeof(tmp32)];

	tmp32 = OFToLittleEndian32((uint32_t)_uncompressedSize);
	[data addItems: &tmp32 count: sizeof(tmp32)];

	tmp32 = OFToLittleEndian32(
	    (uint32_t)_modificationDate.timeIntervalSince1970);
	[data addItems: &tmp32 count: sizeof(tmp32)];

	/* Reserved */
	[data increaseCountBy: 1];

	/* Header level */
	[data addItem: "\x02"];
631
632
633
634
635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
650
651
652
653
654
655

656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672

673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
		tmp16 = OFToLittleEndian16((uint16_t)fileCommentLength + 3);
		[data addItems: &tmp16 count: sizeof(tmp16)];
		[data addItem: "\x3F"];
		[data addItems: [_fileComment cStringWithEncoding: encoding]
			 count: fileCommentLength];
	}

	if (_mode != nil) {
		tmp16 = OFToLittleEndian16(5);
		[data addItems: &tmp16 count: sizeof(tmp16)];
		[data addItem: "\x50"];


		tmp16 = OFToLittleEndian16(_mode.unsignedShortValue);
		[data addItems: &tmp16 count: sizeof(tmp16)];
	}

	if (_UID != nil || _GID != nil) {
		if (_UID == nil || _GID == 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)];
	}

	for (OFData *extension in _extensions) {
		size_t extensionLength = extension.count;

		if (extension.itemSize != 1)
			@throw [OFInvalidArgumentException exception];







|




>
|



|
|






|
>


|



|
|
|

|


|


|
>
|


|
|
|

|


|


|
<
<
|
<
<
<
<
|
<
<
<







630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688


689




690



691
692
693
694
695
696
697
		tmp16 = OFToLittleEndian16((uint16_t)fileCommentLength + 3);
		[data addItems: &tmp16 count: sizeof(tmp16)];
		[data addItem: "\x3F"];
		[data addItems: [_fileComment cStringWithEncoding: encoding]
			 count: fileCommentLength];
	}

	if (_POSIXPermissions != nil) {
		tmp16 = OFToLittleEndian16(5);
		[data addItems: &tmp16 count: sizeof(tmp16)];
		[data addItem: "\x50"];

		tmp16 =
		    OFToLittleEndian16(_POSIXPermissions.unsignedShortValue);
		[data addItems: &tmp16 count: sizeof(tmp16)];
	}

	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(
		    _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;

		if (extension.itemSize != 1)
			@throw [OFInvalidArgumentException exception];
731
732
733
734
735
736
737
738
739
740
741
742






743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764

765
766
767
768
769
770
771
772
773

	objc_autoreleasePoolPop(pool);
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFString *mode = (_mode == nil ? nil
	    : [OFString stringWithFormat: @"%ho", _mode.unsignedShortValue]);
	OFString *extensions = [_extensions.description
	    stringByReplacingOccurrencesOfString: @"\n"
				      withString: @"\n\t"];






	OFString *ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tFile name = %@\n"
	    @"\tCompression method = %@\n"
	    @"\tCompressed size = %" @PRIu32 "\n"
	    @"\tUncompressed size = %" @PRIu32 "\n"
	    @"\tDate = %@\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"
	    @"\tExtensions: %@"
	    @">",
	    self.class, self.fileName, _compressionMethod, _compressedSize,
	    _uncompressedSize, _date, _headerLevel, _CRC16,
	    _operatingSystemIdentifier, _fileComment, mode, _UID, _GID, _owner,

	    _group, _modificationDate, extensions];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end







|
<



>
>
>
>
>
>
|



|
|
|




|
|
|
|
|
<



|
|
>
|








724
725
726
727
728
729
730
731

732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756

757
758
759
760
761
762
763
764
765
766
767
768
769
770
771

	objc_autoreleasePoolPop(pool);
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFString *POSIXPermissions = nil;

	OFString *extensions = [_extensions.description
	    stringByReplacingOccurrencesOfString: @"\n"
				      withString: @"\n\t"];
	OFString *ret;

	if (_POSIXPermissions != nil)
		POSIXPermissions = [OFString stringWithFormat: @"%ho",
		    _POSIXPermissions.unsignedShortValue];

	ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tFile name = %@\n"
	    @"\tCompression method = %@\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"
	    @"\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, _modificationDate, _headerLevel, _CRC16,
	    _operatingSystemIdentifier, _fileComment, POSIXPermissions,
	    _ownerAccountID, _groupOwnerAccountID, _ownerAccountName,
	    _groupOwnerAccountName, extensions];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

Modified src/OFLocale.h from [fcc8f077d2] to [ca12b9c93f].

24
25
26
27
28
29
30


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
 * @def OF_LOCALIZED
 *
 * @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


 */
#define OF_LOCALIZED(ID, ...)						 \
	[[OFLocale currentLocale] localizedStringForID: ID		 \
					     fallback: __VA_ARGS__, nil]

@class OFMutableArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);

/**
 * @class OFLocale OFLocale.h ObjFW/OFLocale.h
 *
 * @brief A class for querying the locale and retrieving localized strings.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFLocale: OFObject
{
	OFString *_Nullable _language, *_Nullable _territory;
	OFStringEncoding _encoding;
	OFString *_decimalPoint;
	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, nonatomic) OFStringEncoding encoding;
@property (class, readonly, nullable, nonatomic) OFString *decimalPoint;
#endif

/**
 * @brief The language of the locale for messages.
 *
 * If the language is unknown, it is `nil`.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *language;

/**
 * @brief The territory of the locale for messages.
 *
 * If the territory is unknown, it is `nil`.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *territory;

/**
 * @brief The native 8-bit string encoding of the locale for messages.
 *
 * This is useful to encode strings correctly for passing them to operating
 * system calls.
 *
 * 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.
 */
@property (readonly, nonatomic) OFString *decimalPoint;

/**
 * @brief Returns the current OFLocale.
 *
 * @warning If you don't use @ref OFApplication, this might be `nil`! In this
 *	    case, you need to manually allocate an instance and call
 *	    @ref init once.
 *
 * @return The current OFLocale instance
 */
+ (nullable OFLocale *)currentLocale;

/**
 * @brief Returns the language of the locale.
 *
 * If the language is unknown, `nil` is returned.
 *
 * @return The language of the locale.
 */
+ (nullable OFString *)language;

/**
 * @brief Returns the territory of the locale.
 *
 * If the territory is unknown, `nil` is returned.
 *
 * @return The territory of the locale.
 */
+ (nullable OFString *)territory;

/**
 * @brief Returns the native 8-bit string encoding for the locale.
 *
 * This is useful to encode strings correctly for passing them to operating
 * system calls.
 *
 * If the native 8-bit encoding is unknown, UTF-8 is assumed.
 *
 * @return The native 8-bit string encoding for the locale
 */
+ (OFStringEncoding)encoding;

/**
 * @brief Returns the decimal point of the system's locale.
 *
 * @return The decimal point of the system's locale
 */
+ (nullable OFString *)decimalPoint;

#ifdef OF_HAVE_FILES
/**
 * @brief Adds a directory to scan for language files.
 *
 * @param path The path to the directory to scan for language files
 */
+ (void)addLanguageDirectory: (OFString *)path;
#endif

/**
 * @brief Initializes the current OFLocale.
 *
 * @warning This sets the locale via `setlocale()`!
 *
 * @warning You should never call this yourself, except if you do not use
 *	    @ref OFApplication. In this case, you need to allocate exactly one
 *	    instance of OFLocale, which will become the current locale, and
 *	    call this method.
 */
- (instancetype)init;

#ifdef OF_HAVE_FILES
/**
 * @brief Adds a directory to scan for language files.
 *
 * @param path The path to the directory to scan for language files
 */
- (void)addLanguageDirectory: (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.
 *
 * @note This takes a variadic argument, terminated by `nil`, that consists of
 *	 pairs of variable names and variable values, which will be replaced
 *	 inside the localized string. For example, you can pass
 *	 `@"name", @"foo", nil`, causing `%[name]` to be replaced with `foo` in
 *	 the localized string.
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 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.
 * @return The localized string
 */
- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (id)fallback, ... OF_SENTINEL;

/**
 * @brief Returns the localized string for the specified ID, using the fallback







>
>
















|

|






|
|

|



|



|


|



|












|

|













|



|

|


|

|

|

|


















|



|

|

|
















|

|

|


















|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
 * @def OF_LOCALIZED
 *
 * @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]

@class OFMutableArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);

/**
 * @class OFLocale OFLocale.h ObjFW/OFLocale.h
 *
 * @brief A class for querying the locale and retrieving localized strings.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFLocale: OFObject
{
	OFString *_Nullable _languageCode, *_Nullable _countryCode;
	OFStringEncoding _encoding;
	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 *languageCode;
@property (class, readonly, nullable, nonatomic) OFString *countryCode;
@property (class, readonly, nonatomic) OFStringEncoding encoding;
@property (class, readonly, nullable, nonatomic) OFString *decimalSeparator;
#endif

/**
 * @brief The language code of the locale for messages.
 *
 * If the language is unknown, it is `nil`.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *languageCode;

/**
 * @brief The country code of the locale for messages.
 *
 * If the territory is unknown, it is `nil`.
 */
@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
 * system calls.
 *
 * If the native 8-bit encoding is unknown, UTF-8 is assumed.
 */
@property (readonly, nonatomic) OFStringEncoding encoding;

/**
 * @brief The decimal separator of the locale.
 */
@property (readonly, nonatomic) OFString *decimalSeparator;

/**
 * @brief Returns the current OFLocale.
 *
 * @warning If you don't use @ref OFApplication, this might be `nil`! In this
 *	    case, you need to manually allocate an instance and call
 *	    @ref init once.
 *
 * @return The current OFLocale instance
 */
+ (nullable OFLocale *)currentLocale;

/**
 * @brief Returns the language code of the locale.
 *
 * If the language is unknown, `nil` is returned.
 *
 * @return The language code of the locale.
 */
+ (nullable OFString *)languageCode;

/**
 * @brief Returns the country code of the locale.
 *
 * If the country is unknown, `nil` is returned.
 *
 * @return The country code of the locale.
 */
+ (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
 * system calls.
 *
 * If the native 8-bit encoding is unknown, UTF-8 is assumed.
 *
 * @return The native 8-bit string encoding for the locale
 */
+ (OFStringEncoding)encoding;

/**
 * @brief Returns the decimal point of the system's locale.
 *
 * @return The decimal point of the system's locale
 */
+ (nullable OFString *)decimalSeparator;

#ifdef OF_HAVE_FILES
/**
 * @brief Adds a directory to scan for localizations.
 *
 * @param path The path to the directory to scan for localizations
 */
+ (void)addLocalizationDirectory: (OFString *)path;
#endif

/**
 * @brief Initializes the current OFLocale.
 *
 * @warning This sets the locale via `setlocale()`!
 *
 * @warning You should never call this yourself, except if you do not use
 *	    @ref OFApplication. In this case, you need to allocate exactly one
 *	    instance of OFLocale, which will become the current locale, and
 *	    call this method.
 */
- (instancetype)init;

#ifdef OF_HAVE_FILES
/**
 * @brief Adds a directory to scan for localizations.
 *
 * @param path The path to the directory to scan for localizations 
 */
- (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.
 *
 * @note This takes a variadic argument, terminated by `nil`, that consists of
 *	 pairs of variable names and variable values, which will be replaced
 *	 inside the localized string. For example, you can pass
 *	 `@"name", @"foo", nil`, causing `%[name]` to be replaced with `foo` in
 *	 the localized string.
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 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 localization files.
 * @return The localized string
 */
- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (id)fallback, ... OF_SENTINEL;

/**
 * @brief Returns the localized string for the specified ID, using the fallback
200
201
202
203
204
205
206
207
208
209
210
211


212
213
214
215
216
217
218
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 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.
 * @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


 */
- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (id)fallback
			 arguments: (va_list)arguments;
@end

OF_ASSUME_NONNULL_END







|




>
>







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 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 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

Modified src/OFLocale.m from [ccac2d9a08] to [e8b5569b76].

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

static OFLocale *currentLocale = nil;
static OFDictionary *operatorPrecedences = nil;

#ifndef OF_AMIGAOS
static void
parseLocale(char *locale, OFStringEncoding *encoding,
    OFString **language, OFString **territory)
{
	locale = OFStrDup(locale);

	@try {
		OFStringEncoding enc = OFStringEncodingASCII;
		char *tmp;








|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

static OFLocale *currentLocale = nil;
static OFDictionary *operatorPrecedences = nil;

#ifndef OF_AMIGAOS
static void
parseLocale(char *locale, OFStringEncoding *encoding,
    OFString **languageCode, OFString **countryCode)
{
	locale = OFStrDup(locale);

	@try {
		OFStringEncoding enc = OFStringEncodingASCII;
		char *tmp;

61
62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
					*encoding = OFStringEncodingParseName(
					    [OFString stringWithCString: tmp
							       encoding: enc]);
			} @catch (OFInvalidArgumentException *e) {
			}
		}

		/* Territory */
		if ((tmp = strrchr(locale, '_')) != NULL) {
			*tmp++ = '\0';

			if (territory != NULL)

				*territory = [OFString stringWithCString: tmp
								encoding: enc];
		}

		if (language != NULL)
			*language = [OFString stringWithCString: locale
						       encoding: enc];
	} @finally {
		OFFreeMemory(locale);
	}
}
#endif

static bool







|



|
>
|
|


|
|
|







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
					*encoding = OFStringEncodingParseName(
					    [OFString stringWithCString: tmp
							       encoding: enc]);
			} @catch (OFInvalidArgumentException *e) {
			}
		}

		/* Country code */
		if ((tmp = strrchr(locale, '_')) != NULL) {
			*tmp++ = '\0';

			if (countryCode != NULL)
				*countryCode = [OFString
				    stringWithCString: tmp
					     encoding: enc];
		}

		if (languageCode != NULL)
			*languageCode = [OFString stringWithCString: locale
							   encoding: enc];
	} @finally {
		OFFreeMemory(locale);
	}
}
#endif

static bool
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314

	[string makeImmutable];

	return string;
}

@implementation OFLocale
@synthesize language = _language, territory = _territory, encoding = _encoding;
@synthesize decimalPoint = _decimalPoint;

+ (void)initialize
{
	OFNumber *one, *two, *three, *four;

	if (self != [OFLocale class])
		return;







|
|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

	[string makeImmutable];

	return string;
}

@implementation OFLocale
@synthesize languageCode = _languageCode, countryCode = _countryCode;
@synthesize encoding = _encoding, decimalSeparator = _decimalSeparator;

+ (void)initialize
{
	OFNumber *one, *two, *three, *four;

	if (self != [OFLocale class])
		return;
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
}

+ (OFLocale *)currentLocale
{
	return currentLocale;
}

+ (OFString *)language
{
	return currentLocale.language;
}

+ (OFString *)territory
{
	return currentLocale.territory;
}

+ (OFStringEncoding)encoding
{
	return currentLocale.encoding;
}

+ (OFString *)decimalPoint
{
	return currentLocale.decimalPoint;
}

#ifdef OF_HAVE_FILES
+ (void)addLanguageDirectory: (OFString *)path
{
	[currentLocale addLanguageDirectory: path];
}
#endif

- (instancetype)init
{
	self = [super init];

	@try {
#ifndef OF_AMIGAOS
		char *locale, *messagesLocale = NULL;

		if (currentLocale != nil)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		_encoding = OFStringEncodingUTF8;
		_decimalPoint = @".";
		_localizedStrings = [[OFMutableArray alloc] init];

		if ((locale = setlocale(LC_ALL, "")) != NULL)
			_decimalPoint = [[OFString alloc]
			    initWithCString: localeconv()->decimal_point
				   encoding: _encoding];

# ifdef LC_MESSAGES
		messagesLocale = setlocale(LC_MESSAGES, "");
# endif
		if (messagesLocale == NULL)
			messagesLocale = locale;

		if (messagesLocale != NULL) {
			void *pool = objc_autoreleasePoolPush();

			parseLocale(messagesLocale, &_encoding,
			    &_language, &_territory);

			[_language retain];
			[_territory retain];

			objc_autoreleasePoolPop(pool);
		}
#else
		void *pool = objc_autoreleasePoolPush();
		char buffer[32];
		struct Locale *locale;







|

|


|

|







|

|



|

|
















|



|













|

|
|







337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
}

+ (OFLocale *)currentLocale
{
	return currentLocale;
}

+ (OFString *)languageCode
{
	return currentLocale.languageCode;
}

+ (OFString *)countryCode
{
	return currentLocale.countryCode;
}

+ (OFStringEncoding)encoding
{
	return currentLocale.encoding;
}

+ (OFString *)decimalSeparator
{
	return currentLocale.decimalSeparator;
}

#ifdef OF_HAVE_FILES
+ (void)addLocalizationDirectory: (OFString *)path
{
	[currentLocale addLocalizationDirectory: path];
}
#endif

- (instancetype)init
{
	self = [super init];

	@try {
#ifndef OF_AMIGAOS
		char *locale, *messagesLocale = NULL;

		if (currentLocale != nil)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		_encoding = OFStringEncodingUTF8;
		_decimalSeparator = @".";
		_localizedStrings = [[OFMutableArray alloc] init];

		if ((locale = setlocale(LC_ALL, "")) != NULL)
			_decimalSeparator = [[OFString alloc]
			    initWithCString: localeconv()->decimal_point
				   encoding: _encoding];

# ifdef LC_MESSAGES
		messagesLocale = setlocale(LC_MESSAGES, "");
# endif
		if (messagesLocale == NULL)
			messagesLocale = locale;

		if (messagesLocale != NULL) {
			void *pool = objc_autoreleasePoolPush();

			parseLocale(messagesLocale, &_encoding,
			    &_languageCode, &_countryCode);

			[_languageCode retain];
			[_countryCode retain];

			objc_autoreleasePoolPop(pool);
		}
#else
		void *pool = objc_autoreleasePoolPush();
		char buffer[32];
		struct Locale *locale;
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
		} else
			_encoding = OFStringEncodingISO8859_1;

		/*
		 * Get it via localeconv() instead of from the Locale struct,
		 * to make sure we and printf etc. have the same expectations.
		 */
		_decimalPoint = [[OFString alloc]
		    initWithCString: localeconv()->decimal_point
			   encoding: _encoding];

		_localizedStrings = [[OFMutableArray alloc] init];

		if (GetVar("Language", buffer, sizeof(buffer), 0) > 0)
			_language = [[OFString alloc]
			    initWithCString: buffer
				   encoding: _encoding];

		if ((locale = OpenLocale(NULL)) != NULL) {
			@try {
				uint32_t territory;
				size_t length;

				territory =
				    OFToBigEndian32(locale->loc_CountryCode);

				for (length = 0; length < 4; length++)
					if (((char *)&territory)[length] == 0)
						break;

				_territory = [[OFString alloc]
				    initWithCString: (char *)&territory
					   encoding: _encoding
					     length: length];
			} @finally {
				CloseLocale(locale);
			}
		}








|






|





|


|



|


|
|







436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
		} else
			_encoding = OFStringEncodingISO8859_1;

		/*
		 * Get it via localeconv() instead of from the Locale struct,
		 * to make sure we and printf etc. have the same expectations.
		 */
		_decimalSeparator = [[OFString alloc]
		    initWithCString: localeconv()->decimal_point
			   encoding: _encoding];

		_localizedStrings = [[OFMutableArray alloc] init];

		if (GetVar("Language", buffer, sizeof(buffer), 0) > 0)
			_languageCode = [[OFString alloc]
			    initWithCString: buffer
				   encoding: _encoding];

		if ((locale = OpenLocale(NULL)) != NULL) {
			@try {
				uint32_t countryCode;
				size_t length;

				countryCode =
				    OFToBigEndian32(locale->loc_CountryCode);

				for (length = 0; length < 4; length++)
					if (((char *)&countryCode)[length] == 0)
						break;

				_countryCode = [[OFString alloc]
				    initWithCString: (char *)&countryCode
					   encoding: _encoding
					     length: length];
			} @finally {
				CloseLocale(locale);
			}
		}

481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523

524
525

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
	currentLocale = self;

	return self;
}

- (void)dealloc
{
	[_language release];
	[_territory release];
	[_decimalPoint release];
	[_localizedStrings release];

	[super dealloc];
}

#ifdef OF_HAVE_FILES
- (void)addLanguageDirectory: (OFString *)path
{
	void *pool;
	OFString *mapPath, *language, *territory, *languageFile;
	OFDictionary *map;

	if (_language == nil)
		return;

	pool = objc_autoreleasePoolPush();

	mapPath = [path stringByAppendingPathComponent: @"languages.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) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	languageFile = [path stringByAppendingPathComponent:
	    [languageFile stringByAppendingString: @".json"]];

	[_localizedStrings addObject: [[OFString stringWithContentsOfFile:
	    languageFile] objectByParsingJSON]];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (id)fallback, ...







|
|
|






|


|


|




|








|
|

|
|

|
>
|
|
>

|




|
|


|







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
	currentLocale = self;

	return self;
}

- (void)dealloc
{
	[_languageCode release];
	[_countryCode release];
	[_decimalSeparator release];
	[_localizedStrings release];

	[super dealloc];
}

#ifdef OF_HAVE_FILES
- (void)addLocalizationDirectory: (OFString *)path
{
	void *pool;
	OFString *mapPath, *languageCode, *countryCode, *localizationFile;
	OFDictionary *map;

	if (_languageCode == nil)
		return;

	pool = objc_autoreleasePoolPush();

	mapPath = [path stringByAppendingPathComponent: @"localizations.json"];
	@try {
		map = [[OFString stringWithContentsOfFile: mapPath]
		     objectByParsingJSON];
	} @catch (OFOpenItemFailedException *e) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	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;
	}

	localizationFile = [path stringByAppendingPathComponent:
	    [localizationFile stringByAppendingString: @".json"]];

	[_localizedStrings addObject: [[OFString stringWithContentsOfFile:
	    localizationFile] objectByParsingJSON]];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (id)fallback, ...

Modified src/OFLocking.h from [ae2e49ea45] to [155544ed9e].

26
27
28
29
30
31
32


33
34
35
36
37
38
39



40
41
42
43
44


45
46
47
48
49
/**
 * @brief The name of the lock.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *name;

/**
 * @brief Locks the lock.


 */
- (void)lock;

/**
 * @brief Tries to lock the lock.
 *
 * @return A boolean whether the lock could be locked



 */
- (bool)tryLock;

/**
 * @brief Unlocks the lock.


 */
- (void)unlock;
@end

OF_ASSUME_NONNULL_END







>
>







>
>
>





>
>





26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * @brief The name of the lock.
 */
@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

Modified src/OFMapTable.h from [db5052bd42] to [382835ac81].

227
228
229
230
231
232
233



234
235
236
237
238
239
240

/**
 * @class OFMapTableEnumerator OFMapTable.h ObjFW/OFMapTable.h
 *
 * @brief A class which provides methods to enumerate through an OFMapTable's
 *	  keys or objects.
 */



@interface OFMapTableEnumerator: OFObject
{
	OFMapTable *_mapTable;
	struct OFMapTableBucket *_Nonnull *_Nullable _buckets;
	uint32_t _capacity;
	unsigned long _mutations, *_Nullable _mutationsPtr, _position;
}







>
>
>







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243

/**
 * @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;
	unsigned long _mutations, *_Nullable _mutationsPtr, _position;
}

Modified src/OFMapTable.m from [155910f8b1] to [61602c79fe].

8
9
10
11
12
13
14


15
16
17
18
19
20
21
 * 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 <stdlib.h>
#include <string.h>

#include <assert.h>







>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 * 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.
 */

#define OF_MAP_TABLE_M

#include "config.h"

#include <stdlib.h>
#include <string.h>

#include <assert.h>

Modified src/OFMemoryStream.m from [1697ea3e2b] to [d9d07c7da0].

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
- (instancetype)initWithMemoryAddress: (void *)address
				 size: (size_t)size
			     writable: (bool)writable
{
	self = [super init];

	@try {
		if (size > SSIZE_MAX || (ssize_t)size != (OFFileOffset)size)
			@throw [OFOutOfRangeException exception];

		_address = address;
		_size = size;
		_writable = writable;
	} @catch (id e) {
		[self release];







|







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
- (instancetype)initWithMemoryAddress: (void *)address
				 size: (size_t)size
			     writable: (bool)writable
{
	self = [super init];

	@try {
		if (size > SSIZE_MAX || (ssize_t)size != (OFStreamOffset)size)
			@throw [OFOutOfRangeException exception];

		_address = address;
		_size = size;
		_writable = writable;
	} @catch (id e) {
		[self release];
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
}

- (bool)lowlevelIsAtEndOfStream
{
	return (_position == _size);
}

- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence

{
	OFFileOffset new;

	switch (whence) {
	case SEEK_SET:
		new = offset;
		break;
	case SEEK_CUR:
		new = (OFFileOffset)_position + offset;
		break;
	case SEEK_END:
		new = (OFFileOffset)_size + offset;
		break;
	default:
		@throw [OFInvalidArgumentException exception];
	}

	if (new < 0 || new > (OFFileOffset)_size)
		@throw [OFSeekFailedException exceptionWithStream: self
							   offset: offset
							   whence: whence
							    errNo: EINVAL];

	return (_position = (size_t)new);
}
@end







|
>

|


|


|
|

|
|





|








97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
}

- (bool)lowlevelIsAtEndOfStream
{
	return (_position == _size);
}

- (OFStreamOffset)lowlevelSeekToOffset: (OFStreamOffset)offset
				whence: (OFSeekWhence)whence
{
	OFStreamOffset new;

	switch (whence) {
	case OFSeekSet:
		new = offset;
		break;
	case OFSeekCurrent:
		new = (OFStreamOffset)_position + offset;
		break;
	case OFSeekEnd:
		new = (OFStreamOffset)_size + offset;
		break;
	default:
		@throw [OFInvalidArgumentException exception];
	}

	if (new < 0 || new > (OFStreamOffset)_size)
		@throw [OFSeekFailedException exceptionWithStream: self
							   offset: offset
							   whence: whence
							    errNo: EINVAL];

	return (_position = (size_t)new);
}
@end

Modified src/OFMessagePackExtension.h from [45f0ba641f] to [99c89d24f1].

22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38
39
40
41

/**
 * @class OFMessagePackExtension \
 *	  OFMessagePackExtension.h ObjFW/OFMessagePackExtension.h
 *
 * @brief A class for representing the MessagePack extension type.
 */

@interface OFMessagePackExtension: OFObject <OFMessagePackRepresentation,
    OFCopying>
{
	int8_t _type;
	OFData *_data;
	OF_RESERVE_IVARS(OFMessagePackExtension, 4)
}

/**
 * @brief The MessagePack extension type.
 */
@property (readonly, nonatomic) int8_t type;








>





<







22
23
24
25
26
27
28
29
30
31
32
33
34

35
36
37
38
39
40
41

/**
 * @class OFMessagePackExtension \
 *	  OFMessagePackExtension.h ObjFW/OFMessagePackExtension.h
 *
 * @brief A class for representing the MessagePack extension type.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFMessagePackExtension: OFObject <OFMessagePackRepresentation,
    OFCopying>
{
	int8_t _type;
	OFData *_data;

}

/**
 * @brief The MessagePack extension type.
 */
@property (readonly, nonatomic) int8_t type;

Modified src/OFMessagePackExtension.m from [7009717fd9] to [a3971287d8].

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAdd(&hash, (uint8_t)_type);
	OFHashAddHash(&hash, _data.hash);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{
	return [self retain];
}
@end







|












170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddByte(&hash, (uint8_t)_type);
	OFHashAddHash(&hash, _data.hash);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{
	return [self retain];
}
@end

Modified src/OFMethodSignature.h from [6c7eab90df] to [996cafd8b2].

20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
@class OFMutableData;

/**
 * @class OFMethodSignature OFMethodSignature.h ObjFW/OFMethodSignature.h
 *
 * @brief A class for parsing type encodings and accessing them.
 */

@interface OFMethodSignature: OFObject
{
	char *_types;
	OFMutableData *_typesPointers, *_offsets;
	OF_RESERVE_IVARS(OFMethodSignature, 4)
}

/**
 * @brief The number of arguments of the method.
 */
@property (readonly, nonatomic) size_t numberOfArguments;








>




<







20
21
22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
@class OFMutableData;

/**
 * @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;

}

/**
 * @brief The number of arguments of the method.
 */
@property (readonly, nonatomic) size_t numberOfArguments;

49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
@property (readonly, nonatomic) size_t frameLength;

/**
 * @brief Creates a new OFMethodSignature with the specified ObjC types.
 *
 * @param types The ObjC types of the method
 * @return A new, autoreleased OFMethodSignature

 */
+ (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

 */
- (instancetype)initWithObjCTypes: (const char *)types;

/**
 * @brief Returns the ObjC type for the argument at the specified index.
 *
 * @param index The index of the argument for which to return the ObjC type







>









>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@property (readonly, nonatomic) size_t frameLength;

/**
 * @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.
 *
 * @param index The index of the argument for which to return the ObjC type
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
extern "C" {
#endif
/**
 * @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

 */
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

 */
extern size_t OFAlignmentOfTypeEncoding(const char *type);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







>








>







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
extern "C" {
#endif
/**
 * @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

Modified src/OFMethodSignature.m from [e6d0a405ec] to [9c702fcecb].

142
143
144
145
146
147
148




149
150
151
152
153
154
155
	(*type)++;
	(*length)--;

	return alignment;
}

static size_t




alignmentOfEncoding(const char **type, size_t *length, bool inStruct)
{
	size_t alignment;

	if (*length == 0)
		@throw [OFInvalidFormatException exception];








>
>
>
>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
	(*type)++;
	(*length)--;

	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)
		@throw [OFInvalidFormatException exception];

Added src/OFMutableArchiveEntry.h version [ffb170561c].

















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * 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 <OFArchiveEntry>

/**
 * @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"

Modified src/OFMutableArray.m from [8e711f8f4a] to [ba1c079bde].

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
		return;

	[self removeObjectAtIndex: count - 1];
}

- (void)removeAllObjects
{
	[self removeObjectsInRange: OFRangeMake(0, self.count)];
}

#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (OFArrayReplaceBlock)block
{
	[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
	    bool *stop) {







|







377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
		return;

	[self removeObjectAtIndex: count - 1];
}

- (void)removeAllObjects
{
	[self removeObjectsInRange: OFMakeRange(0, self.count)];
}

#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (OFArrayReplaceBlock)block
{
	[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
	    bool *stop) {

Modified src/OFMutableData.h from [5eba71457e] to [23548715da].

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 */

#import "OFData.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFURL;

/**
 * @class OFMutableData OFMutableData.h ObjFW/OFMutableData.h
 *
 * @brief A class for storing and manipulating arbitrary data in an array.
 */
@interface OFMutableData: OFData







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 */

#import "OFData.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFURI;

/**
 * @class OFMutableData OFMutableData.h ObjFW/OFMutableData.h
 *
 * @brief A class for storing and manipulating arbitrary data in an array.
 */
@interface OFMutableData: OFData
200
201
202
203
204
205
206
207
208
209
210
211
212

/**
 * @brief Removes all items.
 */
- (void)removeAllItems;

/**
 * @brief Converts the mutable URL to an immutable URL.
 */
- (void)makeImmutable;
@end

OF_ASSUME_NONNULL_END







|





200
201
202
203
204
205
206
207
208
209
210
211
212

/**
 * @brief Removes all items.
 */
- (void)removeAllItems;

/**
 * @brief Converts the mutable data to an immutable data.
 */
- (void)makeImmutable;
@end

OF_ASSUME_NONNULL_END

Modified src/OFMutableData.m from [2aa51e2819] to [b4151ce5ed].

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

	memset(_items + _count * _itemSize, '\0', count * _itemSize);
	_count += count;
}

- (void)removeItemAtIndex: (size_t)idx
{
	[self removeItemsInRange: OFRangeMake(idx, 1)];
}

- (void)removeItemsInRange: (OFRange)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _count)
		@throw [OFOutOfRangeException exception];







|







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

	memset(_items + _count * _itemSize, '\0', count * _itemSize);
	_count += count;
}

- (void)removeItemAtIndex: (size_t)idx
{
	[self removeItemsInRange: OFMakeRange(idx, 1)];
}

- (void)removeItemsInRange: (OFRange)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _count)
		@throw [OFOutOfRangeException exception];

Modified src/OFMutableLHAArchiveEntry.h from [4253f691f5] to [5bfb2f1e47].

10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

110
111

112
113
114
115
116
117
118
119
120
 * 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 "OFLHAArchiveEntry.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
{
	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;

/**
 * @brief The CRC16 of the file.
 */
@property (readwrite, nonatomic) uint16_t CRC16;

/**
 * @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 Converts the OFMutableLHAArchiveEntry to an immutable
 *	  OFLHAArchiveEntry.
 */
- (void)makeImmutable;
@end

OF_ASSUME_NONNULL_END







>









|




<
<
<
<
<





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















|

|
<


|
<
<
|
<
<
<
<
|
<
<
<
<
|
<
<

|


|
<
<
|
<
<
<
<
<
|
<
|
>

<
>









10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31





32
33
34
35
36















37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58


59




60




61


62
63
64
65
66


67





68

69
70
71

72
73
74
75
76
77
78
79
80
81
 * 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 "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 <OFMutableArchiveEntry>
{
	OF_RESERVE_IVARS(OFMutableLHAArchiveEntry, 4)
}






/**
 * @brief The compression method of the entry.
 */
@property (readwrite, copy, nonatomic) OFString *compressionMethod;
















/**
 * @brief The LHA level of the file.
 */
@property (readwrite, nonatomic) uint8_t headerLevel;

/**
 * @brief The CRC16 of the file.
 */
@property (readwrite, nonatomic) uint16_t CRC16;

/**
 * @brief The operating system identifier of the file.
 */
@property (readwrite, nonatomic) uint8_t operatingSystemIdentifier;

/**
 * @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

Modified src/OFMutableLHAArchiveEntry.m from [8d9a6a0cb1] to [84adbb8df8].

12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27
28


29


















30
31
32
33
34
35
36
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFMutableLHAArchiveEntry.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;



















- (id)copy
{
	OFMutableLHAArchiveEntry *copy = [self mutableCopy];

	[copy makeImmutable];








>








|
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#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;
@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];

	[copy makeImmutable];

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
- (void)setCompressionMethod: (OFString *)compressionMethod
{
	OFString *old = _compressionMethod;
	_compressionMethod = [compressionMethod copy];
	[old release];
}

- (void)setCompressedSize: (uint32_t)compressedSize
{
	_compressedSize = compressedSize;
}

- (void)setUncompressedSize: (uint32_t)uncompressedSize
{
	_uncompressedSize = uncompressedSize;
}

- (void)setDate: (OFDate *)date
{
	OFDate *old = _date;
	_date = [date retain];
	[old release];
}

- (void)setHeaderLevel: (uint8_t)headerLevel
{
	_headerLevel = headerLevel;
}







|




|




|

|
|







71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
- (void)setCompressionMethod: (OFString *)compressionMethod
{
	OFString *old = _compressionMethod;
	_compressionMethod = [compressionMethod copy];
	[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;
	_modificationDate = [modificationDate retain];
	[old release];
}

- (void)setHeaderLevel: (uint8_t)headerLevel
{
	_headerLevel = headerLevel;
}
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
- (void)setFileComment: (OFString *)fileComment
{
	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];
	[old release];
}

- (void)setExtensions: (OFArray OF_GENERIC(OFData *) *)extensions
{
	OFArray OF_GENERIC(OFData *) *old = _extensions;
	_extensions = [extensions copy];







|

|
|



|

|
|


<
<
<
<
<
<
|
|
<

|
|



|

|
|



|

|
|







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129






130
131

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
- (void)setFileComment: (OFString *)fileComment
{
	OFString *old = _fileComment;
	_fileComment = [fileComment copy];
	[old release];
}

- (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
{
	OFArray OF_GENERIC(OFData *) *old = _extensions;
	_extensions = [extensions copy];

Modified src/OFMutableString.h from [9300a1f2cf] to [f372a7a142].

50
51
52
53
54
55
56


57
58
59
60
61
62
63
64
65


66
67
68
69
70
71
72
73
74


75
76
77
78
79
80
81
82
83
84
85


86
87
88
89
90
91
92
93
94
95
96
97
98



99
100
101
102
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
 */
- (void)appendCharacters: (const OFUnichar *)characters length: (size_t)length;

/**
 * @brief Appends a UTF-8 encoded C string to the OFMutableString.
 *
 * @param UTF8String A UTF-8 encoded C string to append


 */
- (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


 */
- (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


 */
- (void)appendCString: (const char *)cString
	     encoding: (OFStringEncoding)encoding;

/**
 * @brief Appends a C string with the specified encoding and length to the
 *	  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


 */
- (void)appendCString: (const char *)cString
	     encoding: (OFStringEncoding)encoding
	       length: (size_t)cStringLength;

/**
 * @brief Appends a formatted string to the OFMutableString.
 *
 * 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



 */
- (void)appendFormat: (OFConstantString *)format, ...;

/**
 * @brief Appends a formatted string to the OFMutableString.
 *
 * 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
 * @param arguments The arguments used in the format string

 */
- (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;

/**
 * @brief Converts the string to lowercase.







>
>









>
>









>
>











>
>













>
>
>












>



<
<
<
<
<
<
<
<
<
<
<
<







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125












126
127
128
129
130
131
132
 */
- (void)appendCharacters: (const OFUnichar *)characters length: (size_t)length;

/**
 * @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;

/**
 * @brief Appends a C string with the specified encoding and length to the
 *	  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;

/**
 * @brief Appends a formatted string to the OFMutableString.
 *
 * 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.
 *
 * 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
 * @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 Converts the string to uppercase.
 */
- (void)uppercase;

/**
 * @brief Converts the string to lowercase.

Modified src/OFMutableString.m from [a015aada74] to [eb3bef8a08].

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithContentsOfFile: path
			  encoding: encoding];
}
#endif

- (instancetype)initWithContentsOfURL: (OFURL *)URL
{
	return (id)[[OFMutableUTF8String alloc] initWithContentsOfURL: URL];
}

- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (OFStringEncoding)encoding
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithContentsOfURL: URL
			 encoding: encoding];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	return (id)[[OFMutableUTF8String alloc] initWithSerialization: element];
}







|

|


|



|







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithContentsOfFile: path
			  encoding: encoding];
}
#endif

- (instancetype)initWithContentsOfURI: (OFURI *)URI
{
	return (id)[[OFMutableUTF8String alloc] initWithContentsOfURI: URI];
}

- (instancetype)initWithContentsOfURI: (OFURI *)URI
			     encoding: (OFStringEncoding)encoding
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithContentsOfURI: URI
			 encoding: encoding];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	return (id)[[OFMutableUTF8String alloc] initWithSerialization: element];
}
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#endif

- (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];
	objc_autoreleasePoolPop(pool);
}

- (void)appendString: (OFString *)string
{
	[self insertString: string atIndex: self.length];
}







|







285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#endif

- (void)setCharacter: (OFUnichar)character atIndex: (size_t)idx
{
	void *pool = objc_autoreleasePoolPush();
	OFString *string =
	    [OFString stringWithCharacters: &character length: 1];
	[self replaceCharactersInRange: OFMakeRange(idx, 1) withString: string];
	objc_autoreleasePoolPop(pool);
}

- (void)appendString: (OFString *)string
{
	[self insertString: string atIndex: self.length];
}
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
	@try {
		[self appendUTF8String: UTF8String length: UTF8StringLength];
	} @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
			wordStartTableSize: OFUnicodeUppercaseTableSize
		       wordMiddleTableSize: OFUnicodeUppercaseTableSize];







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







368
369
370
371
372
373
374
















375
376
377
378
379
380
381
	@try {
		[self appendUTF8String: UTF8String length: UTF8StringLength];
	} @finally {
		free(UTF8String);
	}
}

















#ifdef OF_HAVE_UNICODE_TABLES
- (void)uppercase
{
	[self of_convertWithWordStartTable: OFUnicodeUppercaseTable
			   wordMiddleTable: OFUnicodeUppercaseTable
			wordStartTableSize: OFUnicodeUppercaseTableSize
		       wordMiddleTableSize: OFUnicodeUppercaseTableSize];
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
{
	convert(self, OFASCIIToUpper, OFASCIIToLower);
}
#endif

- (void)insertString: (OFString *)string atIndex: (size_t)idx
{
	[self replaceCharactersInRange: OFRangeMake(idx, 0) withString: string];
}

- (void)deleteCharactersInRange: (OFRange)range
{
	[self replaceCharactersInRange: range withString: @""];
}

- (void)replaceCharactersInRange: (OFRange)range
		      withString: (OFString *)replacement
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
{
	[self replaceOccurrencesOfString: string
			      withString: replacement
				 options: 0
				   range: OFRangeMake(0, self.length)];
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
			   options: (int)options
			     range: (OFRange)range
{







|



















|







411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
{
	convert(self, OFASCIIToUpper, OFASCIIToLower);
}
#endif

- (void)insertString: (OFString *)string atIndex: (size_t)idx
{
	[self replaceCharactersInRange: OFMakeRange(idx, 0) withString: string];
}

- (void)deleteCharactersInRange: (OFRange)range
{
	[self replaceCharactersInRange: range withString: @""];
}

- (void)replaceCharactersInRange: (OFRange)range
		      withString: (OFString *)replacement
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
{
	[self replaceOccurrencesOfString: string
			      withString: replacement
				 options: 0
				   range: OFMakeRange(0, self.length)];
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
			   options: (int)options
			     range: (OFRange)range
{
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
	characters = self.characters;

	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)
				    withString: replacement];

		range.length -= searchLength;
		range.length += replacementLength;

		i += replacementLength - 1;








|







465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
	characters = self.characters;

	for (size_t i = range.location; i <= range.length - searchLength; i++) {
		if (memcmp(characters + i, searchCharacters,
		    searchLength * sizeof(OFUnichar)) != 0)
			continue;

		[self replaceCharactersInRange: OFMakeRange(i, searchLength)
				    withString: replacement];

		range.length -= searchLength;
		range.length += replacementLength;

		i += replacementLength - 1;

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527

		if (!OFASCIIIsSpace(c))
			break;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: OFRangeMake(0, i)];
}

- (void)deleteTrailingWhitespaces
{
	void *pool;
	const OFUnichar *characters, *p;
	size_t length, d;







|







497
498
499
500
501
502
503
504
505
506
507
508
509
510
511

		if (!OFASCIIIsSpace(c))
			break;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: OFMakeRange(0, i)];
}

- (void)deleteTrailingWhitespaces
{
	void *pool;
	const OFUnichar *characters, *p;
	size_t length, d;
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
			break;

		d++;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: OFRangeMake(length - d, d)];
}

- (void)deleteEnclosingWhitespaces
{
	[self deleteLeadingWhitespaces];
	[self deleteTrailingWhitespaces];
}







|







524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
			break;

		d++;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: OFMakeRange(length - d, d)];
}

- (void)deleteEnclosingWhitespaces
{
	[self deleteLeadingWhitespaces];
	[self deleteTrailingWhitespaces];
}

Modified src/OFMutableTarArchiveEntry.h from [f3b4444a50] to [d5cbafa370].

10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

















94
95
96
97
98
99
100
101
 * 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 "OFTarArchiveEntry.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
{
	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.
 */
@property (readwrite, nonatomic) OFTarArchiveEntryType type;

/**
 * @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;

/**
 * @brief The device major (if the file is a device).
 */
@property (readwrite, nonatomic) unsigned long deviceMinor;


















/**
 * @brief Converts the OFMutableTarArchiveEntry to an immutable
 *	  OFTarArchiveEntry.
 */
- (void)makeImmutable;
@end

OF_ASSUME_NONNULL_END







>









|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<













<
<
<
<
<
<
<
<
<
<










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31






























32
33
34
35
36
37
38
39
40
41
42
43
44










45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
 * 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 "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 <OFMutableArchiveEntry>
{
	OF_RESERVE_IVARS(OFMutableTarArchiveEntry, 4)
}































/**
 * @brief The type of the archive entry.
 *
 * See @ref OFTarArchiveEntryType.
 */
@property (readwrite, nonatomic) OFTarArchiveEntryType type;

/**
 * @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 device major (if the file is a device).
 */
@property (readwrite, nonatomic) unsigned long deviceMajor;

/**
 * @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

Modified src/OFMutableTarArchiveEntry.m from [bfb188d370] to [693af0fda5].

12
13
14
15
16
17
18

19
20

21
22








23
24


















25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44


45
46
47
48




49





50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFMutableTarArchiveEntry.h"

#import "OFString.h"
#import "OFDate.h"


@implementation OFMutableTarArchiveEntry








@dynamic fileName, mode, UID, GID, size, modificationDate, type, targetFileName;
@dynamic owner, group, deviceMajor, deviceMinor;



















- (id)copy
{
	OFMutableTarArchiveEntry *copy = [self mutableCopy];

	[copy makeImmutable];

	return copy;
}

- (void)setFileName: (OFString *)fileName
{
	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)setModificationDate: (OFDate *)modificationDate
{
	OFDate *old = _modificationDate;
	_modificationDate = [modificationDate retain];
	[old release];







>
|
|
>


>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

















|

|
>
>


|

>
>
>
>
|
>
>
>
>
>


|

|


|

|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFMutableTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
#import "OFDate.h"
#import "OFNumber.h"
#import "OFString.h"

@implementation OFMutableTarArchiveEntry
@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];

	[copy makeImmutable];

	return copy;
}

- (void)setFileName: (OFString *)fileName
{
	OFString *old = _fileName;
	_fileName = [fileName copy];
	[old release];
}

- (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;
	_modificationDate = [modificationDate retain];
	[old release];
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
- (void)setTargetFileName: (OFString *)targetFileName
{
	OFString *old = _targetFileName;
	_targetFileName = [targetFileName copy];
	[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)setDeviceMajor: (unsigned long)deviceMajor
{
	_deviceMajor = deviceMajor;
}







|

|
|



|

|
|







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
- (void)setTargetFileName: (OFString *)targetFileName
{
	OFString *old = _targetFileName;
	_targetFileName = [targetFileName copy];
	[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)setDeviceMajor: (unsigned long)deviceMajor
{
	_deviceMajor = deviceMajor;
}

Modified src/OFMutableURI.h from [28e4adf533] to [9400cfdc05].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50



51
52
53
54
55
56
57
58
59



60
61
62
63
64
65
66
67
68
69
70
71
72



73
74
75
76
77
78
79
80
81
82
83
84
85
86



87
88
89
90
91
92
93
94
95
96
97
98
99
100



101
102
103
104
105
106
107
108



109
110
111
112
113
114
115
116
117
118
119
120
121
122



123
124
125
126
127
128
129
130
131
132
133
134
135
136


137
138
139
140
141
142
143
144
145
146
147
148
149
150



151
152
153
154
155
156
157

158
159
160









161
162
163
164
165
166
167
 *
 * 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;







|




|

|
>

|

|



|
<
<
|
<
|
|
<
<

|
<


|




|
>
>
>

|
|


|


|
>
>
>




|




|

|
|
>
>
>


|


|




|

|
|
>
>
>


|


|

|


|

|
|
>
>
>

|
<


|


>
>
>

|



|




|

|
|
>
>
>


|


|


|

|
|
|
|
>
>


|


|




|

|
|
>
>
>


|


|

>
|

|
>
>
>
>
>
>
>
>
>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32


33

34
35


36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
 *
 * 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 cor